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

来源:互联网 发布:执业药师网络报名机构 编辑:程序博客网 时间:2024/05/18 02:10

检索策略

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

Transaction tx = session.biginTransaction();Customer customer = (Customer)session.load(Customer.class, new Long(1));...order.setCustomer(customer);tx.commit();session.close();customer.getName();//LazyInitializationException


解决:

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

Transaction tx = session.biginTransaction();Customer customer = (Customer)session.load(Customer.class, new Long(1));...order.setCustomer(customer);//显式初始化代理类实例if(!Hibernate.isInitialized(customer)){    Hibernate.initialize(customer);}tx.commit();session.close();customer.getName();//OK

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

<set name="orders"        inverse="true"                lazy = "true"        fetch = "select">    <key column="CUSTOMER_ID"/>    <one-to-many class="com.Order"/></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属性

例如如下场景:

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

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

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

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

select * from ORDERS where CUSTOMER_ID in (1,2,3); //1select * 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:

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

fetch=join:

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


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

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

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

lazy=proxy

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

lazy=no-proxy

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

lazy=false

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

fetch=join

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

join的深度

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

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


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







原创粉丝点击