Hibernate提供了三种检索策略:立即检索、延迟检索和迫切左外连接检索。
类级别检索策略是指定主对象进行检索(如Customer);关联级别检索策略是对与主对象关联的对象进行检索(如Order)。
检索策略作用域 | 可选的检索策略 | 默认的检索策略 | 运行时受影响的检索方法 |
类级别 | 立即检索 延迟检索 | 延迟检索 | 仅影响Session的load()方法 |
关联级别 | 立即检索 延迟检索 迫切左外连接检索 | 延迟检索 | 影响Session的load()和get()方法,以及Query API和Criteria API;例外情况是Query API会忽略迫切左外连接检索策略 |
检索策略 | 类级别 | 关联级别 |
立即检索 | 立即加载检索方法指定的对象 | 立即加载与检索方法指定的对象关联的对象,可以设定批量检索数量 |
延迟检索 | 延迟加载检索方法指定的对象 | 延迟加载与检索方法指定的对象关联的对象,可以设定批量检索数量 |
迫切左外连接检索 | 不适用 | 通过左外连接加载与检索方法指定的对象关联的对象 |
属性 | 类级别 | 一对多关联级别 | 多对一关联级别 |
lazy | <class>元素的lazy属性可选值为:true(延迟检索)和false(立即检索),默认值为true | <set>元素的lazy属性可选值为:true(延迟检索)、extra(增强延迟检索)和false(立即检索),默认值为true | <many-to-one>元素的lazy属性可选值为:proxy(延迟检索)、no-proxy(无代理延迟检索)和false(立即检索),默认值为true |
fetch | 无此属性 | <set>元素的fetch属性可选值为:select(select查询语句)、subselect(带子查询的select语句)和join(迫切左外连接检索),默认值为select | <many-to-one>元素的fetch属性可选值为:select(select查询语句)和join(迫切左外连接检索),默认值为select |
batch-size | 设定批量检索数量,可选值为一个正整数,默认为1。合理取值为3-10,仅适用于关联级别的立即检索与延迟检索,<class>和<set>包含此属性。 |
1.类级别的检索策略
1.1.立即检索
Customer.hbm.xml:
<class name="mypack.Customer" table="CUSTOMERS" lazy="false">
当通过Session的load()方法检索Customer对象时:
Customer customer = (Customer)session.load(Customer.class, new Long(1));
Hibernate会立即执行select语句:
select * from CUSTOMERS where ID=1;
1.2.延迟检索
Customer.hbm.xml:
<class name="mypack.Customer" table="CUSTOMERS">
或者<class name="mypack.Customer" table="CUSTOMERS" lazy="true">
当执行Session的load()方法时,Hibernate不会执行select语句,仅返回Customer类的代理类的实例。
代理类仅初始化了OID属性,其余属性为null,应用程序第一次访问Customer的代理类实例时(如customer.getXXX()), Hibernate会初始化代理类实例,执行select语句真正从数据库中加载Customer对象的所有数据。
访问代理类实例的getId()方法时,不会触发Hibernate初始化代理类的行为。
若加载的Customer对象在数据库中不存在,Session的load()方法不会抛异常,只有当运行getXXX()方法时才会抛异常。
若在整个Session范围内,应用程序没有访问过Customer对象,那么Customer代理类的实例一直不会被初始化,且Customer代理类的实例只有在当前Session范围内才能被初始化。
Hibernate的initialize()静态方法用于在Session范围内显示初始化代理类实例:
Customer customer = (Customer)session.load(Customer.class, new Long(1));
if(!Hibernate.isInitialized(customer)
Hibernate.initialize(customer);
session.close();
customer.getName();
这样,当Session关闭后,可正常访问Customer游离对象。
值得注意的是,Session的get()方法及Query的list()方法在Customer类级别总是使用立即检索策略。
2.一对多和多对多关联的检索策略
Customer.hbm.xml中用以下代码配置Customer和Order的一对多关联关系:
<set name="orders" inverse="true">
<key column="CUSTOMER_ID" />
<one-to-many class="mypack.Order" />
</set>
2.1.立即检索(lazy属性值为false)
Customer.hbm.xml:
<set name="orders" inverse="true" lazy="false">...</set>
当通过Session的get()方法检索Customer对象时:
Customer customer = (Customer)session.get(Customer.class, new Long(1));
Hibernate会立即执行select语句:
select * from CUSTOMERS where ID=1;
select * from ORDERS where CUSTOMER_ID=1;
2.2.延迟检索(lazy属性值为true)
Customer.hbm.xml:
<set name="orders" inverse="true">...</set>
或者
<set name="orders" inverse="true" lazy="true">...</set>
此时运行Session的get()方法检索Customer对象时,只会执行以下select语句:
select * from CUSTOMERS where ID=1;
Customer的orders属性引用了一个没有被初始化的集合代理类实例。以下两种情况会初始化该代理类实例:
(1)应用程序第一次访问它,如调用iterator()、size()、isEmpty()或contains()方法:
Set orders = customer.getOrders();
Iterator it = orders.iterator();
(2)Hibernate的initialize()静态方法初始化
Set orders = customer.getOrders();
Hibernate.initialize(orders);
2.3.增强延迟检索(lazy属性值为extra)
Customer.hbm.xml:
<set name="orders" inverse="extra">...</set>
与一般延迟检索策略(lazy为true)很相似,区别在于初始化代理类实例的时机不同,应用程序第一次访问orders属性的iterator()方法时,会初始化代理类实例,而当应用程序第一次访问orders属性的size()、isEmpty()或contains()方法时,不会初始化代理类实例,仅通过特定的select语句查询必要的信息,而不会检索所有Order对象。
2.4.批量延迟检索和批量立即检索(使用batch-size属性)
Customer.hbm.xml:
<set name="orders" inverse="true" lazy="true" batch-size=3>...</set>
当访问customer1.getOrders().iterator()方法时,会批量初始化三个orders集合代理类实例,Hibernate执行select语句:
select * from ORDERS where CUSTOMER_ID in (1,2,3);
此时,访问customer2/customer3.getOrders().iterator()方法时,不需要再初始化orders集合代理类实例,当访问customer4.getOrders().iterator()方法时,会自动再批量初始化三个orders集合代理类实例。
Customer.hbm.xml:
<set name="orders" inverse="true" lazy="false" batch-size=3>...</set>
对于以下检索方法:
List customers = session.createQuery("from Customer as c").list();
会立即执行以下select语句:
select * from CUSTOMERS;
select * from ORDERS where CUSTOMER_ID in (1,2,3);
2.5.用带子查询的select语句整批量初始化orders集合(fetch属性为subselect)
List customers = session.createQuery("from Customer as c").list(); //第一行
Iterator customerI = customers.iterator(); //第二行
Customer customer = (Customer)customerI.next(); //第三行
Iterator orderI = customer.getOrders().iterator(); //第四行
(1)<set name="orders" inverse="true" lazy="false" batch-size=3>...</set>
第一行时,会立即执行以下select语句:
select * from CUSTOMERS;
select * from ORDERS where CUSTOMER_ID in (1,2,3);
(2)<set name="orders" inverse="true" lazy="false" fetch="subselect">...</set>
第一行时,会立即执行以下select语句:
select * from CUSTOMERS;
select * from ORDERS where CUSTOMER_ID in (select ID from CUSTOMERS);
(3)
<set name="orders" inverse="true" batch-size=3>...</set>
第四行时会初始化orders集合代理类实例,执行以下select语句:
select * from ORDERS where CUSTOMER_ID in (1,2,3);
(4)
<set name="orders" inverse="true" fetch="subselect">...</set>
第四行时会初始化orders集合代理类实例,执行以下select语句:
select * from ORDERS where CUSTOMER_ID in (select ID from CUSTOMERS);
由此可见,假定Session缓存中有n个orders集合代理类实例没有被初始化,那么当fetch属性为"subselect"时,Hibernate能够通过子查询的select语句,来整批量初始化orders集合代理类实例。
当fetch属性为"subselect"时,不必设置batch-size属性,即使设置了也会被忽略。
2.6.迫切左连接检索(fetch属性为"join")
Customer.hbm.xml:
<set name="orders" inverse="true" fetch="join">...</set>
对于以下检索方法:
Customer customer = (Customer)session.get(Customer.class, new Long(1));
会采用迫切左连接检索策略来检索所有关联的Order对象,执行以下select语句:
select * from CUSTOMERS left outer join ORDERS on CUSTOMERS.ID = ORDERS.CUSTOMER_ID where CUSTOMERS.ID=1;
Query的list()方法会忽略
迫切左连接检索策略,
对于以下检索方法:
List customers = session.createQuery("from Customer as c").list();
Hibernate
执行以下select语句:
select * from CUSTOMERS;
3.多对一和一对一关联的检索策略
Order.hbm.xml:
<many-to-one name="customer" column="CUSTOMER_ID" class="mypack.Customer" />
3.1.迫切左外连接检索(fetch属性为"join")
<many-to-one ... fetch="join" />
对于以下检索方法:
Order order= (Order)session.get(Order.class, new Long(1));
若Customer.hbm.xml中<set>元素的lazy属性为false,Hibernate执行以下select语句:
select * from ORDERS left outer join CUSTOMERS on ORDERS.CUSTOMER_ID = CUSTOMERS.ID where ORDERS.ID = 1;
select * from ORDERS where CUSTOMER_ID = 1;
若Customer.hbm.xml中<set>元素的lazy属性为true,Hibernate执行以下select语句:
select * from ORDERS left outer join CUSTOMERS on ORDERS.CUSTOMER_ID = CUSTOMERS.ID where ORDERS.ID = 1;
Query的list()方法会忽略迫切左连接检索策略.
3.2.延迟检索(lazy属性为"proxy")
<many-to-one ... lazy="proxy" />
对于以下检索方法:
Order order= (Order)session.get(Order.class, new Long(1));
Hibernate执行以下select语句:
select * from ORDERS where ID = 1;
当访问customer的getXXX()方法时,Hibernate执行:
select * from CUSTOMERS where ID = 1;
若此时Customer.hbm.xml中<set>元素的lazy属性为false,Hibernate还执行:
select * from ORDERS where CUSTOMER_ID = 1;
对于一对一关联,如果采用延迟加载策略,必须把<one-to-one>元素的constrained属性设为true:
<one-to-one name="customer" class="mypack.Customer" constrained="true" />
3.3.无代理延迟检索(lazy属性为"no-proxy")
对于以下程序代码:
Order order= (Order)session.get(Order.class, new Long(1)); //第一行
Customer customer = order.getCustomer(); //第二行
customer.getId(); //第三行
customer.getName(); //第四行
若Order对象的customer属性使用延迟检索(proxy),程序将在第四行初始化Customer代理类实例。
若Order对象的customer属性使用无代理延迟检索(no-proxy),程序将在第二行初始化Customer代理类实例。
由此可见,lazy属性为proxy时,可以更加延迟加载Customer对象。
3.4.立即检索(lazy属性为"false")
<many-to-one ... lazy="false" />
对于以下程序代码:
Order order= (Order)session.get(Order.class, new Long(1));
Hibernate执行以下select语句:
select * from ORDERS where ID = 1;
select * from CUSTOMERS where ID = 1;
若此时Customer.hbm.xml中<set>元素的lazy属性为false,Hibernate还执行:
select * from ORDERS where CUSTOMER_ID = 1;
3.5.批量延迟检索和批量立即检索(使用batch-size属性)
Order.hbm.xml中<many-to-one>元素的lazy属性为默认值proxy,设置Customer.hbm.xml中<class>元素的batch-size属性:
<class name="mypack.Customer" table="CUSTOMERS" batch-size="3" >
当访问customer.getXXX()方法时,Hibernate会批量初始化三个Customer代理类实例:
select * from CUSTOMERS where ID in (1,2,3);
假如Customer.hbm.xml文件中<class>和<set>元素都设置了
batch-size属性:
<class name="mypack.Customer" table="CUSTOMERS" batch-size="3" >
<set name="orders" inverse="true" batch-size="3" lazy="false" >
并且Order.hbm.xml文件中<many-to-one>元素的lazy属性为false:
<many-to-one name="customer" column="CUSTOMER_ID" class="mypack.Customer" lazy="false" />
那么对于以下程序时:
List orders = session.createQuery("from Order as c").list();
Hibernate会执行以下select语句:
select * from ORDERS;
select * from CUSTOMERS where ID in (1,2,3);
select * from ORDERS where CUSTOMER_ID in (1,2,3);
4.控制迫切左外连接检索的深度
hibernate.cfg.xml:
<property name="max_fetch_depth">1</property>
hibernate.properties:
hibernate.max_fetch_depth = 1
5.在应用程序中显示指定迫切左外连接检索策略
session.createQuery("from Customer as c left join fetch c.orders where c.id=1").list();
会执行以下select语句:
select * from CUSTOMERS left outer join ORDERS on CUSTOMERS.ID = ORDERS.CUSTOMER_ID where CUSTOMER.ID = 1;
6.属性级别的检索策略
在对象-关系映射文件中,<property>和<component>元素的lazy属性如果为true,表示采用延迟检索策略,如果为false,表示采用立即检索策略,默认值为false。
属性级别的延迟检索策略适用于二进制大对象、字符串大对象以及大容量组件类型的属性。