`

Hibernate的关联关系中lazy和fetch的设置

阅读更多
Hibernate的关联关系中lazy和fetch的设置会影响到对数据进行查询时候SQL语句的操作,fetch的设置相对于lazy的优先级更高,而且在class标签上配置的lazy属性不会影响到关联对象.(本例用的版本是Hibernate3)

本例假设有一个主表为MASTTB,有一个子表为DETAILTB.

主表端的fetch可以取 'join','select'和'subselect'(select为默认值):

join:外连接一次查询.

select:1+n 条select语句,第一条查主表,第n条查第n条主表记录对应的子表记录.

subselect: 以 id in(...)的方式做第二条查询,(如果查询主表的是返回单条记录,subselect和select没有区别,如果查询主表的是返回多条记录的话,对子表查询会以id in 的方式).具体见例4.

lazy可以取'true','extra'以及 'false'(true为默认值):

true:默认取值,它的意思是只有在调用这个集合获取里面的元素对象时,才发出查询语句,加载其集合元素的数据.
false:取消延迟加载特性,即在加载对象的同时,就发出第二条查询语句加载其关联集合的数据.
extra:一种比较聪明的延迟加载策略,即调用集合的size/contains等方法的时候,hibernate并不会去加载整个集合的数据,而是发出一条"聪明"的SQL语句,以便获得需要的值,只有在真正需要用到这些集合元素对象数据的时候,才去发出查询语句加载所有对象的数据.
比如看集合的size:

会发出下面的SQL语句

    select
        count(DTID)
    from
        DETAILTB
    where
        MTID =?



1,对主表进行findById查询的测试,当fetch设为join的时候, 不管lazy是true还是false,都是一次以主表左外连接子表的方式把关联的数据全部查出来.SQL如下:

    select
        masttb0_.MID as MID1_1_,
        masttb0_.MASTINFO as MASTINFO1_1_,
        detailtbs1_.MTID as MTID3_,
        detailtbs1_.DTID as DTID3_,
        detailtbs1_.DTID as DTID2_0_,
        detailtbs1_.MTID as MTID2_0_,
        detailtbs1_.DETAILINFO as DETAILINFO2_0_
    from
        MASTTB masttb0_
    left outer join
        DETAILTB detailtbs1_
            on masttb0_.MID=detailtbs1_.MTID
    where
        masttb0_.MID=?


2,对主表进行findById查询的测试,当fetch设为select的时候,lazy 是true的时候,hibernate先用一条SQL将主表的数据查出来,然后在取子表数据的时候(在访问set的iterator的时候),再以子表的外键作为条件,用SQL语句取子表的数据.
JAVA代码如下:
          
 tx = sessionFactory.getCurrentSession().beginTransaction();
            MasttbHome masttbHome = new MasttbHome();
          
            Masttb masttb  = masttbHome.findById(new BigDecimal(1));
            System.out.println("before getting detai set");
          
            Set set = masttb.getDetailtbs();
          
            System.out.println("after getting detai set");
            Iterator itr = set.iterator();
            System.out.println("after getting detai set iterator");
            while(itr.hasNext()){
                Detailtb detailtb = (Detailtb)itr.next();
                System.out.println("after getting detai info " + detailtb.getDtid());
            }

            tx.commit();

运行结果如下:
Hibernate:
  
 select
        masttb0_.MID as MID1_0_,
        masttb0_.MASTINFO as MASTINFO1_0_
    from
        MASTTB masttb0_
    where
        masttb0_.MID=?
before getting detai set
after getting detai set
Hibernate:
    select
        detailtbs0_.MTID as MTID1_,
        detailtbs0_.DTID as DTID1_,
        detailtbs0_.DTID as DTID2_0_,
        detailtbs0_.MTID as MTID2_0_,
        detailtbs0_.DETAILINFO as DETAILINFO2_0_
    from
        DETAILTB detailtbs0_
    where
        detailtbs0_.MTID=?

after getting detai set iterator
after getting detai info 2
after getting detai info 1

3,对主表进行findById查询的测试,当fetch设为select的时候,lazy是false的时候,hibernate先用一条SQL将主表的数据查出来,然后马上再以子表的外键作为条件,用SQL语句取子表的数据.
上面例2的代码会打印:
Hibernate:
  
 select
        masttb0_.MID as MID1_0_,
        masttb0_.MASTINFO as MASTINFO1_0_
    from
        MASTTB masttb0_
    where
        masttb0_.MID=?
Hibernate:
    select
        detailtbs0_.MTID as MTID1_,
        detailtbs0_.DTID as DTID1_,
        detailtbs0_.DTID as DTID2_0_,
        detailtbs0_.MTID as MTID2_0_,
        detailtbs0_.DETAILINFO as DETAILINFO2_0_
    from
        DETAILTB detailtbs0_
    where
        detailtbs0_.MTID=?

before getting detai set
after getting detai set
after getting detai set iterator
after getting detai info 2
after getting detai info 1


*如果将lazy设为true,fetch设为 select,在session关闭后在去访问set里的值,会出异常.
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.test.hb.Masttb.detailtbs, no session or session was closed



4,对主表进行多条记录查询的测试,当fetch设为subselect的时候,lazy是true的时候,hibernate先用一条SQL将主表的数据查出来,然后用id in 方式的SQL语句取子表的数据.

JAVA代码:

         
  tx = sessionFactory.getCurrentSession().beginTransaction();
           
            List mstlist = sessionFactory.getCurrentSession().createQuery("from com.test.hb.Masttb where id in (1,2)").list();

            //下面的hql和上面的hql有同样的效果

           //List mstlist = sessionFactory.getCurrentSession().createQuery("from com.test.hb.Masttb where id <3").list();
            for (Iterator iter = mstlist.iterator(); iter.hasNext();) {
                Masttb masttb = (Masttb) iter.next();
                System.out.println("masttb.getMastinfo=" + masttb.getMastinfo());
                Set set = masttb.getDetailtbs();
                System.out.println(set.size());
                if (set != null && !set.isEmpty()) {
                    for (Iterator itr = set.iterator(); itr.hasNext();) {
                        Detailtb detailtb = (Detailtb) itr.next();
                        System.out.println("detailtb.name=" + detailtb.getDetailinfo());
                    }
                }
            }

            tx.commit();


运行结果:

Hibernate:
    select
        masttb0_.MID as MID1_,
        masttb0_.MASTINFO as MASTINFO1_
    from
        MASTTB masttb0_
    where
        masttb0_.MID in (
            1 , 2
        )
masttb.getMastinfo=mastinfo
Hibernate:
    select
        detailtbs0_.MTID as MTID1_,
        detailtbs0_.DTID as DTID1_,
        detailtbs0_.DTID as DTID2_0_,
        detailtbs0_.MTID as MTID2_0_,
        detailtbs0_.DETAILINFO as DETAILINFO2_0_
    from
        DETAILTB detailtbs0_
    where
        detailtbs0_.MTID in (
            select
                masttb0_.MID
            from
                MASTTB masttb0_
            where
                masttb0_.MID in (
                    1 , 2
                )
        )

number in detail table: 2
detailtb.name=aaaa
detailtb.name=detailinfo2
masttb.getMastinfo=aaa
number in detail table: 1
detailtb.name=adfasdfa

如果fetch=select,lazy=true的话,运行结果为 1 + 2条SQL语句:

Hibernate:
    select
        masttb0_.MID as MID1_,
        masttb0_.MASTINFO as MASTINFO1_
    from
        MASTTB masttb0_
    where
        masttb0_.MID in (
            1 , 2
        )
masttb.getMastinfo=mastinfo
Hibernate:
    select
        detailtbs0_.MTID as MTID1_,
        detailtbs0_.DTID as DTID1_,
        detailtbs0_.DTID as DTID2_0_,
        detailtbs0_.MTID as MTID2_0_,
        detailtbs0_.DETAILINFO as DETAILINFO2_0_
    from
        DETAILTB detailtbs0_
    where
        detailtbs0_.MTID=?

number in detail table: 2
detailtb.name=aaaa
detailtb.name=detailinfo2
masttb.getMastinfo=aaa
Hibernate:
    select
        detailtbs0_.MTID as MTID1_,
        detailtbs0_.DTID as DTID1_,
        detailtbs0_.DTID as DTID2_0_,
        detailtbs0_.MTID as MTID2_0_,
        detailtbs0_.DETAILINFO as DETAILINFO2_0_
    from
        DETAILTB detailtbs0_
    where
        detailtbs0_.MTID=?
number in detail table: 1
detailtb.name=adfasdfa




子表端的fetch可以取 'join'和'select'(select为默认值),

join:外连接一次查询.

select:两条select语句.

lazy可以取'proxy','no-proxy'以及'false'(proxy为默认值).

false:取消延迟加载特性,即在加载对象的同时,就发出第二条查询语句加载其关联的主表的数据.

proxy的意思和主表端lazy取true的作用类似,当要访问主表对象的时候,hibernate返回一个proxy对象,这时候还没有去访问数据库取值.
no-proxy 和proxy效果一样,不过proxy对象不是动态,是在编译的过程中就创建的,需要进行特定的编译(requires build-time bytecode instrumentation).


5,对子表进行findById查询的测试,当fetch设为join的时候,不管lazy是 false还是proxy,都是一次以子表左外连接主表的方式把关联的数据全部查出来.SQL如下:
 
  select
        detailtb0_.DTID as DTID2_1_,
        detailtb0_.MTID as MTID2_1_,
        detailtb0_.DETAILINFO as DETAILINFO2_1_,
        masttb1_.MID as MID1_0_,
        masttb1_.MASTINFO as MASTINFO1_0_
    from
        DETAILTB detailtb0_
    left outer join
        MASTTB masttb1_
            on detailtb0_.MTID=masttb1_.MID
    where
        detailtb0_.DTID=?


6,对子表进行findById查询的测试,当fetch设为 select的时候,lazy是false,hibernate先用一条SQL将子表的数据查出来,然后马上用子表的外键作为主表的主键的值,将主表的数据的查询出来.
Hibernate:
    select
        detailtb0_.DTID as DTID2_0_,
        detailtb0_.MTID as MTID2_0_,
        detailtb0_.DETAILINFO as DETAILINFO2_0_
    from
        DETAILTB detailtb0_
    where
        detailtb0_.DTID=?
Hibernate:
    select
        masttb0_.MID as MID1_0_,
        masttb0_.MASTINFO as MASTINFO1_0_
    from
        MASTTB masttb0_
    where
        masttb0_.MID=?

before accessing mast table data

7,对子表进行findById查询的测试,当 fetch设为select的时候,lazy是proxy,hibernate先用一条SQL将子表的数据查出来,然后在需要访问主表数据的时候,再以子表的外键作为主表的主键的值,用SQL语句取主表表的数据.(而且如果你要访问的值是主键值,因为主键值正好是子表的外键值,proxy对象的主键属性已经有值了,hibernate不会发SQL语句查数据库)
Hibernate:
    select
        detailtb0_.DTID as DTID2_0_,
        detailtb0_.MTID as MTID2_0_,
        detailtb0_.DETAILINFO as DETAILINFO2_0_
    from
        DETAILTB detailtb0_
    where
        detailtb0_.DTID=?
before accessing mast table data (没有访问数据之前是不会发SQL语句的)
Hibernate:
    select
        masttb0_.MID as MID1_0_,
        masttb0_.MASTINFO as MASTINFO1_0_
    from
        MASTTB masttb0_
    where
        masttb0_.MID=?



*同样,如果将lazy设为proxy,fetch设为select,在session关闭后在去访问主表里的值,会出异常(仅仅访问主键值不会有异常).
org.hibernate.LazyInitializationException: could not initialize proxy - no Session





需要注意的一个问题: 在子表和主表两端都设置为lazy="false"的时候,先查询子表的数据,这是会发出SQL语句查询数据库,根据select的设置,SQL会有所不同.查询主表的数据之后,又会发出一条SQL语句(条件为子表的外键=主表的主键)查询子表(这一步是多余的).

如下代码:

            DetailtbHome detailtbHome = new DetailtbHome();
            Detailtb detailtb = detailtbHome.findById(new BigDecimal(1));

SQL:

Hibernate:
  
 select
        detailtb0_.DTID as DTID2_0_,
        detailtb0_.MTID as MTID2_0_,
        detailtb0_.DETAILINFO as DETAILINFO2_0_
    from
        DETAILTB detailtb0_
    where
        detailtb0_.DTID=?
Hibernate:
    select
        masttb0_.MID as MID1_0_,
        masttb0_.MASTINFO as MASTINFO1_0_
    from
        MASTTB masttb0_
    where
        masttb0_.MID=?

Hibernate: //由于主表的lazy为false,会发出下面的SQL查询子表
 
  select
        detailtbs0_.MTID as MTID1_,
        detailtbs0_.DTID as DTID1_,
        detailtbs0_.DTID as DTID2_0_,
        detailtbs0_.MTID as MTID2_0_,
        detailtbs0_.DETAILINFO as DETAILINFO2_0_
    from
        DETAILTB detailtbs0_
    where
        detailtbs0_.MTID=?
after findById detailinfo1




通过上面的例子可以发现,最好的做法是:使用hibernate的lazy和fetch的默认设置,这样hibernate的get和load的时候就不会将关联的记录也取出来.而如果需要将关联的记录取出来的时候,用Criteria进行查询,利用Criteria.setFetchMode在代码中进行控制关联记录的查询. 或者通过在传入Query的HQL或者SQL 来控制.

本文出自:http://blog.csdn.net/kkdelta/article/details/5721217
分享到:
评论
1 楼 lijiejava 2012-03-22  
 

相关推荐

    Hibernate_Annotation关联映射

    对于一对多的双向映射,如果要一对多这一端维护关联关系,你需要删除mappedBy元素并将多对一这端的@JoinColoumn的insertable和updatabel设置为false。这种方案不会得到什么明显的优化,而且还会增加一些附加的UPDATE...

    Hibernate+中文文档

    16.1.3. 处理关联和集合类(Handling associations and collections) 16.1.4. 返回多个实体(Returning multiple entities) 16.1.5. 返回非受管实体(Returning non-managed entities) 16.1.6. 处理继承(Handling ...

    hibernate3.2中文文档(chm格式)

    HIBERNATE - 符合Java习惯的关系数据库持久化 Hibernate参考文档 3.2 -------------------------------------------------------------------------------- 目录 前言 1. 翻译说明 2. 版权声明 1. Hibernate...

    Hibernate中文详细学习文档

    16.1.3. 处理关联和集合类(Handling associations and collections) 16.1.4. 返回多个实体(Returning multiple entities) 16.1.5. 返回非受管实体(Returning non-managed entities) 16.1.6. 处理继承(Handling ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part2

     10.3.2 在应用程序中访问具有组成关系的持久化类  10.4 映射复合组成关系  10.5 小结  10.6 思考题 第11章 Hibernate的映射类型  11.1 Hibernate的内置映射类型  11.1.1 Java基本类型的Hibernate映射类型  ...

    Hibernate 中文 html 帮助文档

    16.1.3. 处理关联和集合类(Handling associations and collections) 16.1.4. 返回多个实体(Returning multiple entities) 16.1.4.1. 别名和属性引用(Alias and property references) 16.1.5. 返回非受管实体...

    Hibernate_3.2.0_符合Java习惯的关系数据库持久化

    HIBERNATE - 符合Java习惯的关系数据库持久化 Hibernate参考文档 3.2 -------------------------------------------------------------------------------- 目录 前言 1. 翻译说明 2. 版权声明 1. Hibernate...

    HibernateAPI中文版.chm

    HIBERNATE - 符合Java习惯的关系数据库持久化 Hibernate参考文档 3.2 -------------------------------------------------------------------------------- 目录 前言 1. 翻译说明 2. 版权声明 1. Hibernate...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part4

     10.3.2 在应用程序中访问具有组成关系的持久化类  10.4 映射复合组成关系  10.5 小结  10.6 思考题 第11章 Hibernate的映射类型  11.1 Hibernate的内置映射类型  11.1.1 Java基本类型的Hibernate映射类型  ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part3

     10.3.2 在应用程序中访问具有组成关系的持久化类  10.4 映射复合组成关系  10.5 小结  10.6 思考题 第11章 Hibernate的映射类型  11.1 Hibernate的内置映射类型  11.1.1 Java基本类型的Hibernate映射类型  ...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part1.rar

     10.3.2 在应用程序中访问具有组成关系的持久化类  10.4 映射复合组成关系  10.5 小结  10.6 思考题 第11章 Hibernate的映射类型  11.1 Hibernate的内置映射类型  11.1.1 Java基本类型的Hibernate映射类型  ...

    Hibernate注释大全收藏

    @Basic(fetch = FetchType.LAZY) String getDetailedComment() { ... } // persistent property @Temporal(TemporalType.TIME) java.util.Date getDepartureTime() { ... } // persistent property @Enumerated(Enum...

    最全Hibernate 参考文档

    7. 关联关系映射 7.1. 介绍 7.2. 单向关联(Unidirectional associations) 7.2.1. 多对一(many to one) 7.2.2. 一对一(one to one) 7.2.3. 一对多(one to many) 7.3. 使用连接表的单向关联(Unidirectional ...

    Hibernate3+中文参考文档

    7. 关联关系映射 7.1. 介绍 7.2. 单向关联(Unidirectional associations) 7.2.1. 多对一(many to one) 7.2.2. 一对一(one to one) 7.2.3. 一对多(one to many) 7.3. 使用连接表的单向关联(Unidirectional ...

    hibernate3.04中文文档.chm

    8. 关联关系映射 8.1. 介绍 8.2. 单向关联(Unidirectional associations) 8.2.1. 多对一(many to one) 8.2.2. 一对一(one to one) 8.2.3. 一对多(one to many) 8.3. 使用连接表的单向关联...

    Hibernate教程

    8. 关联关系映射 8.1. 介绍 8.2. 单向关联(Unidirectional associations) 8.2.1. 多对一(many to one) 8.2.2. 一对一(one to one) 8.2.3. 一对多(one to many) 8.3. 使用连接表的单向关联...

    hibernate 体系结构与配置 参考文档(html)

    7. 关联关系映射 7.1. 介绍 7.2. 单向关联(Unidirectional associations) 7.2.1. 多对一(many to one) 7.2.2. 一对一(one to one) 7.2.3. 一对多(one to many) 7.3. 使用连接表的单向关联...

    Hibernate注解

    fetch指定是否延迟加载,值为FetchType.LAZY表示延迟,为FetchType.EAGER表示立即加载 * 方法一 使用这种配置,在为“一端”添加“多端”时,不会修改“多端”的外键。在“一端”加载时,不会得到“多端”。如果使用...

    hibernate 框架详解

    8. 关联关系映射 8.1. 介绍 8.2. 单向关联(Unidirectional associations) 8.2.1. 多对一(many to one) 8.2.2. 一对一(one to one) 8.2.3. 一对多(one to many) 8.3. 使用连接表的单向关联...

Global site tag (gtag.js) - Google Analytics