I have a many to may relationship CohortGroup and Employee. Any time I insert an Employee into the CohortGroup hibernate deletes the group from the resolution table and ins
I have inserts acting the way I expect them to now. Thanks to Pascal and z5h, I have learned a lot. I believe I have the hashCode and equals implemented correctly. This never solved the problem for me though. Instead I have implemented an Intermediate Entity.
For what it is worth below are the mapping in my Employee, CohortGroup, and now CohortGroupMemeber classes.
Employee:
@OneToMany(mappedBy="member")
public List<CohortGroupMember> getMemberGroups(){
return memberGroups;
}
public void setMemberGroups(List<CohortGroupMember> grps){
memberGroups = grps;
}
CohortGroupMember
@ManyToOne
@JoinColumn(name="USERID")
public Employee getMember(){
return emp;
}
public void setMember(Employee e){
emp = e;
}
@ManyToOne
@JoinColumn(name="COHORT_GROUPID")
public CohortGroup getGroup(){
return group;
}
public void setGroup(CohortGroup cg){
group = cg;
}
CohortGroup
@OneToMany(mappedBy="group")
public List<CohortGroupMember> getMembers(){
return members;
}
public void setMembers(List<CohortGroupMember> emps){
members = emps;
}
The book I followed for this is Java Persistence with Hibernate chapter 7.2.3
I highly suspect that you're not overriding equals
and hashCode
properly. Wrongly overriding them can lead to the kind of behavior you're experiencing (as the hash key is used as keys in maps). Double check what you did with equals
and hashCode
.
Using your annotated entities with good equals
and hashCode
, this code (logically equivalent):
Session session = HibernateUtil.beginTransaction();
Employee emp = (Employee) session.load(Employee.class, 1L);
CohortGroup group = (CohortGroup) session.load(CohortGroup.class, 1L);
group.getMembers().add(emp);
emp.getMemberGroup().add(group); // set the other side too!!
session.saveOrUpdate(group);
HibernateUtil.commitTransaction();
produces the following output on my machine:
08:10:32.426 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - processing flush-time cascades 08:10:32.431 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - dirty checking collections 08:10:32.432 [main] DEBUG org.hibernate.engine.CollectionEntry - Collection dirty: [com.stackoverflow.q2649145.CohortGroup.members#1] 08:10:32.432 [main] DEBUG org.hibernate.engine.CollectionEntry - Collection dirty: [com.stackoverflow.q2649145.Employee.memberGroup#1] 08:10:32.443 [main] DEBUG org.hibernate.engine.Collections - Collection found: [com.stackoverflow.q2649145.CohortGroup.members#1], was: [com.stackoverflow.q2649145.CohortGroup.members#1] (initialized) 08:10:32.448 [main] DEBUG org.hibernate.engine.Collections - Collection found: [com.stackoverflow.q2649145.Employee.memberGroup#1], was: [com.stackoverflow.q2649145.Employee.memberGroup#1] (uninitialized) 08:10:32.460 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 0 insertions, 0 updates, 0 deletions to 2 objects 08:10:32.461 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 0 (re)creations, 2 updates, 0 removals to 2 collections 08:10:32.463 [main] DEBUG org.hibernate.pretty.Printer - listing entities: 08:10:32.473 [main] DEBUG org.hibernate.pretty.Printer - com.stackoverflow.q2649145.CohortGroup{id=1, members=[com.stackoverflow.q2649145.Employee#1]} 08:10:32.474 [main] DEBUG org.hibernate.pretty.Printer - com.stackoverflow.q2649145.Employee{id=1, memberGroup=} 08:10:32.474 [main] DEBUG o.h.p.c.AbstractCollectionPersister - Inserting collection: [com.stackoverflow.q2649145.CohortGroup.members#1] 08:10:32.480 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 08:10:32.491 [main] DEBUG org.hibernate.SQL - insert into MYSITE_RES_COHORT_GROUP_STAFF (COHORT_GROUPID, USERID) values (?, ?) Hibernate: insert into MYSITE_RES_COHORT_GROUP_STAFF (COHORT_GROUPID, USERID) values (?, ?) 08:10:32.496 [main] TRACE org.hibernate.type.LongType - binding '1' to parameter: 1 08:10:32.497 [main] TRACE org.hibernate.type.LongType - binding '1' to parameter: 2 08:10:32.499 [main] DEBUG o.h.p.c.AbstractCollectionPersister - done inserting collection: 1 rows inserted
No delete before the insert!
By the way, note that you should set the link on both sides when working with bi-directional associations, like I did on the group and on the employee.
Or add defensive link management methods on your classes, for example on CohortGroup
:
public void addToMembers(Employee emp) {
this.getMembers().add(emp);
emp.getMemberGroup().add(this);
}
public void removeFromMembers(Employee emp) {
this.getMembers().remove(emp);
emp.getMemberGroup().remove(this);
}
I have same issue. I changed from List to Set. It work for me.
@ManyToMany(cascade = { PERSIST, MERGE, REFRESH })
@JoinTable(name="MYSITE_RES_COHORT_GROUP_STAFF",
joinColumns={@JoinColumn(name="COHORT_GROUPID")},
inverseJoinColumns={@JoinColumn(name="USERID")})
public Set<Employee> getMembers(){
return members;
}
@ManyToMany(mappedBy="members",cascade = { PERSIST, MERGE, REFRESH } )
public Set<CohortGroup> getMemberGroups(){
return memberGroups;
}
As others have suggested, it's probably a problem with hashcode
or equals
.
Specifically: Hibernate proxies cause problems with instaceof
which you are using in your equals
method. That spells bad news.
Check this out: http://community.jboss.org/wiki/ProxyVisitorPattern
You have to define hashCode()
and equals()
on your CohortGroup
and Employee
entities. This can be done automatically by your IDE and it can either be on the primary key (sometimes not a good idea) or on a business key (preferable).
Read this article.
I had this same problem and with some trial and error found that the deletes did not occur if I used a Set instead of a List as my collection. Irritating, given that I'm using JSF and the UI components will only iterate over Lists. But there it is.