本文依旧以Customer类和Order类进行说明。
一、引言:
Hibernate检索Customer对象时立即检索与之关联的Order对象,这种检索策略为立即检索策略。立即检索策略存在两大不足:
A、select语句太多,而且会出现N+1的问题。所谓N+1,1是指先查出所有的Customer集合,N是指针对每个Customer再查询其关联的Order集合。N+1可以通过一条外连接查询语句完成。
B、应用逻辑上可能并不需要Order集合,此时加载Order集合浪费内存空间。
为解决上述问题,Hibernate提供了另外两种检索策略:延迟检索策略和迫切左外连接检索策略。
二、Hibernate的检索策略简介:
分两个层次:类级别检索策略和关联级别的检索策略。
1、类级别和关联级别可选的检索策略及默认的检索策略:
检索策略的作用域 | 可选的检索策略 | 默认的检索策略 | 运行时行为受影响的检索方法 |
类级别 | 立即检索 延迟检索 |
延迟检索 | 仅Session.load()方法。其余的都是立即检索 |
关联级别 | 立即检索 延迟检索 迫切左外连接检索 |
延迟检索 | 影响Session.load()及get()方法,Query API以及Criteria API。Query API会忽略映射文件中设定的迫切左外连接检索策略 |
2、三种检索策略的允许机制:
检索策略的类型 | 类级别 | 关联级别 |
立即检索 | 立即加载检索方法指定的对象 | 立即加载与检索方法指定的对象关联的对象。可以设定批量检索数量。 |
延迟检索 | 延迟加载检索方法指定的对象 | 延迟加载与检索方法指定的对象关联的对象。可以设定批量检索数量。 |
迫切左外连接检索 | 不适用 | 通过左外连接加载与检索方法指定的对象关联的对象。 |
3、用于设定检索策略的几个属性:
属性 | 类级别 | 一对多关联级别 | 多对一关联级别 |
lazy | <class>元素中可选值为:true(延迟)和false(立即) | <set>元素中可选值:true(延迟)、extra(增强延迟)和false(立即) | <many-to-one>元素中的可选值:proxy(延迟)、no-proxy(无代理延迟)和false(立即) |
fetch | 无 | <set>元素中可选值:select(select查询语句)、subselect(带子查询的查询语句)和join(迫切左外连接查询) | <many-to-one>元素中可选值:select(select查询语句)、和join(迫切左外连接查询) |
batch-size | 设定批量检索的数量。仅适用于关联级别的立即检索和延迟检索。 | ||
备注 | fetch属性取值为select或subselect时,决定初始化关联集合的查询语句的形式;取值为join时,决定集合被初始化的时机。若fetch属性为join,则lazy属性被忽略。 |
三、类级别的检索策略:
类级别可选的检索策略包含立即检索和延迟检索,默认是立即检索。
通过以下配置文件:
<class name="mypack.Customer" table="CUSTOMERS" lazy="true/false">
且只在Session.load()方法时生效,其返回的是一个代理对象,注意与其相关的几种异常。可以通过org.hibernate.Hibernate#initialize()方法显式初始化代理类实例。
四、一对多和多对多关联的检索策略:
1、立即检索(lazy属性为false):慎用。
2、延迟检索(lazy属性为true):只有当应用程序第一次访问集合对象时才初始化,比如调用集合的iterator()\size()\isEmpty\contains()方法时。
3、增强延迟检索(lazy属性为extra):与延迟检索类似,但是只有调用集合的iterator()方法时,才会初始化集合对象。
4、批量延迟检索和批量立即检索(使用batch-size属性):
该属性的设置可以对N+1的问题起到缓解效果。
举例说明:一个Customer对象的orders集合中有10个对象,正常情况下全部加载会执行11条语句。
设置了batch-size为4后,将会执行1+3条语句。
select * from CUSTOMERS;
select * from ORDERS where CUSTOMER_ID in (1,2,3,4);
select * from ORDERS where CUSTOMER_ID in (5,6,7,8);
select * from ORDERS where CUSTOMER_ID in (9,10);
5、用带子查询的select语句整批量初始化orders集合(fetch属性为subselect):
即在初始化集合时会采用子查询的方式。
当fetch属性为subselect时,batch-size属性失效。
6、迫切左外连接查询(fetch属性为join):直接使用左外连接查询初始化集合对象。
注意,当使用Query.list()时,该配置失效。
五、多对一和一对一关联的检索策略:
1、迫切左外连接(fetch属性为join):与上述类似,注意和lazy属性的搭配。
2、延迟检索(lazy属性为proxy):对于一对一关联,如果使用延迟加载,必须把<one-to-one>元素的constrained属性设置为true。
3、无代理延迟加载(lazy属性为no-proxy):无代理的延迟检索会在order.getCustomer()时就进行初始化,而又代理的延迟加载时在访问Customer属性时初始化。
4、立即检索(lazy属性为false):与上述类似。
5、批量延迟检索和批量立即检索(使用batch-size属性):与上述类似。
六、控制迫切左外连接检索的深度:
通过hibernate.max_fetch_depth属性来控制,默认值为2.
七、属性级别的检索策略:
主要是针对<property><component>元素,适合于二进制大对象、字符串大对象及大容量组件类型的属性。