总结Hibernate的检索策略

  通过看孙卫琴的精通Hibernate和平时自己的使用,对他的几种检索策略有了更深的认识,再次总结一下

  问题的引出:

CustomerOrder的经典一对多场景<o:p></o:p>

Customer

<o:p>

ID<o:p></o:p>

ORDER_NUMBER<o:p></o:p>

CUSTOMER_ID<o:p></o:p>

1<o:p></o:p>

Tom_order001<o:p></o:p>

1<o:p></o:p>

2<o:p></o:p>

Tom_order002<o:p></o:p>

1<o:p></o:p>

3<o:p></o:p>

Mike_order001<o:p></o:p>

2<o:p></o:p>

4<o:p></o:p>

Jack_order001<o:p></o:p>

3<o:p></o:p>

5<o:p></o:p>

TLinda_order001<o:p></o:p>

4<o:p></o:p>

6<o:p></o:p>

Unknown_order001<o:p></o:p>

null<o:p></o:p>

</o:p>

Oder<o:p></o:p>

ID<o:p></o:p>

NAME<o:p></o:p>

1<o:p></o:p>

Tom<o:p></o:p>

2<o:p></o:p>

Mike<o:p></o:p>

3<o:p></o:p>

Jack<o:p></o:p>

4<o:p></o:p>

Linda<o:p></o:p>

Session缓存中存放的就相互关联的对象图,从数据库中加载Customer,会同时加载所有关联的Order对象,这就产生了如下问题:<o:p></o:p>

当执行sessionfind方法查询所有customer对象时<o:p></o:p>

java 代码 
  1. List customers = session.find(“from Customer as c”);  

运行find方法时,Hibernate将先查询CUSTOMERS表中的所有记录,然后根据记录IDORDER表里查询相关的记录.Hibernate依次执行一下select语句:<o:p></o:p>

sql 代码
  1. Select * from CUSTOMERS;      
  2.      
  3. Select * from ORDERS where CUSTOMER_ID=1;      
  4.      
  5. Select * from ORDERS where CUSTOMER_ID=2;      
  6.      
  7. Select * from ORDERS where CUSTOMER_ID=3;      
  8.      
  9. Select * from ORDERS where CUSTOMER_ID=4;      

  通过以上5select语句,Hibernate最后加载了4Customer对象和5Order对象.Hiberante在检索语customer关联的Order对象时使用了默认的立即检索策略,这种方式存在两大缺点<o:p></o:p>

1. select语句太多,频繁访问DB会影响检索性能.如果需要查询nCustomer对象,那么必须执行n+1select语句,这种检索策略没有利用sql的连接查询功能,<o:p></o:p>

如以上5条语句可以用1句左外连接来完成<o:p></o:p>

<o:p>
sql 代码
  1. select * from CUSTOMERS left outer join ORDERS on CUSTOMERS.ID=ORDERS.CUSTOMERS_ID      

查询出了所有CUSTOMERS表所有记录和匹配的ORDERS表记录<o:p></o:p>

2.如果只需要访问Customer对象,不需要Order对象时,加载Order对象时多余的操作,而且浪费了许多内存空间.<o:p></o:p>

<o:p> Hibernate的三种检索策略,为了解决立即检索的问题,Hibernate提供了其他两种检索策略,即延迟和迫切左外连接检索.</o:p>

<o:p>

.立即检索<o:p></o:p>

Hibernate的默认检索策略,当执行session 的查询方法时,会把相关联的表的数据全部查询出来.

 二延迟检索<o:p></o:p>

包括类级别和集合级别的延迟加载.<o:p></o:p>

1.类级别<o:p></o:p>

某个类采用延迟加载<o:p></o:p>

<class name="”…’" lazy="”true”"></class><o:p></o:p>

只和sessionload方法有关,执行load方法时仅返回Customer的代理类实例.<o:p></o:p>

代理类实例有一下特征<o:p></o:p>

1)Hibernate动态生成,扩展了Customer,因此它继承了Customer的所有属性和放,但对于应用程序是透明的.<o:p></o:p>

2)词代理类实例仅初始化了OID属性,其他属性为null,因此它占的内存很少<o:p></o:p>

3)第一次访问代理类实例的属性(除了OID属性,它已有值)Hiberante会初始化此实例,产生select语句.<o:p></o:p>

:Hibernate采用CGLIB工具来生成持久化类的代理类CGLIB是一个功能强大的JAVA字节码生成工具,它能在程序运行时动态生成扩展Java类或者实现Java接口的代理类.关于CGLIB的更多信息,可到网站http://cglib.sourceforge.net了解.,<o:p></o:p>

2.集合级别<o:p></o:p>

当不使用相关表的数据时, 在Custom映射文件中set属性设置的 lazy="true",此时不会立即检索相关表,

需注意<o:p></o:p>

1)并没有创建Order代理类实例,也无法创建因为还不知道与Customer关联的所有Order对象的OID.这时Customerorders属性引用的是Hibernate提供的集合代理类实例.<o:p></o:p>

2)当应用程序第一次要使用相关表的数据时,才从DB的相关表中检索相关数据。<o:p></o:p>

3)只有当集合代理类的实例处于持久化状态时,才可以初始化,以后才可使用,否则会抛出延迟初始化错误:ERROR:LazerInitiater:63........<o:p></o:p>

.迫切左外连接<o:p></o:p>

    默认情况下,多对一关联采用的方法。如果把映射文件的<many-to-one></many-to-one>many-to-one元素outer-join 值设为"true",则总是采用此策略。<o:p></o:p>

Hibernate允许在应用程序的HQL语句中显示指定迫切左外连接检索,它会覆盖配置文件的检索策略。<o:p></o:p>

  1. session.find(" from Customer as c where c.id=1 " );   
  2.   
  3. session.find(" from Customer as c left join fetch c.orders where c.id=1 " )   

第一句会采用映射文件中的检索策略,而第二句会覆盖映射文件的策略。

                                   下面是三种检索策略的比较

 

检索策略
优点
缺点
优先使用场合
立即检索
1)    对应用程序透明 , 不管对象在持久化或游历状态 . 应用程序都可以方便的从一个对象导航到与它关联的对象
2)    Sql 简单 , 速度快
3)    select 语句数目太多
4)    可能会加载与不需要访问的对象 , 浪费内存空间
1)    类级别
2)    应用需立即访问的对象
3)    使用了二级缓存
延迟检索
1)    有程序决定需要加载那些对象 , 避免多余的 select 语句
2)    避免加载不需要访问的对象 , 节省内存空间 , 提高检索性能
3)    Sql 简单 , 速度快
应用如果访问游历的代理类实例 , 必须保证它在持久化状态时已被初始化
1)    一对多或多对多
2)    应用不会立即或根本不会访问的对象 .
迫切左外连接
1)    对应用程序透明 , 不管对象在持久化或游历状态 . 应用程序都可以方便的从一个对象导航到与它关联的对象
2)    使用了外连接 ,select 语句少 , 减少访问数据库频率
1)    可能会加载不必要的对象 , 浪费空间
2)    如果表连接过于负责 ,sql 负责度高 ,, 会影响性能
1)    多对一或一对一
2)    应用会立即访问的对象
3)    数据库具有良好的表连接性能

 

     总之,对于实际的应用,为了选择合适的检索策略,需要测试应用程序的各个用例,跟踪使用不用检索策略时Hibernate执行的sql语句,可以百Hibernate的配置文件的showsql属性设为true,输出sql。根据特定的关系模型,比较查询性能。

到底是使用外连接查询快,
sql 代码
  1. select * from CUSTOMERS left outer join ORDERS on CUSTOMERS.ID=ORDERS.CUSTOMERS_ID where CUSTOMERS.ID=1    
还是分开的 select 语句快
sql 代码
  1. select * from CUSTOMERS   
  2. select * from ORDERS where CUSTOMER_ID=1  
不断的调解检索策略,以便在减少 select语句和将少select语句复杂度之间找到平衡点,获得最佳的检索性能。
</o:p></o:p>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值