一对一关联,一个对象通过关联属性只能对应一个对象。 在数据库中表现为:一张表中的记录通过外键字段与另一张表建立关联关系,外键作为另一张表对应记录的主键,且每条记录具有不同的外键,来对应另一张表中的记录。
举例: 每一个公民都有对应的身份证,且每个公民对象都有一一对应的身份证号。公民和身份证号这两个对象之间就存在一对一关系。
在hbm.xml映射文件中配置一对一关联映射 我们首先配置idCard表的映射:
将表名配置为t_idcard,主键的生成策略为native(数据库生成)。 紧接着我们配置User表:
表名配置为t_person。主键的配置:主键生成策略为foreign(说明主键的值来自于外部),参数param的name=property,参数的值为idCard(说明来自于当前类的一个名为idCard的属性所指的对象的id中拿到主标识,从而生成当前对象的主标识)。idCard属性配置 :idCard是关联属性,作为外键关联idCard类所对应的表。使用one-to-one标签,为了维护参照完整性,设置关键字constrained=true(指定当前表的主键又作为外键,参照idCard属性的类型所对应的表)。说明:
采用foreign主键生成策略:保证持久态对象是一一对应的;将主键设置成外键:保证两个关联的表中的两条记录是一一对应的我们将使用junit和debug追踪对one-to-one进行探讨。
首先进行创建表:
通过观察Hibernate发送的sql语句,我们发现t_person表中并没有idCard属性对应的字段,而是将主键id设置成了外键,参照t_idcard表的主键id。
创建idCard对象,调用save方法:
查看执行日志:
由于配置的主键生成策略是native(数据库生成),Hibernate自动发送sql语句,并获取数据库生成的主键id。
两个对象都纳入Session缓存,进入持久态。
我们将断点下移,创建Person对象,并调用save方法:
查看执行日志:
我们看到Hibernate没有再发送sql语句,这是因为,我们配置的主键生成策略为foreign(从外部获得)。此时数据库中没有这2条记录。
person1和person2中的id都被初始化,且id的值等于idCard属性所指对象的主键id的值。
person1和person2对象都存储到了Session的缓存当中,两个对象进入持久态。
断点下移,执行commit操作:
查看执行日志:
Hibernate发送sql语句,存储数据。
数据成功存储到数据库中。
我们使用懒加载从person类所对应的的表中,将id为1的记录加载到User对象中:
懒加载不会访问数据库,也不会创建对象,只生成了一个person的代理对象。
我们看到target为null,没有指向任何对象。
我们将断点下移,调用person对象中的方法:
查看执行日志:
Hibernate发送了sql语句,成功将拿到的对应的name字段中的数据打印了出来。
target对象指向了新建的person对象,person对象中idCard属性指向了idCard类的代理对象。
person对象纳入Session缓存,进入持久态。
我们继续将断点下移,通过person对象获取idCard中的数据:
查看执行日志:
Hibernate发送了sql语句,成功将拿到的对应的cardNo字段中的数据打印了出来。
Person代理对象中的target,target指向的Person对象中的属性idCard,idCard指向的idCard代理对象中的target,target指向了新建的idCard对象。(虽然这句话有点别扭,但结合上图理解应该没什么难度)
显而易见,纳入Session管理的Person和idCard这两个对象都处于持久态。
查询方式: 通过主键找到关联表的主键,找到对应的记录,将记录设置到新建的对应的对象中。
如果我们将多个对象关联同一个对象,强行建立多对一的关系,这又会产生什么结果呢?
如图所示,我们将person1和person2中的idCard属性都指向同一个IdCard对象
首先完成对象的创建和赋值
对person1对象调用save方法:
一切正常
注: Hibernate发送sql语句是因为对idCard对象调用save方法
将断点下移,对person2对象执行save方法:
我们看到出现了NonUniqueObjectException异常。 异常的大意:Session的缓存中已经存在具有相同标识符的对象。
在session的缓存中,一个对象必须对应一个主键id,即两个相同类型的对象不能对应同一个id。 Person对象的主键是通过关联对象的id生成的,当两个Person对象关联同一个idCard对象时,这两个对象就会生成相同的主键。
我们知道Person对象的主键是由idCard对象的主键生成的,实质上二者的主键是相等的,那如果Person对象进入持久态时,idCard对象还没有生成主键,会出现异常吗?
首先进行对象的创建:
查看对象的状态
此时idCard对象的主键id为默认值,还没有生成
我们将断点下移,直接对person对象执行save方法:
此时,无论是idCard对象还是person对象中的主键id都已经被初始化
没有产生任何异常,且Hibernate发送了sql语句,说明idCard对象中的id是通过数据库生成
调用commit方法,查看存储结果:
小结: one-to-one标签能够自动级联,对Person执行save操作时,自动发送sql语句为idCard生成主键。
上文中我们介绍了,通过Person表可以找到idCard表中的数据,但是我们无法通过idCard表查询Person表中的数据。
双向关联使可以通过任何一张表找到另一张表中的数据
我们只需在单向关联的环境下,略作更改即可实现双向关联:
打开IdCard.hbm.xml配置文件(Person类的映射文件无需任何更改):
添加的one-to-one标签中没有constrained约束,这意味着:
person属性不会建立表和表之间的联系;person属性不维护关系,不往外键里设值;person属性只用来通过idCard找到对应的Person对象对应的记录;idCard的主键生成策略还是native。创建表: 我们看到创建出的表中person属性没有做任何贡献,从创建表的角度,可以忽略person属性。
加载idCard所对应表中id为1的属性:
断点下移,直接对person对象中的数据进行查询:
Hibernate发送sql语句,并成功将查询到的数据打印出来
idCard对象中person属性指向新建的Person对象
小结: 实现了双向关联后,Hibernate可以通过idCard找到对应的Person中的值,然后加载数据,创建idCard对象,给属性设值,并将person属性指向新建的Person对象。
说明:本文仅用作学习笔记,无其他用途,如有冒犯可联系本人删除