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啊。

[转]Hibernate3如何解决n+1 selects的更多相关文章

  1. Hibernate3疑惑解决

    1.session的get()和load()有什么区别?     # get()如果没有找到持久化类返回null,有可能导致空指针异常.     # load()如果没有找到持久化类直接抛出异常.   ...

  2. Hibernate批量操作(二)

    Hibernate提供了一系列的查询接口,这些接口在实现上又有所不同.这里对Hibernate中的查询接口进行一个小结. 我们首先来看一下session加载实体对象的过程:Session在调用数据库查 ...

  3. Java_SSH项目主要步骤记录

    建立Spring-Struts-Hibernate的步骤整理 1. 建立web project 2. 建立hernate, action, service包 3. 右击项目,add myeclipse ...

  4. Maven 开发hibernate存在的诸多问题

    项目结构: 开发平台: maven version 3.5 eclipse 4. 7 oxyen 最新:hibernate 5.x 引入问题 官网提供的必需选择只有 这个 当然还需要我们单独配置mys ...

  5. org.springframework.orm.hibernate3.LocalSessionFactoryBean的疑惑解决办法

    在项目中使用了SSH框架(Struts2 + Spring3+ Hibernate3),applicationContext中配置了sessionFactory <bean id="s ...

  6. 使用myeclipse自动导入hibernate3的jar包,如何关联hibernate源码的解决办法

    1.在网上找了好久,今天终于解决了,如果你的myeclipse自动生成的添加hibernate3jar包时,依靠通常的方法是无法关联其相应版本的源代码的,就是你在编写代码是,按住ctrl + hibe ...

  7. 如何利用hibernate3解决数据库丢失更新问题?

    首先我们要明白什么叫丢失更新. 比如数据库有一个person表,里面有一条这样的数据 "5 zhangsan shenzhen"; 现在有两个事务A.B同时查找了这一条记录: A事 ...

  8. 解决 spring mvc 3.0 结合 hibernate3.2 使用<tx:annotation-driven>声明式事务无法提交的问题(转载)

    1.问题复现 spring 3.0 + hibernate 3.2 spring mvc使用注解方式:service使用@service注解 事务使用@Transactional 事务配置使用 < ...

  9. 解决 spring mvc 3.+ 结合 hibernate3.+ 使用<tx:annotation-driven>声明式事务无法提交的问题

    spring mvc使用注解方式:service使用@service注解 事务使用@Transactional 事务配置使用 <tx:annotation-driven transaction- ...

随机推荐

  1. cocos2d-html5如何更改预加载时的默认的logo图片和删除loading小圈圈

    找到框架目录(2.1.4)里的cocos2d目录里有个CCLoader.js文件,找到LoaderScene的构造方法ctor,可以看到有一行: this._logoTexture.src= &quo ...

  2. Qt之QPropertyAnimation

    简述 QPropertyAnimation类定义了Qt的属性动画. QPropertyAnimation以Qt属性做差值,作为属性值存储在QVariants中,该类继承自QVariantAnimati ...

  3. Java单例类的简单实现

    对于java新手来说,单例类给我的印象挺深,之前一道web后台笔试题就是写单例类.*.*可惜当时不了解. 在大部分时候,我们将类的构造器定义成public访问权限,允许任何类自由创建该类的对象.但在某 ...

  4. UVa 1346 - Songs

    题目大意:n张唱片,每张都有长度和频率,要求题目中公式值最小.求一个排序,输出该排序下第m张CD的id. 思路:贪心,长度越长放越后面,频率越低放越后面,所以按len/p去排序即可. #include ...

  5. man page的介绍

    假设你知道一个命令名,但不知其使用方法,此时可以求助于在线求助系统,即使用man(manual)命令,如man date. 上图为执行man date语句后的结果.<翻页查看时可按空格键,退出m ...

  6. SQLite 字段类型

    一般数据采用的固定的静态数据类型,而SQLite采用的是动态数据类型,会根据存入值自动判断. SQLite具有以下五种数据类型: 1.NULL:空值. 2.INTEGER:带符号的整型,具体取决有存入 ...

  7. Spring中@Transactional用法深度分析

    引言: 在Spring中@Transactional提供一种控制事务管理的快捷手段,但是很多人都只是@Transactional简单使用,并未深入了解,其各个配置项的使用方法,本文将深入讲解各个配置项 ...

  8. hdu 2795 线段树(二维问题一维化)

    Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  9. kuangbin_ShortPath O (LightOJ 1074)

    这是什么鬼OJ啊都没见过害的我还交错语言CE了一发摔 想着懒得重写了直接把上一题的dij改了改就交了 然后RE 反应过来这题有负环 想着怎么标记负环同时不直接结束spfa 看了别人的代码感叹了一下我还 ...

  10. [AC自动机]题目合计

    我只是想记一下最近写的题目而已喵~ 题解什么的才懒得写呢~ [poj 1625]Censored! 这题注意一个地方,就是输入数据中可能有 ASCII 大于 128 的情况,也就是说用 char 读入 ...