Hibernate的检索策略小结

来源:互联网 发布:软件发明专利 编辑:程序博客网 时间:2024/04/19 10:34

 Hibernate的检索策略包括类级别检索策略和关联级别检索策略。

  类级别检索策略有立即检索和延迟检索,默认的检索策略是立即检索。在Hibernate映射文件中,通过在<class>上配置lazy属性来确定检索策略。对于Session的检索方式,类级别检索策略仅适用于load方法;也就说,对于get、qurey检索,持久化对象都会被立即加载而不管lazy是false还是true.一般来说,我们检索对象就是要访问它,因此立即检索是通常的选择。由于load方法在检索不到对象时会抛出异常(立即检索的情况下),因此我个人并不建议使用load检索;而由于<class>中的lazy属性还影响到多对一及一对一的检索策略,因此使用load方法就更没必要了。

  关联级别检索策略有立即检索、延迟检索和迫切左外连接检索。对于关联级别检索,又可分为一对多和多对多、多对一和一对一两种情况讨论。

  一对多和多对多关联关系一般使用<set>配置。<set>有lazy和outer-join属性,它们的不同取值绝对了检索策略。

  1)立即检索:这是一对多默认的检索策略,此时lazy=false,outer-join=false.尽管这是默认的检索策略,但如果关联的集合是无用的,那么就不要使用这种检索方式。

  2)延迟检索:此时lazy=true,outer-join=false(outer-join=true是无意义的),这是优先考虑的检索方式。

  3)迫切左外连接检索:此时lazy=false,outer-join=true,这种检索策略只适用于依靠id检索方式(load、get),而不适用于query的集合检索(它会采用立即检索策略)。相比于立即检索,这种检索策略减少了一条sql语句,但在Hibernate中,只能有一个<set>配置成outer-join=true.

  多对一和一对一检索策略一般使用<many-to-one>、<one-to-one>配置。<many-to-one>中需要配置的属性是outer-join,同时还需要配置one端关联的<class>的lazy属性(配置的可不是<many-to-one>中的lazy哦),它们的组合后的检索策略如下:

  1) outer-join=auto:这是默认值,如果lazy=true为延迟检索,如果lazy=false为迫切左外连接检索。

  2) outer-join=true,无关于lazy,都为迫切左外连接检索。

  3) outer-join=false,如果lazy=true为延迟检索,否则为立即检索。

  可以看到,在默认的情况下(outer-join=auto,lazy=false),对关联的one端对象Hibernate采用的迫切左外连接检索。依我看,很多情况下,我们并不需要加载one端关联的对象(很可能我们需要的仅仅是关联对象的id);另外,如果关联对象也采用了迫切左外连接检索,就会出现select语句中有多个外连接表,如果个数多的话会影响检索性能,这也是为什么Hibernate通过hibernate.max_fetch_depth属性来控制外连接的深度。对于迫切左外连接检索,query的集合检索并不适用,它会采用立即检索策略。

  对于检索策略,需要根据实际情况进行选择。对于立即检索和延迟检索,它们的优点在于select语句简单(每张表一条语句)、查询速度快,缺点在于关联表时需要多条select语句,增加了访问数据库的频率。因此在选择即检索和延迟检索时,可以考虑使用批量检索策略来减少select语句的数量(配置batch-size属性)。对于切左外连接检索,优点在于select较少,但缺点是select语句的复杂度提高,多表之间的关联会是很耗时的操作。另外,配置文件是死的,但程序是活的,可以根据需要在程序里显示的指定检索策略(可能经常需要在程序中显示指定迫切左外连接检索)。为了清楚检索策略的配置效果如何,可以配置show_sql属性查看程序运行时Hibernate执行的sql语句。

 

 

//////////////////////////////////////////////////////////////////////////////////////

 

5.1 Hibernate的检索策略有:立即检索、延迟检索、迫切左外连接检索。

5.1.1  首先介绍一对多和多对多关系的检索策略

有两个表orderscustomers orderscustomers是多对一关系。Orders外键customer_id参照customers表的主键id,如下所示:

 

orders                          Customers

Id   order_id    customer_id                id   name

1     order1     1                                       1   tom

2     order2     1                                       2   peter

3     order3     2                                       3   kallen

 

在映射文件中,用<set>元素来配置一对多及多对多关联关系。

如:

  <set name=orders inverse=true >

     <key column=coustomer_id>

     <one-to-many class=mypack.order>

 </set>

以下代码通过sessionget()方法加载OID1Customer   对象.

tx = session.beginTransaction();

Customer customer = (Customer) session.get(Customer.class,new Long(1));

Set orders = customer.getOrders();

Iterator it = orders.iterator();

5.1.1.1  立即检索策略

执行sessionget方法时,如果采用立即检索策略,Hibernate会执行以下select 语句:

Select * from customers where id=1;

Select * from orders where customer_id=1;

通过以下select 语句,Hibernate加载了一个Customer对象和两个order对象.Customer对象的orders属性引用的是一个Hibernate提供的Set集合类实例,这个实例引用了两个order对象.现在,如果不需要orders表中的数据那么以上sql语句中,第二个语句就白白浪费了时间和内存。

 

5.1.1.2  延迟检索策略

对于set元素应该优生考虑延迟检索策略.

<set name=orders inverse=true lazy=true>(lazy的默认值是:false)

此时运行sessionget()方法时,立即检索Customer对象,不对order对象进行检索。仅执行以下select 语句:

select  *  form  customer  where  id=1

这样,在不需要orders表数据的情况下可以避免无谓的操作。此时,没有创建order实例,当需要时,Hibernate会初始化order对象。

 

5.1.1.3  批量延迟检索

<set>元素的batch-size 属性,用于为延迟检索或立即检索策略设定批量检索的数量。

lazy=true时,为批量延迟检索。

以下Sessionfind方法,用于检索所有的Customer对象:

tx = session.beginTransaction();

List customerLists = session.find(“from customer as c”);

Iterator it = customerLists.iterator();

Customer customer1 = (Customer)customerIterator.next();

Customer customer2 = (Customer)customerIterator.next();

Customer customer3 = (Customer)customerIterator.next();

 

Iterator orderIterator1 = customer1.getOrder().iterator();

Iterator orderIterator2 = customer2.getOrder().iterator();

Iterator orderIterator3 = customer3.getOrder().iterator();

 

sessionfind方法检索Customer对象时,仅立即执行检索Customer对象的select   语句. Select * from customers;

customers表中共有四条记录,因此,Hibernate将创建四个Customer对象,它们的orders属性各自引用一个代理类实例。

当执行 Iterator orderIterator1 = customer1.getOrder().iterator();  方法时,会初始化OID  1Customer对象的orders对象,Hibernate执行的select语句为:

Select * from orders where customer_id=1

 

当执行 Iterator orderIterator2 = customer2.getOrder().iterator();  方法时,会初始化OID  2Customer对象的orders对象,Hibernate执行的select语句为:

Select * from orders where customer_id=2。依次类推。由此可见,初始化三个orders集合类实例,Hibernate必须执行三条查询语句。为了减少select语句的数目,可以采用批量延迟检索,即设置<set>元素的batch-size属性:

<set name=orders inverse=true lazy=true batch-size=3>

这样,当访问Iterator orderIterator1 = customer1.getOrder().iterator()方法时, Session的缓存中共有三个orders集合类实例未被初始化,现在batch-size=3,因此会批量初始化三个orders集合类实例,Hibernate会执行的select的语句为:

Select * from orders where customer=1 or customer=2 or customer=3。当访问Iterator orderIterator2 = customer2.getOrder().iterator()时,不需要初始化它的orders集合类实例。

 

5.1.1.4   批量立即检索

对于find 方法,List customerLists = session.find(“from customer as c”);

orders集合默认为立即检索策略时,<set name=orders inverse=true>

find方法会立即执行以下语句:

select * from customers;

select * from orders where customer_id=1;

select * from orders where customer_id=2;

select * from orders where customer_id=3;

为了减少 select 语句数量,可以设置 set元素的batch-size属性。

: <set name=orders inverse=true batch-size=2>

此时,find方法会立即执行以下sql语句:

select * from customers;

select * from orders where customer_id=1 or customer_id=2;

select * from orders where customer_id=3;

batch-size=3,此时find()方法会立即执行以下sql语句:

select * from customers;

select * from orders where customer_id=1 or customer_id=2 or customer_id=3;

 

 

5.1.1.5   迫切左外连接检索

如果把outer-join设备true

:<set name=orders inverse=true outer-join=true”>

当执行customer = (Customer)session.get(Customer.class,new Long(1));

时,会执行以下sql语句:

Select * from customers left outer join orders on customer.id=orders.customer_id where customer.id=1;

值得注意的是:sessionfind方法,会忽略映射文件中配置的迫切左外连接,即使outer-join=true,当用List customer = session.find(“from customer as c”);时,

HibernateCustomer对象的orders 集合仍然采用立即检索策略,Hibernate会执行的select 语句为:

Select * from customer;

Select * from orders where customer_id=1;

Select * from orders where customer_id=2;

Select * from orders where customer_id=3;

 

/////////////////////////////////////

 

hibernate中lazy什么时候不用

 

 

hibernate3.0中lazy有三个值,true,false,proxy,默认的是lazy="proxy".
具体设置成什么要看你的需求,并不是说哪个设置就是最好的。
假如在student对象中包含一个head对象
如果你确定在用student对象的时候就要用到head对象里的属性,那你就设置立即加载,因为设置立即加载那么在查询student的同时就会查询student的head,hibernate就会在查询的时候关联两张表从而生成的sql就可能只有一条。而如果你设置的是延迟加载,那么肯定会要生成1+N条sql语句:其中“1”是查询student的语句,“N”是根据N个student的id去查询head的N条语句。而且,延迟加载是要用到的时候才去执行查询,这样系统判断那里需要加载,那里不需要加载也需要时间,性能上肯定就不如立即加载了!
如果,你是有的地方需要用到student的时候才用到head属性,那么你就设置成延迟加载,因为查询2张表的数据肯定要比查询1张表的数据消耗大。
到低要怎样设置就要看你的实际需求了

 

 

 

 

 

 

 

////////////////////////////////////

 

 

 

 

hibernate中lazy的使用 (2008-09-12 13:57:47)
标签:杂谈  分类:hibernate
hibernate中lazy的使用

lazy,延迟加载

Lazy的有效期:只有在session打开的时候才有效;session关闭后lazy就没效了。

lazy策略可以用在:

* <class>标签上:可以取值true/false

* <property>标签上,可以取值true/false,这个特性需要类增强

* <set>/<list>等集合上,可以取值为true/false/extra

* <one-to-one>/<many-to-one>等标签上,可以取值false/proxy/no-proxy

6.1 getload的区别:

* get不支持延迟加载,而load支持。

* 当查询特定的数据库中不存在的数据时,get会返回null,而load则抛出异常。

6.2 (Class)的延迟加载

* 设置<class>标签中的lazy="true",或是保持默认(即不配置lazy属性)

* 如果lazy的属性值为true,那么在使用load方法加载数据时,只有确实用到数据的时候才会发出sql语句;这样有可能减少系统的开销。

* //不会发出查询sql

       System.out.println("group id=" + group.getId());

这里有一个问题,为什么加载主键的时候不需要发出sql语句。

6.3 集合(collection)的延迟加载:可以取值truefalseextra

* 保持集合上的lazy的默认值,此时的效果和lazy="extra"是基本一样的。

   * 设置集合上的lazy=extra,此时的效果和lazy属性的默认值是基本一样的。但是推荐使用这个属性值,因为在统计时这种情况显得比较智能。当然延迟是有效果的。

* 设置集合上的lazy=false

true:默认取值,它的意思是只有在调用这个集合获取里面的元素对象时,才发出查询语句,加载其集合元素的数据

false:取消懒加载特性,即在加载对象的同时,就发出第二条查询语句加载其关联集合的数据

extra:一种比较聪明的懒加载策略,即调用集合的size/contains等方法的时候,hibernate

并不会去加载整个集合的数据,而是发出一条聪明的SQL语句,以便获得需要的值,只有在真正需要用到这些集合元素对象数据的时候,才去发出查询语句加载所有对象的数据

6.4 Hibernate单端关联懒加载策略:即在<one-to-one>/<many-to-one>标签上可以配置

懒加载策略。可以取值为:false/proxy/no-proxy

false:取消懒加载策略,即在加载对象的同时,发出查询语句,加载其关联对象

proxy:这是hibernate对单端关联的默认懒加载策略,即只有在调用到其关联对象的方法的时候才真正发出查询语句查询其对象数据,其关联对象是代理类

no-proxy:这种懒加载特性需要对类进行增强,使用no-proxy,其关联对象不是代理类

注意:在class标签上配置的lazy属性不会影响到关联对象!!!

原创粉丝点击