Hibernate 的检索策略

来源:互联网 发布:用mac给iphone充电 编辑:程序博客网 时间:2024/04/24 22:06

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。

属性级别的延迟检索策略适用于二进制大对象、字符串大对象以及大容量组件类型的属性。

0 0
原创粉丝点击