`
gdfloyd
  • 浏览: 73895 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

改善Hibernate性能――Hibernate的精细化使用总结

阅读更多

 

Hibernate使用不好就很容易出现性能问题。在这里总结一些方法以供参考。

 

 

 

  • 1.按需加载

多对一的情况下一方的加载,一对多的情况多方的加载,默认都是延迟加载,为避免产生多条语句而导致多次DB往返,可以改变抓取策略,让Hibernate采用表连接来一次获取。例如,ItemHistory是一对多。

使用Hsql

 

From Item item join fetch item.historys

使用Crerteria

 

session. createCriteria(History.class).setFetchMode("item", FetchMode.JOIN).list();

 

 

  • 2.采用多方进行管理,对多方进行筛选

一对多的情况下多方的加载。从一方去加载多方,多方会是一个Full List,默认不会对其进行DB级别的筛选。为了弥补性能,采用多方来表达业务逻缉会更好。有时候这不得不牺牲高层次的业务语义为代价的,因为从业务的角度来看用一方去表达更自然更清晰。例如:只抓取Item符合某种条件的History,而不是所有History的集合。这时候可以设定属性where来指定SQL语句来筛选。

 

 

<set name="historys" inverse="true" lazy="true" table="HISTROY"  where="ACTION = 1">...</set>

 

对于一对多的特殊化:一对一。集合中符合条件的对象只有一个。虽然可以设定where来过滤,又或者由多方去管理,但是,用一对多去映射明显不符合业务语义。例如,要求抓取Item并抓取其最新的History,其余的History忽略。个人认为,这种复杂条件动态表关联查询,查询性能容易不理想。DB适度冗余便可以了。

 

 

  • 3.使用公式

悲剧是这样产生的:获取Item所有History的数量。

 

 

item.getHistorys().size()

 

如果是个Item List查询的话,行返回数量可能是n*m,简直是惨不忍睹。可以用公式(formula)这个属性,包含一个子查询语句来映射属性,当然,这个属性对Hibernate来说是只读的。

 

<property name="incompletedActionCount" type="int">
  <formula>
  (	
	select count(*) from HISTROY h where h.ITEM_ID = ITEM_ID
  )
  </formula>
</property> 

启用show_sql可以看到,Hibernate会自动给ITEM_ID加上主表的别名,不用担心列名冲突。

 

 

  • 4.细粒度的映射

ibatis可以针对某个经过调整的SQL Statement,定制一个专属的结果集映射。Hibernate也可以很灵活,持久化对象与数据表之间的可以进行多次不同的映射,他们之间通过entity-name来进行区分。entity-name我是理解成为对象不同环境下的profile标识,正如一个人很复杂具有多面性的。例如,对于基础数据表,不同模块的需要应用不同的粒度映射;在集合的映射方面,使用元素集合 / 组件集合而非持久化对象集合来映射;在某些模块,对某些不需要进行插入更新的属性设为insert="false",update="false",减少对宽表更新所影响到的列。

 

XML配置:

 

 

<class name="xx.yy.Item" table="ITEM" entity-name=" Item.moduleName.functionName">
  ...
  <set name="historys" inverse="true" lazy="true" table="HISTORY" where="ACTION = 0">
            ...
            <one-to-many entity-name=" History.moduleName.functionName" />
   </set>
</class>

<class name="xx.yy.History" table="History" entity-name=" History.moduleName.functionName">
  ...
  <many-to-one name="item" entity-name="Item.moduleName.functionName">
    ...
  </many-to-one>
</class>

Java Code:

 

 session.get("Item.moduleName.functionName", "ItemIdValue");
 session.createQuery("from Item.moduleName.functionName");

 

 

  • 5.命名查询

除此之外,Hibernate也可以像ibatis一样SQL语句XML化来进行个性化定义。命名查询(Named Query)有ibatis的影子。使用DB专有特性的原生SQL来调整性能,可以用这种方式。命名查询返回的列必须和持久化对象的映射的全部属性一应对应,如果返回的是Hibernate托管对象的话。这往往需要结合细粒度映射一起进行。

 

<resultset name="">
      <return alias="alias in the sql" entity-name="entity name"></return>
      <return-join alias="alias in the sql" property="entity-alias.property-path"></return-join>
 </resultset>
<sql-query name="sql query name">
	…
</sql-query>
 session.getNamedQuery("sqlQueryName").list();

 

 

  • 6.其他一些技巧集锦

 

A.把一级缓存,对象生命周期,自动脏检查,自动级联等ORM对象容器的高级功能阉割掉,让Hibernate退成ibatis/spring-jdbc。大批量更新的时候启用改善会明显。

 

sessionFactory.openStatelessSession()

 

B.返回非hibernate托管对象。可以不受返回映射列数目的限制,返回的是非字节码增强对象。不过ms仅支持简单命名的值类型属性,不支持引用对象类型的映射,用处有限。

 

session.getNamedQuery("xx")
  .setResultTransformer(Transformers.aliasToBean(XXDTO.class))
  .list()

 

 

C.DML风格对象更新

启用dynamic-update并不一定带来性能的改善。瞬时/脱管态对象的更新会额外多一次查询。这时候,可以采用DML式更新来指明真正需要更新的列,减少更新影响的列。这个API容易产生歧义,又是query又是update,比较别扭。

 

 

session.createQuery("xxx")
		executeUpdate();

 

 

D.获取Hibernate映射元数据。

可以用来扩展hibernate功能 / 再造ORM轮子 / 自定义代码生成模版。详细看Hiberante文档

 

 

sessionfactory.getClassMetadata(XX.class)

 

 

最后, 附上Hibernate推荐最佳实践。有些问题不仅仅是性能问题,性能问题只是对ORM的理解和使用的问题的集中体现。

 

 

 

  • 设计细颗粒度的持久类并且使用 <component> 来实现映射:

使用一个 Address 持久类来封装 streetsuburbstatepostcode 这将有利于代码重用和简化代码重构(refactoring)的工作。

 

 

  • 对持久类声明标识符属性(identifier properties):

Hibernate 中标识符属性是可选的,不过有很多原因来说明你应该使用标识符属性。我们建议标识符应该是“人造”的(自动生成,不涉及业务含义)。

 

 

  • 使用自然键(natural keys)标识:

对所有的实体都标识出自然键,用 <natural-id> 进行映射。实现 equals() hashCode(),在其中用组成自然键的属性进行比较。

 

 

  • 为每个持久类写一个映射文件:

不要把所有的持久类映射都写到一个大文件中。把 com.eg.Foo 映射到 com/eg/Foo.hbm.xml中。在团队开发环境中,这一点尤其重要。

 

 

  • 把映射文件作为资源加载:

把映射文件和他们的映射类放在一起进行部署。

 

 

  • 考虑把查询字符串放在程序外面:

如果你的查询中调用了非 ANSI 标准的 SQL 函数,那么这条实践经验对你适用。把查询字符串放在映射文件中可以让程序具有更好的可移植性。

 

 

  • 使用绑定变量

就像在 JDBC 编程中一样,应该总是用占位符 "?" 来替换非常量值,不要在查询中用字符串值来构造非常量值。你也应该考虑在查询中使用命名参数。

 

 

  • 不要自己来管理 JDBC 连接:

Hibernate 允许应用程序自己来管理 JDBC 连接,但是应该作为最后没有办法的办法。如果你不能使用 Hibernate 内建的 connections providers,那么考虑实现自己来实现org.hibernate.connection.ConnectionProvider

 

 

  • 考虑使用用户自定义类型(custom type):

假设你有一个 Java 类型,来自某些类库,需要被持久化,但是该类没有提供映射操作需要的存取方法。那么你应该考虑实现 org.hibernate.UserType 接口。这种办法使程序代码写起来更加自如,不再需要考虑类与 Hibernate type 之间的相互转换。

 

 

  • 在性能瓶颈的地方使用硬编码的 JDBC

在系统中对性能要求很严格的一些部分,某些操作也许直接使用 JDBC 会更好。但是请先确认这的确是一个瓶颈,并且不要想当然认为 JDBC 一定会更快。如果确实需要直接使用 JDBC,那么最好打开一个 Hibernate Session 然后将 JDBC 操作包裹为 org.hibernate.jdbc.Work 并使用 JDBC 连接。按照这种办法你仍然可以使用同样的transaction 策略和底层的 connection provider

 

 

  • 理解 Session 冲刷(flushing):

Session 会不时的向数据库同步持久化状态,如果这种操作进行的过于频繁,性能会受到一定的影响。有时候你可以通过禁止自动 flushing,尽量最小化非必要的 flushing 操作,或者更进一步,在一个特定的 transaction 中改变查询和其它操作的顺序。

 

 

  • 在三层结构中,考虑使用脱管对象(detached object):

当使用一个 servlet / session bean 类型的架构的时候, 你可以把已加载的持久对象在session bean 层和 servlet / JSP 层之间来回传递。使用新的 session 来为每个请求服务,使用 Session.merge() 或者 Session.saveOrUpdate() 来与数据库同步。

 

 

  • 在两层结构中,考虑使用长持久上下文(long persistence contexts):

为了得到最佳的可伸缩性,数据库事务(Database Transaction)应该尽可能的短。但是,程序常常需要实现长时间运行的“应用程序事务(Application Transaction)”,包含一个从用户的观点来看的原子操作。这个应用程序事务可能跨越多次从用户请求到得到反馈的循环。用脱管对象(与 session 脱离的对象)来实现应用程序事务是常见的。或者,尤其在两层结构中,把 Hibernate Session JDBC 连接中脱离开,下次需要用的时候再连接上。绝不要把一个 Session 用在多个应用程序事务(Application Transaction)中,否则你的数据可能会过期失效。

 

 

  • 不要把异常看成可恢复的:

这一点甚至比“最佳实践”还要重要,这是“必备常识”。当异常发生的时候,必须要回滚Transaction ,关闭 Session。如果你不这样做的话,Hibernate 无法保证内存状态精确的反应持久状态。尤其不要使用 Session.load() 来判断一个给定标识符的对象实例在数据库中是否存在,应该使用 Session.get() 或者进行一次查询。

 

 

  • 对于关联优先考虑 lazy fetching

谨慎的使用主动抓取(eager fetching)。对于关联来说,若其目标是无法在第二级缓存中完全缓存所有实例的类,应该使用代理(proxies)与/或具有延迟加载属性的集合(lazy collections)。若目标是可以被缓存的,尤其是缓存的命中率非常高的情况下,应该使用lazy="false",明确的禁止掉 eager fetching。如果那些特殊的确实适合使用 join fetch

场合,请在查询中使用 left join fetch

 

使用 open session in view 模式,或者执行严格的装配期(assembly phase)策略来避免再次抓取数据带来的问题:Hibernate 让开发者们摆脱了繁琐的 Data Transfer ObjectsDTO)。在传统的 EJB 结构中,DTO 有双重作用:首先,他们解决了 entity bean 无法序列化的问题;其次,他们隐含地定义了一个装配期,在此期间,所有view层需要用到的数据,都被抓取、集中到了 DTO 中,然后控制才被装到表示层。Hibernate 终结了第一个作用。然而,除非你做好了在整个渲染过程中都维护一个打开的持久化上下文(session)的准备,你仍然需要一个装配期(想象一下,你的业务方法与你的表示层有严格的契约,数据总是被放置到脱管对象中)。这并非是 Hibernate 的限制,这是实现安全的事务化数据访问的基本需求。

 

 

  • 考虑把 Hibernate 代码从业务逻辑代码中抽象出来:

Hibernate 的数据存取代码隐藏到接口(interface)的后面,组合使用 DAO ThreadLocal Session 模式。通过 Hibernate UserType,你甚至可以用硬编码的 JDBC 来持久化那些本该被 Hibernate 持久化的类。然而,该建议更适用于规模足够大应用软件中,对于那些只有 5 张表的应用程序并不适合。

 

 

  • 不要用怪异的连接映射:

多对多连接用得好的例子实际上相当少见。大多数时候你在“连接表”中需要保存额外的信息。这种情况下,用两个指向中介类的一对多的连接比较好。实际上,我们认为绝大多数的连接是一对多和多对一的。因此,你应该谨慎使用其它连接风格。

 

 

  • 偏爱双向关联:

单向关联更加难于查询。在大型应用中,几乎所有的关联必须在查询中可以双向导航。

 

 

 

分享到:
评论

相关推荐

    精通Hibernate:Java对象持久化技术详解.pdf

    《精通Hibernate:Java对象持久化技术详解》这本书深入剖析了Hibernate这一流行的Java对象关系映射(ORM)框架,旨在帮助开发者全面理解并熟练掌握Hibernate的使用。Hibernate是Java开发中的重要工具,它简化了...

    Hibernate Tool 使用文档

    ### Hibernate Tools 使用文档知识点概述 #### 一、下载与安装Hibernate Tools - **JBoss Eclipse IDE**:在使用JBoss Eclipse IDE的情况下,可以利用插件管理器直接安装Hibernate Tools插件。 - **Eclipse IDE**...

    JDBC与Hibernate区别

    总结起来,JDBC和Hibernate各有优势,选择使用哪种取决于项目需求。对于性能敏感、需要高度控制的场景,JDBC可能是更好的选择。而对于重视开发效率、代码简洁性的项目,Hibernate的优势更为明显。在实际应用中,...

    J2EE系统之-hibernate学习总结

    ### J2EE系统之-hibernate学习总结 #### 对象持久化理论 - **对象持久化定义**:将对象中的数据转换存储至外部持久性存储设备的过程,如数据库、磁盘等。 - **对象持久化的原因**: - 内存易失性:断电后数据丢失...

    hibernate最新jar包(2017-hibernate5.1.9)

    6. **模块化设计**:Hibernate 5引入了模块化结构,允许开发者按需选择和依赖所需的组件,减少不必要的依赖。 **文件名称列表解析** 压缩包子文件的文件名称"hibernate-release-5.1.9.Final"表明这是Hibernate ...

    Hibernate Search In Action

    书中还提到,虽然Hibernate Search已经提供了丰富的功能,但在某些特定的场景下,可能还是需要直接访问底层的Lucene API来进行更为精细的控制。Hibernate Search提供了一种机制,允许开发者在Hibernate Search无法...

    Hibernate框架包

    - 精细化HQL和Criteria查询,避免全表扫描。 ### 8. 实践应用 Hibernate广泛应用于企业级Web应用开发,尤其是在Spring框架中,配合Spring Data JPA,可以构建高效、简洁的持久层代码。 总结,"Hibernate框架包...

    Hibernate与 MyBatis的比较

    总结来说,Hibernate和MyBatis各有优势,选择哪个取决于项目需求、团队技能以及对性能和开发效率的平衡。在实际项目中,有时也会根据不同的业务模块,结合两者的优势进行选择。阅读《hibernate与mybatis异同.doc》...

    ibatis和hibernate的区别

    **Hibernate** 是一个全面的ORM框架,它提供了从对象到数据库的全自动化映射,包括对象的持久化、查询、事务管理等功能。Hibernate通过XML或注解定义对象与数据库表的映射,可以在运行时自动生成SQL语句。它还支持...

    iBatis和Hibernate的区别

    - Hibernate通过减少手工编码量提高了开发效率,但对于某些特定场景下的性能优化可能不如iBatis灵活。 - **学习曲线**: - iBatis的学习曲线相对较平缓,易于上手。 - Hibernate由于其高级特性较多,学习起来有...

    hibernate-release-5.2.10

    总结,Hibernate 5.2.10提供了一套完整的持久化解决方案,简化了Java应用程序与数据库的交互。通过理解和掌握其核心概念、API用法及最佳实践,开发者可以更高效地进行数据操作,提高项目的开发效率和可维护性。

    hibernate框架官方4.4.0版本下载

    Hibernate框架是Java开发中常用的持久化层解决方案,它简化了数据库操作,使开发者可以使用对象关系映射(ORM)方式来处理数据。4.4.0版本是Hibernate的一个稳定版本,提供了许多改进和新特性。 在Java Web后端开发...

    hibernate-release-5.2.16.Final源码包

    总结,通过深入研究Hibernate 5.2.16.Final源码,结合c3p0和dom4j的使用,Web开发者不仅能提升对Hibernate框架的理解,还能掌握数据库连接管理和XML处理的技巧,从而在实际项目中更好地利用这些工具,提升开发效率和...

    ibatis和hibernate的简单介绍

    - 在大多数情况下,**ibatis**由于其灵活性和对SQL语句的精细控制,可能具有更好的性能表现。 - 而**Hibernate**通过其内置的缓存机制和其他优化手段,在某些场景下也能达到优秀的性能。 4. **社区支持**: - **...

    Hibernate-Release-4.2.3

    4. **实体生命周期管理**:Hibernate提供了对实体生命周期的精细控制,包括瞬时、持久化、托管和脱管状态,以及相应的转换方法如`persist()`, `merge()`, `remove()`等。 5. **事务管理**:支持JTA(Java ...

    hibernate4.1

    在性能优化方面,Hibernate 4.1引入了更精细的控制,比如延迟加载(Lazy Loading)策略的改进,允许开发者更有效地管理资源,减少内存消耗。同时,实体状态管理更加智能,可以更好地处理脏检查和变更检测,以提升...

    Hibernate v3.04中文参考手册(PDF)

    通过阅读《Hibernate v3.04中文参考手册》,开发者可以深入了解这些概念,熟练掌握Hibernate的使用,从而在实际项目中实现高效的数据操作。手册中的实例和详细解释有助于理解和应用这些知识,对于提升Java开发者的...

    hibernate-extensions-2.1.3

    1. **延迟加载(Lazy Loading)增强**:Hibernate核心库已经支持延迟加载,但扩展可能提供了更精细的控制,例如按需加载关联的对象集合,减少内存占用和提高性能。 2. **批量操作**:批量插入、更新和删除功能可以...

    hibernate-release-5.3.16.Final.zip

    总结,Hibernate 5.3.16.Final是Hibernate框架的一个成熟版本,它在功能、性能和稳定性上都进行了优化。对于使用SSH框架的企业级项目,这个版本的更新是值得考虑的,它将为开发者带来更高效、更可靠的数据库操作体验...

Global site tag (gtag.js) - Google Analytics