项目中遇到多对多关联的情况,刚开始很简单的按一般的设置,在配置文件中指定了第三方表,后来发现这样更新SET的时候不太方便,也看到网上的高手们建议最好把第三方表也实例化成对象,做成两个多对一关联。
根据角色动态显示菜单,因此角色跟菜单是多对多关联,利用中间类RoleMenu做成 1<--->多<--->1
这样配置起来似乎没有什么问题,但是在修改一个角色所拥有的菜单集合时却遇到了问题:
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance
在网上找帮助终于知道通过以下三个步骤可以解决这个问题。
1.把实体类的Set集合方法设置成private :private void setMenus(RoleMenu rm)
2.设置集合的方法成了私有的,所以要增加add方法
3.在实现类里
清理完原来的集合后再根据add方法重新给集合添加对象。
这样更新的问题解决了,删除又出现问题了。要删除一个角色,并需要把这个第三方对象的集合也一同删除,却碰到了因为外键关联不能删除的问题。看看配置文件应该没有什么问题。想不通为什么更新role的集合对象时就可以自动删除,到了真正删除的时候却不能删除set里的对象,于是看hibernate发出的sql语句
这是更新set集合里的对象时,先往第三方表里插入新对象,再把原来的对象删除
Hibernate: insert into t_role_menu (menuid, roleid) values (?, ?)
Hibernate: delete from t_role_menu where id=?
而在删除Role对象时,只发出了一条删除role的语句,删除代码:
public void delRoles(Object[] ids) { Role r = null; for(int i = 0; i < ids.length; i++){ r = getRole((Integer)ids[i]); r.getMenus().clear();//与更新时一样,清空集合对象 } this.getSessionFactory() .getCurrentSession() .createQuery("delete from Role as r where r.id in (:ids)") .setParameterList("ids", ids) .executeUpdate(); }
没有发出删除集合对象的语句
Hibernate: delete from t_role where id in (?) 严重: Servlet.service() for servlet action threw exception com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails
于是把最后一条语句注释,留下清空集合的语句,看看会如何,当然成功的删除了集合里的对象,然后到MySQL命令行里删除这个role对象的记录,可以删除。看来是必须把这个role里的集合先删除了才能删除role对象,但是hibernate不先发送删除集合的命令, 难道要分成两个事务来完成删除一个role对象的动作吗?应该让hibernate先发出删除集合的sql才对。这个时候想到了session的清理缓存:
public void delRoles(Object[] ids) { Role r = null; for(int i = 0; i < ids.length; i++){ r = getRole((Integer)ids[i]); r.getMenus().clear(); //加上这个,可以在删除1之前先把多删除,避免出现外键关联约束 this.getSessionFactory().getCurrentSession().flush(); } this.getSessionFactory() .getCurrentSession() .createQuery("delete from Role as r where r.id in (:ids)") .setParameterList("ids", ids) .executeUpdate(); }
终于,问题解决了,可以更新删除了