java - 在事务之外使用的 JPA Spring Data 实体

标签 java spring spring-data-jpa

我有一个 Spring Boot 应用程序,其服务返回一个暴露给 Controller 的 Spring Data 实体。问题是我知道在数据库事务之外使用实体不是一个好主意,那么最佳做法是什么?

考虑以下服务:

@Transactional
public MyData getMyData(Long id) {
    return myDataRepository.findById(id);
}

其中 MyData 是一个数据库 @EntitymyDataRepository 是一个 JpaRepository

此服务方法从 Controller 类调用, Controller 类将此对象以 JSON 格式发送到调用此方法的客户端。

@RequestMapping("/")
public ResponseEntity<?> getMyData(@RequestParam Long id) {
    return myService.getMyData(id);
}

如果我将 MyData 暴露给 Controller ,那么它将暴露在事务之外,并可能导致各种 hibernate 错误。这些场景的最佳实践是什么?我应该在服务中将实体转换为 POJO 并在 MyService 中返回 MyDataPOJO 而不是 MyData 吗?

最佳答案

在事务之外使用实体不一定会导致问题;它实际上可能有有效的用例。然而,有相当多的变数在起作用,一旦你让它们离开你的视线,事情可能会而且将会向南发展。考虑以下场景:

  1. 您的实体与其他实体没有任何关系,或者这些关系非常肤浅且急切获取。您从存储库中检索该实体,将其与持久性单元分离(隐式或显式)并传递给 Controller ​​。 Controller 不会尝试修改实体;它仅将其序列化为 JSON - 完全安全。
  2. 与上面相同,但 Controller 在将实体序列化为 JSON 之前修改实体 - 同样,完全安全(只是不要期望这些更改会反射(reflect)在数据库中)
  3. 与上述相同,但您忘记了从 PU 分离实体 - 糟糕,如果 Controller 更改了实体,您可能会看到它反射(reflect)在数据库中或得到事务关闭异常;两者都很可能是意想不到的后果。
  4. 同上,但实体的某些关系是惰性的。同样,您可能会或可能不会收到任何异常,具体取决于是否正在访问这些惰性属性。
  5. 还有更多有意和无意的设计选择组合...

如您所见,事情很快就会失控。尤其是当您的模型必须发展时:不久您就会发现自己摆弄着 JSON View 、@JsonIgnore、实体投影等等。因此,经验法则:虽然偷工减料并将您的实体暴露给外部层似乎很诱人,但这很少是一个好主意。正确设计的解决方案总是在层与层之间明确分离关注点:

  • 持久层永远不会公开比业务逻辑所需更多的方法或实体。此外,相同的表可以而且应该映射到几个不同的实体,具体取决于它们参与的用例。
  • 业务逻辑层(顺便说一句,这是您的 API,而不是 REST 服务!见下文)永远不会从持久层泄漏任何细节。它的方法清楚地定义了问题域中的用例。
  • 表示层仅将业务逻辑提供的 API 转换为适合客户端的一种或另一种形式,从不实现其他用例。请记住,REST Controller 、SOAP 服务等在逻辑上都是表示层的一部分,而不是业务逻辑。

是的,简短的回答是:持久性实体不应暴露给外部层。一种常见的技术是改用 DTO;此外,DTO 对象提供额外的抽象层,以防您需要更改实体但保持 API 不变,反之亦然。如果在某些时候您的 DTO 恰好与您的实体非常相似,可以使用 Java bean 映射框架(如 Dozer、Orika、MapStruct、JMapper、ModelMapper 等)来帮助消除样板代码。

尝试使用谷歌搜索“六角形建筑”。对于设计完全分离的层来说,这是一个非常有趣的概念。这是关于此主题的一篇文章 https://blog.octo.com/en/hexagonal-architecture-three-principles-and-an-implementation-example/ ;它使用 C# 示例,但它们非常简单。

关于java - 在事务之外使用的 JPA Spring Data 实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53917747/

相关文章:

java - Spring IllegalState无法在单元测试中创建数据源不稳定行为

mysql - Hibernate不创建连接表

java - Jasper Reports + WebSphere 7,ExceptionInInitializerError 异常

java - 多个 GUI 对象的最佳实践

java - maven中如何继承其他库的依赖?

java - Spring Web MVC Tiles 异常

java - 什么是类对象(java.lang.Class)?

java - 使用 libgdx 绘制带纹理的多边形

Java 并发查询同一表以进行电子邮件处理

Spring数据投影和错误: "No aliases found in result tuple! Make sure your query defines aliases!"