http://blog.csdn.net/vking_wang/article/details/8742573
原作者
检索策略
可选的检索策略 | 默认值 | 受影响的检索方法 | |
---|---|---|---|
类级别 | 立即检索 延迟检索 | 延迟 | Session.load() ——Session.get()/Query.list()都会立即检索 |
关联级别 | 立即检索 延迟检索 迫切左外连接检索 | 延迟 | Session.load()/get() Query(注意,Query会忽略xml中配置的迫切左外连接策略) Criteria |
三种检索策略
检索策略 | 优点 | 缺点 | 使用场景 |
立即检索 |
|
|
|
延迟检索 |
|
|
|
迫切 左外连接 |
|
|
|
作用 | 类级别 | 一对多关联级别<set> | 多对一关联级别<many-to-one> | |
---|---|---|---|---|
lazy | 决定被初始化的时机 |
|
|
|
fetch | 决定初始化属性对象时的查询语句形式 |
|
|
|
——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
lazy取值 | fetch取值 | 检索策略 |
---|---|---|
true | 未设置 | 采用延迟检索策略,是默认选择 |
false | 未设置 | 采用立即检索策略。——当使用Hibernate二级缓存时,可考虑采用立即检索 |
extra | 未设置 | 采用增强延迟检索策略,尽可能延迟集合属性的初始化时机 |
ANY | select | 通过select语句来初始化集合属性: select * from ORDERS where CUSTOMER_ID in (1,2,3); |
ANY | subselect | 通过subselect语句来初始化集合属性: select * from ORDERS where CUSTOMER_ID in (select ID from CUSTOMERS) |
ANY | join | 采用迫切左外连接检索策略;此时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();
- ...
- //再分别查询其orders
- customer1.getOrders().iterator();//1
- customer2.getOrders().iterator();//2
- customer3.getOrders().iterator();//3
- customer4.getOrders().iterator();//4
若不配置batch-size,则SQL如下:
- select * from ORDERS where CUSTOMER_ID=1; //1
- select * from ORDERS where CUSTOMER_ID=2; //2
- select * from ORDERS where CUSTOMER_ID=3; //3
- select * from ORDERS where CUSTOMER_ID=4; //4
而若配置batch-size=3,则SQL优化为:
- select * from ORDERS where CUSTOMER_ID in (1,2,3); //1
- 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:
- 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.查询数据库Customer
- Customer 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=1
- Order 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