1.概述
检索数据时的2个问题:
- 不浪费内存:当Hibernate从数据库中加载Customer对象时,如果同时加载所有关联的Order对象,而程序仅仅需要访问Customer对象,那么关联的Order对象就白白浪费了许多内存
- 更高的查询效率:发送尽可能少的SQL语句
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLxYzMwMDOykDMxITMwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
2.类级别的检索策略
- 包括立即检索和延迟检索,默认为延迟检索
- 立即检索:立即加载检索方法指定的对象
- 延迟检索:延迟加载检索方法指定的对象
- 类级别的检索策略可以通过<class>元素的lazy属性进行设置
- 如果程序加载一个对象的目的是为了访问它的属性,可以采取立即检索
- 如果仅仅是为了获得它的引用,可以采用延迟索引
- 无论<class>元素的lazy属性是true还是false,Session的get()方法和Query的list()方法在类级别总是使用立即检索策略
<class name="Order" table="ORDERS" lazy="true">具体内容省略...</class>
3.一对一和多对多的检索策略
- 在映射文件中,用<set>元素来配置一对多关联及多对多管理关系。<set>元素有lazy和fetch属性
- lazy:主要决定orders集合被初始化的实际
- fetch:取值为“select”或“subselect”时,决定初始化orders的查询语句的形式;若取值为“join”,则决定orders集合被初始化的时机
- 若把fetch设置为“join”,lazy属性将被忽略
- <set>元素的batch-size属性:用来为延迟检索策略或理解检索策略设定批量检索的数量
lazy属性(默认:true) | fetch(默认:select) | 检索策略 |
---|---|---|
true | select | 延迟检索 |
false | select | 立即检索 |
extra | select | 加强延迟检索 |
true,false或extra | select | select查询:select * from orders where customer_id in(1,2,3,4) |
true,false或extra | subselect | 子查询:select * from orders where customer_id in(select id from customers) |
true | join | 采用迫切左外连接策略 |
(1)延迟检索
- Hibernate在以下情况下初始化集合代理类实例
- 应用程序第一次访问集合属性,iterator(),size(),isEmpty(),contains()等方法
- 通过Hibernate.initialize()静态方法显式初始化
(2)增强延迟检索
- 进一步延迟Customer对象的Orders集合代理实例的初始化时机
- 当程序第一次访问orders属性的iterator()方法,会导致orders集合代理类实例的初始化
- 当程序第一次访问order属性的size(),contains()和isEmpty()方法时,Hibernate不会初始化orders集合类的实例,仅通过特定的select语句查询必要的信息。比如:select count(*) from order where 条件
(3)<set>元素的batch-size属性
<set> 元素有一个 batch-size 属性, 用来为延迟检索策略或立即检索策略设定批量检索的数量. 批量检索能减少 SELECT 语句的数目, 提高延迟检索或立即检索的运行性能
若batch-size设置为3,有5条数据
select * from order where customer_id in(1,2,3)
select * from order where customer_id in(4,5)
(4)迫切左外连接
- 检索Customer对象时,会采用迫切左外连接(通过左外连接加载与检索指定的对象关联的对象)策略来检索所有关联的Order对象
- lazy属性将被忽略
- Query的list()方法会忽略映射文件中配合的迫切左外连接策略,依旧采用延迟加载策略
@Test
public void testSetFetch2(){
Customer customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer.getOrders().size());
}
@Test
public void testSetFetch(){
//1. 若取值为 join. 则
//1.1 在加载 1 的一端的对象时, 使用迫切左外连接(使用左外链接进行查询, 且把集合属性进行初始化)的方式检索 n 的一端的集合属性
//1.2 忽略 lazy 属性.
//1.3 HQL 查询忽略 fetch=join 的取值
List<Customer> customers = session.createQuery("FROM Customer").list();
System.out.println(customers.size());
for(Customer customer: customers){
if(customer.getOrders() != null)
System.out.println(customer.getOrders().size());
}
}
4.多对一的检索策略
- <many-to-one>元素也有一个lazy属性和fetch属性
- 若fetch属性为join,那么lazy属性将被忽略
- 无代理延迟检索需要增强持久化类的字节码才能实现
lazy属性(默认:proxy) | fetch属性(默认:select) | 检索策略 |
---|---|---|
proxy | select | 延迟检索 |
no-proxy | select | 无代理延迟检索 |
false | select | 立即检索 |
proxy | join | 迫切左外连接 |
<hibernate-mapping package="com.yfy.hibernate.strategy">
<class name="Order" table="ORDERS">
<id name="orderId" type="java.lang.Integer">
<column name="ORDER_ID" />
<generator class="native" />
</id>
<property name="orderName" type="java.lang.String">
<column name="ORDER_NAME" />
</property>
<many-to-one
name="customer" class="Customer"
column="CUSTOMER_ID"
lazy="false"
fetch="join"></many-to-one>
</class>
</hibernate-mapping>
public class Customer {
private Integer customerId;
private String customerName;
private Set<Order> orders = new HashSet<>();
}