天天看点

Hibernate之检索策略

1.概述

检索数据时的2个问题:

  • 不浪费内存:当Hibernate从数据库中加载Customer对象时,如果同时加载所有关联的Order对象,而程序仅仅需要访问Customer对象,那么关联的Order对象就白白浪费了许多内存
  • 更高的查询效率:发送尽可能少的SQL语句
Hibernate之检索策略

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<>();
}
           

继续阅读