|||
一、一对多和多对多的关联策略
<set>元素的属性lazy的默认值为true,即采用延迟加载的策略。采用这种策略时,当调用Session实例的get()方法时,返回的customer对象的orders属性引用的是Hibernate为Set集合类提供的代理类PersistentSet的实例。在这个集合代理类实例中没有存放任何Orders类对象,也就是说Orders类对象的集合代理类实例未被初始化。
注:集合的代理类org.hibernate.collection.PersistentSet与持久化类不同的是,不管有没有设置延迟加载,
Hibernate的各种检索方法在为Customers类对象的属性orders赋值时,orders属性总是引用PersistentSet集合代理类的实例。
那么,Orders类对象的集合代理类实例什么时候才会被初始化呢?(1) 调用这个集合代理类实例的iterator()、size()、isEmpty()或contains()方法时,执行select语句初始化;(2) 调用Hibernate.initialize(obj)显示初始化。
Customers customer = (Customers)hibernateSession.get(cls, id);
Set set = customer.getOrders();
if (!Hibernate.isInitialized(set)) {
Hibernate.initialize(set);
}
1. 批量检索(batch-size
分为批量延迟检索和批量立即检索,目的是减少select语句性能。
(1) 批量延迟检索
Session hibernateSession = InitSessionUtil.openSession();
Query query = hibernateSession.createQuery("FROM Customers");
// 执行select * from customers,初始化N个Customers对象,
// 每个对象的orders属性引用一个集合代理类实例
List list = query.list();
for (int i = 0; i < list.size(); i++) {
Customers customer = (Customers)list.get(i);
Set set = customer.getOrders();
//执行select * from orders,初始化集合代理类实例
Iterator it = set.iterator();
while (it.hasNext()) {
Orders order = (Orders)it.next();
System.out.println(order.getOrderNumber());
}
}
InitSessionUtil.closeSession();
上述代码实现从数据库检索出每个客户所发出订单的订购数量,若客户表有N条记录,则会执行1 + N次select语句,显然,频繁的执行SQL语句会降低运行性能。为减少执行select语句条数,可采用批量延迟加载,即<set>元素中设置属性batch-size。
<set name="orders" inverse="true" lazy="true" batch-size="3">
当执行到set.iterator()时,若Session的缓存中存在多余3个的集合代理类实例未被初始化,则会按顺序批量初始化3个集合代理类实例,即:
select * from orders where CUSTOMER_ID in (?, ?, ?)
若不足3个集合代理类实例未被初始化,则批量初始化剩余的集合代理类实例。这样,当下次执行set.iterator()时,若集合代理类实例已被初始化,则不会再执行select语句,从而达到减少执行select语句的目的。
(2) 批量立即检索
2. 用带子查询的select语句批量初始化集合代理类实例
<set>元素的fetch属性可选择"select"(默认)和"subselect"。若映射文件中的<set>元素设置如下:
<set name="orders" inverse="true" lazy="true" fetch="subselect">
则当调用set.iterator()后,会执行下面子查询:
select * from orders where CUSTOMER_ID in (select ID from customers)
也就是说,若Session缓存中有N个集合代理类实例,则Hibernate能够通过带子查询的select语句,批量初始化这N个集合代理类实例。
注:当fetch="subselect"时不必设置batch-size;当fetch="select"时可显示设置batch-size。
3. 迫切左外连接检索(fetch属性为join)
<set name="orders" inverse="true" lazy="true" fetch="join">
当调用Session的get()方法(总是立即检索)后,会执行left outer join查询:
select * from customers left outer join orders on ID=CUSTOMER_ID where ID=?
虽然Query的list()方法和get()方法一样,总是立即检索,但前者常忽略映射文件中配置的迫切左外连接检索策略,Hibernate对于Customers对象的orders集合仍然采用延迟加载策略。
二、多对一和一对一关联的检索策略
在映射文件中,<many-to-one>和<one-to-one>分别用来设置多对一和一对一关联关系,若没有显示设置lazy属性(proxy/no-proxy/false)和fetch属性(join/select),则采用默认的延迟检索策略。如果fetch="join",则lazy属性被忽略,此时是否显示lazy属性已无意义。
1. 迫切左外连接检索(fetch="join")
若<many-to-one>元素的fetch属性为join,则在检索Orders对象时,对关联的Customers对象使用迫切左外连接检索策略。对于以下程序代码:
Session hibernateSession = InitSessionUtil.openSession();
Orders o = (Orders)hibernateSession.get(cls, id);
Customers c = o.getCustomer();
InitSessionUtil.closeSession();
在运行hibernateSession.get(cls, id)方法时,Hibernate需要决定:
(1) 类级别的检索策略:get()方法在类级别总是使用立即检索策略;
(2) 与Orders多对一关联的Customers对象的检索策略取决于Orders映射文件中<many-to-one>元素的fetch和lazy属性,这里fetch="join",因此lazy属性是否显示已无意义,采用迫切左外连接检索策略;
(3) 与Customers一对多的Orders对象的检索策略取决于Customers映射文件中的<set>元素的属性lazy和fetch属性。这里lazy="false",即立即检索策略。
因此,Hibernate执行以下select语句:
select * from orders o inner join customers c on o.CUSTOMER_ID=c.ID where o.ID=4
select * from orders o where oCUSTOMER_ID=4
若(3)中<set>元素的lazy=true,则执行以下select语句:
select * from orders o inner join customers c on o.CUSTOMER_ID=c.ID where o.ID=4
注意,Query的list()方法会忽略映射文件中配置的迫切左外连接检索策略。例如以下程序代码:
Session hibernateSession = InitSessionUtil.openSession();
Query query = hibernateSession.createQuery("FROM Orders o");
List list = query.list();
for (int i = 0, size = list.size(); i < size; i++) {
Orders o = (Orders)list.get(i);
// o.getCustomer()返回Customers代理类实例的引用,这个代理类实例的OID由orders表中
// 的 CUSTOMER_ID决定
Customers c = o.getCustomer();
// 执行select语句,初始化Customers代理类实例
System.out.println(c.getName());
}
InitSessionUtil.closeSession();
运行query.list()方法时,执行以下select语句:
select * from orders o
当第一次运行到c.getName()时,初始化Customers代理类实例,执行select语句如下:
select * from customers c where c.ID=1
2. 延迟检索
如果希望检索Orders对象时,延迟检索关联的Customers对象,只要将Orders类对应的映射文件中的<many-to-one>的lazy和fetch属性设置为proxy和select即可。
对于一对一关联,若使用延迟加载策略,必须将<one-to-one>元素的属性constrained置为"true",它表明Orders对象必须与一个Customers对象关联,即Orders对象的customer属性不允许为null。
3. 无代理延迟加载
如果对Orders对象的customer属性使用无代理延迟检索,需将<many-to-one>元素的lazy属性置为"no-proxy"。与延迟加载的区别在于:(1)延迟加载,可以提供更长的延迟加载Customers对象的时间;(2)无代理延迟加载,可以直接提供更加透明的持久化服务,从而避免仅提供由Hibernate生成的Customers代理类实例。
4. 批量延迟检索和批量立即检索
如果希望检索Orders对象时,批量延迟检索关联的Customers对象,可通过设置Customers类对应的映射文件<class>元素的batch-size属性实现。
<class name="org.test.pojo.Customers" table="customers" batch-size="3">
若<many-to-one>的lazy属性置为proxy,则第一次调用Customers代理类实例的属性时将会批量初始化Customers代理类实例。例如以下程序代码:
Session hibernateSession = InitSessionUtil.openSession();
Query query = hibernateSession.createQuery("FROM Orders o");
List list = query.list();
for (int i = 0, size = list.size(); i < size; i++) {
Orders o = (Orders)list.get(i);
// 返回Customers代理类实例
Customers c = o.getCustomer();
// 执行select语句,初始化Customers代理类实例
System.out.println(c.getName());
}
InitSessionUtil.closeSession();
当调用c.getName时会批量初始化Session缓存中的3个Customers代理类实例。注意这里应根据实际情况确定批量检索数目,合同的批量检索数目应该控制在3-10个,若批量检索过大,延迟加载将会失去意义。
Archiver|手机版|科学网 ( 京ICP备07017567号-12 )
GMT+8, 2024-5-19 02:25
Powered by ScienceNet.cn
Copyright © 2007- 中国科学报社