我在一家大型零售商的咨询工作中刚刚完成了任务,我们通过一些相当简单的变化,成功地将Hibernate应用程序的性能提高了大约两个数量级。这真的让我深刻认识到,我见过的几乎所有性能问题都可以通过以下一种或两种方式解决:

  • 合适的会话处理
  • 合适的关联抓取策略

(请注意,我还没有遇到一个严重的性能问题,我无法在Hibernate 2.x中快速解决。)

Hibernate的Session对象是一种强大的抽象,它允许一些非常灵活的架构选择。不幸的是,这种灵活性是有代价的:许多人似乎都把它搞砸了!有三种被广泛理解的管理Hibernate会话的正确模式(实际上,是三个半,因为我最近发现了),以及三种常见的反模式。反模式比应有的更常见,这表明我们现有的文档确实存在问题,并突出了我们需要尽快出版这本书的事实!

我们之前无法解释正确处理Hibernate会话的正确方式的主要原因是,我们根本就没有一个合适的语言来描述我们的想法。由于我们在写书的过程中开发了这个语言,所以解释起来要容易得多。关键概念是“应用程序事务”的概念。应用程序事务是从用户的角度来看的“工作单元”;它跨越多个请求和多个数据库事务——然而,它确实有一个明确的开始和结束。即使你目前没有明确地使用这个概念,你很可能在你的应用程序中隐式地使用了它。

简要地说,三种可接受的方法是:请求会话、带有分离对象的请求会话和应用程序事务会话。第三种方法的变体是最近发现的将刷新延迟到最后请求的应用程序事务会话(哎呀!)。三种错误的方法是:操作会话(即请求中多个会话)、用户会话会话和应用程序会话。如果您正在使用任何这些方法,请立即停止。

三种可接受的方法各有不同的性能和架构影响,并没有一个最佳解决方案。选择最适合您特定应用的方法非常重要(这正是上述提到的量级改进的关键所在)。

我认为在我们的文档中已经很好地涵盖了关联获取的内容,但我们仍然有时看到人们在与令人讨厌的n+1 SELECT问题作斗争。所以让我非常明确地说:Hibernate完全解决了n+1 SELECT问题!然而,用户需要动一些脑筋,并做一些(非常)小的努力才能充分利用这一事实。我们建议所有关联都默认配置为延迟获取。然后,对于特定的用例,可以通过在HQL查询中指定LEFT JOIN FETCH子句或通过调用setFetchMode()来选择外连接获取。如果您太懒不想做这项工作,甚至可以尝试Hibernate 2.1的新批获取特性。然而,我们不推荐这种不那么优雅的方法。

较少见的问题可以通过使用二级缓存或偶尔手动管理刷新(将session刷新模式设置为FlushMode.NEVER并在需要时手动刷新)来解决。然而,在几乎所有情况下,通过关注上述两项内容,都可以实现可接受的性能。


回到顶部