【问题标题】:DTOs with different granularity不同粒度的 DTO
【发布时间】:2015-06-10 04:03:47
【问题描述】:

我的项目使用最新的 Spring+Hibernate 来实现持久性和实现 REST API。 数据库中的不同表包含大量记录,这些记录也非常大。因此,我创建了许多 DAO 来检索不同级别的详细信息及其随附的 DTO。

例如,如果我在数据库中有一些 Employee 表,其中包含有关每个员工的大量信息。如果我知道任何使用我的应用程序的客户端都会从检索 Employee 实体的不同细节级别中受益匪浅(而不是每次都被整个实体轰炸),那么到目前为止我一直在做的事情是这样的:

class EmployeeL1DetailsDto
{
    String id;
    String firstName;
    String lastName;
}

class EmployeeL2DetailsDto extends EmployeeL1DetailsDto
{
    Position position;
    Department department;
    PhoneNumber workPhoneNumber;
    Address workAddress;
}

class EmployeeL3DetailsDto extends EmployeeL2DetailsDto
{
    int yearsOfService;
    PhoneNumber homePhoneNumber;
    Address homeAddress;
    BidDecimal salary;
}

等等……

您在这里看到我已将员工信息划分为不同的详细级别。 随附的 DAO 看起来像这样:

class EmployeeDao
{
    ...

    public List<EmployeeL1DetailsDto> getEmployeeL1Detail()
    {
        ...
        // uses a criteria-select query to retrieve only L1 columns
        return list;
    }

    public List<EmployeeL2DetailsDto> getEmployeeL2Detail()
    {
        ...
        // uses a criteria-select query to retrieve only L1+L2 columns
        return list;
    }

    public List<EmployeeL3DetailsDto> getEmployeeL3Detail()
    {
        ...
        // uses a criteria-select query to retrieve only L1+L2+L3 columns
        return list;
    }

    .
    .
    .
    // And so on
}

我一直在使用 hibernate 的 aliasToBean() 将检索到的实体自动映射到 DTO。尽管如此,我觉得整个过程中的样板数量(所有 DTO、DAO 方法、所需细节级别的 URL 参数等)有点令人担忧,让我认为可能有更清洁的方法到这个。

所以,我的问题是:是否有更好的模式来从持久化实体中检索不同级别的细节? 我对 Spring 和 Hibernate 还很陌生,所以请随意指出您认为我不知道的任何基本知识。

谢谢!

【问题讨论】:

  • 你能详细说明你为什么要限制细节吗?安全性、性能、...?
  • 主要是为了性能和一点安全性(减少敏感数据被返回给低权限服务调用的可见性)。但是,是的,我主要关心的是性能。

标签: java spring hibernate design-patterns dto


【解决方案1】:

我会尽可能少地使用不同的查询。我宁愿让关联在我的映射中变得惰性,然后让它们根据需要使用适当的 Hibernate 获取策略进行初始化。

我认为每个业务模型实体拥有多个不同的 DTO 类并没有错,而且它们通常使代码更具可读性和可维护性。

但是,如果 DTO 类的数量趋于爆炸式增长,那么我会在可读性(可维护性)和性能之间做出平衡。

例如,如果 DTO 字段未在上下文中使用,我会将其保留为 null 或填充它,如果这确实不昂贵的话。然后,如果它为空,您可以指示您的对象编组器在生成 REST 服务响应(JSON、XML 等)时排除空字段(如果它确实打扰了服务使用者)。或者,如果您正在填写它,那么当您在应用程序中添加新功能并开始在上下文中使用它时,它总是受欢迎的。

【讨论】:

  • 谢谢。我想我会这样做,使编组器排除空值。这样我就可以保留不需要的属性。与填充时相比,它们的大小非常小,但看到每条记录返回 50 个或更多空属性仍然很烦人。
【解决方案2】:

您必须以一种或另一种方式定义不同的粒度版本。您可以尝试将未加载/设置为 null 的子对象(如其他答案中所建议的那样),但它很容易变得很尴尬,因为您将开始通过安全问题而不是域模型来构造数据。 因此,对单个类执行此操作毕竟不是一个糟糕的方法。

您可能希望它更具动态性(可能是因为您甚至想用更多数据扩展数据库端的数据模型)。​​

如果是这种情况,您可能希望将定义从代码移到某些配置(甚至可以在运行时动态)。这当然也需要在 Java 端使用动态数据模型,例如使用哈希图(请参阅here 了解如何做到这一点)。您因此获得了一个动态数据模型,但失去了类型安全性(至少在一定程度上)。在其他语言中可能感觉很自然,但在 Java 中则不太常见。

现在由您的 HQL 来定义您希望如何填充对象。 你想要走的路径现在很大程度上取决于上下文,你的对象将如何被使用

【讨论】:

    【解决方案3】:

    另一种方法是仅在 Dao 级别使用域对象,并将所需的信息子集定义为 DTO 用于每种用途。然后使用通用 DTO 转换器将 Employee 实体转换为每个 DTO,正如我最近在我的专业 Spring 活动中使用的那样。 MIT 许可的模块可在 Maven 存储库工件 dtoconverter 中获得。 以及作者 Wiki 上的更多信息和用户指南:

    http://ratamaa.fi/trac/dtoconverter

    您从那里的示例页面获得的最快想法:

    狩猎愉快……

    【讨论】:

      【解决方案4】:

      Blaze-Persistence Entity Views 正是为这样的用例而创建的。您将 DTO 结构定义为接口或抽象类,并具有到实体属性的映射。查询时,您只需传入类,库将负责为投影生成优化查询。

      这里是一个简单的例子

      @EntityView(Cat.class)
      public interface CatView {
          @IdMapping("id")
          Integer getId();
      
          String getName();
      }
      

      CatView 是 DTO 定义,这里是查询部分

      CriteriaBuilder<Cat> cb = criteriaBuilderFactory.create(entityManager, Cat.class);
      cb.from(Cat.class, "theCat")
          .where("father").isNotNull()
          .where("mother").isNotNull();
      
      EntityViewSetting<CatView, CriteriaBuilder<CatView>> setting = EntityViewSetting.create(CatView.class);
      List<CatView> list = entityViewManager
                              .applySetting(setting, cb)
                              .getResultList();
      

      请注意,基本部分是 EntityViewSetting 具有应用于现有查询的 CatView 类型。生成的 JPQL/HQL 针对 CatView 进行了优化,即它只选择(并加入!)它真正需要的内容。

      SELECT
          theCat.id,
          theCat.name
      FROM
          Cat theCat
      WHERE theCat.father IS NOT NULL
        AND theCat.mother IS NOT NULL
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多