使用 JPA 的事务边界和 DTO 转换

Transaction Boundary and DTO conversion with JPA

一直在想这个异常应该怎么处理:

  1. DTO应该在控制器中转换,服务层不需要知道它们。
  2. 事务边界由服务层定义。

但是你如何避免 JPA LazyInitialization 异常呢? DTO 转换可能需要 Lazy Fetched 数据,但由于事务是由服务层处理的,因此无法做到。

我能想到的办法有很多,但都是丑陋的。将DTO转换放在服务层现在对我来说似乎是最好的。

是的,在服务层操作DTO肯定更好。当使用 DTO 中包含的更改更新实体时尤其如此,否则您将需要获取和更新分离的实体,将它们传递给服务,将它们再次合并到持久性上下文中,等等。

"DTO's should be converted in the controller, the service layer does not need to know about them."

除此之外,我会说更好的经验法则是控制器不需要了解实体。但是对于简单的情况,您可以使用分离的实体而不是 DTO,以避免创建大量小的 DTO 类,尽管我个人总是使用 DTO 只是为了保持一致并使以后的更改更容易。

Raised 'LazyInitializationException' 只是表示某些部分数据未加载的信号,因此最好的解决方案是从控制器方法多次调用服务级别并获取 DTO 所需的所有字段。

不太优雅的选项是:

  1. 可以检测未通过 'org.hibernate.Hibernate.isInitialized' 方法加载的字段并在 DTO 构建期间跳过它们,请参见此处的完整示例: How to test whether lazy loaded JPA collection is initialized?

  2. 您可以将控制器方法标记为事务性的,在调用服务级别后将打开休眠会话,这样延迟加载就可以工作了。

DTO 是您应该在服务之上的层中使用的模型。只有服务应该知道实体模型。在简单的退化情况下,DTO 模型可能看起来几乎像实体模型,这就是为什么许多人只使用实体模型的原因。在人们获得 真正的需求 将迫使他们改变使用数据的方式之前,这种方法一直有效。这就是 DTO = Entity 的错觉崩溃的时候。

DTO 通常是实体模型的子集或转换。关于 LazyInitializationException 的观点是幻觉何时开始崩溃的完美例子。

服务应该 return 完全初始化 DTO,即不仅仅是一些委托给实体对象的对象。在 DTO 从服务 returned 之后,不应该有任何延迟加载 inovlved。这意味着您必须准确获取 DTO 所需的状态,并将该数据连接到要 returned 的对象中。由于这通常需要相当多的样板代码并且有时会导致不得不重复逻辑,因此人们倾向于通过在各处散布一些获取连接以使 LazyInitializationException 消失来更长时间地坚持 DTO = 实体错觉。

这就是我启动 Blaze-Persistence Entity Views 项目的原因,该项目将使您两全其美。易于使用,样板文件少,性能好,模型安全,可避免意外错误。也许您想试一试,看看它能为您做什么?