Hibernate3中取得多层数据的所产生的n+1 selects问题的解决:

其实这个问题在Hibernate in Action中已经有很多种解决办法了。但我觉得其中最好的办法是用Criteria

的FetchMode来解决,但是Hibernate in Action中写的很不详细。我昨晚试了好长时间来的到答案。下

面总结一下。

需求这样的,我有四张表(one,two,three,four)从one一直外键关联到four。结构如下

现在在Session中得到One,并从One里一直取到Four里的内容。如果简单的用Session.get来实现是这

样的。

Java代码

01.One one = (One)session.get(One.class,new Integer(1));
02. 
03.Iterator iterone = one.getTwos().iterator();
04. 
05.while(iterone.hasNext()){
06. 
07.Two two = (Two) iterone.next();
08. 
09.Iterator itertwo =
10.two.getThrees().iterator();
11. 
12.while(itertwo.hasNext()){
13. 
14.Three three = (Three) itertwo.next();
15.three.getFours().size(); 
16. 
17.}
18. 
19.}

这样我在Session关闭后返回的One里是从One到Four的信息都有的。
然而这样做所导致的结果是生成大量的SQL查询,这是一个典型的n+1 Selects问题。如果系统结构层次多,符合条件的记录多,那么Hibernate为你生成的SQL查询将是难以接受的。
对于这个例子生成的SQL是这样的
SQL代码

01.Hibernate: select one0_.c_one_id as c1_0_,
02.one0_.c_one_text as c2_3_0_ from One one0_ where
03.one0_.c_one_id=?
04. 
05.Hibernate: select twos0_.c_one_id as c2_1_,
06.twos0_.c_two_id as c1_1_, twos0_.c_two_id as c1_0_,
07.twos0_.c_one_id as c2_2_0_, twos0_.c_two_text as
08.c3_2_0_ from Two twos0_ where twos0_.c_one_id=?
09. 
10.Hibernate: select threes0_.c_two_id as c2_1_,
11.threes0_.c_three_id as c1_1_, threes0_.c_three_id
12.as c1_0_, threes0_.c_two_id as c2_1_0_,
13.threes0_.c_three_text as c3_1_0_ from Three
14.threes0_ where threes0_.c_two_id=?
15. 
16.Hibernate: select fours0_.c_three_id as c2_1_,
17.fours0_.c_four_id as c1_1_, fours0_.c_four_id as
18.c1_0_, fours0_.c_three_id as c2_0_0_,
19.fours0_.c_four_text as c3_0_0_ from Four fours0_
20.where fours0_.c_three_id=?
21. 
22.Hibernate: select fours0_.c_three_id as c2_1_,
23.fours0_.c_four_id as c1_1_, fours0_.c_four_id as c1_0_, fours0_.c_three_id as c2_0_0_,
24.fours0_.c_four_text as c3_0_0_ from Four fours0_
25.where fours0_.c_three_id=?
26. 
27.Hibernate: select threes0_.c_two_id as c2_1_,
28.threes0_.c_three_id as c1_1_, threes0_.c_three_id
29.as c1_0_, threes0_.c_two_id as c2_1_0_,
30.threes0_.c_three_text as c3_1_0_ from Three
31.threes0_ where threes0_.c_two_id=?
32. 
33.Hibernate: select fours0_.c_three_id as c2_1_,
34.fours0_.c_four_id as c1_1_, fours0_.c_four_id as
35.c1_0_, fours0_.c_three_id as c2_0_0_,
36.fours0_.c_four_text as c3_0_0_ from Four fours0_
37.where fours0_.c_three_id=?
38. 
39.Hibernate: select fours0_.c_three_id as c2_1_,
40.fours0_.c_four_id as c1_1_, fours0_.c_four_id as
41.c1_0_, fours0_.c_three_id as c2_0_0_,
42.fours0_.c_four_text as c3_0_0_ from Four fours0_
43.where fours0_.c_three_id=?

对于这样的问题,在没有Hibernate以前我们一般都用jdbc来做,那样的话我们其实用一个进行3次join

的sql语句就可以实现,但 是这样解决也有问题,就是返回的ResultSet中的数据非常多,而且杂乱,其

实是从one到four平行排列的。对于这样的结果集我们要把它手动影射 曾对象结构也是一个很复杂的操

作。

幸好Hibernate3可以为我们做这些事情(我再一次被Hibernate的强大所震撼)。

上面的实现可以用Criteria来实现: Java代码

01.session = sessionFactory.openSession();
02. 
03.Criteria criteria =
04.session.createCriteria(One.class);
05. 
06.criteria.add(Expression.eq("COneId",new
07.Integer(1)));
08. 
09.one =
10.(One)criteria.setFetchMode("twos",FetchMode.JOIN).
11.setFetchMode("twos.threes",FetchMode.JOIN).
12.setFetchMode("twos.threes.fours",FetchMode.JOIN).
13.uniqueResult();
14. 
15.session.close();

这里的重点是这句话

criteria.setFetchMode("twos",FetchMode.JOIN).

setFetchMode("twos.threes",FetchMode.JOIN).

setFetchMode("twos.threes.fours",FetchMode.JOIN).uniqueResult();

在用Criteria之前先设置FetchMode,应为Criteria是动态生成sql语句的,所以生成的sql就是一层层Join

下去的。

setFetchMode(String,Mode)第一个参数是association path,用"."来表示路径。这一点具体的例子

很少,文档也没有写清楚。我也是试了很久才试出来的。

就这个例子来所把因为取道第四层,所以要进行三次setFetchMode

第一次的路径是twos,一位one中有two的Set。这个具体要更具hbm.xml的配置来定。

第二个路径就是twos.threes

第三个就是twos.threes.fours

一次类推,一层层增加的。

这样做法最终生成的SQL是这样的:

SQL代码

01.Hibernate: select this_.c_one_id as c1_3_,
02.this_.c_one_text as c2_3_3_, twos2_.c_one_id as c2_5_,
03.twos2_.c_two_id as c1_5_, twos2_.c_two_id as c1_0_,
04.twos2_.c_one_id as c2_2_0_, twos2_.c_two_text as
05.c3_2_0_, threes3_.c_two_id as c2_6_,
06.threes3_.c_three_id as c1_6_, threes3_.c_three_id as
07.c1_1_, threes3_.c_two_id as c2_1_1_,
08.threes3_.c_three_text as c3_1_1_, fours4_.c_three_id
09.as c2_7_, fours4_.c_four_id as c1_7_,
10.fours4_.c_four_id as c1_2_, fours4_.c_three_id as
11.c2_0_2_, fours4_.c_four_text as c3_0_2_ from One this_
12.left outer join Two twos2_ on
13.this_.c_one_id=twos2_.c_one_id left outer join Three
14.threes3_ on twos2_.c_two_id=threes3_.c_two_id left
15.outer join Four fours4_ on
16.threes3_.c_three_id=fours4_.c_three_id where
17.this_.c_one_id=?

虽然很长但是只有一条SQL语句。性能要好很多。Hibernate的强大之处是它会把返回的ResultSet自动

影射到你的对象模型里面去。这就为我们省了很多事。

看来Hibernate真是一个耐人寻味的Framework啊。

最新文章

  1. 上传文件fileupload
  2. java 内存模型
  3. ylbtech-数据库设计与优化-对作为复选框/单选列表的集合表的设计
  4. 【Pure】
  5. SystemParametersInfo
  6. jqPlot,一个 jQuery这个 JavaScript 框架的绘图插件
  7. 删除左右两边的空格trim
  8. ajax 传值 中文乱码问题
  9. ubuntu配置jdk脚本以及导致开不了机的解决方案
  10. Vi编辑器入门
  11. Android makefile 组织结构
  12. 数据库文件导入导出操作,以及赋予权限SQL语句
  13. ios小型服务器环境配置
  14. 关于CTO角色的认知
  15. vue(2)—— vue简单语法运用,常用指令集
  16. Visual Studio 2015的安装及单元测试练习
  17. php 历史版本下载地址
  18. hung task机制
  19. mybatis的快速入门
  20. 第十九课 golang中的下划线

热门文章

  1. 暴力枚举——Help Me with the Game
  2. mybatis和model关联
  3. Openstack学习历程_1_视频
  4. javascript 中 typeof 的使用
  5. bootStrap-1
  6. C++ Primer : 第十二章 : 动态内存之shared_ptr与new的结合使用、智能指针异常
  7. windows7下python3.4.3 添加库路径(转)
  8. error_log() 范例
  9. scala言语基础学习三
  10. URAL 1291 Gear-wheels(BFS)