hibernate对单表的控制能力比较优秀,但是对于比较复杂的再加上分页的动态查询,它能否做到更方便呢?
对于特别庞大的数据,hibernate的get和load方法也是不管有多少全查的,对大数据,有没有更好的配置去优化呢?
hibernate如何执行批量的sql语句,控制它的事务?这些问题,我一直都是避开了hibernate本身,自己手动获取了它的connection,拼尽了所学的老底用jdbc去解决的,现在回头想想,对hibernate还是不甚了解,想听听大家的看法

解决方案 »

  1.   

    为什么不抛弃hibernate,用jdbc呢?
      

  2.   

    是不是用hibernate大家只用他的orm和他的缓存机制呢?我也想知道hibernate的优化。
      

  3.   

    Hibernate 是一个优秀的 ORM 框架,但不是高效的。大数据量的查询,以及需要使用本地数据库语言对 SQL 进行逐步调优的应用不适合使用 Hibernate
      

  4.   

    如果整天做报表或者其他复杂查询的话,还是抛弃H,只是吸纳他的精神和部分好处,结合自己项目实际,自己动手扩展一个小的jdbc扩展框架。
      

  5.   

    当项目发展到几乎加一个功能,就要动手些HQL的时候,就是该抛弃H的时候了
      

  6.   

    有没有人知道,使用hibernate分页功能,第一页是从最后一条记录开始,要怎么做.效率高点
      

  7.   

    hibernate,我了解的甚少,也一直期望了解更多,不知道哪位有这方面的朋友可以分析下
      

  8.   

    hibernate对单表的控制能力比较优秀,但是对于比较复杂的再加上分页的动态查询,它能否做到更方便呢?
    仅仅是查询,没有问题。hibernate的分页也已经针对不同的数据库进行了优化的
    比如oracle,hibernate就采用的是oracle最快的分页方式,具体的可以去看OracleDialect源代码
    其他的数据库也是
    对于特别庞大的数据,hibernate的get和load方法也是不管有多少全查的,对大数据,有没有更好的配置去优化呢?
    hibernate如何执行批量的sql语句,控制它的事务? 
    get或者load是获取一条数据,这里都需要用延迟加载的
    批量sql的时候,需要设置batch size,并且关闭二级缓存,同时使用flush来同步数据库,在使用clear来清空session缓存,这样不至于内存溢出,hibernte文档上有这个例子
    或者用存储过程,如果你了解hibernate你就会用他的长处,而避免其短处
      

  9.   

    为什么不用jdbc哪!i like jdbc。
      

  10.   

    楼上解释的很精辟,赞一个!
    另外对hibernate二级缓存,我还有个疑问,就是咱们在用二级缓存的时候,有时候会根据用户的使用习惯对一些常用的查询做"查询缓存",里面的机制就是将查询条件一字不漏的作为key放入到缓存中,以便下次读取,这种动态查询的缓存如果是只对单表(即单个对象)查询固然有效,单如果是关联查询,其中关联的某个对象发生了更新操作,查询缓存它是否自己能够知道,并且去更新自己的缓存?还有一个疑问,萦绕在心底很久,也是有关hibernate优化问题的,就是list与iterator,众所周知,List仅仅会填充二级缓存,却不能利用二级缓存,而iterator可以读二级缓存,然而无法命中的话,效率却很低。一个最好的办法就是,第一次查询使用List,随后的查询使用iterator,现在的问题是如何做到第一次查询使用List,随后查询使用iterator?
      

  11.   

    hibernate的查询缓存key是查询语句,但是key对应的value其实是数据库表的主键当查询被缓存后,再次调用查询的时候,通过key找到对应的id集合,然后一个一个的去class缓存中去load
    class里没有,再去数据库中获取。所以这又涉及到类缓存上了,当用hibernate进行数据库的修改和删除等操作的时候,都会更新一二级缓存,所以查询缓存会获取最新的数据
      

  12.   

    java语言的特点,没办法,不用框架也至少还要自己写框架,无非都是围绕在几种设计模式下,基础类底层实现下,最终都是为了形成一个最最行之有效可重复利用的解决问题的办法来,如果个人技术牛x,为什么可以摒弃ssh,只不过它是目前开源架构里面公认比较好的一种而已,呵呵
      

  13.   

    Hibernate人家那么大的hQL语句市场居然说没有用!。·。·。
    悲哀悲哀··
    hibernate自带分页功能
    setFirst....忘记了,是从第几条开始..起点为0
    setMax....忘记了,是一共要返回多少条记录,
    sql能做到的hql有做不到的吗?那只能说你会的太少。。
    hql能做到的sql就不一定能做到!!!
      

  14.   


    Hibernate可以应用在任何使用JDBC的场合
      

  15.   


    很多情况下复杂的关联查询vo和po并不对应,hibernate的HQL固然是很好用,可是它仅限于查询所得的内容包含于po中,业务层vo很多情况下是不需要持久化保存在数据库中的,仅仅是为了在查询显示短暂的过度,HQL不可以灵活的包装的search vo 它仅仅能包装已经配置好的和数据库持久化对应的po而已,如果要包装自己的vo,复杂查询的通配做法都是"hibernate的本地化sql支持",但这有时候往往还是很麻烦,远不如jdbc更加方便,取舍之间,难以在性能和便捷之中做出选择,举个简单的例子:
    联查7张表,查询得出结果集有20个元素,其中10个结果是表中没有的字段,另外再对查询得出的结果,进行分页.16楼说hql能做到的事,sql不一定能做到,好像hql解释执行到最终都是sql标准语言,或许是在下愚钝,因为会的确实很少,发帖中也说明了,还望给个详细指教,对于查询结果在表中没有的元素,不用本地sql的话hql如何更好的灵活装配?或者这样吧,你留下一行HQL调用存储过程的例子,让在下瞻仰下,HQL的强大市场和无所不能
      

  16.   


    哈哈哈搞得好像hib最后不是调的jdbc一样你能弄个不加数据库jdbc驱动的hib程序出来操作数据库给大家瞻仰膜拜下?
      

  17.   


    hib能调procedure,这倒是勿庸置疑的。只是对于hql性能比jdbc高的看法不敢苟同hql做的什么?封装了下jdbc让你能够方便的coding,不去考虑一些本该由你考虑的东西比如缓存制度,比如表映射到pojo。说白了就是满足了一些不想知道底层怎么实现更好只需要方便只需要能快速coding的需求。
      

  18.   

    今天,上午公司的所有项目组在一起讨论了这个问题:ORM 框架目前项目用的是jpa 的实现 OpenJpa。在性能测试的时候,结果不是很理想,而且系统不是很稳定。由于开发人对Openjpa 不太熟悉,而且网上相关的资料也不是很多(相对hibernate),所以对使用openjpa 而不使用hibernate意见比较大。
    所以想在下一版本做更换。偏向hibernate的人,有人给出了这样的观点:hibernate 肯定比openjpa性能好。论据是:1、用的人多是肯定是好的。
            2、同样的环境,用openjpa 做持久框架 压力测试迸发是300多点,而用hibernate能到达600多。(对于他这个数据,我很是怀疑)我觉得不能绝对的说哪个性能更好,哪个性能更差。(反正我是不知道哪个绝对比另一个好) 如果对这两个框架的运用都把握的比较好,我觉得是不应该出现他说的那么大的性能差距。我不过以后用哪个,我决定以后只用它们来简单的增、删、改。复杂的的查询:如涉及多表的查询、统计型的查询一律直接用sql。最好能如LZ说的一样,总结出一个比较同样的方法来。不通过hibernate是可以直接获取到jdbc 连接的,通过openjpa是em(entityManager)好像得不到 jdbc连接,有试过没成功,不知道是真不能还是我弄错了。所以openjpa要执行原生的sql还得配置SqlResultMapping,觉得比较烦。
      

  19.   

    记得还想聚集性的数据库操作 hibernate 并不适合
    可以考虑用spring的jdbcTemplate  或者simpleJdbcTemplate
    这2个类是spring 封装好的 比完全jdbc好用点
      

  20.   

    24楼的哥们提的建议比较靠谱,spring在管理hibernate上有独到的地方可以顺手拿来用,我也是想在能不抛弃hibernate的基础上尽可能多挖掘一下它的一些性能提升上的做法,总结大家的看法,基本得出一致结论:复杂查询依靠jdbc的sql或者hibernate提供的本地化sql封装,或者使用spring的管理,都可以提升性能和效率.我认为对于hibernate的性能优化应该是无止境的.基于大家对此的看法意见和网上的一些资料,加上自己的测试,收录如下,望大家多提宝贵意见:在项目中使用Hibernate进行大数据量的性能测试,有一些总结,  
            1) 在处理大数据量时,会有大量的数据缓冲保存在Session的一级缓存中,这缓存大太时会严重显示性能,所以在使用Hibernate处理大数据量的,可以使用session.clear()或者session. Evict(Object) 在处理过程中,清除全部的缓存或者清除某个对象。  
            2) 对大数据量查询时,慎用list()或者iterator()返回查询结果,  
            1. 使用List()返回结果时,Hibernate会所有查询结果初始化为持久化对象,结果集较大时,会占用很多的处理时间。  
            2. 而使用iterator()返回结果时,在每次调用iterator.next()返回对象并使用对象时,Hibernate才调用查询将对应的对象初始化,对于大数据量时,每调用一次查询都会花费较多的时间。当结果集较大,但是含有较大量相同的数据,或者结果集不是全部都会使用时,使用iterator()才有优势。  
            3. 对于大数据量,使用qry.scroll()可以得到较好的处理速度以及性能。而且直接对结果集向前向后滚动。  
            3) 对于关联操作,Hibernate虽然可以表达复杂的数据关系,但请慎用,使数据关系较为简单时会得到较好的效率,特别是较深层次的关联时,性能会很差。  
            4) 对含有关联的PO(持久化对象)时,若default-cascade="all"或者 “save-update”,新增PO时,请注意对PO中的集合的赋值操作,因为有可能使得多执行一次update操作。  
            5) 在一对多、多对一的关系中,使用延迟加载机制,会使不少的对象在使用时方会初始化,这样可使得节省内存空间以及减少的负荷,而且若PO中的集合没有被使用时,就可减少互数据库的交互从而减少处理时间。  数据库什么叫n+1次select查询问题?在Session的缓存中存放的是相互关联的对象图。默认情况下,当Hibernate从数据库中加载Customer对象时,会同时加载所有关联的Order对象。以Customer和Order类为例,假定ORDERS表的CUSTOMER_ID外键允许为null,图1列出了CUSTOMERS表和ORDERS表中的记录。以下Session的find()方法用于到数据库中检索所有的Customer对象:List customerLists=session.find("from Customer as c");运行以上find()方法时,Hibernate将先查询CUSTOMERS表中所有的记录,然后根据每条记录的ID,到ORDERS表中查询有参照关系的记录,Hibernate将依次执行以下select语句:select * from CUSTOMERS; 
    select * from ORDERS where CUSTOMER_ID=1;
    select * from ORDERS where CUSTOMER_ID=2;
    select * from ORDERS where CUSTOMER_ID=3;
    select * from ORDERS where CUSTOMER_ID=4;通过以上5条select语句,Hibernate最后加载了4个Customer对象和5个Order对象,在内存中形成了一幅关联的对象图,参见图2。Hibernate在检索与Customer关联的Order对象时,使用了默认的立即检索策略。这种检索策略存在两大不足:(a) select语句的数目太多,需要频繁的访问数据库,会影响检索性能。如果需要查询n个Customer对象,那么必须执行n+1次select查询语句。这就是经典的n+1次select查询问题。这种检索策略没有利用SQL的连接查询功能,例如以上5条select语句完全可以通过以下1条select语句来完成:select * from CUSTOMERS left outer join ORDERS 
    on CUSTOMERS.ID=ORDERS.CUSTOMER_ID 以上select语句使用了SQL的左外连接查询功能,能够在一条select语句中查询出CUSTOMERS表的所有记录,以及匹配的ORDERS表的记录。(b)在应用逻辑只需要访问Customer对象,而不需要访问Order对象的场合,加载Order对象完全是多余的操作,这些多余的Order对象白白浪费了许多内存空间。
    为了解决以上问题,Hibernate提供了其他两种检索策略:延迟检索策略和迫切左外连接检索策略。延迟检索策略能避免多余加载应用程序不需要访问的关联对象,迫切左外连接检索策略则充分利用了SQL的外连接查询功能,能够减少select语句的数目。刚查阅了hibernate3的文档: 
    查询抓取(默认的)在N+1查询的情况下是极其脆弱的,因此我们可能会要求在映射文档中定义使用连接抓取: <set name="permissions" 
                fetch="join"> 
        <key column="userId"/> 
        <one-to-many class="Permission"/> 
    </set 
    <many-to-one name="mother" class="Cat" fetch="join"/> 
    在映射文档中定义的抓取策略将会有产生以下影响: 通过get()或load()方法取得数据。 只有在关联之间进行导航时,才会隐式的取得数据(延迟抓取)。 条件查询 在映射文档中显式的声明 连接抓取做为抓取策略并不会影响到随后的HQL查询。 通常情况下,我们并不使用映射文档进行抓取策略的定制。更多的是,保持其默认值,然后在特定的事务中, 使用HQL的左连接抓取(left join fetch) 对其进行重载。这将通知 Hibernate在第一次查询中使用外部关联(outer join),直接得到其关联数据。 在条件查询 API中,应该调用 setFetchMode(FetchMode.JOIN)语句。
            6) 对于大数据量新增、修改、删除操作或者是对大数据量的查询,与数据库的交互次数是决定处理时间的最重要因素,减少交互的次数是提升效率的最好途径,所以在开发过程中,请将show_sql设置为true,深入了解Hibernate的处理过程,尝试不同的方式,可以使得效率提升。  
            7) Hibernate是以JDBC为基础,但是Hibernate是对JDBC的优化,其中使用Hibernate的缓冲机制会使性能提升,如使用二级缓存以及查询缓存,若命中率较高明,性能会是到大幅提升。  
            8) Hibernate可以通过设置hibernate.jdbc.fetch_size,hibernate.jdbc.batch_size等属性,对Hibernate进行优化。hibernate.jdbc.fetch_size 50hibernate.jdbc.batch_size 25这两个选项非常非常非常重要!!!将严重影响Hibernate的CRUD性能!C = create, R = read, U = update, D = deleteFetch Size 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数。例如一次查询1万条记录,对于Oracle的JDBC驱动来说,是不会1次性把1万条取出来的,而只会取出Fetch Size条数,当纪录集遍历完了这些记录以后,再去数据库取Fetch Size条数据。因此大大节省了无谓的内存消耗。当然Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch Size越小,读数据库的次数越多,速度越慢。这有点像平时我们写程序写硬盘文件一样,设立一个Buffer,每次写入Buffer,等Buffer满了以后,一次写入硬盘,道理相同。Oracle数据库的JDBC驱动默认的Fetch Size=10,是一个非常保守的设定,根据我的测试,当Fetch Size=50的时候,性能会提升1倍之多,当Fetch Size=100,性能还能继续提升20%,Fetch Size继续增大,性能提升的就不显著了。因此我建议使用Oracle的一定要将Fetch Size设到50。不过并不是所有的数据库都支持Fetch Size特性,例如MySQL就不支持。MySQL就像我上面说的那种最坏的情况,他总是一下就把1万条记录完全取出来,内存消耗会非常非常惊人!这个情况就没有什么好办法了 :(Batch Size是设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,有点相当于设置Buffer缓冲区大小的意思。Batch Size越大,批量操作的向数据库发送sql的次数越少,速度就越快。我做的一个测试结果是当Batch Size=0的时候,使用Hibernate对Oracle数据库删除1万条记录需要25秒,Batch Size = 50的时候,删除仅仅需要5秒!!!//
    我们通常不会直接操作一个对象的标识符(identifier),因此标识符的setter方法应该被声明为私有的(private)。这样当一个对象被保存的时候,只有Hibernate可以为它分配标识符。你会发现Hibernate可以直接访问被声明为public,private和protected等不同级别访问控制的方法(accessor method)和字段(field)。 所以选择哪种方式来访问属性是完全取决于你,你可以使你的选择与你的程序设计相吻合。所有的持久类(persistent classes)都要求有无参的构造器(no-argument constructor);因为Hibernate必须要使用Java反射机制(Reflection)来实例化对象。构造器(constructor)的访问控制可以是私有的(private),然而当生成运行时代理(runtime proxy)的时候将要求使用至少是package级别的访问控制,这样在没有字节码编入(bytecode instrumentation)的情况下,从持久化类里获取数据会更有效率一些。而hibernate.max_fetch_depth 设置外连接抓取树的最大深度 
    取值. 建议设置为0到3之间
     就是每次你在查询时,会级联查询的深度,譬如你对关联vo设置了eager的话,如果fetch_depth值太小的话,会发多很多条sql Hibernate的Reference之后,可以采用批量处理的方法,当插入的数据超过10000时,就flush session并且clear。 下面是一个测试method。1   /** */ /** 
    2      * 测试成批插入数据的事务处理,返回是否成功 
    3      *  
    4      *  @param objPO Object 
    5      *  @return boolean 
    6  */ 
    7   public boolean  insertBatch( final  Object objPO)   { 
    8  boolean  isSuccess  = false ; 
    9         Transaction transaction  = null ; 
    10         Session session  = openSession(); 
    11   try  { 
    12             transaction  = session.beginTransaction(); 
    13   for  ( int  i  = 0 ; i  < 100000 ; i ++ )   { 
    14                 session.save(objPO); 
    15   if  (i  % 50 == 0 )   { 
    16  //  flush a batch of inserts and release memory 
    17                      session.flush(); 
    18                     session.clear(); 
    19                 } 
    20             } 
    21             transaction.commit(); 
    22             logger.info( " transaction.wasCommitted: " 
    23  + transaction.wasCommitted()); 
    24             isSuccess  = true ; 
    25          } catch  (HibernateException ex)   { 
    26   if  (transaction  != null )   { 
    27   try  { 
    28                     transaction.rollback(); 
    29                     logger.error( " transaction.wasRolledBack: " 
    30  + transaction.wasRolledBack()); 
    31                  } catch  (HibernateException ex1)   { 
    32                     logger.error(ex1.getMessage()); 
    33                     ex1.printStackTrace(); 
    34                 } 
    35             } 
    36             logger.error( " Insert Batch PO Error: " + ex.getMessage()); 
    37             ex.printStackTrace(); 
    38          } finally  { 
    39   if  (transaction  != null )   { 
    40                 transaction  = null ; 
    41             } 
    42             session.close(); 
    43         } 
    44  return isSuccess; 
    45     } 
    46  这只是简单的测试,实际项目中遇到的问题,要比这个复杂得多。 
    这时候,我们可以让Spring来控制Transaction,自己来控制Hibernate的Session,随时更新数据。 
    首先,利用HibernateDaoSupport类来自定义个方法打开Session;1public Session openSession(){ 

    3 return getHibernateTemplate().getSessionFactory().openSession(); 

    5    }然后,用打开的Session处理你的数据;1protected void doBusiness(Session session) { 

    3 while (true) { 
    4 //do your business with the opening session        
    5            someMethod(session); 
    6            session.flush(); 
    7            session.clear(); 
    8            logger.info("good job!");             
    9        } 
    10}每做一次数据操作,就更新一次Session,这样可以保证每次数据操作都成功,否则就让Spring去控制它roll back吧。 
    最后,记得关闭Session。1  Session session  =  openSession(); 
    2 doBusiness(session); 
    3 session.close();  // 关闭session本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/shimiso/archive/2010/04/29/5543631.aspx
      

  21.   

    我们根据项目去选适合的技术,不是单单讨论某个技术好不好,这样会比较很片面,但Hibernate也只是一个封装过的JDBC,我们应该先了解他的根本再去考虑他的使用价值。
      

  22.   

    所谓hibernate是仆人。简单的叫他做了,基本crud.复杂深层关联我觉得还是jdbc灵活些,比如要查询组合成一个非持久化的。目前我们这个项目就是这么做的..
      

  23.   

    在25楼上学到不少,什么N+1,设置Hibernate抓取、批处理值的优化等等。
      

  24.   

    说的不错,虽然也常常使用Hibernate,但是从来没有自己用所了解的语言来进行过词总结,LZ以及各位大神的见解,都不错,值得好好理理!
      

  25.   

    唉,我也喜欢JDBC,看怎么组合查询就怎么组合。
      

  26.   

    最近也遇到了这方面的问题,hibernate中list()查询显示速度真的很慢,看了这个帖子,有点解决问题的思路了,好贴!