您的位置:首页 > 其它

Hibernate的检索(抓取)策略

2013-07-22 09:31 274 查看
1、 建立模拟数据  ---- 建立3个Customer 和 30个Order(每个客户10个订单)

2、 立即检索和延迟检索

         立即检索: 立即加载检索方法指定的对象

         延迟检索: 延迟加载检索方法指定的对象

hibernate框架检索一个主要优化策略就是延迟检索

get和load 的区别 ?

         get默认使用立即检索

         load默认使用延迟检索

3、 hibernate 延迟检索中代理对象

         load方法返回代理对象 ,默认是目标对象子类对象,对象由 javassist工具包生成

Javassist作用

         一个开源的分析、编辑和创建Java字节码的类库(字节码.class文件 ,ClassLoader 类加载器会加载字节码,生成Class对象 )

简单的使用方式
// javassist 修改字节码
// 动态修改方法
ClassPool cp = ClassPool.getDefault(); // 类加载池
CtClass cc = cp.get("cn.itcast.javassist.HelloService"); // 类
CtMethod m = cc.getDeclaredMethod("sayHello"); // 方法
// 在目标方法 sayHello 执行前 增加一段代码
m.insertBefore("{ System.out.println(\"HelloService.sayHello():\"); }");
//将其转换为class类
Class c = cc.toClass();
HelloService helloService2 = (HelloService) c.newInstance();
helloService2.sayHello();

hibernate生成代理对象特点

         1)  返回目标类子类对象

         2)   在对象存在一个handler,handler 提供了一个initialized的属性值为 false ,id 封装查询对象id,target值null,当访问代理对象id之外的其他属性,就执行查询语句, 初始化后,将 initialized   属性置为true ,target中封装查询结果数据。

 

手动进行初始化

         hibernate框架中 提供工具类 Hibernate类

         方法:static void initialize(Object proxy) 对延迟加载代理对象进行初始化

                     static boolean isInitialized(Objectproxy)  判断延迟加载代理对象是否初始化(数据只需要初始化一次)

4、 区分类级别检索策略和集合级别的检索策略

         1) 类级别检索策略

         默认 session.load(Customer.class,1) ; 采用延迟检索, 在Customer.hbm.xml 配置 <class> lazy属性来控    制load 可以延迟还是立即检索,如果设置 lazy="false" 的话,load 将采用立即检索,效果等同于 get。 这里说的是否检索customer,就为类级别检索。 

         2) 关联级别检索策略

         当程序已经获得 Customer的持久态对象,customer类中关联 Set<Order>,当需要访问orders订单集合时,采用立即检索还是延迟检索,叫做关联级别的检索 。这里说的是否检索customer中的order,就为关联检索。

         默认情况下关联级别检索 使用延迟加载策略,也可以在hbm文件进行配置,控制关联对象是立即还是延迟

        

5、 类级别的检索策略

类级别可选的检索策略包括立即检索和延迟检索,
默认为延迟检索,对于默认延迟只针对的是load方法


         session.get(xxx.class,id)  立即加载

         session.load(xxx.class,id) 默认延迟加载

         query.list()立即加载

类级别的检索策略可以通过 <class>
元素的 lazy 属性进行设置(配置hbm)

设置hbm文件 <class> 元素 lazy="false"  立即检索策略 (load
方法效果就等同于get了 )

         注:Session的get 方法, Query的list方法 默认使用 立即检索,不会受 <class> lazy的影响

        

6、 关联级别的检索策略

         已经查询类级别数据,相关联对象数据检索

         例如:已经查询出customer对象,又要查询customer关联的order对象

关联级别的检索分为两部分

         第一部分:一对多和多对多元素的关联 (都用set标签,关联的是一个集合对象)

         <set>

                   <one-to-many>/ <many-to-many>

         </set>

<set> 元素提供两个属性,共同决定检索策略  lazy和fetch

         lazy:决定关联集合对象加载的时机

         fetch:决定生成SQL语句形式

* fetch 可以取值有 join、select默认、subselect  ;lazy 可以取值有true默认、false、extra

 


1)当fetch取值为 join时,lazy属性会被忽略(迫切左外连接检索)

         fetch为join时,生成左外连接SQL语句 

         例:select * from customer left outer join orders on customer.id =orders.customer_id ;

备注:内连接是保证两个表中所有的行都要满足连接条件,而外连接则不然。在外连接中,某些不满条件的列也会显示出来,也就是说,只限制其中一个表的行,而不限制另一个表的行。这种连接形式在      
许多情况下是非常有用的。外连接只能用于两个表中。

         在 Ansi
语法形式中,包含下列三种外连接关键字:

         ◆Left Outer Join
包含了左表中的全部行(表达式中第一个表)

         ◆Right Outer Join
包括了右表中的全部行(表达式中第二个表)

         ◆Full Outer Join
包括了左表和右表中所有不满足条件的行

         忽略lazy的原因:当查询出customer时,语句要作为左外连接查询customer,order数据也会被检索出,所以无论lazy取任何值,都会被立即检索。






2) fetch取值为 select
(将采用多条简单SQL语句)


         lazy="false"立即检索

         lazy="true"  延迟检索

         lazy="extra"极其懒惰 (比延迟更加延迟) 增强延迟检索

         必须在获得order数据时,才会查询,如果查询order记录数量会用到count(*),而不用对象查询






3) fetch取值为 subselect (将采用子查询方式产生SQL语句)

         lazy="false"立即检索

         lazy="true"  延迟检索

         lazy="extra"极其懒惰 (增强延迟检索)



注意问题 :

1、使用延迟检索的时候

         当设置 <set> lazy="true"时,Hibernate 在以下情况下初始化集合代理类实例。

•   应用程序第一次访问集合属性: iterator(), size(), isEmpty(),contains() 等方法

•   通过 Hibernate.initialize() 静态方法进行显式初始化

2、使用增强延迟检索的时候

         主要区别是增强延迟检索策略能进一步延迟 Customer对象的 orders
集合代理实例的初始化时机


         当设置 <set> lazy="extra"时,Hibernate 在以下情况下初始化集合代理类实例。

•   当程序第一次访问 orders 属性的 iterator() 方法时, 会导致 orders集合代理类实例的初始化

•   当程序第一次访问 order 属性的 size(), contains() 和 isEmpty()方法时, Hibernate 不会初始化orders 集合类的实例,
仅通过特定的 select 语句查询必要的信息,
不会检索所有的 Order
对象。

3、案例中,使用get/load查询customer时,配置fetch="join" 采用迫切左外连接查询,order会被查询,采用立即检索 lazy被忽略,但是如果使用Query的list方法查询时,hibernate不会自动生成sql语句了,他要根据手动编写的HQL语句生成SQL语句,因此对于fetch="join"的左外连接语句就会被忽略不起作用了,这是的lazy属性就重新生效了。

 


         第二部分 :多对一和一对一元素关联(关联的是一个实体对象)

         <many-to-one>/ <one-to-one>

也是由两个属性决定 fetch
和 lazy


         fetch:决定SQL语句形式,

         lazy:决定加载时间

fetch有两个取值 join 和 select ; lazy 有三个取值 false 、proxy 、no-proxy



1) 当fetch
取值 join
,采用迫切左外连接  lazy
被忽略(与关联集合一样)


         针对 get/load 有效

         如果Query,fetch="join" 被忽略,lazy 将重新生效



2) 当fetch取值 select
,产生多条SQL
查询


         lazy="false"采用立即查询

         lazy="proxy"  将由Customer.hbm.xml的<class>中的lazy属性决定是延迟还是立即(由对方类级别检索策略决定)

 


案例分析练习一

many2one立即检索+set立即检索



案例分析练习二

many2one迫切左外+set立即检索



案例分析练习三

many2one立即检索+set迫切左外



批量检索查询优化(N+1
问题解决)     


         1、从一的一端 进行批量检索

N+1 问题:查询每个客户的订单,如果10000个客户,会产生 10001 条SQL,其中1条为查询出所有的客 户,另外的10000条都是一样的sql,都是查询订单表中customer_id=?,所以会产生多条相同  的sql。



解决方法:在<set>中配置batch-size
属性,表示一次查询几个客户的订单(配置在Customer.hbm.xml)

         例如 :10000 个客户,如果设置 batch-size="10",那么就会产生 1001条SQL,其中每十个客户用了同一条sql,当然也可以配置为10000,这样就只有两条sql了,就得到了优化。实际上是将sql语句写成了这样:select
* from order where oreder.customer_idin(?,?,?.....);


 

         2、 从多的一端进行批量检索

         问题:查询每个订单客户的姓名

         产生4条SQL,一条查询订单,三条查询三个客户(订单关联的客户是重复的,前十个订单关联一个客户,只要查询出一个客户后,就会放人一级缓存了,下次再查询就直接获取了,不用编写sql进行查询了), 配置一次多查询几个客户  (配置Customer.hbm.xml)



解决方法:在Customer的hbm文件中的class中配置batch-size

<class name="cn.itcast.domain.fetch.Customer"table="customer" catalog="hibernate3day3"lazy="true" batch-size="2">

 

小结:在实际开发中,优先使用延迟加载策略(提高性能),但是延迟加载有重要问题,延迟加载返回的是      
代理对象,代理对象要想加载延迟的数据,Session就不能close,如果连接关闭了就不能再初始化延        
迟的代理对象了。如果在session关闭后再进行初始化的话就会抛出如下异常

         org.hibernate.LazyInitializationException:failed to lazily initialize a collection of role:cn.itcast.domain.fetch.Customer.orders, no session or session was closed
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息