A collection with cascade="all-delete-orphan" was no longer referenced by the

 项目中遇到多对多关联的情况,刚开始很简单的按一般的设置,在配置文件中指定了第三方表,后来发现这样更新SET的时候不太方便,也看到网上的高手们建议最好把第三方表也实例化成对象,做成两个多对一关联。

中间表
<class name="RoleMenu" table="t_role_menu" >
<many-to-one name="menuid" cascade="save-update" not-found="ignore"/>
<many-to-one name="roleid" cascade="save-update" not-found="ignore"/>
</class>
角色
<class name="Role" table="t_role">
<set name="menus" cascade="all-delete-orphan" inverse="true" batch-size="20" lazy="true">
    <key column="roleid"/>
    <one-to-many class="RoleMenu" not-found="ignore"/>
</set>
</class>

 

菜单
<class name="Menu" table="t_menu" batch-size="30">
<set name="roles" cascade="all-delete-orphan" inverse="true" lazy="true" batch-size="20">
    <key column="menuid"/>
    <one-to-many class="RoleMenu" not-found="ignore"/>
</set>
</class>

 

 根据角色动态显示菜单,因此角色跟菜单是多对多关联,利用中间类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方法

public void addRoleMenu(RoleMenu roleMenu){
    roleMenu.setRoleid(this);
    getMenus().add(roleMenu);
}

3.在实现类里

role.getMenus().clear(); //清除集合里的内容,hibernate会并根据cascade="all-delete-orphan"把关联的第三方记录删除

清理完原来的集合后再根据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();
	}

终于,问题解决了,可以更新删除了

先发出了删除集合对象的语句,再发出删除role对象的语句
Hibernate: delete from t_role_menu where id=?
Hibernate: delete from t_role where id in (?)

你可能感兴趣的:(sql,Hibernate,mysql,servlet,jdbc)