我在我的应用程序中映射了两个实体,以便为Team
及其Members
建模。 Team
可以包含多个Members
,而Member
可以包含不超过一个Team
。处理这个概念一切都很好。当我尝试将Member
从一个现有的Team
移动到另一个transfer()
时出现问题。
下面以简化形式介绍实体。最后一种方法Member
是应该从Team
中删除某个@Entity
public class Member extends Person {
@ManyToOne
private Team team;
protected Member() {
super();
}
public Member(Team team, String name) {
super(name);
this.team = team;
}
// Trivial getters and setters...
public Team getTeam() {
return team;
}
protected void setTeam(Team team) {
this.team = team;
}
}
@Entity
public class Team {
@Id
private long id;
private String name;
@OneToMany(mappedBy="team", cascade=CascadeType.ALL)
private List<Member> members = new ArrayList<Member>();
protected Team() {
}
public Team(String name) {
this.name = name;
}
// trivial getters and setters...
public Member addMember(String name) {
Member member = new Member(this, name);
members.add(member);
return member;
}
protected void addMember(Member member) {
members.add(member);
member.setTeam(this);
}
public void removeMember(Member member) {
members.remove(member);
}
public Member memberByName(String memberName) {
for(Member member : members)
if(member.getName().equals(memberName))
return member;
return null;
}
public Collection<Members> getMembers() {
return Collections.unmodifiableCollection(members);
}
public void transfer(Member member, Team destination) {
members.remove(member);
destination.addMember(member);
}
}
并将其发送到另一个Team teamA = teamRepository.teamById(idTeamA);
Team teamB = teamRepository.teamById(idTeamB);
Team teamC = teamRepository.teamById(idTeamC);
Member zaki = teamA.memberByName("Zaki");
Member denise = teamA.memberByName("Denise");
EntityTransaction t = teamRepository.transactionBegin();
teamA.transfer(zaki, teamB);
teamA.transferir(denise, teamC);
t.commit();
的方法。
commit()
我有这个单元测试代码,用于验证传输服务
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: application.domain.Member
我在transfer()
行
public void transfer(Member member, Team destination) {
member.setTeam(this);
}
有什么想法吗?
我决定执行一点测试并更改transfer()
方法的代码,如下所示
public void transfer(Member member, Team destination) {
destination.addMember(member);
members.remove(member);
}
结果很奇怪:没有错误,但也没有更新表。 Hibernate无法跟踪更新,转移根本就没有发生。
我决定尝试Steve K的建议,并将addMember()
方法更改为以下内容:
removeMember()
查看Team
和 protected void addMember(Member member) {
members.add(member);
member.setTeam(this);
}
public void removeMember(Member member) {
members.remove(member);
}
方法(下方),我们发现Team
也已开始更新。
Team
因此,该成员被添加到目标集合,其团队被设置为目标EntityTransaction t = teamRepository.transactionBegin();
teamA.transfer(zaki, teamB);
teamA.getEntityManager().refresh(teamA); // I get an exception here!!!
t.commit();
,然后该成员将从当前集合中删除(当前refresh()
)。
测试用例已更改为
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: domain.Member
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
// ... many calls
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: domain.Member
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
// ... many calls
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1335)
... 28 more
在{{1}}行中,我有以下例外:
{{1}}
毕竟,Hibernate不支持将实例从一个集合转移到另一个集合(实现一个简单的聚合)!
答案 0 :(得分:3)
您的映射和数据访问逻辑很好。
您需要在代码的开头移动事务边界:
EntityTransaction t = teamRepository.transactionBegin();
Team teamA = teamRepository.teamById(idTeamA);
...
teamA.transferir(denise, teamC);
t.commit();
但你得到的例外并非来自你向我们展示的代码:
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: application.domain.Member
可能有一个待处理的持久操作排队,只有translated during flush时间(在您的情况下为提交时间)。所以你需要activate the SQL logs被执行并尝试找出分离的实体。
答案 1 :(得分:1)
将Member
从一个列表移动到另一个列表不会更改team
值。更好的想法是在team
实体中直接将Member
从A更改为B.
答案 2 :(得分:0)
有一些事情尚不清楚。第一个是你如何在这个测试中处理hibernate会话?整个测试都是活跃的吗?第二,teamRepository.teamById(idTeamA)
是否也会创建成员?因为如果未创建Member zaki = teamA.memberByName("Zaki")
,null
将返回Zaki
。
我的设置会是这样的:
@Before
方法中,创建初始数据(teamA(与Zaki和Denise合作),teamB和teamC)在测试方法中,开始事务并通过id
获取这些实体 teamRepository.transactionBegin();
Team teamA = teamRepository.teamById(idTeamA); // does this create a team, or returns an existing one? here, it should only return existing team
... // get all teams
Member zaki = teamA.memberByName("Zaki");
... // get all members
转会员
teamA.transfer(zaki, teamB);
teamA.transfer(denise, teamC);
提交交易
t.commit();
答案 3 :(得分:0)
transfer()
方法完全是多余的。您需要做的就是调用member.setTeam(destination)
,Hibernate将相应地更新集合。
您的更新代码:
member.setTeam(this);
什么都不做。它应该是
member.setTeam(destination);
您没有看到任何更新,因为您没有更改该值。该成员已经是this
Team
的成员。
尝试将transfer()方法更改为:
public void transfer(Member member, Team destination){
member.setTeam(destination);
}
在您移动了所有要移动的团队成员之后,您可以提交或刷新事务,然后再转到下一个工作单元。除非您的工作单元定义不明确,否则无需在本地列表中移动项目。转移你的人,然后提交。然后,您已准备好进行下一步处理。如果您需要在事务中间执行此操作,请调用entityManager.flush()
,它将执行当前排队的更新,但仍允许您稍后提交或回滚。但是,以这种方式冲洗是一种反模式。如果您需要知道成员在处理过程中属于哪个团队,请询问成员,而不是团队(无论如何都要提高效率)。