DTO具有不同的粒度

时间:2015-06-10 04:03:48

标签: java spring hibernate design-patterns dto

我正在使用最新的Spring + Hibernate进行持久化和实现REST API的项目。 数据库中的不同表包含大量记录,而这些记录又相当大。所以,我创建了很多DAO来检索不同级别的细节及其附带的DTO。

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

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的新手,所以请随意指出任何你认为我不知道的基本知识。

谢谢!

4 个答案:

答案 0 :(得分:1)

我会尽可能少地查询。我宁愿在我的映射中使关联变得懒惰,然后使用适当的Hibernate获取策略按需初始化它们。

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

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

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

答案 1 :(得分:1)

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

您可能希望让它更具动态性(可能是因为您希望在数据库端扩展数据模型并使用更多数据)。

如果是这种情况,您可能希望将定义从代码移到某些配置(甚至可以在运行时动态)。这当然也需要Java端的动态数据模型,比如使用hashmap(参见here如何做)。从而获得动态数据模型,但是类型安全性(至少在某种程度上)是松散的。在其他语言中,可能会感觉很自然,但在Java中则不太常见。

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

答案 2 :(得分:0)

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

http://ratamaa.fi/trac/dtoconverter

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

快乐狩猎......

答案 3 :(得分:0)

已经为这样的用例创建了

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