城东小巷分享 http://blog.sciencenet.cn/u/chengdong166

博文

Hibernate检索策略(二)

已有 3300 次阅读 2013-9-15 16:26 |个人分类:Java学习|系统分类:科研笔记| Hibernate, 检索策略

一、一对多和多对多的关联策略
       <set>元素的属性lazy的默认值为true,即采用延迟加载的策略。采用这种策略时,当调用Session实例的get()方法时,返回的customer对象的orders属性引用的是Hibernate为Set集合类提供的代理类PersistentSet的实例。在这个集合代理类实例中没有存放任何Orders类对象,也就是说Orders类对象的集合代理类实例未被初始化。
   注:集合的代理类org.hibernate.collection.PersistentSet与持久化类不同的是,不管有没有设置延迟加载,

Hibernate的各种检索方法在为Customers类对象的属性orders赋值时,orders属性总是引用PersistentSet集合代理类的实例。
       那么,Orders类对象的集合代理类实例什么时候才会被初始化呢?(1) 调用这个集合代理类实例的iterator()、size()、isEmpty()或contains()方法时,执行select语句初始化;(2) 调用Hibernate.initialize(obj)显示初始化。

                     Customers customer = (Customers)hibernateSession.get(cls, id);
                     Set set = customer.getOrders();
                     if (!Hibernate.isInitialized(set)) {
                          Hibernate.initialize(set);
                     }
1.  批量检索(batch-size

       分为批量延迟检索和批量立即检索,目的是减少select语句性能。
(1) 批量延迟检索
                     Session hibernateSession = InitSessionUtil.openSession();
                    Query query = hibernateSession.createQuery("FROM Customers");
                     // 执行select * from customers,初始化N个Customers对象,

                     // 每个对象的orders属性引用一个集合代理类实例
                     List list = query.list();
                     for (int i = 0; i < list.size(); i++) {
                          Customers customer = (Customers)list.get(i);
                          Set set = customer.getOrders();
                          //执行select * from orders,初始化集合代理类实例
                          Iterator it = set.iterator();
                          while (it.hasNext()) {
                               Orders order = (Orders)it.next();
                               System.out.println(order.getOrderNumber());
                          }
                     }
                     InitSessionUtil.closeSession();
      上述代码实现从数据库检索出每个客户所发出订单的订购数量,若客户表有N条记录,则会执行1 + N次select语句,显然,频繁的执行SQL语句会降低运行性能。为减少执行select语句条数,可采用批量延迟加载,即<set>元素中设置属性batch-size。
                   <set name="orders" inverse="true" lazy="true" batch-size="3">
       当执行到set.iterator()时,若Session的缓存中存在多余3个的集合代理类实例未被初始化,则会按顺序批量初始化3个集合代理类实例,即:
                  select * from orders where CUSTOMER_ID in (?, ?, ?)
       
若不足3个集合代理类实例未被初始化,则批量初始化剩余的集合代理类实例。这样,当下次执行set.iterator()时,若集合代理类实例已被初始化,则不会再执行select语句,从而达到减少执行select语句的目的。
(2) 批量立即检索

2. 用带子查询的select语句批量初始化集合代理类实例
        <set>元素的fetch属性可选择"select"(默认)和"subselect"。若映射文件中的<set>元素设置如下:
                   <set name="orders" inverse="true" lazy="true" fetch="subselect">
        则当调用set.iterator()后,会执行下面子查询:
              select * from orders where CUSTOMER_ID in (select ID from customers)
        也就是说,若Session缓存中有N个集合代理类实例,则Hibernate能够通过带子查询的select语句,批量初始化这N个集合代理类实例。
        注:当fetch="subselect"时不必设置batch-size;当fetch="select"时可显示设置batch-size。

3. 迫切左外连接检索(fetch属性为join)
               <set name="orders" inverse="true" lazy="true" fetch="join">
       当调用Session的get()方法(总是立即检索)后,会执行left outer join查询:
               select * from customers left outer join orders on ID=CUSTOMER_ID where ID=?
       虽然Query的list()方法和get()方法一样,总是立即检索,但前者常忽略映射文件中配置的迫切左外连接检索策略,Hibernate对于Customers对象的orders集合仍然采用延迟加载策略。

二、多对一和一对一关联的检索策略
       在映射文件中,<many-to-one>和<one-to-one>分别用来设置多对一和一对一关联关系,若没有显示设置lazy属性(proxy/no-proxy/false)和fetch属性(join/select),则采用默认的延迟检索策略。如果fetch="join",则lazy属性被忽略,此时是否显示lazy属性已无意义。
1. 迫切左外连接检索(fetch="join")
       若<many-to-one>元素的fetch属性为join,则在检索Orders对象时,对关联的Customers对象使用迫切左外连接检索策略。对于以下程序代码:
             Session hibernateSession = InitSessionUtil.openSession();
             Orders o = (Orders)hibernateSession.get(cls, id);
             Customers c = o.getCustomer();
             InitSessionUtil.closeSession();
      在运行hibernateSession.get(cls, id)方法时,Hibernate需要决定:
      (1) 类级别的检索策略:get()方法在类级别总是使用立即检索策略;
      (2) 与Orders多对一关联的Customers对象的检索策略取决于Orders映射文件中<many-to-one>元素的fetch和lazy属性,这里fetch="join",因此lazy属性是否显示已无意义,采用迫切左外连接检索策略;
      (3) 与Customers一对多的Orders对象的检索策略取决于Customers映射文件中的<set>元素的属性lazy和fetch属性。这里lazy="false",即立即检索策略。
       因此,Hibernate执行以下select语句:
               select * from orders o inner join customers c on o.CUSTOMER_ID=c.ID where o.ID=4
               select * from orders o where oCUSTOMER_ID=4
       若(3)中<set>元素的lazy=true,则执行以下select语句:
              select * from orders o inner join customers c on o.CUSTOMER_ID=c.ID where o.ID=4
       注意,Query的list()方法会忽略映射文件中配置的迫切左外连接检索策略。例如以下程序代码:
                 Session hibernateSession = InitSessionUtil.openSession();
                 Query query = hibernateSession.createQuery("FROM Orders o");
                 List list = query.list();
                 for (int i = 0, size = list.size(); i < size; i++) {
                      Orders o = (Orders)list.get(i);
                      // o.getCustomer()返回Customers代理类实例的引用,这个代理类实例的OID由orders表中

                      // 的 CUSTOMER_ID决定
                      Customers c = o.getCustomer();
                      // 执行select语句,初始化Customers代理类实例
                      System.out.println(c.getName());
               }
                 InitSessionUtil.closeSession();
      运行query.list()方法时,执行以下select语句:
                    select * from orders o
     当第一次运行到c.getName()时,初始化Customers代理类实例,执行select语句如下:
                     select * from customers c where c.ID=1

2. 延迟检索
      如果希望检索Orders对象时,延迟检索关联的Customers对象,只要将Orders类对应的映射文件中的<many-to-one>的lazy和fetch属性设置为proxy和select即可。
      对于一对一关联,若使用延迟加载策略,必须将<one-to-one>元素的属性constrained置为"true",它表明Orders对象必须与一个Customers对象关联,即Orders对象的customer属性不允许为null。
3. 无代理延迟加载
      如果对Orders对象的customer属性使用无代理延迟检索,需将<many-to-one>元素的lazy属性置为"no-proxy"。与延迟加载的区别在于:(1)延迟加载,可以提供更长的延迟加载Customers对象的时间;(2)无代理延迟加载,可以直接提供更加透明的持久化服务,从而避免仅提供由Hibernate生成的Customers代理类实例。
4. 批量延迟检索和批量立即检索
      如果希望检索Orders对象时,批量延迟检索关联的Customers对象,可通过设置Customers类对应的映射文件<class>元素的batch-size属性实现。
       <class name="org.test.pojo.Customers" table="customers" batch-size="3">
      若<many-to-one>的lazy属性置为proxy,则第一次调用Customers代理类实例的属性时将会批量初始化Customers代理类实例。例如以下程序代码:
                     Session hibernateSession = InitSessionUtil.openSession();
                      Query query = hibernateSession.createQuery("FROM Orders o");
                     List list = query.list();
                     for (int i = 0, size = list.size(); i < size; i++) {
                          Orders o = (Orders)list.get(i);
                          // 返回Customers代理类实例
                          Customers c = o.getCustomer();
                          // 执行select语句,初始化Customers代理类实例
                          System.out.println(c.getName());
                       }
                     InitSessionUtil.closeSession();
       当调用c.getName时会批量初始化Session缓存中的3个Customers代理类实例。注意这里应根据实际情况确定批量检索数目,合同的批量检索数目应该控制在3-10个,若批量检索过大,延迟加载将会失去意义。

 

 



https://blog.sciencenet.cn/blog-448935-725141.html

上一篇:Hibernate检索策略
下一篇:数据库事务
收藏 IP: 122.82.216.*| 热度|

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...
扫一扫,分享此博文

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-5-19 02:25

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部