Hibernate 映射关联关系.docx
- 文档编号:11514752
- 上传时间:2023-03-02
- 格式:DOCX
- 页数:21
- 大小:21.36KB
Hibernate 映射关联关系.docx
《Hibernate 映射关联关系.docx》由会员分享,可在线阅读,更多相关《Hibernate 映射关联关系.docx(21页珍藏版)》请在冰豆网上搜索。
Hibernate映射关联关系
Hibernate映射关联关系
一、映射多对一关联关系。
1.单向的多对一
(1)以Customer和Order为例:
一个用户可以发出多个订单,而一个订单只能属于一个客户。
从Order到Customer是多对一关联关系。
(2)创建Customer和Order表。
Create
(3)用IntellijIdea自动生成关联关系,以及对应的Entitiy.hbm.xml和持久化类。
说明:
其中Type是用来修饰对应的AttributeName的。
在Order端,定义Customer类,一个订单属于一个客户。
而在Customer端,一个客户可以有多个订单,因为是单向的,所以这里放弃属性的添加。
在JoinColumns定义了Order和Customer之间的关联关系,order表中的customer_id外键和customer表中的customer_id主键关联。
来看生成的Schema:
没有勾选customer_id,是因为IntellijIdea没法直接映射为Customer类型的customer。
Order.hbm.xml
使用
name属性:
多这一端关联的一那一端的属性的名称。
class属性:
关联的一端的属性的类型。
column属性:
一那一端在多的一端对应的数据表中的外键。
可以任意命名,但需要和数据表中的字段对应。
(4)单向多对一的CRUD以及需要注意的问题。
<1>新增
①先保存一的一端Customer,后保存多的一端Order。
Save.java
打印SQL:
Output
结论:
发送了3条INSERT语句。
②先保存多的一端Order,再保存一的一端Customer。
Save2.java
打印SQL:
Output2
结论:
发送了3条INSERT语句,2条UPDATE语句。
总结:
在单向多对一的关联关系下,先插入1的一端会减少SQL语句的执行,性能更高。
<2>删除
先删除1的一端。
Delete.java
控制台打印:
Cannotdeleteorupdateaparentrow:
aforeignkeyconstraintfails(`hibernate`.`order`,CONSTRAINT`FK_m6q2ofkj1g5aobtb2p00ajpqg`FOREIGNKEY(`customer_id`)REFERENCES`customer`(`customer_id`))
结论:
在不设置级联关系的前提下,不能删除1的一端。
<3>更新
Update.java
Output
<4>查询
①查询n的一端,但是不使用查询出来关联的1的一端的对象。
@Test
publicvoidtestMany2OneGet(){
Orderorder=(Order)session.get(Order.class,1);
System.out.println(order.getCustomer().getClass().getName());
}
复制代码
Hibernate:
select
order0_.order_idasorder_id1_1_0_,
order0_.order_nameasorder_na2_1_0_,
order0_.customer_idascustomer3_1_0_
from
hibernate.orderorder0_
where
order0_.order_id=?
order1
com.nucsoft.hibernate.Customer_$$_jvst30c_1
复制代码
②查询n的一端,使用查询出来关联的1的一端的对象。
@Test
publicvoidtestMany2OneGet(){
Orderorder=(Order)session.get(Order.class,1);
System.out.println(order.getCustomer().getClass().getName());
order.getCustomer().getCustomerName();
}
复制代码
Hibernate:
select
order0_.order_idasorder_id1_1_0_,
order0_.order_nameasorder_na2_1_0_,
order0_.customer_idascustomer3_1_0_
from
hibernate.orderorder0_
where
order0_.order_id=?
com.nucsoft.hibernate.Customer_$$_jvst30c_1
Hibernate:
select
customer0_.customer_idascustomer1_0_0_,
customer0_.customer_nameascustomer2_0_0_
from
hibernate.customercustomer0_
where
customer0_.customer_id=?
复制代码
总结:
可以发现,采用的是懒加载机制,即获取到的1的一端的对象是一个代理对象。
只有在使用这个对象的属性的情况下,才会发送SQL语句。
③由懒加载机制引发的懒加载异常。
复制代码
@Test
publicvoidtestMany2OneGet(){
Orderorder=(Order)session.get(Order.class,1);
System.out.println(order.getCustomer().getClass().getName());
session.close();
order.getCustomer().getCustomerName();
}
复制代码
org.hibernate.LazyInitializationException:
couldnotinitializeproxy-noSession
在需要使用对象之前,关闭了Session连接,由此会引发LazyInitializationException异常。
2.双向的多对一
(1)还是以Order和Customer为例:
双向的多对一不仅仅要在Order类中定义一个Customer属性,而在Customer类中也需定义存放Order对象的集合属性。
(2)创建Order和Customer表和创建单向多对一相同。
(3)通过IntellijIdea生成简单的持久化类和Entity.hbm.xml文件。
手动的去建立关联关系。
<1>生成简单的持久化类和Entity.hbm.xml文件
Customer.java
Order.java
Customer.hbm.xml
Order.hbm.xml
<2>手动建立关联关系
①在Order一端建立多对一的关联关系。
在Order持久化类中添加Customer类型的一个属性customer。
在Order.hbm.xml文件中添加多对一的关联关系。
同时修改主键生成方式为native。
②在Customer一端建立一对多的关联关系。
在Customer持久化类中添加Order的一个集合orders。
在Customer.hbm.xml添加一对多的关联关系。
同时修改主键生成方式为native。
③详细说明:
在Customer.hbm.xml文件中添加一对多的关联关系。
当Session从数据库中加载Java集合时,创建的是Hibernate内置的集合类的实例。
因此在持久化类中定义集合属性时需要定义成接口类型,不能是具体的某个实现类。
Hibernate内置的集合具有集合代理功能,因为有代理功能,所以支持延迟检索策略。
在定义集合的时候,通常将其初始化为集合实现类的一个实例,防止NullPointerException。
Hibernate使用
1的一端的Set类型属性数据还是存放在n的一端。
④set元素
name属性:
待映射的Set类型的属性的属性名称。
table属性:
待映射的Set属性的泛型类型所对应的表。
key子元素:
column属性,多的一端的外键名称。
one-to-many子元素:
class属性,n的一端的持久化类名称。
对应关系如图。
⑤最终的实体类和Entity.hbm.xml文件。
Customer.java
Order.java
Customer.hbm.xml
Order.hbm.xml
(4)通过IntellijIdea直接生成双向的多对一的关联关系。
<1>为生成的每个Entity.hbm.xml文件添加主键生成方式。
<2>为Customer类中的orders属性进行初始化。
<3>最终的持久化类和Entity.hbm.xml。
Customer.java
Order.java
Customer.hbm.xml
Order.hbm.xml
<4>对比发现,通过IntellijIdea自动生成的Customer.hbm.xml文件中set元素多了一个inverse属性。
稍后进行说明。
(5)双向多对一的CRUD和需要注意的问题
<1>新增
①双方都维护关联关系,即没有设置inverse属性,且没有添加非空约束。
先保存1的一端,再保存n的一端。
Save.java
打印SQL:
Output
结果:
打印了3条INSERT语句,2条UPDATE语句
先保存n的一端,再保存1的一端。
Save2.java
打印SQL:
Output2
结果:
打印了3条INSERT语句,4条UPDATE语句。
原因,双方都维护这关联关系。
②双方都维护关联关系,即没有设置inverse属性,对order表中的customer_id列添加非空约束(需要更改两个地方)。
先保存n的一端,再保存1的一端,会抛出异常。
org.hibernate.TransientPropertyValueException:
Not-nullpropertyreferencesatransientvalue-transientinstancemustbesavedbeforecurrentoperation:
com.nucsoft.hibernate.Order.customer->com.nucsoft.hibernate.Customer
③1的一端放弃维护关联关系,只由n的一端来维护。
即设置Customer.hbm.xml的set元素inverse属性值为true。
先保存1的一端,后保存n的一端。
Output
结果:
只会发送3条INSERT语句。
④总结:
介绍了双向的多对一的下的保存操作,若都维护关联关系,则会多出UPDATE语句。
且若外键存在非空约束时,不能先保存n的一端。
所以在进行Hibernate双向多对一保存的时候,最好的做法就是:
1的一端放弃维护关联关系,即设置set节点的inverse属性为true。
同时在保存的时候先保存1的一端,后保存n的一端。
<2>删除
Delete
同删除单向的多对一相同,会抛出异常:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
Cannotdeleteorupdateaparentrow:
aforeignkeyconstraintfails
存在外键约束。
<3>更新
Update
Output
<4>查询
@Test
publicvoidtestMany2OneBothGet(){
Customercustomer=(Customer)session.get(Customer.class,5);
System.out.println(customer.getOrders().getClass());
}
打印结果:
复制代码
Hibernate:
select
customer0_.customer_idascustomer1_0_0_,
customer0_.customer_nameascustomer2_0_0_
from
hibernate.customercustomer0_
where
customer0_.customer_id=?
classorg.hibernate.collection.internal.PersistentSet
复制代码
并没有查询关联的Order集合,实际类型为Hibernate内置的一个Set实现类。
证明了:
当Session从数据库中加载Java集合时,创建的是Hibernate内置的集合类的实例。
因此在持久化类中定义集合属性时需要定义成接口类型,不能是具体的某个实现类。
也证明了:
Hibernate内置的集合具有集合代理功能,因为有代理功能,所以支持延迟检索策略。
<5>说明
这里没有介绍cascade属性,是因为在实际的项目中,为了保护数据,很少设置cascade属性,而是手动去处理。
二、映射一对一关联关系。
1.这里只介绍双向的一对一关联关系如何映射。
单向的一对一只需要把没有外键的一端去掉就好了。
2.基于外键映射的双向一对一
(1)以Dempartment和Manager为例。
一个部门只能有一个经理,一个经理职能管理一个部门。
(2)创建Department和Manager表。
在Department表建立Manager表的外键。
department.sql
Manager.xml
(3)通过IntellijIdea自动生成的双向1对1无法映射列。
这里通过IntellijIdea生成简单的持久化类和Entity.hbm.xml文件,然后手动的添加映射。
Department.java
Manager.java
Department.hbm.xml
Manager.hbm.xml
(4)说明:
在department表中来维护外键。
在映射的时候选择
在manager表中没有维护外键。
在映射时候选择
(5)CRUD以及需要注意的地方。
<1>save
Save1.java
Output1
Save2.java
Output2
对比发现,先保存没有外键列的对象,会减少UPDATE语句的发送,提高性能。
<2>delete
Delete1.java
Output1
Delete2.java
Cannotdeleteorupdateaparentrow:
aforeignkeyconstraintfails(`hibernate`.`department`,CONSTRAINT`FK_kpcmf8csabfn9epikikcfqbk0`FOREIGNKEY(`manager_id_fk`)REFERENCES`manager`(`manager_id`))
外键关联对象存在的情况下,不能先删除拥有外键的对象。
<3>update
Update.java
Output
<4>get
Get1.java
Output1
可以看到,查询拥有外键对象关联的对象时,采用的还是懒加载机制。
此种情况下,若session关闭,再去调用关联对象的某个属性,会发生懒加载异常。
查询双向一对一中没有外键的一端:
Get2.java
复制代码
Hibernate:
select
manager0_.manager_idasmanager1_1_1_,
manager0_.manager_nameasmanager2_1_1_,
department1_.dept_idasdept1_0_0_,
department1_.dept_nameasdept2_0_0_,
department1_.manager_id_fkasmanager3_0_0_
from
hibernate.managermanager0_
leftjoin
hibernate.departmentdepartment1_
onmanager0_.manager_id=department1_.dept_id
where
manager0_.manager_id=?
com.nucsoft.hibernate.Manager@8ba
复制代码
复制代码
Hibernate:
select
manager0_.manager_idasmanager1_1_1_,
manager0_.manager_nameasmanager2_1_1_,
department1_.dept_idasdept1_0_0_,
department1_.dept_nameasdept2_0_0_,
department1_.manager_id_fkasmanager3_0_0_
from
hibernate.managermanager0_
leftouterjoin
hibernate.departmentdepartment1_
onmanager0_.manager_id=department1_.manager_id_fk
where
manager0_.manager_id=?
com.nucsoft.hibernate.Manager@8ba
复制代码
可以发现,在第一次查询时,没有设置property-ref属性。
左外链接查询时,虽然结果正确,但是连接条件不正确。
至于说,为什么查询Manager对象的时候,使用了左外链接而不是懒加载,因为Manager端没有Deparment的外键。
它不知道谁与它有关系。
只能通过左外链接查询一次查询。
3.基于主键的双向的一对一
(1)本质上和基于外键的双向一对一关联一样,只不过是以主键作为了外键来使用的。
(2)还是以Department和Manager为例。
(3)deparment表做了改动,因为是基于主键的映射,这里将department表中manager_id_fk去掉了。
(4)Department.hbm.xml和Manager.hbm.xml文件
复制代码
复制代码
对于Department.hbm.xml文件,主键生成方式改为foreign,并且指定了主键生成所依赖的属性所对应的持久化类的主键生成方式。
需要注意的是,需要在
复制代码
复制代码
将Manager.hbm.xml文件的
(5)CRUD及需要注意的问题
<1>save
Save1.java
Save2.java
Output
结论:
发现不论是先保存department,还是先保存manager,都是先插入的manager。
因为department的主键生成方式是依赖于manager的。
<2>update
Update.java
Output
Update2.java
Output2
<3>get
Get.java
Output
通过department获取到的manager采用的是懒加载机制。
而从manager获取dept,是通过左外链接查询的。
至于原因,第一小点已经提到,本质上和基于外键的双向一对一关联一样,只不过是以主键作
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Hibernate 映射关联关系 映射 关联 关系