hibernate查漏补缺3
hibernate的检索策略
转载请注明: TheViper http://www.cnblogs.com/TheViper
先看个大概
1.类级别
立即检索 <class lazy='false'>;延迟检索 <class lazy='true'>.默认为延迟检索。
如果加载一个持久化对象是为了获取它的属性,用立即检索;而如果仅仅是为了获取它的引用,用延迟加载。比如,
Friend_Category fc = new Friend_Category(); fc.setCategory_name("已经存在的好友分类"); User_Friend_pk pk = new User_Friend_pk(); pk.setFriend_name(“新好友”); pk.setName("TheViper"); User_Friend uf = new User_Friend(); uf.setUser_friend_pk(pk); Friend_Category fc1 = (Friend_Category) session.load( Friend_Category.class, 1); uf.setFriend_categorys(fc1); // uf.setSort("好基友"); session.saveOrUpdate(fc1); session.save(uf);
可以看到,加载Friend_Catagory类只是为了把新加好友加入到已经存在的好友分类里。这里用load()返回一个代理类,里面只有id属性,其他属性均为null,并没有返回所有的属性,因此占用的内存很少。
另外,不管<class> 的lazy属性是true还是false,get(),list()总是用立即加载,但是并不会去加载与之相关联的类(关联级别检索为默认设置延迟加载)。
2.关联级别
- 一对多和多对多关联的检索策略
由<set>的lazy和fetch属性决定
lazy:决定关联集合被初始化的时机。即是在加载时就被初始化,还是在被访问时才初始化。
fetch:取值为select或subselect时,决定初始化关联集合时查询语句的形式;取值为fetch时,决定关联集合被初始化的时机,这时显式设置lazy属性是无意义的。
lazy=true时,关联集合类的代理在下面情况会被初始化:
1.应用程序第一次访问它,如调用它的iterator(),size(),isEmpty()或contains().
2.通过org.hibernate.Hibernate类的initialize()静态方法初始化。
对于lazy=extra,hibernate会进一步延迟关联对象的初始化时机。具体说就是仅当应用程序第一次调用关联对象的iterator()时才会加载。只有这一个方法能起作用。
对于lazy=false,
Feeling f = (Feeling) session.get(Feeling.class, 1);
<set name="feelingComments" inverse="true" lazy="false"> <key column="feeling_id" /> <one-to-many class="model.FeelingComment" /> </set>
当取出Feeling类时,便会一并取出FeelingComment类
Hibernate:
select
feeling0_.feeling_id as feeling1_1_0_,
feeling0_.content as content1_0_,
feeling0_.id as id1_0_
from
feeling feeling0_
where
feeling0_.feeling_id =?
Hibernate:
select
feelingcom0_.feeling_id as feeling3_1_1_,
feelingcom0_.feelingComment_id as feelingC1_1_,
feelingcom0_.feelingComment_id as feelingC1_2_0_,
feelingcom0_.content as content2_0_,
feelingcom0_.feeling_id as feeling3_2_0_,
feelingcom0_.id as id2_0_
from
feeling_comment feelingcom0_
where
feelingcom0_.feeling_id=?
下面设置fetch=subselect,
Session session = sf.openSession(); Transaction transaction = session.beginTransaction(); Query q = session.createQuery("from Feeling feeling"); List<Feeling> feelings = (List<Feeling>) q.list(); for (Feeling feeling : feelings) { Set comments = feeling.getFeelingComments(); System.out.println(comments); } transaction.commit(); session.close();
Hibernate:
select
feeling0_.feeling_id as feeling1_1_,
feeling0_.content as content1_,
feeling0_.id as id1_
from
feeling feeling0_
Hibernate:
select
feelingcom0_.feeling_id as feeling3_1_1_,
feelingcom0_.feelingComment_id as feelingC1_1_,
feelingcom0_.feelingComment_id as feelingC1_2_0_,
feelingcom0_.content as content2_0_,
feelingcom0_.feeling_id as feeling3_2_0_,
feelingcom0_.id as id2_0_
from
feeling_comment feelingcom0_
where
feelingcom0_.feeling_id in (
select
feeling0_.feeling_id
from
feeling feeling0_
)
[model.FeelingComment@17f2fe3, model.FeelingComment@1211a4b, model.FeelingComment@533fc3]
[model.FeelingComment@a9a994, model.FeelingComment@455d96]
[model.FeelingComment@35c6f]
可以看到hibernate是用in子查询的方式抓取关联对象的,这时batch-size属性会被忽略。
fetch=join表示采用左外连接的检索策略抓取关联对象,注意list()会自动忽略这个设置,这个很重要。
还是上面的代码,结果
Hibernate: select feeling0_.feeling_id as feeling1_1_, feeling0_.content as content1_, feeling0_.id as id1_ from feeling feeling0_ Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=? [model.FeelingComment@1fbf047, model.FeelingComment@82a092, model.FeelingComment@15bd7c5] Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=? [model.FeelingComment@c407e, model.FeelingComment@5e3212] Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=? [model.FeelingComment@69757f]
而这时设置lazy,batch-size属性没有意义。
关于batch-size属性,它用来为检索策略设定批量检索的数量,可以减少select语句的数量,提供检索的运行性能。
比如batch-size设为2,关联集合里面有3个对象。
Session session = sf.openSession(); Transaction transaction = session.beginTransaction(); Query q = session.createQuery("from Feeling feeling"); List<Feeling> feelings = (List<Feeling>) q.list(); for (Feeling feeling : feelings) { Set comments = feeling.getFeelingComments(); System.out.println(comments); } transaction.commit(); session.close();
Hibernate:
select
feeling0_.feeling_id as feeling1_1_,
feeling0_.content as content1_,
feeling0_.id as id1_
from
feeling feeling0_
Hibernate:
select
feelingcom0_.feeling_id as feeling3_1_1_,
feelingcom0_.feelingComment_id as feelingC1_1_,
feelingcom0_.feelingComment_id as feelingC1_2_0_,
feelingcom0_.content as content2_0_,
feelingcom0_.feeling_id as feeling3_2_0_,
feelingcom0_.id as id2_0_
from
feeling_comment feelingcom0_
where
feelingcom0_.feeling_id in (
?, ?
)
[model.FeelingComment@177151, model.FeelingComment@16e125, model.FeelingComment@110a0ca]
Hibernate:
select
feelingcom0_.feeling_id as feeling3_1_1_,
feelingcom0_.feelingComment_id as feelingC1_1_,
feelingcom0_.feelingComment_id as feelingC1_2_0_,
feelingcom0_.content as content2_0_,
feelingcom0_.feeling_id as feeling3_2_0_,
feelingcom0_.id as id2_0_
from
feeling_comment feelingcom0_
where
feelingcom0_.feeling_id=?
[model.FeelingComment@1c8f247, model.FeelingComment@1d67998]
[model.FeelingComment@ac44e3]
注意,如果对Query接口用iterator()返回数据的话,会忽略batch-size!
Hibernate: select feeling0_.feeling_id as col_0_0_ from feeling feeling0_ Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =? Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=? [model.FeelingComment@ee7d2a, model.FeelingComment@fb3758, model.FeelingComment@17596bc] Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =? Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=? [model.FeelingComment@855e17] Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =? Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=? [model.FeelingComment@85ba73, model.FeelingComment@191bae7]
iterator()参见上一篇说明.
- 多对一和一对一关联的检索策略
fetch=join
<set name="feelingComments" inverse="true"> <key column="feeling_id" /> <one-to-many class="model.FeelingComment" /> </set>
<many-to-one name="feelings" column="feeling_id" class="model.Feeling" not-null="false" fetch="join"> </many-to-one>
FeelingComment f = (FeelingComment) session .get(FeelingComment.class, 1);
Hibernate:
select
feelingcom0_.feelingComment_id as feelingC1_2_1_,
feelingcom0_.content as content2_1_,
feelingcom0_.feeling_id as feeling3_2_1_,
feelingcom0_.id as id2_1_,
feeling1_.feeling_id as feeling1_1_0_,
feeling1_.content as content1_0_,
feeling1_.id as id1_0_
from
feeling_comment feelingcom0_
left outer join
feeling feeling1_
on feelingcom0_.feeling_id=feeling1_.feeling_id
where
feelingcom0_.feelingComment_id =?
上面的很简单。稍微变一下,在<set>添加lazy=false.
Hibernate:
select
feelingcom0_.feelingComment_id as feelingC1_2_1_,
feelingcom0_.content as content2_1_,
feelingcom0_.feeling_id as feeling3_2_1_,
feelingcom0_.id as id2_1_,
feeling1_.feeling_id as feeling1_1_0_,
feeling1_.content as content1_0_,
feeling1_.id as id1_0_
from
feeling_comment feelingcom0_
left outer join
feeling feeling1_
on feelingcom0_.feeling_id=feeling1_.feeling_id
where
feelingcom0_.feelingComment_id =?
Hibernate:
select
feelingcom0_.feeling_id as feeling3_1_1_,
feelingcom0_.feelingComment_id as feelingC1_1_,
feelingcom0_.feelingComment_id as feelingC1_2_0_,
feelingcom0_.content as content2_0_,
feelingcom0_.feeling_id as feeling3_2_0_,
feelingcom0_.id as id2_0_
from
feeling_comment feelingcom0_
where
feelingcom0_.feeling_id=?
可以看到,这次发出了两条sql,第二次会再次根据feeling_id取出feeling_comment,个人感觉没意义啊。而在一对多和多对多中就不会出现这种情况。
另外,list()也会像一对多和多对多,忽略fetch=join.
lazy=proxy(默认)
这个像一对多和多对多里面的lazy=true.即查询“多”那边时,不会查询“一”那边的关联对象。只有当实际要用关联对象时才会初始化它。
注意对于<one-to-one>,必须要设置constrained=true.表明“一”那边的关联对象不能为空。
lazy=no-proxy
这个和lazy=proxy很相似。不过需要编译时字节码增强,否则和proxy没区别 。
这样做了后的效果。
FeelingComment f = (FeelingComment) session .get(FeelingComment.class, 1); Feeling f1 = f.getFeelings(); f1.getContent();
当运行到getFeelings()时,hibernate会发出sql.而lazy=proxy时,要运行到getContent()时才发出。
lazy=false
当运行到get()时就会发出两条sql.
Hibernate:
select
feelingcom0_.feelingComment_id as feelingC1_2_0_,
feelingcom0_.content as content2_0_,
feelingcom0_.feeling_id as feeling3_2_0_,
feelingcom0_.id as id2_0_
from
feeling_comment feelingcom0_
where
feelingcom0_.feelingComment_id =?
Hibernate:
select
feeling0_.feeling_id as feeling1_1_0_,
feeling0_.content as content1_0_,
feeling0_.id as id1_0_
from
feeling feeling0_
where
feeling0_.feeling_id =?
另外如果
<many-to-one name="feelings" column="feeling_id" class="model.Feeling" not-null="false" lazy="false">
同时在“一”那边
<class name="model.Feeling" table="feeling" lazy="false"> </class>
便会出现和fetch=join一样的sql.
FeelingComment f = (FeelingComment) session .get(FeelingComment.class, 1);
Hibernate:
select
feelingcom0_.feelingComment_id as feelingC1_2_1_,
feelingcom0_.content as content2_1_,
feelingcom0_.feeling_id as feeling3_2_1_,
feelingcom0_.id as id2_1_,
feeling1_.feeling_id as feeling1_1_0_,
feeling1_.content as content1_0_,
feeling1_.id as id1_0_
from
feeling_comment feelingcom0_
left outer join
feeling feeling1_
on feelingcom0_.feeling_id=feeling1_.feeling_id
where
feelingcom0_.feelingComment_id =?
在多对一和一对一中使用批量检索
在<many-to-one>,<one-to-one>中没有batch-size属性,需要“一”那边设置batch-size.比如,需要加装三个关联实例(一),batch-size=2.
<class name="model.Feeling" table="feeling" batch-size="2"> </class>
Query q = session.createQuery("from FeelingComment"); List<FeelingComment> feelings = (List<FeelingComment>) q.list(); for (FeelingComment feelingComment : feelings) { Feeling feeling = feelingComment.getFeelings(); System.out.println(feeling); }
Hibernate:
select
feelingcom0_.feelingComment_id as feelingC1_2_,
feelingcom0_.content as content2_,
feelingcom0_.feeling_id as feeling3_2_,
feelingcom0_.id as id2_
from
feeling_comment feelingcom0_
Hibernate:
select
feeling0_.feeling_id as feeling1_1_0_,
feeling0_.content as content1_0_,
feeling0_.id as id1_0_
from
feeling feeling0_
where
feeling0_.feeling_id in (
?, ?
)
model.Feeling@1e684bd
model.Feeling@1e684bd
model.Feeling@1e684bd
model.Feeling@1149b6c
Hibernate:
select
feeling0_.feeling_id as feeling1_1_0_,
feeling0_.content as content1_0_,
feeling0_.id as id1_0_
from
feeling feeling0_
where
feeling0_.feeling_id =?
model.Feeling@fb8996
model.Feeling@1149b6c
注意上面的例子<many-to-one>中的lazy是默认的proxy.如果lazy=false的话,当运行到feelingComment.getFeelings();时就会发出后面的sql了。
- 属性级别的检索策略
只需在<property>,<component>上设置lazy=true.这种策略适用于二进制大对象,字符串大对象,大容量组件类型的属性。比如User类的image属性储存着图片的二进制数据,
User user=(User)session.get(User.class,1);user.getImage();
get()时,hibernate没有加载image属性,运行到getImage()时才会加载。
注意,用的时候必须向多对一,一对一lazy=no-proxy一样,需要编译时字节码增强。