【问题标题】:Do JPA entities and DTOs belong to Service or Spring Repository layerJPA 实体和 DTO 是否属于 Service 或 Spring Repository 层
【发布时间】:2017-04-29 15:15:28
【问题描述】:

我想编写一个 JPA 应用程序,但我在理解存储库和服务概念时遇到了问题:

假设我们有 3 个实体: A、B 和 C。

实体A需要设置B和C:A.setB(B), A.setC(C)才保存。

另外为了阅读,我只想返回 DTO,因为它们会让我从 LazyInitializationExceptionOpen Session In View Anti-Pattern 等中解放出来,并且为了修改数据,我将使用实体(这些准则也在本书 High-Performance Java Persistence 中进行了描述) .

首先,用户将使用一些数据进行 HTTP POST,这些数据将被转换为 ABCGuiObject。 REST 控制器将调用 serviceA.save(ABCGuiObject)。

坚持选项:

选项 #1 - 该服务将创建 3 个对象 A、B、C 并将它们传递给 repositoryA.save(A,B,C)。 repositoryA 将在 a.setB 和 a.setC 中执行。

选项 #2 - 该服务将创建 3 个对象 A、B、C,执行 a.setB 和 a.setC 并调用 repositoryA.save(A)。

DTO 检索

我想编写一个 JPQL 查询来获取一些 DTO。我不想将实体转换为 DTO,而是直接使用 JPQL 检索它们。

选项 #1 - repositoryA 将直接返回一个 DTO。

选项 #2 - 服务会将 JPQL 查询传递给存储库 A,该存储库将具有返回 DTO 的通用查询方法。

您推荐什么方法?

【问题讨论】:

    标签: java spring hibernate jpa service-layer


    【解决方案1】:

    写入数据

    服务层应该构造实体ABC并相应地连接它们(a.setBa.setC)并将ABC传递给存储库。如果有一对多关联,可以cascadeentity state transitions

    读取数据

    如果要返回 DTO,最好直接从 JPA 和 Hibernate 获取 DTO。所以,你有两个选择:

    【讨论】:

    • Vlad 但是我需要为每个实体类创建一个存储库吗?我从您那里了解到的是,该服务将从 ABCGuiObject 构建 3 个实体类,然后以正确的顺序或将使用级联调用 repositoryA.save(a)、repositoryB.save(b) 和 respoitoryC.save(c) .它是否正确?此外,对于 DTO,有人说从存储库返回 DTO 是不可行的,可能是因为您会将 DTO 转发给服务,然后再转发给 REST。您对此有何看法?
    • Spring Data 要求您为每种实体类型创建一个存储库。至于 DTO,当然需要从 Repository 中返回它们。否则,您将选择实体,然后将它们转换为 DTO。但是,当您只需要 DTO 投影时,为什么要选择多个列来获取实体呢?
    • 但是服务层呢?我应该为每个实体类提供服务还是提供具有相关业务方法和适当调用存储库的通用服务?
    • 在我看来,服务层与业务逻辑有关,而不是与您拆分实体的方式有关。但是,这更像是一个偏好问题,这是一个主观问题。
    • @Vlad 我很高兴我找到了这个答案。正如您提到的,存储库可以返回 DTO 进行检索。我应该为服务层创建另一个(类似的)dto,将 repo dto 映射到服务 dto 并返回它,还是应该从 repo 中返回 dto。如果来自 repo 的 DTO 离开服务并且我从控制器调用我的服务,我不会直接访问我的 repo 层对象吗?例如,从控制器中的 repo 层导入?这不是紧耦合吗?
    【解决方案2】:

    这是一个主观的事情,但我会分享我的意见。 对于持久选项,我推荐选项 #2。理想情况下,存储库应该只执行“存储库”职责、服务,或者如果过于复杂,您可以委托工厂来构建 A 实体。

    对于 DTO 检索,我会发出查询以检索实体(因此存储库将始终返回实体),并将它们转换为 DTO。有人可能需要相同的存储库功能,他们可以选择转换为 DTO,而不是编写另一个带有 DTO 查询的 JPQL。

    【讨论】:

    • 但是当通过 JPQL 构建 DTO 更有效时,为什么要返回实体然后将它们转换为 DTO?您知道要选择的数据,并且连接在数据库级别被过滤,而不是 Hibernate 在内存中。
    • 实体也是POJO,可以在UI层使用。保存时无需将它们映射到 DTO 并返回到实体。当事务仍处于活动状态时,可以使用 .size() 获取惰性集合,例如在 EJB 方法中。
    • @user3030447 如今,“内存占用”论点在大多数情况下都站不住脚。大多数时候,您加载少量实体并将它们转换为 DTO 实际上不是问题。即使在报告中,您也不应该加载超过几千条记录。映射到 DTO 为您提供了更大的灵活性,因为 UI 通常与实体结构不“匹配”。您可以在简单的情况下使用没有 DTO 的实体。
    【解决方案3】:

    坚持选项 2。

    DTO 对象通常加载惰性字段。如果您不为不同的用例编写单独的查询/映射,这可能会损害性能。不要使用 DTO,而是根据用例初始化惰性字段以避免 LazyIntializationException。此外,它还可以为您节省大量从实体映射到实体以进行保存的工作。

    【讨论】:

    • DTO 不会加载惰性字段。认为我会在JPQL中编写手动连接,并且过滤将在数据库级别完成,因此只有相关信息会在那里。我不会使用 join fetch 或 fetch 实体然后将它们转换为 DTO。
    • 是的,这取决于您如何将实体映射到 DTO。使用像 Dozer 集合这样的映射框架将始终被加载。这在性能方面并不是最优的。
    【解决方案4】:

    关于持久化,我会选择选项 2,但不适用于所有与服务相关的操作。如果您有简单的操作,例如addComment,启用/禁用,那么最好只传递 id 并让服务掌握实体本身。

    对于 DTO,我建议你看看 Blaze-Persistence Entity Views,看看 DTO 模式的实现有多容易。这是一个很好的 article 开始使用它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-06
      • 2015-06-21
      • 1970-01-01
      • 1970-01-01
      • 2022-01-04
      • 1970-01-01
      • 1970-01-01
      • 2022-01-12
      相关资源
      最近更新 更多