zoukankan      html  css  js  c++  java
  • Hibernate 抓取策略fetch-1 (select join subselect)

    原文 :http://4045060.blog.51cto.com/4035060/1088025

    部分参考:http://www.cnblogs.com/rongxh7/archive/2010/05/12/1733088.html

    Hibernate3 定义了如下几种抓取策略:

    连接抓取(Join fetching) - Hibernate通过 在SELECT语句使用OUTER JOIN 
    (外连接)来 获得对象的关联实例或者关联集合.


    查询抓取(Select fetching) - 另外发送一条 SELECT 语句抓取当前对象的关联实 
    体或集合。除非你显式的指定lazy="false"禁止 延迟抓取(lazy fetching),否 
    则只有当你真正访问关联关系的时候,才会执行第二条select语句.

       备注: 查询抓取, 这种策略是在集合抓取的时候的默认策略, 即如果集合需要初始化, 那么会重新发出一条 SQL 语句进行查询;

            这是集合默认的抓取策略, 也就是我们常会出现N+1次查询的查询策略;

     


    子查询抓取(Subselect fetching) - 另外发送一条SELECT 语句抓取在前面查询到 
    (或者抓取到)的所有实体对象的关联集合。除非你显式的指定lazy="false" 禁止延迟 
    抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条 
    select语句


    批量抓取(Batch fetching) - 对查询抓取的优化方案, 通过指定一个主键或外键 
    列表,Hibernate使用单条SELECT语句获取一批对象实例或集合

     

    一、hibernate抓取策略单端代理的批量抓取fetch=select(默认)/join)

     1)保持默认,同fetch="select" 

    <many-to-one name="group" column="group_id" fetch="select" />

    另外发送一条select语句抓取当前对象关联实体或集合.

    测试代码

            Session session = null;
            try {
                session = HibernateUtils.getSession();
                /**
                 * 只读操作,不使用事务也可进行.Hibernate建议任何操作都使用事务.
                 */
                User u = (User) session.load(User.class,"402881e6466ae95a01466ae95c4d0004");// 加载User
                System.out.println("执行了session.load方法");
                System.out.println("[value]" + u.getName());
                System.out.println("[value]" + u.getGroup().getName());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (session != null) {
                    // 关闭session
                    session.close();
                }
            }

     执行效果

    执行了session.load方法
    Hibernate: 
        select
            user0_.id as id0_0_,
            user0_.name as name0_0_,
            user0_.password as password0_0_,
            user0_.createTime as createTime0_0_,
            user0_.updateTime as updateTime0_0_,
            user0_.group_id as group6_0_0_ 
        from
            TEST_User user0_ 
        where
            user0_.id=?
    [value]james
    Hibernate: 
        select
            group0_.id as id1_0_,
            group0_.name as name1_0_ 
        from
            TEST_Group group0_ 
        where
            group0_.id=?
    [value]集团2

    2)设置fetch="join"

     <many-to-one name="group" column="group_id" fetch="join" />

    hibernate会通过select语句使用外连接来加载其关联实体或集合. 此时lazy会失效.  执行结果:一条join语句 

    测试代码同上

    执行效果

    执行了session.load方法
    Hibernate: 
        select
            user0_.id as id0_1_,
            user0_.name as name0_1_,
            user0_.password as password0_1_,
            user0_.createTime as createTime0_1_,
            user0_.updateTime as updateTime0_1_,
            user0_.group_id as group6_0_1_,
            group1_.id as id1_0_,
            group1_.name as name1_0_ 
        from
            TEST_User user0_ 
        left outer join
            TEST_Group group1_ 
                on user0_.group_id=group1_.id 
        where
            user0_.id=?
    [value]james
    [value]集团2

     二、hibernate抓取策略(集合代理的批量抓取,fetch=select(默认)/join/subselect)

    1)保持默认,同fetch="select",如  

    <set name="users" fetch="select" >
       <key column="group_id"/>
       <one-to-many class="com.test.entity.User" />
      </set>fetch="select",

    另外发送一条select语句抓取当前对象关联实体或集合
    测试结果:2条独立的查询语句

     测试代码

    Session session = null;
            try {
                session = HibernateUtils.getSession();
                /**
                 * 只读操作,不使用事务也可进行.Hibernate建议任何操作都使用事务.
                 */
                Group g = (Group) session.load(Group.class,"402881e6466ae95a01466ae95c460001");// 加载Group
                System.out.println("执行了session.load方法");
                System.out.println("[value]" + g.getName());
                Set<User> us = g.getUsers();
                for (Iterator iter = us.iterator(); iter.hasNext();) {
                    User u = (User) iter.next();
                    System.out.println("[value]" + u.getName());
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (session != null) {
                    // 关闭session
                    session.close();
                }
            }

    执行效果

    执行了session.load方法
    Hibernate: 
        select
            group0_.id as id1_0_,
            group0_.name as name1_0_ 
        from
            TEST_Group group0_ 
        where
            group0_.id=?
    [value]集团2
    Hibernate: 
        select
            users0_.group_id as group6_1_,
            users0_.id as id1_,
            users0_.id as id0_0_,
            users0_.name as name0_0_,
            users0_.password as password0_0_,
            users0_.createTime as createTime0_0_,
            users0_.updateTime as updateTime0_0_,
            users0_.group_id as group6_0_0_ 
        from
            TEST_User users0_ 
        where
            users0_.group_id=?
    [value]james
    [value]kobe
    [value]jordan

     2)设置fetch="join"

     如:

      <set name="users" fetch="join" >
       <key column="group_id"/>
       <one-to-many class="com.test.entity.User" />
      </set>

     fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合. 此时lazy会失效
    测试结果:1条独立的join查询语句

    测试代码: 同上例

    执行效果:

    Hibernate: 
        select
            group0_.id as id1_1_,
            group0_.name as name1_1_,
            users1_.group_id as group6_3_,
            users1_.id as id3_,
            users1_.id as id0_0_,
            users1_.name as name0_0_,
            users1_.password as password0_0_,
            users1_.createTime as createTime0_0_,
            users1_.updateTime as updateTime0_0_,
            users1_.group_id as group6_0_0_ 
        from
            TEST_Group group0_ 
        left outer join
            TEST_User users1_ 
                on group0_.id=users1_.group_id 
        where
            group0_.id=?
    [value]集团2
    [value]james
    [value]jordan
    [value]kobe

     3)设置fetch="subselect"

       <set name="users" fetch="subselect" >
       <key column="group_id"/>
       <one-to-many class="com.test.entity.User" />
      </set>

     测试结果: fetch="subselect",另外发送一条select语句抓取在前面查询到的所有实体对象的关联集合.

    测试代码: 同上例

    执行效果:

    执行了session.load方法
    Hibernate: 
        select
            group0_.id as id1_0_,
            group0_.name as name1_0_ 
        from
            TEST_Group group0_ 
        where
            group0_.id=?
    [value]集团2
    Hibernate: 
        select
            users0_.group_id as group6_1_,
            users0_.id as id1_,
            users0_.id as id0_0_,
            users0_.name as name0_0_,
            users0_.password as password0_0_,
            users0_.createTime as createTime0_0_,
            users0_.updateTime as updateTime0_0_,
            users0_.group_id as group6_0_0_ 
        from
            TEST_User users0_ 
        where
            users0_.group_id=?
    [value]kobe
    [value]jordan
    [value]james

     为更好体现subselect的效果用例如下: 通过调节获取符合条件的Group集合,访问其中一个Group的关联集合User

    测试代码:

    List<Group> gs=session.createQuery(" from Group g where g.name like'集团%'").list();
                Group g0=gs.get(0);
                System.out.println("[value]" + g0.getName());
                System.out.println("[value]" + g0.getUsers().size());

    执行效果: 在之前查询语句的基础上,发出子查询.

    Hibernate: 
        select
            group0_.id as id1_,
            group0_.name as name1_ 
        from
            TEST_Group group0_ 
        where
            group0_.name like '集团%'
    [value]集团2
    Hibernate: 
        select
            users0_.group_id as group6_1_,
            users0_.id as id1_,
            users0_.id as id0_0_,
            users0_.name as name0_0_,
            users0_.password as password0_0_,
            users0_.createTime as createTime0_0_,
            users0_.updateTime as updateTime0_0_,
            users0_.group_id as group6_0_0_ 
        from
            TEST_User users0_ 
        where
            users0_.group_id in (
                select
                    group0_.id 
                from
                    TEST_Group group0_ 
                where
                    group0_.name like '集团%'
            )
    [value]3

    若将fetch="subselect"的配置去掉.

    则执行效果如下 , 用了默认的select fetch查询方式

    Hibernate: 
        select
            group0_.id as id1_,
            group0_.name as name1_ 
        from
            TEST_Group group0_ 
        where
            group0_.name like '集团%'
    [value]集团2
    Hibernate: 
        select
            users0_.group_id as group6_1_,
            users0_.id as id1_,
            users0_.id as id0_0_,
            users0_.name as name0_0_,
            users0_.password as password0_0_,
            users0_.createTime as createTime0_0_,
            users0_.updateTime as updateTime0_0_,
            users0_.group_id as group6_0_0_ 
        from
            TEST_User users0_ 
        where
            users0_.group_id=?
    [value]3

    总结:
     


     hibernate抓取策略

    1.单端代理的批量抓取 (如Group.getUser().getName()这样)
     1.1 保持默认,同fetch="select",如:<many-to-one name="classes" column="classesid" fetch="select"/>
       fetch="select", 有用到lazy, 实际用到关联属性才会发出SQL.(另外发送一条select语句抓取当前对象关联实体或集合)
       1.2设置fetch="join",如:<many-to-one name="classes" column="classesid" fetch="join"/>
        fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合. 只发出1条SQL.
        此时lazy会失效
     

    2.集合代理的批量抓取 (如Set<User> us=Group.getUsers();  然后获取集合中User的属性)
     2.1 保持默认,同fetch="select",如:<set name="students" inverse="true" cascade="all" fetch="select">
         fetch="select",另外发送一条select语句抓取当前对象关联实体或集合
      2.2  设置fetch="join",如:<set name="students" inverse="true" cascade="all" fetch="join">
        fetch="join", 有用到lazy, 实际用到关联属性才会发出SQL.( hibernate会通过select语句使用外连接来加载其关联实体或集合)
       此时lazy会失效

      2.3设置fetch="subselect",如:<set name="students" inverse="true" cascade="all" fetch="subselect">
       fetch="subselect",另外发送一条select语句抓取在前面查询到的所有实体对象的关联集合
     
    3.hibernate批量抓取,Hibernate提供了两种批量抓取方案:类级别和集合级别。 

             类级别的批量查询。如果一个Session中需要载入30个student实例,在student中拥有一个成员变量class,该class执行Class对象。如果lazy=“true”,如果们需要变量整个student集合,每一个student都需要getClass(),hibernate在默认情况下回执行30次select查询语句,得到Class对象。这个时候可以通过在映射文件的Class属性,batch-size。

     

    1. <class name=”Class” batch-size=”15”>….</class>  

     

             这样Hibernate将只需要执行两次查询即可:15,15。

             集合级别的批量查询。如果我们需要遍历Class所拥有的所有Student对象,在Session中需要载入30个Class对象,遍历Class集合将会引起30次Select查询,每次查询都会调用getStudents()。如果在Class的映射定义中,允许对Students进行批量抓取,则Hibernate就会预先加载整个集合。

     

    1. <set name=”students” batch-size=”15”></set>  
  • 相关阅读:
    TransformAroundPointPlugin
    探讨VMP 2.12.3 导入表修复
    spring+quartz 实现定时任务二
    一个奇怪的sql异常
    php程序里面使用sudo来执行应用程序
    spring+quartz 实现定时任务一
    让Git忽略SSL证书错误技巧
    个人开发网站集合
    完美解决KMplayer无法播放RMVB、RM电影问题
    如何知道自己的Windows是否已激活? 如何实现免激活升级? 重装Windows不需再激活?
  • 原文地址:https://www.cnblogs.com/redcoatjk/p/3777489.html
Copyright © 2011-2022 走看看