Hibernate中的cascade和inverse属性

我个人觉得在学习Hibernate的过程中最不好理解的就是这两个属性了,今天用一个例子加深一下对他们两个的认识

这里有两个表:

(1)class   (班级表)
            cid    varchar(32) 主键 not-null (班级id)
            cname varchar(16)      not-null (班级名称) 

(2)student (学生表)
            sid    varchar(32) 主键 not-null (学生id)
            sname varchar(16)      not-null (学生姓名)
            class_id varchar(32)   not-null (学生所属班级)

一个班级(class)对应多个学生(student),所以班级表(class)就是“one-to-many”端,反之student就是many-to-one端

//--------Class类的代码--------
public class Class implements.....
{
private cId = "";
private cName = "";
private students = java.util.HashMap();
// 省略对应的 geter setter

Hibernate

//--------Class.hbm.xml--------
<hibernate-mapping>
<class name="lcx.vo.Class" table="class"
   catalog="demo">
   <id name="cid" type="java.lang.String">
    <column name="cid" length="32" />
    <generator class="uuid.hex" />
   </id>
   <property name="name" type="java.lang.String">
    <column name="cname" length="16" not-null="true" />
   </property>
   <set name="students" table="student" cascade="save-update">
    <key column="class" />
    <one-to-many class="lcx.vo.Student" />
   </set>
</class>
</hibernate-mapping>
//--------Student类的代码---------
public class Student implements.....
{
private sId = "";
private sName = "";
private Class class = null;
// 省略对应的 geter setter
}

Hibernate

// Student.hbm.xml
<hibernate-mapping>
<class name="lcx.vo.Student" table="student" catalog="demo">
   <id name="cid" type="java.lang.String">
    <column name="sid" length="32" />
    <generator class="uuid.hex" />
   </id>
   <many-to-one name="class"
    class="lcx.vo.Class"
    column="class_id"
    not-null="true"
   />   
</class>
</hibernate-mapping>

那么现在我们来看看cascade

当Hibernate持久化一个“临时对象(也叫自由态对象)”时,在默认的情况下(即:没有设置cascade属性或cascade=none时),Hibernate不会自动“持久化他所关联”的其他临时对象。那么什么叫不会自动 “持久化”关联的临时对象呢?

那么请看看如下代码:

// 创建一个 临时对象(也叫自由态对象)
// 也就是说这个 class 没有被Hibernate纳入Session缓存管理。
Class class = new Class();
//class.id 为自动生成
class.setName("一年级1班");

Student stu = new Student();
//student.id 为自动生成
stu.setName("小明");
stu.setClass(class);

// 关键就是这里
class.getStudents().add(stu);

session.save(class);
// 提交

// 注意: Class.hbm.xml文件中,cascade="save-update"并且也没有设置inverse属性,也就是说inverse=false;
// 此时如果你开启了Hibernate的显示HQL语句功能,那么控制台将会显示如下3条HQL:

insert into demo.class (cid, cname) values (1, 一年级1班)
insert into demo.student (sid,sname,class_id) values (1, 小明, 1)
update demo.student set class_id=1 where sid=1

那么为什么会出现,这3条HQL语句呢,我们来分析一下:

第1条翻译出来的HQL语句:
其实第一条HQL比较好理解,当我们调用 session.save(class) 后,在Hibernate进行提交的时候,会发现“有”一条“新”的数据要插入(insert),所以就往class表中,插入了这条新的class记录。

第2条HQL语句:
注意问题就在这里:这里为什么又出现了一条insert语句呢?而且还是向student表中插入数据。我们在上面的代码中,并没有编写类似“session.save(student)”这样的语句的嘛。这是为什么呢?
其实原因,是这么回事:因为我们在class端,设置了"级联更新"(即:cascade="save-update"),也就是说,当Hibernate在向class表中插入“新”对象记录时,会检查“Class对象”所关联的属性(就是<set>对应的属性),是否发生过变化,如果发生了变化,就按照“级联属性(cascade)”所设定的内容进行操作。

上面讲的这句话到底是什么意思呢?
我们换句话说就是:因为调用了 class.getStudents().add(stu);
所以,在Hibernate在进行插入 class对象的时候,发现class对象,所关联的集合中,有一条“自由态”的对象,而又因为class端设置了“级联属性cascade”,所以,在插入这条 “新class对象”时,也一同把他内部的那些,还属于“自由态”的其他对象,也一同插入到,他们所对应的表中去了。

第3条HQL语句:
第三条HQL语句是一条update语句,是不是觉得,很莫名其妙。假如:我们把 class端的配置文档中的 invser属性设置为true(即:inverse=true),在执行上面的程序,发现,就变成2条insert语句了。

我们再来看看inverse属性

当调用 Class.getStudents().add(stu)方法,进行添加操作时,
(即:向 "这个Class对象"所属的“集合 (也就是调用getStudents方法所返回的那个Set集合)”中添加一个Student(即 add(stu)),也就是说,这个“新”添加的Student对象(stu),他的Student.class_id字段“必须”,要等于“被添加方Class”的主键(即:Class.cid)。
从“数据库”层面来讲,也就是说,这个“新”添加的“Student”的class_id字段,必须要与“Class”的cid字段,存在"主外键关联"。) 正因为如此:所以Hibernate“怕” 在进行 "Class.getStudents().add(stu)" 这样的操作时,出现意外情况(如: stu.getClass=null,即:stu没有所属班级),即“添加方”(Student)与“被添加方”(Class),存在“外键”不一致的情况发生。
所以就出现了 那条多余的update语句。即:one-to-many(Class端)主动去维护Child.Class_id,所以就是说,Hibernate怕出错,就给你多执行一次无用的更新语句,以保证 add 到 Class“集合”中的所有Student都是要与Class有外键关联的。

用普通话说就是:
一年1班.getStudents().add(小明);
一年1班.getStudents().add(大明);

也就是说现在不管是 小明 还是 大明
如果他们,目前还没有自己的班级的话,
一年1班的班主任就会主动邀请他们成为一年1班的同学啦~。

也就是说 一年1班的班主任 主动邀请 同学,而不是 同学自己来报名 所以效率也降低了,所以我们一般把 一对多端 invser设置为true,即:不让主控端去维护主键关联,(即:让同学自己去找班级)说白了,就是,one-to-many端不用去管理 “新添加对象” 的主外键约束问题。

把one-to-many端(即:class端)的invser设置为true(即:每次向class.getStudents这个集合中添加 student时,不去主动update对应的外键),而是在student端去手动设置
例如:
student.setClass(class);
session.save(student);
这样手动设置 student与class关联
所以上面的程序“最好”还是写成这样:

Class class = new Class();
class.setName("一年级1班");
session.save(class);

Student stu = new Student();
stu.setName("小明");
stu.setClass(class);
session.save(class);
class.getStudents().add(stu);
// 提交

总结:
当inverse=false 并且向one-to-many端的关联集合,添加“新对象(即: 自由态对象)” 时,Hibernate就会自动,去update那“个刚刚到来的” “自由态对象”的外键。
(如果你向,one-to-many端添的集合中,add一个“已经持久化了的对象”,那就不会出现update了(因为已经持久化过了),除非,你去更改“那个持久化对象”所对应的外键

// 如果你懂了上面的内容,那么来看一下,下面的东西。
假如,将one-to-many端(即:Class端)的 hbm.xml 文档中的cascade移除掉或把cascade="none",那么上面的代码会出现什么情况呢??
结果会出现2条HQL,和一堆Exception

insert into demo.class (cid, cname) values (1, 一年级1班)
update demo.student set class_id=1 where sid=1
Hibernate Exceptinon.............

相比较cascade被设置"save-update"的时候,缺少了1条 insert语句,而且也多了一些Exception。

那么,到底是少了哪1条insert语句呢?
就是这条:
insert into demo.student (sid,sname,class_id) values (1, 小明,1)

之所以会出现,这样的现象,想必您已经早就看出来了。
因为,我没有设置Class端的Cascade,所以在save(class)的时候,并没有自动将其所关联的“自由态对象”进行持久化操作。
然而,又因为 Class端的inverse=false,所以,Class会自动去维持,那个 “新来的student” 的外键。
所以会出现,没有insert就要update了,然后在就是Exception了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值