Hibernate】Hibernate的检索策略:lazy、fetch

http://blog.csdn.net/vking_wang/article/details/8742573

原作者


检索策略

1、类级别和关联级别
 可选的检索策略默认值受影响的检索方法
类级别立即检索
延迟检索
延迟Session.load()
——Session.get()/Query.list()都会立即检索
关联级别立即检索
延迟检索
迫切左外连接检索
延迟Session.load()/get()
Query(注意,Query会忽略xml中配置的迫切左外连接策略)
Criteria


三种检索策略

检索策略优点缺点使用场景
立即检索
  • 不管对象处于持久化状态、游离状态,都可以从一个对象导航到与他关联的另一个对象
  • select语句数目多;
  • 可能加载程序不需要访问的对象,浪费内存
  • 类级别
  • 程序需要立即访问的对象
  • 使用了二级缓存
延迟检索
  • 由应用程序决定需要加载哪些对象
  • 避免立即检索的缺点
  • 如果要访问游离状态的代理类实例,则必须保证其在持久化状态时已经被初始化
  • 一对多、多对多关联时
  • 程序不需要立即访问的对象
迫切
左外连接
  • 不管对象处于持久化状态、游离状态,都可以从一个对象导航到与他关联的另一个对象
  • select语句数目少
  • 复杂的表连接会影响性能;
  • 可能加载程序不需要访问的对象,浪费内存
  • 多对一、一对一关联
  • 程序需要立即访问的对象
  • 数据库系统具有良好的表连接性能



2、lazy与fetch
 作用类级别一对多关联级别<set>多对一关联级别<many-to-one>
lazy决定被初始化的时机
  • true:延迟检索(default)
  • false:立即检索
  • true:延迟检索(default)
  • extra:增强延迟检索
  • false:立即检索
  • proxy:延迟检索
  • no-proxy:无代理延迟检索
  • false:立即检索
fetch决定初始化属性对象时的查询语句形式
  • 无此属性
  • select:select查询语句(default)
  • subselect:带子查询的select语句
  • join:迫切左外连接检索
  • select:select查询语句
  • join:迫切左外连接检索

——fetch配置为join时,会覆盖lazy配置


类级别的检索策略


立即检索

配置为立即检索时,执行session.load()时会立即查询数据库,返回BO。——如果查不到则返回null

session.get()、query.list()总是会进行立即检索。


延迟检索

配置为延迟检索时,执行session.load()时并不立即查询数据库,不执行select,仅返回BO的一个代理类实例

  • 这个代理类扩展自BO类,由Hibernate在运行时使用CGLIB工具动态产生;
  • 代理类实例仅初始化OID属性,其他属性都是null
  • 第一次访问BO属性(非ID属性)时,Hibernate会执行select+初始化代理类实例

——若select查不到,则抛ObjectNotFoundException

——getId()并不能触发查询。


问题:

若session关闭后,才去访问游离状态的BO的属性,会抛出LazyInitializationException

  1. Transaction tx = session.biginTransaction();  
  2. Customer customer = (Customer)session.load(Customer.classnew Long(1));  
  3. ...order.setCustomer(customer);  
  4. tx.commit();  
  5. session.close();  
  6.   
  7. customer.getName();//LazyInitializationException  


解决:

Hibernate.initialize()可在session范围内显式初始化代理类实例:

  1. Transaction tx = session.biginTransaction();  
  2. Customer customer = (Customer)session.load(Customer.classnew Long(1));  
  3. ...order.setCustomer(customer);  
  4. //显式初始化代理类实例  
  5. if(!Hibernate.isInitialized(customer)){  
  6.     Hibernate.initialize(customer);  
  7. }  
  8. tx.commit();  
  9. session.close();  
  10.   
  11. customer.getName();//OK  

一对多、多对多关联的检索策略

  1. <set name="orders"  
  2.         inverse="true"  
  3.           
  4.         lazy = "true"  
  5.         fetch = "select">  
  6.   
  7.     <key column="CUSTOMER_ID"/>  
  8.     <one-to-many class="com.Order"/>  
  9. </set>  

参考表2、表3

3、一对多关联
lazy取值fetch取值检索策略
true未设置采用延迟检索策略,是默认选择
false未设置采用立即检索策略。——当使用Hibernate二级缓存时,可考虑采用立即检索
extra未设置采用增强延迟检索策略,尽可能延迟集合属性的初始化时机
ANYselect通过select语句来初始化集合属性:
select * from ORDERS where CUSTOMER_ID in (1,2,3);
ANYsubselect通过subselect语句来初始化集合属性:
select * from ORDERS where CUSTOMER_ID in (select ID from CUSTOMERS)
ANYjoin采用迫切左外连接检索策略;此时lazy的取值会被忽略
select * from CUSTOMERS left outer join ORDERS
          on CUSTOMER.ID = ORDERS.CUSTOMER_ID
          where CUSTOMERS.ID = 1;

与类级别不同:不管是否延迟加载,都会返回代理类:org.hibernate.collections.PersistentSet

lazy=true延迟加载时

返回的PersistentSet中的元素为空。

调用customer.getOders()并不能触发select,只有当调用其iterator、size、isEmpty、contains方法时才会执行数据库查询。


lazy=extra增强延迟加载时:

调用size、isEmpty、contains时也不能触发select,只有当调用iterator时才执行数据库查询。


batch-size属性

例如如下场景:

  1. //先查询出4个customer。  
  2. //-------其order属性为代理类实例,即此时session缓存中有4个未初始化的order集合代理类实例  
  3. List list = session.createQuery("from Customer").list();  
  4. ...  
  5. //再分别查询其orders  
  6. customer1.getOrders().iterator();//1  
  7. customer2.getOrders().iterator();//2  
  8. customer3.getOrders().iterator();//3  
  9. customer4.getOrders().iterator();//4  

若不配置batch-size,则SQL如下:

  1. select * from ORDERS where CUSTOMER_ID=1; //1  
  2. select * from ORDERS where CUSTOMER_ID=2; //2  
  3. select * from ORDERS where CUSTOMER_ID=3; //3  
  4. select * from ORDERS where CUSTOMER_ID=4; //4  

而若配置batch-size=3,则SQL优化为:

  1. select * from ORDERS where CUSTOMER_ID in (1,2,3); //1  
  2. select * from ORDERS where CUSTOMER_ID=4; //4  

  • 执行代码行1时,查询order集合,由于session缓存中共有4个oders集合代理类实例未被初始化,所以执行in (1,2,3)批量初始化
  • 执行代码行2时,无需再初始化其orders集合代理类,所以就无需再查数据库
  • 执行代码行3时,无需再初始化其orders集合代理类,所以就无需再查数据库
  • 执行代码行4时,本应再批量初始化3个orders集合代理类实例,但是session缓存中不足3个代理类实例,所以仅初始化剩余的实例。

——作用:减少了查询语句数目。

fetch=select与subselect的区别:

subselect时,batch-size的设置就无意义了。因为上例代码会转换为如下SQL:

  1. select * from ORDERS where CUSTOMER_ID in   
  2.          (select ID from CUSTOMERS); --代码中查询Customer的语句  

fetch=join:

query.list()会忽略join设置。默认为延迟加载、select


多对一、一对一关联的检索策略

参考表2,lazy取值可为proxy、no-proxy、false

与一对多不同,将true分为proxy、no-proxy两种情况

lazy=proxy

  1. //1.查询Order: select * from ORDER where id=1;  
  2. //  此时order.customer属性时Customer代理类实例  
  3. Order order = (Order)session.get(Order.classnew Long(1));  
  4. //2.返回Customer代理类实例  
  5. Customer customer = order.getCustomer();  
  6. //3.初始化Customer代理类实例  
  7. String name = customer.getName();  

lazy=no-proxy

  1. //1.查询Order: select * from ORDER where id=1;  
  2. //  此时order.customer属性=null,----并非代理类实例!!  
  3. Order order = (Order)session.get(Order.classnew Long(1));  
  4. //2.查询数据库Customer  
  5. Customer customer = order.getCustomer();  
  6. //3.  
  7. String name = customer.getName();  

lazy=false

  1. //1.查询Order: select * from ORDER where id=1  
  2. //    并且查其Customer:select * from CUSTOMER where id=1;  
  3. //    随之查询Customer属性:select * from ORDERS where CUSTOMER_ID=1  
  4. Order order = (Order)session.get(Order.classnew Long(1));  
  5. //2.  
  6. Customer customer = order.getCustomer();  
  7. //3.  
  8. String name = customer.getName();  

fetch=join

  1. //1.查询Order及其Customer  
  2. //    select * from ORDERS left outer join CUSTOMERS  
  3. //             on ORDERS.CUSTOMER_ID = CUSTOMER.ID   
  4. //             where ORDERS.ID=1;  
  5.   
  6. //  若Customer.hbm.xml中lazy=false,则同时还查ORDERS:  
  7. //    select * from ORDERS where CUSTOMER_ID=1    
  8. Order order = (Order)session.get(Order.classnew Long(1));  
  9. //2.  
  10. Customer customer = order.getCustomer();  
  11. //3.  
  12. String name = customer.getName();  

join的深度

注意上面这个例子,fetch=join,查Order时会对Customer进行左连接;

假如Customer又和表A左连接、表A又和表B左连接,那查询Order岂不要连接多个表?


hibernate考虑了这个问题,默认情况下hibernate.max_fetch_depth=2


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值