• [转]Hibernate3如何解决n+1 selects


     
     

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





  • 相关阅读:
    [AngularFire 2] Protect Write Access Using Security Rules
    [Ramda] Handle Branching Logic with Ramda's Conditional Functions
    [Angular2 Router] Setup page title with Router events
    [HTML] Creating visual skip links in HTML and CSS
    [AngularFire2] Signup and logout
    [AngularFire2] Building an Authentication Observable Data Service
    [Swift] Add Scroll View
    [Swift] Storyboard outlet and action
    [AngularFire2] Auth with Firebase auth -- email
    网站优化与Cdn文件传输服务
  • 原文地址:https://www.cnblogs.com/redcoatjk/p/3780379.html
Copyright © 2020-2023  润新知