文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

很多开发者对JPA和Hibernate都似乎是既爱又恨。他们喜欢它,是因为它可以很容易地实现大多数的用例,而恨它则是因为它也很容易使查询变得很低效。

以前我也跟这些开发者一样。我喜欢使用Hibernate,但我同时也在不断的忍受缓慢的数据库查询和其他性能问题的折磨。随着岁月的流逝,我花了越来越多的时间从事关于Hibernate的教学工作,这些都有了变化。

当你分析了足够多的低效的查询,你就会认识到,只需要遵循一些简单的建议,就可以避免其中大多数问题。 

第 1 段(可获 1.38 积分)

编写高效查询的5个技巧

1. 选择一种适合你的用例的投影

这个提示是显而易见的,因为它很重要:你应该始终使用适合您的用例的投影方式。

这很明显,不是吗?

当我在我的Hibernate性能调优培训课程中推荐这个的时候,所有的学生都在点头表示同意。但当我们谈论细节的时候却是经常变化的。你必须为每个用例决定它需要什么信息,它需要执行和选择哪种操作。

如果你需要更新或删除一条记录,那么实体会是个很适合的选择。它们也可以用于需要读取(几乎)所有实体属性的用例.。 但请记住,持久化上下文需要管理的实体,比起一个DTO投影来说,会有额外的开销。

第 2 段(可获 1.79 积分)

对于那些只需要读取一条记录,并且提供所有需要的且没有额外属性的用例来说,DTO很适合 。这也经常要求你当你要实现一个新的用例的时候,你得去创建一个新的DTO。这也是大多数讨论开始的地方。你如果想要为获得效率提升而进行优化的话,不可能为所有的用例都重用相同的DTO和数据访问服务。

不过别担心,这并不一定是一个非黑即白的决定。大多数开发团队会决定每一方面都做一点点。他们接受在他们的数据库访问时些许的低效率,并且创建一些相当好但不最优的匹配多个用例的DTO,从而来提升重用性。 这样做完全没问题。只是你需要了解它,这样当你遇到性能问题时你就可以轻易的改变它。

第 3 段(可获 1.83 积分)

2. 在你的映射对应文件中避免使用即时获取(eager fetching)

从性能的角度来看,为你的实体关联选择正确的FetchTypes 是最重要的步骤之一。FetchType 定义了Hibernate何时去执行初始化关联关系的额外查询来。它可以在它加载实体时使用FetchType.EAGER,或者在当使用关联时,就用FetchType.LAZY来完成。

在你知道需要之前去执行额外的查询来加载数据是没有任何意义的。你应该默认使用 FetchType.LAZY ,而当一个用例需要使用实体关联的时候就可以应用下一个技巧。

第 4 段(可获 1.26 积分)

3. 在查询中初始化所有必需的关联

使用FetchType.LAZY可以在你的用例中避免额外使用对不用的关联进行初始化的查询。 这显然是件好事,但如果你的用例需要这些关联你该怎么办呢?

最简单的办法就是你可以调用该关联的getter方法。之后Hibernate就会从数据库里发起另外一个查询去获取需要的信息。这是初始化懒加载实体关联最简单但是同时也是最低效的方式。当你要为多个实体操作这些的时候,你就会面临另外一个性能问题,被称为n+1选择问题

第 5 段(可获 1.38 积分)

你可以通过在加载你的实体的查询中初始化所有需要的关联,从而很轻易的避免这样的事情发生。你可以通过一个独立于查询的EntityGraph 或者在你的 JPQL 或 Criteria 查询中使用一个简单的JOIN FETCH语句 来完成。

4. 当你要查询实体列表时请使用分页

当你要获取一个很大的实体或DTO(数据传输对象)列表时,你应该总是要问问你自己,你是否真的需要获取所有的数据。如果你是需要将它们展现给你的用户,那么答案通常是:不!

人类是无法处理拥有数百个元素的列表的。因此,大多数的UI会将它们分割成为多个块,并将每一块展示在一个单独的页面上。

第 6 段(可获 1.36 积分)

在这些情况下,要想使用一个查询就获取所有的实体或者DTO就显得没有任何意义了。UI不需要它们,而且这样只会拖慢你的应用。在你的查询中使用同样的分页方式,并且只获取那些显示在UI上的记录,这样做要好多了。你可以通过给Query接口的firstResult和maxResult设置适当的值来完成。

5. 为SQL语句记录日志

如果你已经运用了之前的所有建议,那么你就已经成功避免了最常见的陷阱。但是你还是会不时的创建一些低效的查询而浑然不知。JPA和Hibernate 会隐藏掉JPQL、EntityManager和Criteria API背后的SQL语句。这样使得它们能够比较方便实用,但这样也使得要理解你的所有API调用的实现就显得比较困难了。

第 7 段(可获 1.73 积分)

因此当你对你的代码进行修改时,总是要检查一下运行的SQL语句。 最简单的实现办法是在你的开发配置中激活SQL语句的日志记录功能。 你可以将org.hibernate.SQL 的日志级别更改为DEBUG 来实现。

你也可以激活Hibernate Statistics 来获取关于当前会话的更多详情。它能告诉你很多有用的信息,比如已执行的查询的数量,它们所花费的时间以及缓存交互的数量。

所有这些信息可以使你识别那些开发中引入的低效的查询,从而可以让你及时修复掉它们而避免在生产环境下产生麻烦。

第 8 段(可获 1.41 积分)

总结

JPA 和 Hibernate使得我们可以很容易的实现大多数用例,但是也会使我们很容易的创建低效的查询。当你遵循一些简单的建议之后,就可以避免其中的大部分问题。你应该做如下事情:

  • 使用一个能够只查询到你需要信息的投影,
  • 只有在你需要的时候才使用 FetchType.LAZY 来获取到关联信息,
  • 当你加载一个实体的时候,初始化所有必须的关联,
  • 当使用到大容量的实体或DTO列表列表时使用分页,
  • 当你要修改代码时要检查生成的SQL语句。

这些就是我使用Hibernate书写高效数据库查询的5个技巧。如果你习惯这篇文章,你也应该看看另外一篇文章 《Hibernate性能调优在线培训》,在这篇文章中我会向你展示怎么提高写操作,使用缓存来避免不必要的查询以及并发的管理。

第 9 段(可获 1.8 积分)

文章评论