您的位置:首页 > 数据库

hibernate 级联删除策略(注解版、数据库版)

2012-12-20 20:06 357 查看
数据库中的表一般都是相互关联的,它们通过foreign key产生关系。

定义foreign key约束时可以指定三种引用行为:delete cascade、delete set null、delete no action,默认是delete on action。它们的含义是:

1、delete cascade : 删除主表的同时也删除子表有关的记录。这个行为适合主从表关系较为紧密的情况,比如菜单和子菜单。当主表的记录不存在时,从表的数据已经没有意义,存在也是多余,所以当删除主表时,从表相关记录也一同删除。
2、delete set null : 删除主表时将子表外键设置为NULL。这个行为适合主从表关系不是相当密切的情况,比如角色和用户,一个角色可以对应多个用户(一个用户也可以有多重角色),不能因为删除了某个角色,而把属于这个角色的用户都删除了,因为这些用户可能还和其他表有很大关系。
3、delete no action: 不做任何操作。这个情况下,如果主表的某个记录已经被引用,删除这条记录会失败,主要提醒用户注意数据完整性。

以上3种情况在hibernate下如果实现呢?下面以多对一关联举例。

假设新闻与用户存在多对一关联

1、删除用户的同时也删除他发表的新闻

News映射文件:

<many-to-one name="editer" class="Account">

<column name="editer_id"></column>

</many-to-one>

Account映射文件:

<set name="news" lazy="true" cascade="delete" inverse="true">

<!-- 主键对应的关联表外键 -->

<key column=" editer_id"></key>

<!-- 关联表 -->

<one-to-many class="News"/>

</set>

2、删除用户时不删除他发表的新闻

其实新闻表除了保存用户ID外,我建议还保存用户的名字,多了一点数据冗余,但是在查询新闻时不用外连接用户表,而且在删除新闻时,虽然外键置NULL,但用户名还在,我们还能知道这新闻是谁发布的。

<set name="news" lazy="true" cascade="save-update" inverse="false">

<!-- 主键对应的关联表外键 -->

<key column=" editer_id"></key>

<!-- 关联表 -->

<one-to-many class="News"/>

</set>

注意以上的inverser=false

3、删除用户时不执行额外操作

只要在<set>中设置 cascade="none"

注意

以上情况只是一对多关系,对于多于多等关系还是有些区别的,比如我通过中间表映射的多对多关系,在没有设置cascade的情况下,删除记录时,也会主动删除中间表中的记录,如:

menu映射文件

<set name="roles" lazy="true" table="MenuRole" >

<!-- 主键对应的中间表的外键 -->

<key column="menu_id" />

<many-to-many class="Role" column="role_id"/>

</set>
删除菜单时,会主动从中间表menurole删除记录

很多人对持久层概念搞不清JPA、Hibernate、EJB3.0的关系,这里做一下简单的说明:JPA是一个持久层设计接口,EJB3.0和Hibernate是具体的实现类,EJB3.0和Hibernate的功能近似相等的(Hibernate没有Session Bean,Spring MVC3的SessionAttribute跟Session Bean近似)。

理论是使用JPA接口可以无缝切换持久层实现,但是仅仅是理论上!!!

JPA是在Hibernate成熟并大行其道的时候才推出的,基本上是借鉴Hibernate的优点,做了一个统一的标准而已,JPA1.0没有一对多的级联删除配置,也许JPA2.0里才有吧(这里没做过调研)

@OneToMany(mappedBy = "commentTeam")

@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,org.hibernate.annotations.CascadeType.DELETE_ORPHAN})

private Set<CommentTeamMember> commentTeamMembers;

这里为了说明,只贴出一对多的关键代码,其它无关的注解已忽略掉,以免造成干扰。

这里重点说明一下四个常用的注解配置的区别:

CascadeType.SAVE_UPDATE

CascadeType.ALL

CascadeType.DELETE

CascadeType.DELETE_ORPHAN

之所有之列出这四个,是因为我不想跟书本上把所有的概念都罗列出来。基本上开发时其中的3个都以及足够用了,下面我结合代码演示一下他们之间的区别,以及使用的时候注意的地方。

CascadeType.SAVE_UPDATE:Hibernate专有的,JPA并不支持,作用是级联保存、级联更新(注:JPA很恶心,要么你配置

CascadeType.ALL,要么你配CascadeType.SAVE+CasadeType.Merge。八卦一句:专家虽牛,多年不写代码,定的标准让编码麻烦呀!)

CascadeType.ALL:级联保存、修改、删除、同步,一般很少用,看看控制台的一长串SQL就知道性能低下,你没改的关联表也给你发update语句,我从来没用过这个属性。

CascadeType.DELETE:当调用session.delete(A)的时候,级联删除关联的对象。(注:先调用A.setB(null),再调用session.delete(A),这样是级联删不掉B的。

CascadeType.DELETE_ORPHAN:一对多级联删除。

下面重点来说说这个CascadeType.DELETE_ORPHAN:

看过API、开发指南,级联删除就一个经典的

@OneToMany(mappedBy = "commentTeam")

@Cascade({CascadeType.SAVE_UPDATE,CascadeType.DELETE_ORPHAN})

private Set<CommentTeamMember> commentTeamMembers;

mappedBy不可少,映射A->B一对多的另一边控制反转(谁控谁的问题),新版的Hibernate3.4中配置更简单,变一句了,更简洁吧?

@OneToMany(mappedBy = "commentTeam",orphanRemoval=true) 

private Set<CommentTeamMember> commentTeamMembers;

以上两种配置方式是等价的,下面是在实际开发中的使用了,有些时候代码方面不注意,会误以为明明配置正确了,但为什么不起作用呢? 下面例举一下代码,请看Action代码(实际上我在Spring Controller里,N年不写DAO了,Service很少用,Manager一边去!)

CommentTeam commentTeam=this.getHibernateTemplate.get(CommentTeam.class,id);

commentTeam.setCommentTeamMember(null);//想级联删除子表数据

this.getHibernateTemplate.saveOrUpdate(commentTeam);

这样级联删除却没有发生?为什么呢?

再来一个例子

CommentTeam commentTeam=this.getHibernateTemplate.get(CommentTeam.class,id);

Set<CommentTeamMember> commentTeamMembers=new HashSet<CommentTeamMember>();

commentTeam.setCommentTeamMember(commentTeamMembers);//想级联删除子表数据或增减替换对象

this.getHibernateTemplate.saveOrUpdate(commentTeam);

这个例子级联删除的效果也没发生!即使commentTeamMembers理由有若干个对象。

成功执行级联删除的语法:

CommentTeam commentTeam=this.getHibernateTemplate.get(CommentTeam.class,id);

commentTeam.getCommentTeamMember().clear();//注意这里引用的集合还是原理的集合,这里没有重新new过

commentTeam.getCommentTeamMember().add(new CommentTeamMember());//如果想替换为新的集合可以用addAll方法

this.getHibernateTemplate.saveOrUpdate(commentTeam);

分析一下原因:级联删除起作用的前提是关联的集合对象不能重新指向新的引用,必须在原有的集合里操作新增、删除、清空元素,像上面的setXXX(null)的方法等是起步到级联删除作用的,大概是Hibernate自认自己原生的集合对象吧,自己New的放进行级联删除无效!



转自:http://blog.sina.com.cn/s/blog_7210524f0100xvd5.html

转自:http://chenahong.diandian.com/post/2012-07-18/40029456186
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: