在之前两篇博文中,介绍了Hibernate框架的基本操作和对象的状态和一级缓存的知识,在本篇博文中将介绍对象之间的关系操作.
关系操作
一对多的单项关联
持久化类和映射文件
|
|
|
|
可以看到只能通过Classes查找到Student,但是不能通过student查找到Classes。
|
|
从映射文件可以看出,set元素描述了两个方面的内容:
- key从外键的角度描述了两者之间的关系,用于生成SQL语句。
- one-to-many 从类与类的角度描述了两者的关系,用于客户端的编码
|
|
说明
1、在hibernate中,通过国session.save方法保存一个持久化对象,这种方式称为显式保存。
2、在hibernate中,通过级联的方式来操作保存一个对象,这样的方式称为隐式保存。
3、对student对象进行隐式的保存操作,是因为studnet是一个临时状态的对象,在数据库中有没对应的记录,所以应该对student执行insert语句。
更新班级级联更新学生
|
|
说明:
- 当执行Classes classes = (Classes)session.get(Classes.class, 2L);的时候产生如下的SQL语句。
2.当执行Set
- 当执行 transaction.commit();的时候执行如下的SQL语句。该语句有两条,得到的set集合中有两条记录。
- 没有发出更新classess的update语句,因为classes的属性没有发生改变。
总结
Cascade:
Save-update
在session.save/update 一个对象的时候,级联操作关联对象,关联对象或者执行save语句或者执行update语句或者什么都不执行
Delete
在session.delete一个对象的时候,级联删除关联对象
All
save-update和delete的结合
在次讨论session.flush
- 检查session一级缓存中所放入持久化对象的状态,决定发出insert语句或者update语句。
- 会检查所有的持久化对象关联对象,如果有级联操作,则对关联对象进行insert语句或update语句。
关系操作
让一个新的学生加入已经存在的班级
|
|
说明:
- 当执行 Classes classes = (Classes)session.get(Classes.class, 2L);语句的时候,hibernate会发出加载classes的SQL语句。
- 当执行classes.getStudents().add(student);的时候,会发出加载classes班级中的所有的学生的SQL语句
- 因为Classes.hbm.xml 文件中set元素的cascade属性设置了save-update,所以在更新classes的时候,要检查classes中的student集合,发现集合中多了一个对象,所以要对该对象进行insert语句。
4.当执行session.flush的时候,会发出维护关系的update语句,因为classes负责维护classes与student之间的关系。
Classes.hbm.xml文件中的set元素:
重新建立关系1
|
|
说明:
- 执行 classes.getStudents().remove(student);的时候,发出维护关系的SQL语句
因为解除了关系了,所以cid为null- classes2.getStudents().add(student);的时候,发出更新的操作。
因为建立了关系,所以更新了外键
- 从上面的代码和SQL语句可以看出,外键变了两次,但是不需要改变两次,请看下面的优化版的重新建立关系。
重新建立关系2
|
|
说明:只需要建立关系就可以了,因为发出的是更新外键的SQL语句,把外键的值改变了就可以了。
解除一个学生和一个班级之间的关系
|
|
解除该班级和所有学生之间的关系
|
|
一般来说hibernate发出的SQL语句越少的话,效率是越高的,当执行 classes.setStudents(null) 的时候,不需要根据学生进行加载了,这样执行效率会更加的高效一些。
删除班级
|
|
说明:
- classes负责维护关系。
- 在执行session.flush的时候,在删除班级之前,解除该班级和所有的学生之间的关系(Student表中的外键全变为null,即解除了关系)。 如果你设置在配置文件中的set 节点中inverse 为true(即不维护表之间的关系),那么在删除班级的时候是不会进行Student表的外键解除约束的,这样就会报错。在这种情况下,只能先去删除学生,再去删除班级了,所以说,不同的配置,就对应不同的代码和逻辑的操作。
- 解除关系以后,再删除班级。
- 上诉的代码是:先查询班级,再查询该班级中的每一个学生,再根据每一个学生的sid删除每一个学生,这样的效率比较低。
删除班级级联删除学生
|
|
在hibernate中Cascade 与inverse的区别
- cascade描述的是对象与对象之间的关系, cascade和外键没有关系,在Studen表中,sid、name、description和cascade有关系,但是cid和cascade没有关系。
- inverse描述的是对象与外键之间的关系,inverse只和cid有关系,如果维护,则发出update语句(更新外键的SQL语句),如果不维护,则不管。
总结
- 只能通过classes操作student
- 只要classes维护关系,就会发出维护关系的update语句,所以让classes维护关系效率比较低。
- 让student维护效率比较高(让较多的一方维护效率更高)
- Session.flush的时候,hibernate内部做的事情
1、检查一级缓存中所有的持久化对象,决定发出insert语句还是update语句
2、检查持久化对象的关联对象,看持久化对象的映射文件中的cascade,决定关联对象是否发出insert语句或者update语句或者delete语句
3、检查持久化对象的映射文件的针对关联对象的inverse属性,来决定是否维护关系,如果维护关系,则发出update语句(维护关系的语句)
一对多双向关联
Classes对象
Student对象
|
|
Student映射文件如下,其中可以发现配置中 Classes中有一个外键,Student建立关联的时候也有一个外键,那么会不会有冲突呢?事实上是,通过谁建立关系,就看谁的映射文件,那么就使用按个外键。不会进行冲突的。
|
|
操作
保存学生,级联保存班级
说明:
- studnet.setClasses(classes)是通过student建立student与classes之间的关联,所以看外键时是看Student.hubm.xml文件,不看Classes文件了。
- 在Student.hbm.xml文件中,设置了针对classes的级联
12 <many-to-one name="classes" column="cid" class="com.wcc.hibernate.domain.Classes" cascade="save-update"></many-to-one>
3.在保存学生的时候级联保存了班级
4.保存学生相当于维护了关系。在保存学生的时候,cid有值了,不需要发出维护关系的update语句。
新建一个学生,并且和一个已存在的班级级联
|
|
学生转班操作
|
|
说明: 从上述的语句可以看出不仅更新了外键,而且把其他的属性也更新了,所以更新关系指的就是更新student 对象本身的操作。
解除一个班级和一个学生之间的关系
|
|
解除该班和所有的学生的关系
|
|
总结
一般情况下,一对多,多的一方维护关系效率比较高。