您的位置:首页 > Web前端

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

2016-01-26 10:23 387 查看
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 来控制.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: