如何在Hibernate中的集合之间移动项目

时间:2014-11-19 12:24:51

标签: java hibernate jpa orm hibernate-mapping

我在我的应用程序中映射了两个实体,以便为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);
}

有什么想法吗?

更新1:

我决定执行一点测试并更改transfer()方法的代码,如下所示

    public void transfer(Member member, Team destination) {
        destination.addMember(member);
        members.remove(member);
    }

结果很奇怪:没有错误,但也没有更新表。 Hibernate无法跟踪更新,转移根本就没有发生。

更新2:

我决定尝试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不支持将实例从一个集合转移到另一个集合(实现一个简单的聚合)!

4 个答案:

答案 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的成员。

UPDATE:

尝试将transfer()方法更改为:

public void transfer(Member member, Team destination){
    member.setTeam(destination);
}

在您移动了所有要移动的团队成员之后,您可以提交或刷新事务,然后再转到下一个工作单元。除非您的工作单元定义不明确,否则无需在本地列表中移动项目。转移你的人,然后提交。然后,您已准备好进行下一步处理。如果您需要在事务中间执行此操作,请调用entityManager.flush(),它将执行当前排队的更新,但仍允许您稍后提交或回滚。但是,以这种方式冲洗是一种反模式。如果您需要知道成员在处理过程中属于哪个团队,请询问成员,而不是团队(无论如何都要提高效率)。