ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Hibernate5 与 Spring Boot2 最佳性能实践(2)

2021-05-30 10:58:07  阅读:235  来源: 互联网

标签:Hibernate5 Hibernate SpringBoot 示例 Spring Boot2 github https com


[上一篇][1]介绍了 Hibernate 5 和 Spring Boot 2 一些性能方面的最佳实践,这一篇会继续介绍剩下的内容。让我们开始吧!


[26. 如何通过 SqlResultSetMapping & NamedNativeQuery 提取 DTO][2]

[27. 如何通过 javax.persistence.Tuple 和原生 SQL 提取 DTO][3]

[28. 如何通过 javax.persistence.Tuple 和 JPQL 提取 DTO][4]

[29. 如何通过 Constructor 表达式和 JPQL 提取 DTO][5]

[30. 如何通过 ResultTransformer 和原生 SQL 提取 DTO][6]

[31. 如何通过 ResultTransformer 和 JPQL 提取 DTO][7]


[1]:https://dzone.com/articles/50-best-performance-practices-for-hibernate-5-amp

[2]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoSqlResultSetMappingAndNamedNativeQuery

[3]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoTupleAndSql

[4]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoTupleAndJpql

[5]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoConstructorExpression

[6]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoResultTransformer

[7]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoResultTransformerJpql


32. 通过 Blaze-Persistence 实体视图实现 DTO


"描述:" 获取不必要的数据很容易造成性能损失,使用 DTO 允许只提取必须的数据。在这个示例中,使用了 Blaze-Persistence 实体视图。


技术要点


  • 在 Maven `pom.xml` 中,添加 Blaze-Persistence 依赖

  • 配置 Blaze-Persistence 中 `CriteriaBuilderFactory` 和 `EntityViewManager`

  • 按照 Blaze-Persistence 风格接口开发 *entity view*

  • 编写 Spring repository 继承 `EntityViewRepository`

  • 调用 repository 中的方法,比如 `findAll()`、`findOne()` 等

  • 使用 Spring Data Projection 请参考例9


[示例代码][8]


[8]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoBlazeEntityView


33. 没有 @OrderColumn 时使用 @ElementCollection 的执行效果


没有 `@OrderColumn` 时,对 `@ElementCollection` 进行插入和删除会造成性能损失。有 `@OrderColumn` 时,情况会好一些。


"描述:"在这个示例中,展示了没有 `@OrderColumn` 时使用 `@ElementCollection` 可能带来的性能损失。接下来的示例(例34)加上 `@OrderColumn` 能减少性能损失。


技术要点


  • `@ElementCollection` 未设置主键

  •  `@ElementCollection` 映射到独立的数据库表

  • 需要频繁插入、删除操作时,避免使用 `@ElementCollection`。因为执行插入或删除前,数据库会删除所有记录

  • 数据表记录越多,性能损失越大


示例输出


图片


[示例代码][9]


[9]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootElementCollectionNoOrderColumn


34. 使用 OrderColumn 时 @ElementCollection 的执行效果


没有 `@OrderColumn` 时,对 `@ElementCollection` 进行插入和删除会造成性能损失。有 `@OrderColumn` 时,情况会好一些。


"描述:"在这个示例中,展示了没有 `@OrderColumn` 时使用 `@ElementCollection` 可能带来的性能损失。加上 `@OrderColumn` 后,能减少对集合尾部操作(例如,在集合尾部执行 add/remove 操作)的性能损失。主要原因是操作对象左边的元素都不会移动,性能开销主要集中在集合尾部。


技术要点


  • `@ElementCollection` 未设置主键

  • `@ElementCollection` 映射到独立的数据库表

  • 需要频繁插入、删除操作时,使用 `@ElementCollection` 同时加上 `@OrderColumn`

  • 在数据表开始的地方,执行插入或移除记录越多性能损失越大


示例输出


图片


[示例代码][10]


[10]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootElementCollectionWithOrderColumn


35. 如何避免 Open Session In View 反模式带来的延迟加载实体问题(1 Session/1 HTTP Request-Response)


只要存在延迟加载实体,无论将来是否用到 Open-Session In View 都会获取,从而造成严重的性能问题。


"描述:"Spring Boot 会默认开启 Open Session In View 反模式。如果确实需要使用,最好能够减少由此带来的性能损失。一种优化方法,可以把 `Connection` 标记为只读,这样能够避免数据库服务器记录事务日志。另一种办法,对那些不希望被延迟加载的实体属性显式标记。


技术要点


  • 获取实体并显式设置延迟加载属性

  • 可以根据需要在 Service 或 Controller,但"一定要在事务之外设置"

  • 为什么这种方法有效?为什么能够设置托管实体属性却不触发刷新操作?答案可以从 `OpenSessionInViewFilter` 文档中找到:


>>>

“注意:该 filter 默认不会刷新 Hibernate Session,刷新模式默认设为 `FlushMode.NEVER`。这里假定 Service 层事务会处理刷新:在事务读写期间,活动事务管理器会临时把刷新模式更改为 `FlushMode.AUTO`,事务结束时重置刷新模式为 `FlushMode.NEVER`。如果需要在不使用事务的情况下使用 filter,可以考虑修改 `flushMode` 更改默认刷新模式。”

>>>


示例输出



示例代码][11]


[11]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootSuppressLazyInitInOpenSessionInView


36. 如何使用 Spring Projection(DTO) 和 Inner Join


SQL JOIN 和 DTO 有助于解决 N+1 问题,例36至42包含了很多相关示例。


> 译注:“N+1 问题”即执行一次查询 N 条主数据后,由于关联引起的 N 次从数据查询,因此会带来了性能问题。一般来说,通过延迟加载可以部分缓解 N+1 带来的性能问题。


"描述:"这个示例使用 JPQL 和原生 SQL(MySQL)验证 Spring Projection(DTO) 和 Inner Join 方案。


技术要点


  • 定义若干实体:例如 `Tournament` 和 `Player`,保持双向 `@OneToMany`

  • 用测试数据填充数据库:例如,`resources/data-mysql.sql` 中的数据

  • 开发从数据库中获取列信息 getter 接口(Projection):例如,`TournamentPlayerNameDto`、`PlayerRankNameDto`、`TournamentIdNameDto`

  • 使用 JPQL 或原生 SQL 编写 Inner Join 查询,例如

  • 查询所有球员的比赛:`localhost:8080/tournamentsOfPlayersNamesInnerJoinJpql`

  • 查询所有排名小于或等于 `rank` 球员的比赛:`localhost:8080/tournamentsIdNameByRankInnerJoinSql`


[示例代码][12]


[12]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoViaInnerJoins


你可能也会对下面内容感兴趣


[37. 如何使用 Spring Projections(DTO) 和 Left Join][13]

[38. 如何使用 Spring Projections(DTO) 和 Right Join][14]

[39. 如何使用 Spring Projections(DTO) 和 Full Join][15]

[40. 如何使用 Spring Projections(DTO) 和 Left Excluding Join][16]

[41. 如何使用 Spring Projections(DTO) 和 Right Excluding Join][17]

[42. 如何使用 Spring Projections(DTO) 和 Outer Excluding Join][18]


[13]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoViaLeftJoins

[14]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoViaRightJoins

[15]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoViaFullJoins

[16]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoViaLeftExcludingJoins

[17]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoViaRightExcludingJoins

[18]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoViaOuterExcludingJoins


43. 如何使用 Spring Post 提交


本例中描述的现象通常出现在生产环境,这种环境的负载很高。Spring post-commit hook 会在执行完成前让数据库连接保持打开状态。提交结束后,在 hook 函数中执行耗时任务。高负载时会导致连接池处于饥饿状态。显然,这种情况下需要更长时间才能连接成功。


"描述:"在这个示例展示了 Spring Post Commit Hook.


技术要点


  • 避免在 Post Commit 中执行耗时任务,数据库连接会一直保持打开状态直到代码执行完成。


[示例代码][19]


[19]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootPostCommit


44. 如何在无关实体上使用 Spring Projection(DTO) 和 Join(Hibernate 5.1+)

Hibernate 5.1 引入了 Explicit Join 可以在无关实体上使用,语法和执行效果与 SQL JOIN 类似。


"描述:" 在这个示例展示了 Spring Projection 和无关实体连接。Hibernate 5.1 引入了 Explicit Join 可以在无关实体上使用,语法和执行效果与 SQL JOIN 类似。


技术要点


  • 定义若干实体:例如,`Patient` 和 `Clinic` 无关实体

  • 用测试数据填充数据库:例如,`resources/data-mysql.sql` 中的数据

  • 开发从数据库中获取列信息 getter 接口(Projection):例如,`PatientNameAndMedicalHistoryDto`

  • 使用 JPQL 或原生 SQL 编写 Join 查询,例如

  • 查询目前不在治疗中的所有病人的姓名和病史:`localhost:8080/allPatientsNameAndMedicalHistoryNoTreatmentInnerJoinJpql`


[示例代码][20]


[20]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDtoUnrelatedEntities


45. 为什么要避免在实体上使用 Lombok @EqualsAndHashCode


Lombok 时目前非常流行的一个开发库。但请注意,在实体上使用 Lombok `@EqualsAndHashCode` 可能会造成严重问题。


"描述:"实体必须像[下面][21]这样实现 `equals()` and `hashCode()`,而且必须在 *transient*、*attached*、*detached* 和 *removed* 状态转换中保持 equal。Lombok `@EqualsAndHashCode` 无法满足上述要求。


[21]:https://vladmihalcea.com/the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate/


技术要点


"避免"


  • 使用 Lombok `@EqualsAndHashCode` 默认行为:实体 `LombokDefaultProduct`,测试 `LombokDefaultEqualsAndHashCodeTest`

  • 只基于主键使用 Lombok `@EqualsAndHashCode`:实体 `LombokIdProduct`,测试 `LombokEqualsAndHashCodeWithIdOnlyTest`

  • 使用默认的 `equals()` 和 `hashCode()` :实体 `DefaultProduct`,测试 `DefaultEqualsAndHashCodeTest`

  • 只基于主键实现 `equals()` 和 `hashCode()` :实体 `IdProduct`,测试 `IdEqualsAndHashCodeTest`


"推荐"


  • 依赖 `@NaturalId`:实体 `NaturalIdProduct`,测试 `NaturalIdEqualsAndHashCodeTest`

  • 依赖 *Primary Key*:实体 `GoodProduct`,测试 `GoodEqualsAndHashCodeTest`


`equals()` 和 `hashCode()` 的优秀实现:


图片


[示例代码][22]


[22]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootLombokEqualsAndHashCode


46. 如何通过 JOIN FETCH 避免 LazyInitializationException


如果你从未遇到 `famousLazyInitializationException`,说明你从未用过 Hibernate :) 加入从 `LAZY` 转为 `EAGER` 解决了这个异常,这个例子就是为你准备的。


"描述:" 当遇到 `LazyInitializationException` 时,通常会把获取类型从 `LAZY` 改为 `EAGER`。这是一种很糟糕的[代码异味][23]。避免这种异常的最好办法是 `JOIN FETCH` + DTO(如果有需要)。这个示例展示了无 DTO 的 JOIN FETCH,通过单个 `SELECT` 查询获取实体。但是,基于上述 DTO 示例也可以很方便地改为使用 DTO。


[23]:https://vladmihalcea.com/eager-fetching-is-a-code-smell/


技术要点


  • 定义两个相关实体,例如 `Category` 和 `Product` 保持*一对多*双向延迟加载关系

  • 编写 JPQL `JOIN FETCH` 获取包括 product 在内的一个 category

  • 编写 JPQL `JOIN FETCH` 获取所有 product 包括 category


示例输出


图片


[示例代码][24]


[24]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootJoinFetch


47. 如何合并实体集合


如何正确地合并集合不是一件轻松的事情!


"描述:"这个 Spring Boot 示例基于[这篇文章][25]。这是 Vlad 例子的一个函数式实现。'强烈推荐阅读此文'


[25]:https://vladmihalcea.com/merge-entity-collections-jpa-hibernate/


技术要点


  • 从数据库中移除不在目标集合中出现的行

  • 在数据库中更新所有出现在目标集合中行

  • 为数据库添加所有出现在目标集合中,但没有在数据库快照中找到的行


[示例代码][26]


[26]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootMergeCollections


48. 如何根据需要延迟获取数据库连接(Hibernate 5.2.10)


得到数据库连接后不立即使用可能导致性能下降。需要数据库连接的人可不会一直等待。


"描述:" 这个 Spring Boot 示例展示了如何利用 Hibernate 5.2.10 特性根据需要延迟获取连接。通常,调用 `@Transactional` 注解的方法后会立即得到连接。假如这个方法在执行第一条 SQL 语句后包含了一些耗时的任务,那么就会毫无意义地一直保持连接。但是,Hibernate 5.2.10 允许根据需要延迟获取连接。示例采用 HikariCP 作为 Spring Boot 默认连接池。


技术要点


  • 在 `application.properties` 中设置 `spring.datasource.hikari.auto-commit=false`

  • 在 `application.properties` 中设置 `spring.jpa.properties.hibernate.connection.provider_disables_autocommit=true`


示例输出


图片


[示例代码][27]


[27]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootDelayConnection


49. 如何通过 Hibernate hi/lo 算法获取主键


通常,我们不太关心如何生成或获取主键。如果真正关注的话,会考虑使用 `SEQUENCE` 这种便捷、灵活且高效的方法。然而,每个 sequence 都会执行一个数据库行程导致性能问题。hi/lo 是一种标识符序列生成优化算法。


"描述:" 这个 Spring Boot 示例使用 hi/lo 算法,在10个数据库行程中,批量执行1000次插入(每次10条记录)并获取1000个主键。


技术要点


  • 使用 `SEQUENCE` 生成器类型,例如 PostgreSQL

  • 在 `Player.java` 实体中配置 `hi/lo` 算法


示例输出


图片


[示例代码][28]


[28]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootHiLo


50. 如何编写高效双向 @ManyToMany 关联


你是否需要一个“多对多”关系的数据表,而且不希望使用两个 `@OneToMany` 关联进行映射?如果你更喜欢 `@ManyToMany` 双向关联,可以变得更高效一点。


"描述:" 这个示例是一个关于如何编写高效双向 `@ManyToMany` 关联的验证


技术要点


  • 使用 `Tournament` 和 `Player` 两个实体。一个锦标赛可以有多个玩家,一个玩家可以参加多个锦标赛


示例输出


图片


[示例代码][29]


[29]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootManyToManyBidirectional


51. 在 @ManyToMany 关系中优先使用 Set 而非 List


在 `@ManyToMany` 中使用 `List` 而不是 `Set` 可能会生成比预期更多的 SQL,例如删除操作。从而导致降低下降。


"描述:"这个 Spring Boot 示例,在双向 `@ManyToMany` 关系中分别使用了 `List` 与 `Set` 删除记录。结论是 `Set` 效果更好,该结论对于单项关系也成立。


技术要点


  • 使用 `Set` 比 `List` 效率更高


示例输出


图片


[示例代码][30]


[30]:https://github.com/AnghelLeonard/Hibernate-SpringBoot/tree/master/HibernateSpringBootManyToManyBidirectionalListVsSet


如果喜欢本文,你可能也会对[这本书][31]感兴趣。


[31]:https://leanpub.com/java-persistence-performance-illustrated-guide


标签:Hibernate5,Hibernate,SpringBoot,示例,Spring,Boot2,github,https,com
来源: https://blog.51cto.com/u_15127686/2832717

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有