当您拥有与 POJO(实体)相同的对象时,拥有 DTO 对象有什么意义?

What's the point of having DTO object when you have the same object as POJO (Entity)?

我想了解当您已经拥有 POJO 对象(作为实体)时创建 DTO 对象有什么好处。

在我的项目中我有两个:

如果我查看 DTO 对象 class(我们称之为 MyObjDTO)和相同的 class 但 POJO 端(我们称之为 MyObjPOJO)除了 MyObjPOJO 作为注释外没有任何区别因为它是一个@Entity。

所以事实上,我在我的项目中得到了 2 class 看起来相同(相同的属性,相同的方法)但用于不同的 pprose。

IMO,在这种情况下,DTO class 是无用的并且会增加应用程序的复杂性,因为我使用 DTO class 所做的一切我可以使用我的 POJO class 来完成,而且,对于我必须维护至少 2 classes(DTO 和 POJO)的单一类型的对象,例如,如果我添加一个属性,我必须在两个 classes.

我不是专家,我正在质疑我的想法;你怎么看?

大部分归结为清洁架构和对关注点分离的关注

我最大的实体用例是这样我就不会在 DTO 中乱扔我为了方便而添加的运行时变量或方法(例如显示名称/值或 post-计算值)

如果它是一个非常简单的实体,那么它就没什么大不了的,但是如果你对 Clean 非常严格,那么就会有很多冗余模型(DTO、DBO、实体)

这确实是您愿意为严格的 Clean 架构投入多少的偏好

https://medium.com/android-dev-hacks/detailed-guide-on-android-clean-architecture-9eab262a9011

此答案是 stack exchange 上的内容的副本。恕我直言,应该关闭 OP,因为它发布在错误的论坛中。它目前也吸引了自以为是的答案,尽管不一定如此,并且与 java 没有任何特定的联系。

DTO 是一种模式,它独立于实现 (POJO/POCO)。 DTO 说,由于对任何远程接口的每次调用都是昂贵的,因此对每个调用的响应应该带来尽可能多的数据。所以,如果某个任务需要多次请求带数据,可以将要带的数据组合在一个DTO中,这样只需一个请求就可以带所有需要的数据。 Patterns of Enterprise Application Architecture 的目录有更多详细信息。

DTO 是一个基本概念,并未过时。

有点过时的是让 DTO 完全不包含任何逻辑的概念,仅用于传输数据并在传输到客户端之前从域对象“映射”,并在传递它们之前映射到视图模型到显示层。在简单的应用中,领域对象往往可以直接作为DTO复用,直接传递给展示层,这样就只有一个统一的数据模型。对于更复杂的应用程序,您不想将整个域模型暴露给客户端,因此需要从域模型到 DTO 的映射。拥有一个单独的视图模型来复制 DTO 中的数据几乎没有任何意义。

然而,这个概念已经过时而不是完全错误的原因是一些(主要是旧的)frameworks/technologies 需要它,因为他们的域和视图模型不是 POJOS,而是直接绑定到框架.

最值得注意的是,EJB 3 标准之前的 J2EE 中的实体 Bean 不是 POJO,而是应用服务器构建的代理对象 - 根本不可能将它们发送到客户端,因此您别无选择有一个单独的 DTO 层 - 这是强制性的。

虽然 DTO 不是一个过时的模式,但它经常被不必要地应用,这可能会使它显得过时。

来自 Java 大师 Adam Bien:

The most misused pattern in the Java Enterprise community is the DTO. DTO was clearly defined as a solution for a distribution problem. DTO was meant to be a coarse-grained data container which efficiently transports data between processes (tiers). ~ Adam Bien

来自马丁·福勒:

DTOs are called Data Transfer Objects because their whole purpose is to shift data in expensive remote calls. They are part of implementing a coarse grained interface which a remote interface needs for performance. Not just do you not need them in a local context, they are actually harmful both because a coarse-grained API is more difficult to use and because you have to do all the work moving data from your domain or data source layer into the DTOs. ~ Martin Fowler

这是一个 Java EE 的特定示例,它是 DTO 模式常见但不正确的用法。如果您不熟悉 Java EE,您只需要了解 MVC 模式:“JSF ManagedBean”是视图使用的 class,而“JPA 实体”是视图中的模型MVC 模式。

例如,假设您有一个 JSF ManagedBean。一个常见的问题是 bean 应该直接持有对 JPA 实体的引用,还是应该维护对稍后转换为实体的某个中间对象的引用。我听说这个中间对象被称为 DTO,但是如果您的 ManagedBean 和实体在同一个 JVM 中运行,那么使用 DTO 模式几乎没有好处。

此外,请考虑 Bean Validation 注释(同样,如果您不熟悉 Java EE,请知道 Bean Validation 是用于验证数据的 API)。您的 JPA 实体可能带有 @NotNull 和 @Size 验证注释。如果您正在使用 DTO,您将希望在 DTO 中重复这些验证,以便使用您的远程接口的客户端不需要发送消息来发现它们没有通过基本验证。想象一下在您的 DTO 和实体之间复制 Bean 验证注释的所有额外工作,但是如果您的视图和实体在同一个 JVM 中运行,则无需承担这项额外工作:只需使用实体。

Catalog of Patterns of Enterprise Application Architecture 提供了 DTO 的简明解释,这里有更多我发现有启发性的参考资料:

即使非常小,在架构中分离层并让对象在穿过层时“变形”也是有好处的。 解耦 允许您以最小的更改替换软件中的任何层,只需更新 2 个对象和所有集合之间的字段映射。

如果这 2 个对象具有相同的成员...好吧,这就是 Apache Commons BeanUtils.copyProperties() 的用途 ;)

DTO的好处其他人已经说了,这里我就说说如何解决多维护一个DTO版本对象的麻烦。 我开发了一个库 beanKnife 来自动生成一个 dto。它将在原始 pojo 的基础上创建一个新的 class。您可以过滤继承的属性、修改现有属性或添加新属性。您只需要编写一个配置 class,库将为您完成剩下的事情。配置支持继承特性,可以抽取公共部分,进一步简化配置。 这是例子

@Entity
class Pojo1 {
   private int a;
   @OneToMany(mappedBy="b")
   private List<Pojo2> b;
}

@Entity
class Pojo2 {
   private String a;
   @ManyToOne()
   private Pojo1 b;
}

// Include all properties. By default, nothing is included.
// To change this behaviour, here use a base configuration and all other final configuration will inherit it.
@PropertiesIncludePattern(".*")
// By default, the generated class name is the original class name append with "View",
// This annotation change the behaviour. Now class Pojo1 will generate the class Pojo1Dto
@ViewGenNameMapper("${name}Dto")
class BaseConfiguration {
}

// generate Pojo1Dto, which has a pojo2 info list b instead of pojo2 list
@ViewOf(value = Pojo1.class)
class Pojo1DtoConfiguration extends BaseConfiguration {
    private List<Pojo2Info> b;
}

// generate Pojo1Info, which exclude b
@ViewOf(value = Pojo1.class, genName="Pojo1Info", excludes = "b")
class Pojo1InfoConfiguration extends BaseConfiguration {}

// generate Pojo2Dto, which has a pojo1 info b instead of pojo1
@ViewOf(value = Pojo2.class)
class Pojo2DtoConfiguration extends BaseConfiguration {
    private Pojo1Info b;
}

// generate Pojo2Info, which exclude b
@ViewOf(value = Pojo2.class, genName="Pojo2Info", excludes = "b")
class Pojo2InfoConfiguration extends BaseConfiguration {}

将生成

class Pojo1Dto {
   private int a;
   private List<Pojo2Info> b;
}

class Pojo1Info {
   private int a;
}

class Pojo2Dto {
   private String a;
   private Pojo1Info b;
}

class Pojo2Info {
   private String a;
}

那就这样用吧

Pojo1 pojo1 = ...
Pojo1Dto pojo1Dto = Pojo1Dto.read(pojo1);
Pojo2 pojo2 = ...
Pojo2Dto pojo2Dto = Pojo2Dto.read(pojo2);