精通hibernate学习笔记(7)[检索策略]

Customer和Order为例

1、hibernate检索策略简介

Hibernate执行检索方法时(load、get、find),需要获得以下信息:

a.类级别检索策略:session.load,session.get,session.find方法,直接指定检索的是Customer对象,对Customer对象到底采用立即检索,还是延迟检索?

b.关联级别检索策略:对与Customer关联的Order对象,即Customer的orders集合,到底采用立即检索还是延迟检索,还是迫切左外联接检索?

精通hibernate学习笔记(7)[检索策略]

精通hibernate学习笔记(7)[检索策略]

精通hibernate学习笔记(7)[检索策略]Hibernate还允许在应用程序中以编程方式显式设定检索策略,同时会覆盖映射文件中配置的检索策略,如果代码没有设定,使用映射文件中配置的检索策略。但是HQL检索方式会忽略映射文件的迫切左外连接检索策略。

2、类级别检索策略

类级别可选策略包括立即检索和延迟检索,默认为立即检索。如果<class>元素的lazy属性为true,表示延迟检索;如果lazy属性为false,表示才用立即检索。lazy属性还能决定多对一、一对一关联界别的检索策略。

2.1立即检索

会立即进行sql查询

2.2延迟检索

设置<class>中的lazy="true",当session.load()方法执行时,不会立即执行select语句,仅仅返回Customer类的代理类的实例,这个代理类具有以下特征:

a.由hibernate在运行时动态生成,它扩展了Customer类,但是它的实现对于应用程序是透明的

b.当hibernate创建Customer代理类的时候,仅仅初始化了他的OID属性,其他属性都是null,因此,占用的内存很少。

c.当应用程序第一次访问Customer代理类实例时(如调用get或set方法等),hibernate会初始化代理类实例,在初始化过程中执行select语句,真正从数据库中加载Customer对象的所有数据,getID()方法例外。

lazy=true时,会影响load方法的运行时行为:

a.如果对应的Customer在数据库中不存在,load方法不会抛出异常,只有在getName方法时才会抛出异常。

b.如果在session范围内,没有访问过Customer对象,那么其代理类的实例不会被初始化,不会执行任何select语句。

c.Hibernate类的initialize静态方法用于在Session范围内显示初始化代理类实例,isInitialized()方法判断代理类实例是否已经被初始化。

d.程序调用getId()方法时,不会触发Hibernate初始化代理类实例的行为。

注:<class>的lazy="true"不会影响get和find方法,他们还是使用立即检索策略。

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

在映射文件中,<set>元素用来配置一对多,多对多关联关系。其中有lazy和outer-join属性。

<set>元素不仅用于映射一对多,多对多关联关系,还能映射存放值类型数据的集合(待讲)。

Hibernate为Set集合类也提供了代理类,它扩展了Set接口,与持久化类的代理类不同的是,不管有没有设置延迟检索策略,Session的检索方法在为Customer对象的orders属性赋值时,orders属性总是引用集合代理类的实例。

3.1立即检索

一对多关联关系的默认检索策略。

包括对Customers表和关联表Orders的检索。在一对多关联级别中不能随意使用立即检索策略。

3.2延迟检索

设置<set>的属性lazy="true"

仅立即检索Customer对象,同时也不会创建Order代理类的实例,因为无法知道与Customer关联的所有Order对象的OID。getOrders()方法返回的是Hibernate提供的集合代理类实例,当应用程序第一次访问它,如调用orders.getIterator()方法时,Hibernate会初始化这个集合代理类实例,并检索所有关联的Order对象。

3.3批量延迟检索和批量立即检索

<set>元素有一个batch-size属性,用于为延迟检索或立即检索策略设定批量检索的数量。批量检索能减少select语句的数目,提高延迟检索或立即检索的性能。

3.3.1批量延迟检索

注:批量指的是set中的元素

ListcustomerLists=session.find("fromCustomerasc");

IteratorcustomerIterator=customerLists.iterator();

Customercustomer1=(Customer)customerIterator.next();

Customercustomer2=(Customer)customerIterator.next();

Customercustomer3=(Customer)customerIterator.next();

Customercustomer4=(Customer)customerIterator.next();

IteratororderIterator1=customer1.getOrders().iterator();//执行一个selectOID=1,初始化orders集合代理类实例

//select*fromORDERSwhereCUSTOMER_ID=1

IteratororderIterator2=customer2.etOrders().iterator();//执行一个selectOID=2,初始化orders集合代理类实例

//select*fromORDERSwhereCUSTOMER_ID=2

IteratororderIterator3=customer3.etOrders().iterator();//执行一个selectOID=3,初始化orders集合代理类实例

//select*fromORDERSwhereCUSTOMER_ID=3

IteratororderIterator4=customer4.etOrders().iterator();//执行一个selectOID=4,初始化orders集合代理类实例

//select*fromORDERSwhereCUSTOMER_ID=4

可见,为了初始化4个Orders集合代理类实例,必须执行四条查询Orders表的select语句。为了减少select语句的数目,可以采用批量延迟策略,设置<set>元素的batch-size属性。

<setname="ordersinverse="true"lazy="true"batch-size=3>

当访问customer1.getOrders().iterator()方法时,此时Session的缓存中共有四个orders集合代理类实例没有被初始化,由于<set>元素的batch-size=3,因此会批量初始化三个orders集合代理类实例:

select*fromORDERSwhereCUSTOER_ID=1orCUSTOER_ID=2orCUSTOER_ID=3

3.3.2批量立即检索

<setnam="orders"invers="true"batch-size="3">

3.4迫切左外连接检索

<setname="orders"inverse="true"outer-join="true">

注:也是针对<set>

对于customer=(Customer)session.get(Customer.class,newLong(1));会执行一下SQL:

select*fromCUSTOMERSleftouterjoinORDERSonCUSTOMERS.ID=ORDERS.CUSTOMER_IDwhereCUSTOMERS.ID=1;

但是find方法会忽略映射文件中配置的策略。就算配置文件中设置了outer-join="true",但是使用find方法时,仍然会采用立即检索策略:

select*fromCUSTOMERS;

select*fromORDERSwhereCUSTOMER_ID=1;

select*fromORDERSwhereCUSTOMER_ID=2;

select*fromORDERSwhereCUSTOMER_ID=3;

select*fromORDERSwhereCUSTOMER_ID=4;

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

<many-to-one

name="customer"

column="CUSTOMER_ID"

class="mypack.Customer"

/>

<many-to-one>元素有一个outer-join属性,它有三个可选值。

a.auto:这是默认值。如果<class>元素的lazy属性为true,那么对与Order关联的Customer对象采用延迟检索策略,否则采用迫切左外连接检索策略。

b.true:不管<class>元素的lazy属性为true还是false,对与Order关联的Customer对象都采用迫切左外连接检索策略。

c.false:始终不会对与Order关联的Customer对象采用迫切左外连接检索策略。

对于多对一或一对一关联,应该优先考虑外连接检索策略。因为它使用的select语句数目少。

4.1迫切左外连接检索

默认情况下,<many-to-one>元素的outer-join属性为auto,<class>元素的lazy属性为false,因此在检索Order对象时,对关联的Customer对象使用迫切左外连接检索策略。

对于以下代码:

order=(Order)session.get(Order.class,NewLong(1));

在运行session.get()方法时,Hibernate需要决定以下检索策略:

a.类级别的Order对象的检索策略,get()方法在类级别总是使用立即检索策略。

b.与Order多对一关联的Customer对象的检索策略:Order.hbm.xml中<many-to-one>元素配置,设outer-join="true",使用迫切左外连接检索策略。

c.与Customer一对多关联的Order对象的检索策略:Customer.hbm.xml中<set>元素的lazy和outer-join属性配置,设lazy和outer-join属性都为false,使用立即检索策略。

按照以上策略,会执行一下SQL:

select*fromORDERSleftouterjoinCUSTOMERSonORDERS.CUSTOMER_ID=CUSTOMER.IDwhereORDERS.ID=1

select*fromORDERSwhereCUSTOMER_ID=1

通过以上两条select语句,实际上加载了三个持久化对象:

如果Customer.hbm.xml中的<set>元素的outer-join属性为true,因此对与Customer的order对象也采用迫切外连接检索策略,那么SQL会是这样:

select*fromORDERSo1

leftouterjoinCUSTOMERSc1ono1.CUSTOMER_ID=c1.ID

leftouterjoinORDERSo2onc1.ID=o2.CUSTOMER_ID

whereo1.ID=1

如果外连接表的数目太多,会影响检索性能,此时可以通过Hibernate配置文件中的hibernate.max_fetch_depth属性来控制外连接的深度,如果设置hibernate.max_fetch_depth=1则只允许外连接一张表,所以SQL还是两个:

select*fromORDERSleftouterjoinCUSTOMERSonORDERS.CUSTOMER_ID=CUSTOMER.IDwhereORDERS.ID=1

select*fromORDERSwhereCUSTOMER_ID=1

注:对于find方法,会忽略映射文件中配置的迫切左外连接检索策略。

4.2延迟检索

如果希望检索Order对象的时候延迟检索关联的Customer对象,需要把Customer.hbm.xml文件的<class>元素的lazy属性设置为true,Order.hbm.xml文件中的<many-to-one>元素的outer-join属性可以设置为auto或false.

order=(Order)session.get(Order.class,newLong(1));

//执行:select*fromORDERSwhereID=1

Customercustomer=order.getCustomer();

//返回一个代理实例

customer.getName();

//执行:select*fromCUSTOMERSwhereID=1;

//select*fromORDERSwhereCUSTOMER_ID=1;(设Customer.hbm.xml中<set>的lazy和outer-join属性都为false,故采用立即检索策略。)

对于一对一关联,如果使用延迟加载策略,必须把<one-to-one>元素的constrained属性设为true:

<one-to-onename="customer"class="mypack.Customer"constrained="true"/>

constrained属性与<many-to-one>元素的not-null属性语义上有些相似,表明Order对象必须和一个Customer对象关联。

4.3立即检索

Order.hbm.xml中

<many-to-one

name="customer"

column="CUSTOMER_ID"

class="mypack.Customer"

outer-join="false"

/>

如果Customer.hbm.xml文件的<class>元素的lazy=false,会对Order关联的Customer对象采用立即检索策略。

对于:Orderorder=(Order)session.get(Order.class,newLong(1));

会执行以下SQL:

select*fromORDERSwhereID=1;

select*fromCUSTOMERS whereID=1;

select*fromORDERSwhereCUSTOMER_ID=1;//假定关联的Order立即检索

4.4批量延迟检索和批量立即检索

4.4.1批量延迟检索

对于以下代码:

ListorderLists=session.find("fromOrderasc");

//select*fromorders

IteratororderIterator=orderLists.iterator();

Orderorder1=(Order)orderIterator.next();

Orderorder2=(Order)orderIterator.next();

Orderorder3=(Order)orderIterator.next();

Orderorder4=(Order)orderIterator.next();

Orderorder5=(Order)orderIterator.next();

Orderorder6=(Order)orderIterator.next();

Customercustomer1=order1.getCustomer();

if(customer1!=null)customer1.getName();//customer.id=1

//select*fromCUSTOMERSwhereID=1

//select*fromORDERSwhereCUSTOMER_ID=1

Customercustomer2=order2.getCustomer();

if(customer2!=null)customer2.getName();//customer.id=2

//与order1关联同一个customer

Customercustomer3=order3.getCustomer();

if(customer3!=null)customer3.getName();//customer.id=3

//select*fromCUSTOMERSwhereID=2

//select*fromORDERSwhereCUSTOMER_ID=2

Customercustomer4=order4.getCustomer();

if(customer4!=null)customer4.getName();//customer.id=4

//select*fromCUSTOMERSwhereID=3

//select*fromORDERSwhereCUSTOMER_ID=3

Customercustomer5=order5.getCustomer();

if(customer5!=null)customer5.getName();//customer.id=5

//select*fromCUSTOMERSwhereID=4

//select*fromORDERSwhereCUSTOMER_ID=4

Customercustomer6=order6.getCustomer();

if(customer6!=null)customer6.getName();//customer.id=6

//不存在

实际执行过程:

可见,四个Customer对象必须执行四条Sql语句,如果为了减少Select语句的数目,可以设置:

<classname="mypack,Customer"table="CUSTOMERS"lazy="true"batch-size="3">

注:设置的是Customer.hbm.xml

会批量初始化三个Customer代理类实例

select*fromCUSTOMERS whereID=1orID=2orID=3;

select*fromORDERSwhereCUSTOMER_ID=1;

select*fromORDERSwhereCUSTOMER_ID=2;

select*fromORDERSwhereCUSTOMER_ID=3;

注:batch-size=3~10;

4.4.2批量立即检索

设:

Customer.hbm.xml

<classname="mypack,Customer"table="CUSTOMERS"lazy="true"batch-size="4">

<setname="orders"inverse="true"batch-size="4">

Order.hbm.xml

<many-to-onename="customer"column="CUSTOMER_ID"class="mypack.Customer"outer-join="false'/>

当执行:

session.find("fromOrderasc");

Order对象采取立即检索,与Order关联的Customer对象采取批量立即检索,对于Customer关联的Order对象也采用批量立即检索:

select*fromORDERS;

select*fromCUSTOMERSwhereID=1orID=2orID=3orID=4;

select*fromORDERSwhereCUSTOMER_ID=1orCUSTOMER_ID=2orCUSTOMER_ID=3orCUSTOMER_ID=4

5、Hibernate对迫切左外连接检索的限制

Select语句如果包含多个一对多关联的外连接,会导致一次检索出大批量数据,从而影响检索性能,所以对迫切左外连接检索作了以下限制

a.在一个select语句中只允许包含一个一对多关联或多对多关联的迫切左外连接。

b.在一个select语句中允许包含多个一对一关联或多对一关联的迫切左外连接

c.如果一个类有多个<set>元素,只允许由一个<set>元素的out=join=true.

d.不限制一条select语句中多对一或者一对一迫切左外连接的数目。

6、在应用程序中显式制定迫切左外连接检索策略

映射文件中设定的检索策略是固定的三种:延迟检索、立即检索、外连接检索。

Hibernate允许在应用程序中覆盖映射文件中设定的检索策略,在运行时决定检索的深度

session.find("fromCustomerascwherec.id=1");

session.find("fromCustomerascleftjoinfetchc.orderswherec.id=1");

第二句会覆盖映射文件的配置策略,而使用左外连接检索!

7、总结

精通hibernate学习笔记(7)[检索策略]

相关推荐