hibernatemany-to-manyhibernate-annotationscascading-deleteshibernate-cascade

Hibernate deletes record from many-to-many association table after updating


In my teamwork oriented app I have many-to-many relationship between User and Team, so hibernate creates association table. Problem is, that after updating the User which has Team, hibernate deletes corresponding association record from USER_TEAM table.

User entity:

@Entity
@Table(name="USERS")
public class User extends SelectItem {

@Id
@Column(name="EMAIL")
private String email;

@Column(name="PASSWORD")
private String password;

@Column(name="NAME")
private String name;

@Column(name="GROUPNAME")
private String group;

@ManyToMany(
    targetEntity=Team.class
)
@ForeignKey(name="FK_TEAM_TO_USER", inverseName = "FK_USER_TO_TEAM")
@JoinTable(
    name="USER_TEAM",
    joinColumns=@JoinColumn(name="EMAIL"),
    inverseJoinColumns=@JoinColumn(name="TEAMNAME")
)
@LazyCollection(LazyCollectionOption.FALSE)
private List<Team> teamList;

@OneToMany(
        fetch = FetchType.EAGER,
        mappedBy="user")
private List<Invitation> invitationList;

//getters setters

Team entity:

@Entity
@Table(name="TEAM")
public class Team extends SelectItem {

@Id
@Column(name="TEAMNAME")
private String name;

@ManyToOne
@ForeignKey(name="FK_TEAM_TO_TEAMLEADER")
@JoinColumn(name="TEAMLEADER")
private User teamLeader;

@ManyToMany(
    mappedBy = "teamList",
    targetEntity = User.class
)
@LazyCollection(LazyCollectionOption.FALSE)
private List<User> memberList;

//getters setters

Here is the log:

INFO: 20.11.2012 22:50:00,170 DEBUG org.hibernate.transaction.JDBCTransaction.begin: begin
INFO: 20.11.2012 22:50:00,175 DEBUG org.hibernate.transaction.JDBCTransaction.begin: current autocommit status: true
INFO: 20.11.2012 22:50:00,175 DEBUG org.hibernate.transaction.JDBCTransaction.begin: disabling autocommit
INFO: 20.11.2012 22:50:00,175 DEBUG hibernate.jdbc.util.SQLStatementLogger.logStatement: select user_.EMAIL, user_.GROUPNAME as GROUPNAME0_, user_.NAME as NAME0_, user_.PASSWORD as PASSWORD0_ from USERS user_ where user_.EMAIL=?
INFO: Hibernate: select user_.EMAIL, user_.GROUPNAME as GROUPNAME0_, user_.NAME as NAME0_, user_.PASSWORD as PASSWORD0_ from USERS user_ where user_.EMAIL=?
INFO: 20.11.2012 22:50:00,176 TRACE type.descriptor.sql.BasicBinder.bind: binding parameter [1] as [VARCHAR] - a@b.com
INFO: 20.11.2012 22:50:00,177 TRACE type.descriptor.sql.BasicExtractor.extract: found [users] as column [GROUPNAME0_]
INFO: 20.11.2012 22:50:00,178 TRACE type.descriptor.sql.BasicExtractor.extract: found [default] as column [NAME0_]
INFO: 20.11.2012 22:50:00,178 TRACE type.descriptor.sql.BasicExtractor.extract: found [default] as column [PASSWORD0_]
INFO: 20.11.2012 22:50:00,251 DEBUG org.hibernate.transaction.JDBCTransaction.commit: commit
INFO: 20.11.2012 22:50:00,252 DEBUG hibernate.jdbc.util.SQLStatementLogger.logStatement: update USERS set GROUPNAME=?, NAME=?, PASSWORD=? where EMAIL=?
INFO: Hibernate: update USERS set GROUPNAME=?, NAME=?, PASSWORD=? where EMAIL=?
INFO: 20.11.2012 22:50:00,260 TRACE type.descriptor.sql.BasicBinder.bind: binding parameter [1] as [VARCHAR] - users
INFO: 20.11.2012 22:50:00,261 TRACE type.descriptor.sql.BasicBinder.bind: binding parameter [2] as [VARCHAR] - andy
INFO: 20.11.2012 22:50:00,261 TRACE type.descriptor.sql.BasicBinder.bind: binding parameter [3] as [VARCHAR] - break
INFO: 20.11.2012 22:50:00,262 TRACE type.descriptor.sql.BasicBinder.bind: binding parameter [4] as [VARCHAR] - a@b.com
INFO: 20.11.2012 22:50:00,264 DEBUG hibernate.jdbc.util.SQLStatementLogger.logStatement: delete from USER_TEAM where EMAIL=?
INFO: Hibernate: delete from USER_TEAM where EMAIL=?
INFO: 20.11.2012 22:50:00,271 TRACE type.descriptor.sql.BasicBinder.bind: binding parameter [1] as [VARCHAR] - a@b.com

INFO: 20.11.2012 22:50:00,274 DEBUG
org.hibernate.transaction.JDBCTransaction.toggleAutoCommit: re-enabling autocommit INFO: 20.11.2012 22:50:00,274 DEBUG org.hibernate.transaction.JDBCTransaction.commit: committed JDBC Connection

The update operation is provided by spring's hibernateTemplate in this simple method in UserDAO:

public void saveUser(User user){
    hibernateTemplate.saveOrUpdate(user);
}    

(I know that hibernateTemplate shouldn't be used, but I think that's not the point in this problem)

And DAO method is simply called in transaction by spring service bean UserServiceImpl:

@Transactional(readOnly=false)
public void saveUser(User user){
    userDao.saveUser(user);
}


As you can see, I don't have any cascade annotations and in update I don't change PK of User (EMAIL) of course, so I don't understand this behaviour. I'm using spring 3.1.0.RELEASE and hibernate 3.6.10.Final.

Thanks for any suggestions or explanations.


Solution

  • You don't need any cascade for this to happen. The contents of the join table for a given user is determined by the contents of the teamList collection of this user. So if you call saveOrUpdate() with a User that has an empty team list, Hibernate will remove the contents of the join table for this user.