一对多关联映射
什么是one-to-many?
概念
一对多关联,一个对象通过关联属性能够对应多个对象。
在数据库中表现为:一张表中的记录通过在另一张表中设立外键来与其建立关联关系,外键在另一张表中对应本表中对应记录的主键,且可以拥有相同的外键来对应本表中的同一条记录。
举例:
学生在学校中都有所属的班级,一个班级对象中可以有多个学生对象,而每个学生对象都对应着一个班级。班级和学生之间的关系就是多对一关系。
建立模型
使用并配置one-to-many
定义实体类
Student类:
Classes类:
使用set集合定义关联属性的类型。
PS: Hibernate对set进行了重写,使set支持懒加载,因此Hibernate中一般使用的是set而不是list
略过Student.hbm.xml映射文件,我们主要看Classes.hbm.xml:
主键生成策略为native(数据库生成)
使用set标签映射set集合 (没有进行泛型的集合可以存放任何类型的对象),设置set中存放Student类型的对象。
通过key标签指定在one-to-many所指的Student类所对应的表中创建名为classesid字段,该字段作为外键,参照当前类所对应表中的主键。
PS: key标签用来在其他表中设立关联本表的外键字段。
配置原理
将外键字段建立在另一张表中,这个字段参照的是当前表的主键,从而建立两张表的联系。
one-to-many的应用
使用一对多关联进行表的创建和使用
创建表:
我们看到t_student表中添加了classid字段,且classid作为外键,参照t_classes表中的主键。
插入数据
运用junit和debug追踪分析代码的执行过程
创建Student对象,并调用save方法:
Hibernate访问数据库,从而生成主键,对象进入持久态,这里就不做赘述了,直接将断点下移。
创建出Classes对象,并将Student对象的地址都放入set集合中,然后将set集合的地址放入Classes对象中的students属性中,最后调用save方法:
查看对象的状态:
可以看到,3个对象都进入持久态,查看执行日志:
Hibernate确实是发送了sql语句,但执行Save方法只进行了主键的获取,classid并没有生成。
执行commit操作:
Hibernate发送update说明数据发生了改变,生成了classid
我们查看t_student表中的数据:
我们看到classid中的数据对应着t_classes表中的主键id,形成了一对多关系。
classid的生成:
- 执行commit方法,Classes对象从students属性中依次取得student对象的地址;
- 通过对象的id找到该对象的类型;
- 找到该类型在数据库中对应表中的记录;
- 将classes中生成的主键id设到该记录中的classid字段中(通过update更新)
补充说明:
one-to-many没有自动级联,操作不当可能会出现TransientObjectException。
查询数据
使用load加载数据:
创建Classes对象,通过id找到表中对应的记录设到Classes对象的属性中
Hibernate根据hbm.xml映射文件给students属性设值:
- 通过id找到关联表的外键;
- 根据外键找到记录,创建student对象;
- 由于该属性通过one-to-many标签配置,于是又在表中依次找到与id相等的外键,并创建出对象;
- 自动创建一个set集合,将找到的记录设到set集合中;
- 将封装了student对象的set集合自动设到classes对象的属性students中。
查询结果:
缺点:
- 为了维护关系会发送过多多余的sql语句(update):
从上述实例我们看出,调用commit方法,Hibernate会为每一条关联的记录发送sql语句,如果关联对象的数量较大,就会因频繁发送sql语句而影响效率。 - 如果对classid设置为非空,在key中加上not-null=“true”,将无法正常存储数据:
classid是通过关联对象的主键生成,如果限定classid为非空,在纳入Session管理时就会抛出异常,导致数据无法存储。
实现one-to-many双向关联
建立模型
配置双向关联
在Student实体类中添加classes属性,属性的类型为Classes
保持Classes.gbm,xml映射文件不变,在Student.gbm,xml映射文件中添加由many-to-one标签配置的classes属性,该属性在t_Student表中对应的字段名为classid
由Student自己建立外键字段classid 参照Classes类对应表中的主键,两张表使用相同的外键,从而实现多对一的双向关联。
注: 建立外键的名字必须和由classes建立的外键名相同。
关系的维护
在多对一的单向关联中:
虽然外键字段classid在t_student表中,但classid中的数据是由Classes设置的,因此,Student和Classes之间的关系是通过Classes维护/创建的。
在多对一的双向关联中:
两个对象都能堆关系进行维护/创建
为了解决发送多余的sql语句问题
应尽量使用建立多对一关联关系的对象维护关系。(本例中的Student)因此,我们采用翻转:
在Classes.hbm.xml映射文件中,配置student属性的set关键字中加上 inverse=“true” 使classes对象不能维护关系。
实例测试:
实例1:
创建相关对象
两个Student对象中的classes属性都为null,student对象没有设置关联属性,此时对象处于能够维护关系但不维护关系的状态。
对classes对象调用save方法
Classes对象中的students属性中存储了Student对象的地址,但由于我们配置了classes对象不能维护关系,且Student对象中关联属性为空,也不维护关系。
执行commit,查看表中存储的结果:
数据库中外键classid的值为null,记录之间没有建立关系。
实例2:
既然不让Classes维护关系,那么索性就由Student来维护关系
Student对象的关联属性classes中存放了关联对象classes的地址
执行commit,查看数据库存储结果:
小结:
通过此方法存储数据的过程实质上就是==many-to-one(多对一)==关系的存储过程。
实例3:
查看对象的状态
Student对象的classes属性都存储了Classes对象的地址
对classes对象调用save方法:
自动生成id
Hibernate发送sql语句
我们看到,生成了classid
classid的生成过程:
Classes对象通过students属性找到Student对象后,由于不能维护关系,因此要由Student对象来维护,通过级联,使Student对象进入持久态,自动生成id,将classes属性所指Classes对象的的id设到外键字段classid上。
这样就由Student建立了两张表之间的关系
说明:本文仅用作学习笔记,无其他用途,如有冒犯可联系本人删除