I have two entities mapped in my application in order to model a Team
and its Members
. A Team
can have many Members
and a Member
can belong to no more than one Team
. Everything is fine about handling this concepts. The problem comes when I try to move a Member
from one existing Team
to another.
The entities are presented below, in simplified form. The very last method, transfer()
, is the one that should perform the removal of a certain Member
from its Team
and send it to another one.
@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);
}
}
I have this unit test code that is intended to validate the transfer service
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();
I have the following exception in the commit()
line
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: application.domain.Member
Any ideas?
UPDATE 1:
I decided to perform a little test and changed the code of the transfer()
method as follows
public void transfer(Member member, Team destination) {
member.setTeam(this);
}
The result was curious: no error, but also no update on the tables. Hibernate couldn't track the update and the transfer simply didn't happen.
UPDATE 2:
I decided to give it a try on the suggestion from Steve K and changed the transfer()
method to the following:
public void transfer(Member member, Team destination) {
destination.addMember(member);
members.remove(member);
}
Looking the addMember()
and removeMember()
methods (below) we see that the Team
is begin updated too.
protected void addMember(Member member) {
members.add(member);
member.setTeam(this);
}
public void removeMember(Member member) {
members.remove(member);
}
So, the member is being added to the destination collection, its Team is being set to the destination Team
and then the member is being removed from the current collection (current Team
).
The test case was changed to
EntityTransaction t = teamRepository.transactionBegin();
teamA.transfer(zaki, teamB);
teamA.getEntityManager().refresh(teamA); // I get an exception here!!!
t.commit();
In the refresh()
line I have the following exception:
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
It seems, after all, that transfering instances from one collection to another (that are implementing a simple aggregation) is not supported in Hibernate!