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

转载 2013年12月04日 10:06:38


Hibernate3中取得多层数据的所产生的n+1 selects问题的解决。 其实这个问题在Hibernate in Action中已经有很多种解决办法了。但我觉得其中最好的办法是用Criteria的FetchMode来解决,但是Hibernate in Action中写的很不详细。我昨晚试了好长时间来的到答案。下面总结一下。
需求这样的,我有四张表(one,two,three,four)从one一直外键关联到four。结构如下
hibernatefetch.jpg
现在在Session中得到One,并从One里一直取到Four里的内容。如果简单的用Session.get来实现是这样的。
One one = (One)session.get(One.class,new Integer(1));
         Iterator iterone = one.getTwos().iterator();
        while(iterone.hasNext()){
             Two two = (Two) iterone.next();
             Iterator itertwo = two.getThrees().iterator();
            while(itertwo.hasNext()){
                 Three three = (Three) itertwo.next();
                 three.getFours().size();                
             }
         }这样我在Session关闭后返回的One里是从One到Four的信息都有的。
然而这样做所导致的结果是生成大量的SQL查询,这是一个典型的n+1 Selects问题。如果系统结构层次多,符合条件的记录多,那么Hibernate为你生成的SQL查询将是难以接受的。
对于这个例子生成的SQL是这样的
Hibernate: select one0_.c_one_id as c1_0_, one0_.c_one_text as c2_3_0_ from One one0_ where one0_.c_one_id=?
Hibernate: select twos0_.c_one_id as c2_1_, twos0_.c_two_id as c1_1_, twos0_.c_two_id as c1_0_, twos0_.c_one_id as c2_2_0_, twos0_.c_two_text as c3_2_0_ from Two twos0_ where twos0_.c_one_id=?
Hibernate: select threes0_.c_two_id as c2_1_, threes0_.c_three_id as c1_1_, threes0_.c_three_id as c1_0_, threes0_.c_two_id as c2_1_0_, threes0_.c_three_text as c3_1_0_ from Three threes0_ where threes0_.c_two_id=?
Hibernate: select fours0_.c_three_id as c2_1_, fours0_.c_four_id as c1_1_, fours0_.c_four_id as c1_0_, fours0_.c_three_id as c2_0_0_, fours0_.c_four_text as c3_0_0_ from Four fours0_ where fours0_.c_three_id=?
Hibernate: select fours0_.c_three_id as c2_1_, fours0_.c_four_id as c1_1_, fours0_.c_four_id as c1_0_, fours0_.c_three_id as c2_0_0_, fours0_.c_four_text as c3_0_0_ from Four fours0_ where fours0_.c_three_id=?
Hibernate: select threes0_.c_two_id as c2_1_, threes0_.c_three_id as c1_1_, threes0_.c_three_id as c1_0_, threes0_.c_two_id as c2_1_0_, threes0_.c_three_text as c3_1_0_ from Three threes0_ where threes0_.c_two_id=?
Hibernate: select fours0_.c_three_id as c2_1_, fours0_.c_four_id as c1_1_, fours0_.c_four_id as c1_0_, fours0_.c_three_id as c2_0_0_, fours0_.c_four_text as c3_0_0_ from Four fours0_ where fours0_.c_three_id=?
Hibernate: select fours0_.c_three_id as c2_1_, fours0_.c_four_id as c1_1_, fours0_.c_four_id as c1_0_, fours0_.c_three_id as c2_0_0_, fours0_.c_four_text as c3_0_0_ from Four fours0_ where fours0_.c_three_id=?
对于这样的问题,在没有Hibernate以前我们一般都用jdbc来做,那样的话我们其实用一个进行3次join的sql语句就可以实现,但是这样解决也有问题,就是返回的ResultSet中的数据非常多,而且杂乱,其实是从one到four平行排列的。对于这样的结果集我们要把它手动影射曾对象结构也是一个很复杂的操作。
幸好Hibernate3可以为我们做这些事情(我再一次被Hibernate的强大所震撼)。
上面的实现可以用Criteria来实现:
session = sessionFactory.openSession();
         Criteria criteria = session.createCriteria(One.class);
         criteria.add(Expression.eq("COneId",new Integer(1)));
         one = (One)criteria.setFetchMode("twos",FetchMode.JOIN).setFetchMode("twos.threes",FetchMode.JOIN).setFetchMode("twos.threes.fours",FetchMode.JOIN).uniqueResult();
         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是这样的:
Hibernate: select this_.c_one_id as c1_3_, this_.c_one_text as c2_3_3_, twos2_.c_one_id as c2_5_, twos2_.c_two_id as c1_5_, twos2_.c_two_id as c1_0_, twos2_.c_one_id as c2_2_0_, twos2_.c_two_text as c3_2_0_, threes3_.c_two_id as c2_6_, threes3_.c_three_id as c1_6_, threes3_.c_three_id as c1_1_, threes3_.c_two_id as c2_1_1_, threes3_.c_three_text as c3_1_1_, fours4_.c_three_id as c2_7_, fours4_.c_four_id as c1_7_, fours4_.c_four_id as c1_2_, fours4_.c_three_id as c2_0_2_, fours4_.c_four_text as c3_0_2_ from One this_ left outer join Two twos2_ on this_.c_one_id=twos2_.c_one_id left outer join Three threes3_ on twos2_.c_two_id=threes3_.c_two_id left outer join Four fours4_ on threes3_.c_three_id=fours4_.c_three_id where this_.c_one_id=?
虽然很长但是只有一条SQL语句。性能要好很多。Hibernate的强大之处是它会把返回的ResultSet自动影射到你的对象模型里面去。这就为我们省了很多事。

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

源码,没什么东西。

相关文章推荐

weblogic10与hibernate3中antlr.jar包冲突问题解决

【转自:http://zhucaiguai.iteye.com/blog/850394】环境配置:weblogic 10.3 spring hibernate 3.3.0 问题描述:以上配置...

关于Hibernate3,mysql5 数据库连接超过8小时 连接超时的问题及解决方案(部分摘自网络)

首先我用的ssh框架初始报错:Last packet sent to the server was 0 ms ago    MySQL服务在长时间不连接之后断开了,断开之后的首次请求会抛出这个异常...
  • LLLJava
  • LLLJava
  • 2013年05月06日 18:59
  • 575

weblogic10与hibernate3中antlr.jar包冲突问题解决(bonita bpm部署到weblogic10遇到的问题)

环境配置:weblogic 10.3 spring hibernate 3.3.0 问题描述:以上配置的项目在tomcat下运行没有问题,发布到weblogic后,启动时也是没有问题的,但不能执行数据...

Spring3+Hibernate3(Jpa) 配置多个数据源的解决方案(基于注解)

Spring3+Hibernate3(Jpa) 配置多个数据源的解决方案(基于注解) ----------------配置文件----------------------------...

Spring3+Hibernate3(Jpa) 配置多个数据源的解决方案(基于注解)

Spring3+Hibernate3(Jpa) 配置多个数据源的解决方案(基于注解) ----------------配置文件----------------------------...

Spring3+Hibernate3(Jpa) 配置多个数据源的解决方案(基于注解)

----------------配置文件-------------------------------- -------------------------------------META-INF/...

Spring2.5.6整合hibernate3遇到的异常1

最近做ssh的整合,第一次整合,不出意外的碰到了很多问题。 碰到了很多异常,为了以后查找方便,还有其他朋友碰到时查找的方便,我直接把异常复制。然后解决方法也写在下面。 关键异常描述,用红色...

org.springframework.orm.hibernate3.HibernateQueryException: unexpected token: where near line 1, col

org.springframework.orm.hibernate3.HibernateQueryException: unexpected token: where near line 1, col...

Strut1+Spring2+Hibernate3 基础工作代码 流程

struts配置文件中配置控制跳转: ap

hibernate3(1)

0.  M  V  C M model JDBC/hibernate/mybatis V view  JSP C contorller  struts2 java           ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Hibernate3中取得多层数据的所产生的n+1 selects问题的解决
举报原因:
原因补充:

(最多只允许输入30个字)