检索策略
当我们检索数据的时候,存在两个问题
不浪费内存:当 Hibernate 从数据库中加载 Customer 对象时, 如果同时加载所有关联的 Order 对象, 而程序实际上仅仅需要访问 Customer 对象, 那么这些关联的 Order 对象就白白浪费了许多内存.
更高的查询效率:发送尽可能少的 SQL 语句
很多时候这两点都是冲突的,我们可以看看hibernate是如何处理这种冲突的
类级别的检索策略
- 无论 class 元素的 lazy 属性是 true 还是 false, Session 的 get() 方法及 Query 的 list() 方法在类级别总是使用立即检索策略
若 class 元素的 lazy 属性为 true 或取默认值, Session 的 load() 方法不会执行查询数据表的 SELECT 语句, 仅返回代理类对象的实例, 该代理类实例有如下特征:
- 由 Hibernate 在运行时采用 CGLIB 工具动态生成
- Hibernate 创建代理类实例时, 仅初始化其 OID 属性
- 在应用程序第一次访问代理类实例的非 OID 属性时, Hibernate 会初始化代理类实例
<class name="Customer" table="CUSTOMERS" batch-size="5" lazy="true"> @Test void testClassLevelStrategy() { //类级别的懒加载检索策略仅适用load方法,注意不要发生懒加载异常 Customer customer = this.session.load(Customer.class, 1); //com.zc.cris.n21.both.Customer_$$_jvst9f0_0 System.out.println(customer.getClass().getName()); //不会发送sql语句,而是直接将代理对象的主键打印出来(1) System.out.println(customer.getId()); }
一对多和多对多的检索策略
在映射文件中, 用 元素来配置一对多关联及多对多关联关系. 元素有 lazy 和 fetch 属性
lazy: 主要决定 orders 集合被初始化的时机. 即到底是在加载 Customer 对象时就被初始化, 还是在程序访问 orders 集合时被初始化
fetch: 取值为 “select” 或 “subselect” 时, 决定初始化 orders 的查询语句的形式; 若取值为”join”, 则决定 orders 集合被初始化的时机
- 若把 fetch 设置为 “join”, lazy 属性将被忽略
- 元素的 batch-size 属性:用来为延迟检索策略或立即检索策略设定批量检索的数量. 批量检索能减少 SELECT 语句的数目, 提高延迟检索或立即检索的运行性能
配置代码
<set name="orders" inverse="true" table="ORDERS" order-by="ID desc" lazy="true" batch-size="2" fetch="subselect"> <key column="CUSTOMER_ID"></key> <one-to-many class="Order"/> </set>
测试代码
@Test void testSetBatchSize() { List<Customer> customers = this.session.createQuery("from Customer").list(); System.out.println(customers.size()); for(Customer customer : customers) { System.out.println(customer.getOrders().size()); } //set元素的batch-size属性可以设定一次初始化set集合的数量 } @Test void testOne2ManyLevelStrategy() { Customer customer = this.session.get(Customer.class, 1); System.out.println(customer.getName()); //强制代理对象初始化 Hibernate.initialize(customer.getOrders()); // ---------- set的lazy属性 ----------- //1. 1-n 或者 n-n 的集合属性默认使用懒加载检索策略 //2. 可以通过设置set的lazy属性为false来修改默认的懒加载检索策略,但是不建议修改,默认为true就好 //3. lazy还可以设置为extra,即增强的延迟检索策略,尽可能延迟集合初始化的时机,很少使用,了解 System.out.println(customer.getOrders().size()); //需要在Order类重写hashCode和equals方法 Order order = new Order(); order.setId(1); System.out.println(customer.getOrders().contains(order)); } @Test void testSetFetch() { List<Customer> customers = this.session.createQuery("from Customer").list(); System.out.println(customers.size()); for(Customer customer : customers) { System.out.println(customer.getOrders().size()); } //set元素的fetch属性:确定初始化orders集合的方式 //1. 默认为select,通过正常的方式来初始化set元素 //2. 可以取值为subselect,通过子查询初始化所有的set集合, //将子查询作为where 字句的in的条件出现,子查询所有一的一端的id,此时lazy属性有效,但是batch-size失效 //3. 可以取值为join //3.1 在加载一的一端的时候(hibernate4.0采用的是迫切左外连接 //的方式检索n的一端,5.1版本采用普通select查询),忽略lazy属性 //hql 查询忽略fetch=join的取值,仍然采用懒加载策略 } @Test void testSetBatchSize() { List<Customer> customers = this.session.createQuery("from Customer").list(); System.out.println(customers.size()); for(Customer customer : customers) { System.out.println(customer.getOrders().size()); } //set元素的batch-size属性可以设定一次初始化set集合的数量 //用来为延迟检索策略或立即检索策略设定批量检索的数量. 批量检索能减少 SELECT 语句的数目, 提高延迟检索或立即检索的运行性能 }
console打印sql语句如下:
拓展(延迟检索和增强延迟检索)
在延迟检索(lazy 属性值为 true) 集合属性时, Hibernate 在以下情况下初始化集合代理类实例
- 应用程序第一次访问集合属性: iterator(), size(), isEmpty(), contains() 等方法
- 通过 Hibernate.initialize() 静态方法显式初始化
增强延迟检索(lazy 属性为 extra): 与 lazy=“true” 类似. 主要区别是增强延迟检索策略能进一步延迟 Customer 对象的 orders 集合代理实例的初始化时机:
当程序第一次访问 orders 属性的 iterator() 方法时, 会导致 orders 集合代理类实例的初始化
当程序第一次访问 order 属性的 size(), contains() 和 isEmpty() 方法时, Hibernate 不会初始化 orders 集合类的实例, 仅通过特定的 select 语句查询必要的信息, 不会检索所有的 Order 对象
多对一和一对一的检索策略
- 和 一样, 元素也有一个 lazy 属性和 fetch 属性
代码:
配置代码
<many-to-one name="customer" column="CUSTOMER_ID" class="Customer" fetch="join" lazy="false" />
测试代码
@Test void testManyToOneStrategy() { // Order order = this.session.get(Order.class, 1); // System.out.println(order.getName()); List<Order> list = this.session.createQuery("from Order o").list(); for(Order order : list) { if(order.getCustomer() != null) { System.out.println(order.getCustomer().getName()); } } //1. lazy取值为proxy或者false分别表示对应的one的一端属性采用延迟检索和立即检索 //2. fetch:join(采用迫切左外连接的方式初始化customer),忽略lazy属性 //3. batch-size需要设置在1 的那端的class元素中: //<class name="Customer" table="CUSTOMERS" batch-size="5"> //作用:一次初始化1的这一端的代理对象的个数 }