大家好。在做Hibernate查询的时候,对于一对多的查询效率有很大的疑问。比如说“班级”表,和“学生”表,当获得班级表的时候,Hibernate将获得所有的学生记录(Lazy=false)。可能有的朋友会说设置lazy=true,使用延迟加载来分散工作强度。但这在我的例子里不适用。延迟加载是当你需要的时候加载子类(子表),在我的程序里,查询到“班级”表之后,我需要把这个类转化成另一个结构相同但package不同的类(做GWT时服务器端的代码在客户端不能识别,所以要进行转化)。由于一个类里有很多子类,而子类还包含别的子类,所以手动转化很困难。我用一个第三方的工具进行转化,这个工具会对里面所有的属性遍历,这样的话即便在Hibernate里设置了延迟加载,转化的时候还是会查找获得所有的子类信息。这样的话,只要有几个表里有几百条记录就会影响到查询速度。而且更严重的是在GWT里用RPC从服务器端获取类的时候,如果这个类所包含的数据量太大,当从服务器向客户传输的时候,所形成的JSON字符串就特别大,有时候甚至会导致服务器死机。我想目前的解决办法只能是一个类一个类的查询,需要哪个的时候再查询这个。不知道各位有没有什么好的办法,有没有朋友遇到过类似情况,把你们的经验和方法都说出来听听吧。

解决方案 »

  1.   

    hibernate仅是or映射工具,他的加载策略就是这样,所以可以采用如下方式避免:
    1 用hql来写查询语句,这是最高效的办法(推荐)
    2 用jdbc,脱离了hibernate范畴,缓存方面和乐观所方面会出现不一致,而且语句变得繁琐了(不推荐)
    3 将前台要用到的实体,独立设计成单独的类,没有任何关联,都是单表,用到的只是hibernate的封装以及简单的or映射(视情况而定)
    ......
      

  2.   

    听你意思就是Set student是被迫加载的
    如果用JSON-plugin进行转化的, 在"班级"类的getStudend()方法加前@JSON(serialize=false),hibernate的student任然可以懒加载
      

  3.   


    谢谢回复。你的第二个建议相当于放弃hibernate,正如你所说会繁琐很多,如果这样我还不如用iBatis了,那样的话自己定义需要获得的子类。第三个建议比较有意思,我会试试。问题是前台问题可能会解决,但服务器端如果子表数量太大的话还是会降低效率。第一个建议我不太清楚你的意思,比如HQL: 
    SELECT grade FROM Grade grade WHERE grade.pkId = :pkId查询Grade的时候不是一样会获得所有和它相关的子类吗?能仔细说明一下吗?
      

  4.   


    我没有用JSON-plugin进行转化,我用的是dozer。dozer是进行类之间的转化。JSON是由GWT自动生成的,所以在声称JSON的时候Hibernate 相关的操作已经完成了。
      

  5.   

    1 hibernate不设置一对多,需要就单独写一个查询方法.2 "另一个结构相同但package不同的类"中不要写List类型的属性(只写班级的基本属性,不要写学生list),转换的时候是不是就不会自动去取子表数据了呢?你试一下.
      

  6.   


    Hibernate一对多关联最大的好处是在更新操作上 而不是在查询方面体现出来的 最好的办法就是查询出一个实体 然后根据这个实体再查询出对应的数据 
    至于是否懒加载 得分你SESSION的管理情况 一般查询单个实体(get、load)的时候我会把懒加载去掉 而我从来不会使用一对多关系给的那个set集合 所以关联信息的时候我通常都会设置成TRUE(一对一关联的话 设置成FALSE 总之就是看数据量的大小以及该数据是否有从这个关联里取出的必要)
      

  7.   


    谢谢回复。对于您的答案,提出点疑问1. 在Hibernate里不设置一对多的话,岂不是放弃了数据的结构和完整性,那样还不如iBatis好用了2. 这条看似管用,但实际操作起来却很麻烦。和1楼landor2004朋友的方法一样。但转化的时候,工具读取源类的时候还是会遍历所有属性,虽然在转化的时候不会把这些属性复制到目标类里,但是数据库的负担没有减轻,没有从根本上解决问题。再次感谢你的回帖,谢谢。
      

  8.   


    谢谢回复。恩。思路是好的,不过不太适合我的情况。如果仔细看我的描述,我的问题导致lazy不管是true还是false都会加载所有子表的东西,因为当我得到一个类的时候,有一个工具会遍历其中所有的属性。可能我真的不得不用iBatis来控制子表的内容和数据量大小了。
      

  9.   

    一般不建议用one2many的关系,像你这样的情况就是用个瞬时的Collection在班级那里,到用的时候再去查询,将结果丢里边,这样可以控制实体个数,也可以实现分页,只要有many2one的关系就好了。什么东西都是有取舍的,不要因为用而用。hibernate自己的说明文档中也说,在复杂的情况下用sql
      

  10.   

    或许你应该使用代理,而不是直接复制,例如:
    @Entity
    public class OneSide {    @Id
        private int id;
        @OneToMany
        private List<ManySide> manySideList;    // getters and setters
        ...
    }public class OneSideConverter {    private OneSide entity;
        private int expandLevel;    public OneSide(OneSide oneSide, int expandLevel) {
            this.expandLevel = expandLevel;
            this.entity = oneSide;
        }    public List<ManySide> getManySideList() {
            return (expandLevel > 0) ? entity : null;
        }
    }
    Netbeans的RESTful Web Service工程中,大量使用这种方式,实现Java对象和JSON的转换。
      

  11.   

    这个问题我也思考过。说下我的想法。
    首先,hibernate是以对象方式映射的关系数据库。数据库里的外键关系在对象里就是引用关系。即,表A有一个表B的外键,在映射的时候就是class A里持有class B的一个引用,也就是B类型的一个成员变量。这样,我们load一个对象时,其实拿到的是一个对象图。我们现实中的项目,很少有孤零零的表,表之间总是有关联的,映射到对象,就是对象总是引用别的对象,被引用的对象又引用别的对象...如此反复,甚至还有循环引用...当我们拿到一个对象时,就拿到了整个对象图。
    如果是一般的web开发,hibernate提供懒加载机制和openSessionInView机制,我们在渲染视图时,按需取出对象,没有用到的对象,则不从数据库里加载出来。
    现在,楼主是一个RPC调用,而不是典型的web应用,没有一个明确的view可供渲染,所以你就把取得的对象图整个都扔了过去...
    解决的办法就是,明确指出输出的内容。
    你可以把输出的对象想象成一棵树,这棵树,你必须指出,哪些枝和叶需要输出,哪些不需要。这棵树可以从客户端传来,也可在定义接口时由服务器指定,这个就看业务要求了。表现形式么,我觉得xml挺适合。
    这里17楼的回复,似乎是正确的,你可以验证一下。我猜它那个expandLevel参数,是明确指出取几层数据。这也是一个可行方案。
      

  12.   

    学习了 
    hibernate一对多就是这样 数据量大时  查询是弱点
      

  13.   


    public class OneSideConverter {    private OneSide entity;
        private int expandLevel;    public OneSide(OneSide oneSide, int expandLevel) {
            this.expandLevel = expandLevel;
            this.entity = oneSide;
        }    public List<ManySide> getManySideList() {
            return (expandLevel > 0) ? entity : null;
        }
    }
      

  14.   


    我觉得如果这样的话,还不如用iBatis,这样可以建立ResultMap来决定是否载入子类。昨天调查了一下iBatis,本来最让我头疼的iBatis初始架构,那一大堆XML和SQL能把人写疯。但发现iBatis提供的iBator可以从数据库生成Java Model,XML还有DAO,而且使用动态SQL,这样基本上不用修改什么就可以直接扔到Spring里让它去管理,在需要的时候从Spring Context调用生成的DAO,配合其中的selectByExample,使用动态SQL,很方便。甚至可以把常用的几个动态SQL写到DAO里供以后直接调用。这些从数据库(数据库是开始做的时候Hibernate自己建立的)生成的Java Model和基本的ORM有些不同。如果在iBator里不加说明的话,指向父类的reference是父类的主键(数字)而不是父类本身,这样就不能用 getParentUser()来直接得到父类,而需要运行另一个SQL来获得实体类。在Hibernate里定义的子类集合没有了。因为子类的集合在数据库表里“一对多”的“一”那边表现不出来,所以iBator不生成这些集合。这是最让我高兴的地方,它打断了一对多之间的关联,可控性增强了很多。这样的转换从开始配置iBator到能够实际使用只需要5分钟时间,而在今后可预见的开发过程中,我估计iBatis带来的灵活性能够减轻很多Hibernate可能会出现的痛苦,譬如说关联,海量数据库查询之类的。这些是我昨天调查的结果。各位大虾,高手,强人,兄弟姐妹给点参考意见,我是否应该沿着这条路走下去?
      

  15.   


    说得很对,看来哥们真正了解到我的痛苦了!!!不知道对iBatis和Hibernate的选择有什么建议呢?
      

  16.   

    hibernate只对表映射
    你只有遵循它的一些方法
     如
    :hql
    按他的严格格式写要提高效率~~
     只有设计好你的数据库表结构
    建立好的索引
     还有写好的查询语句·~~~(如:like %ss% 不会被索引检测的  会全表扫描的 )
      

  17.   


    谢谢回复。HQL 基本上不能解决我的问题,后面的两个建议还是不对症。
      

  18.   

    我已经开始往iBatis上转了。经过了一些研究和试验,我发现可能iBatis才是更好的选择。首先iBatis的速度是很有吸引力的。有人做过对比,相同的查询,iBatis比Hibernate最高可以快3-4倍其次是iBatis的灵活性,这也是这个帖子一直在讨论的问题。相对于Hibernate的全自动来说,iBatis的半自动应该更符合我在这个工程里的要求。在测试里,我把所含子类数量不大,而且固定的的类和其子类连接起来;把子类数量大而且会在实际中增加的类与其子类进行半连接,即在子类里可以得到父类的实例,而从父类得到子类则需要运行另一个SQL。这样速度快了很多,而且对以后数据库的增长也应该有信心。 ^..^现在初步是这么做的,增加了一些代码,但增强了控制。目前良好,但不知道这是否是最佳的解决方案,而且对于速度的测试也不是一天半天就能得出来的。我想让这个帖子再开几天,多点讨论,多点思想。大家放心,近期就会结帐,谢谢每一个关注这个帖子的朋友。
      

  19.   

    iBatis 好像运行起来是比Hibernate要快些
      

  20.   


    有3-4倍这么夸张?用个复杂的hibernate和单表的ibatis比较差不多,我想没有多少人能写出比hibernate效率高这么多的sql吧。。
      

  21.   

    jie tie!Xie Xie Guan Zhu