`

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关联关系总结

    Hibernate关联关系是Java持久化框架Hibernate中的核心概念,它允许我们在数据库中建立对象之间的关系映射,以便在程序中操作对象时,可以自动处理与数据库的交互。本篇将深入探讨Hibernate的四种主要关联关系:一对...

    hibernate关联关系实例

    以上就是关于Hibernate关联关系和HQL的基本实例。在实际开发中,这些关联关系和查询方式可以帮助我们构建灵活、可扩展的数据模型,提升代码的可读性和可维护性。在使用时,要注意合理选择关联类型,避免冗余数据,并...

    Hibernate教程15_关联关系中的CRUD Cascade_Fetch

    标题"Hibernate教程15_关联关系中的CRUD Cascade_Fetch"主要关注两个核心概念:关联关系和它们在创建(Create)、读取(Read)、更新(Update)和删除(Delete)操作中的级联(Cascade)以及懒加载和立即加载(Fetch...

    hibernate一对一关联关系

    在Hibernate中,设置一对一关联关系可以帮助我们更好地管理和操作这些具有唯一对应关系的数据。 1. **配置一对一关联** - 在两个相关的实体类中,我们需要定义一个属性,该属性的类型是另一个实体类的实例。 - ...

    hibernate关联关系映射

    "hibernate关联关系映射"是Hibernate的核心概念之一,它定义了如何在Java对象和数据库表之间建立关系。以下是对这个主题的详细讲解: 一、一对一关系映射(One-to-One) 一对一关系是指一个实体对应另一个实体的...

    hibernate各种常用关联关系的基本配置

    配置时使用`@OneToMany`注解,可设置`mappedBy`、`fetch`(默认为EAGER,可改为LAZY实现懒加载)和`cascade`(级联操作)等属性。 ```java @Entity public class User { @OneToMany(mappedBy = "user", cascade = ...

    Hibernate关联关系疑问

    以上是关于Hibernate关联关系的一些基本概念和使用技巧,深入理解和应用这些知识能够帮助开发者更高效地使用Hibernate进行数据库操作。如果你在实践中遇到了具体的问题,可以参考相关文档、教程或社区讨论来获取更...

    hibernate 中的lazy属性祥解.txt

    在Hibernate中,当设置关联属性的`fetch`或`lazy`属性为`true`时,表示该关联属性采用懒加载方式。这意味着,在获取主对象时,Hibernate不会立即加载与其关联的子对象。例如,在用户(User)与帖子(Post)之间的多...

    Hibernate关联关系的CRUD和集合映射(annotation)

    ### Hibernate关联关系 关联关系是指在Java对象与数据库表之间建立的连接。主要有以下几种类型: 1. **一对一(OneToOne)**:一个实体对应表中的唯一一行数据。可以通过`@OneToOne`注解来定义,可以设置`fetch`...

    Hibernate Fetch 的作用

    在Hibernate中,主要有两种Fetch策略:Eager(急切)和Lazy(懒惰)。默认情况下,Hibernate使用Lazy策略,这意味着关联对象不会立即加载,除非显式访问它们。这有助于提高应用程序的性能,因为不必要的数据不会被...

    Hibernate_关联关系映射配置

    本教程将详细讲解Hibernate中的关联关系映射配置,帮助你理解和掌握如何在Hibernate中设置不同类型的关联。 一、一对一(One-to-One)关联 在现实世界中,两个实体之间可能存在一对一的关系,例如一个人只有一个...

    hibernate在单端关联上的lazy策略

    标题中的“hibernate在单端关联上的lazy策略”指的是Hibernate框架中的一种对象关系映射(ORM)技术,用于优化数据加载效率。在Hibernate中,懒加载(Lazy Loading)是一种延迟加载策略,它允许我们在需要时才加载...

    hibernate关联映射的作用和常用属性解释

    ### Hibernate关联映射的作用与常用属性详解 #### 关联映射概述 在对象关系映射(Object Relational Mapping,简称ORM)技术中,Hibernate作为Java领域内非常成熟且功能强大的框架之一,它允许开发者将Java类映射...

    hibernate关联关系之一对一双向关联

    这种关联关系可以是单向的,也可以是双向的,而这里的"hibernate关联关系之一对一双向关联"着重讨论的是后者。双向关联意味着两个实体类都可以直接访问对方,提供了更方便的数据操作。 在Hibernate中,一对一双向...

    当设置lazy=true时,怎样使用extjs和Hibernate

    当`@ManyToOne`, `@OneToOne`, `@OneToMany`, 或 `@ManyToMany`注解中的`fetch`属性设置为`LAZY`时,关联的实体将不会在主实体加载时立即加载,而是在第一次访问这些关联属性时才加载。 - 这种策略避免了在不需要...

    SSH笔记-检索策略(lazy、fetch、batch-size)

    本笔记将聚焦于Hibernate中的类级别检索策略以及`lazy`、`fetch`和`batch-size`这三个属性的使用,帮助开发者更高效地管理数据库查询。 1. **懒加载(Lazy Loading)** `lazy`属性是Hibernate的懒加载机制,用于...

    hibernate 中 fetch=FetchType.LAZY 懒加载失败处理方法

    hibernate 中 fetch=FetchType.LAZY 懒加载失败处理方法 Hibernate 中 fetch=FetchType.LAZY 懒加载失败处理方法是 Hibernate 框架中的一种常见问题。当我们在 Hibernate 中使用懒加载特性时,可能会遇到 ...

    Hibernate lazy延迟加载

    1. 在实体类中,我们通常会使用`@OneToMany`, `@ManyToOne`, `@OneToOne` 或 `@ManyToMany` 注解来定义关联关系,然后通过 `fetch = FetchType.LAZY` 来指定关联关系的加载策略为懒加载。 2. 当执行查询并返回实体时...

    Hibernate ORM - 一对一连接表关联关系

    在一对一关联中,如果希望关联对象在需要时才加载,可以设置`fetch="lazy"`;否则,关联对象会在主对象加载时一同加载,这称为即时加载。 8. **生命周期管理**: Hibernate提供了对对象状态的管理,包括瞬时态、持久...

    传智播客hibernate教程_保存多对一关联关系的更进一步分析

    通过“传智播客hibernate教程_保存多对一关联关系的更进一步分析”,你将学习到如何在实际项目中有效地处理多对一关联,包括关联的配置、保存策略、事务管理和性能优化。该教程通过实例讲解,有助于加深理解和应用...

Global site tag (gtag.js) - Google Analytics