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

  • 本文关键字:对象 使用 DTO POJO 实体 java pojo dto
  • 更新时间 :
  • 英文 :


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

在我的项目中,我有两个:

  • DTO类用于在Web服务和应用程序之间进行通信
  • POJO实体类(JPA)用于数据库和应用程序之间的通信

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

所以事实上,在我的项目中,我有两个类,它们看起来是一样的(相同的属性,相同的方法),但有不同的puprose。

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

我不是专家,我在质疑自己的想法;你觉得怎么样?

这个答案是堆栈交换中的副本。IMHO OP应该因为发布在错误的论坛而关闭。它目前也吸引了固执己见的答案,尽管不一定如此,而且与java没有任何特定的联系。

DTO是一种模式,它与实现(POJO/POCO)无关。DTO表示,由于对任何远程接口的每次调用都很昂贵,因此对每次调用的响应应该带来尽可能多的数据。因此,如果需要多个请求来携带特定任务的数据,则可以在DTO中组合要携带的数据,这样只有一个请求可以携带所有所需的数据。企业应用程序体系结构模式目录提供了更多详细信息。

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

有些过时的是具有完全不包含逻辑的DTO的概念;映射的";在传输到客户端之前从域对象,并在将它们传递到显示层之前映射到视图模型。在简单的应用程序中,域对象通常可以直接重用为DTO,并直接传递到显示层,因此只有一个统一的数据模型。对于更复杂的应用程序,您不希望向客户端公开整个域模型,因此从域模型到DTO的映射是必要的。拥有一个独立的视图模型来复制来自DTO的数据几乎是没有意义的。

然而,这个概念之所以过时而不是完全错误,是因为一些(主要是旧的)框架/技术需要它,因为它们的域和视图模型不是POJOS,而是直接与框架绑定。

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

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

来自Java大师Adam Bien:

Java企业社区中被滥用最多的模式是DTO。DTO被明确定义为分发问题的解决方案。DTO是一个粗粒度的数据容器,可以有效地在进程(层)之间传输数据Adam Bien

马丁·福勒:

DTO被称为数据传输对象,因为它们的全部目的是在昂贵的远程调用中转移数据。它们是实现远程接口性能所需的粗粒度接口的一部分。您不仅不需要在本地环境中使用它们,而且它们实际上是有害的,因为粗粒度的API更难使用,而且因为您必须完成将数据从域或数据源层移动到DTO的所有工作。~Martin Fowler

下面是一个特定于Java EE的DTO模式常见但不正确使用的示例。如果你不熟悉JavaEE,你只需要知道MVC模式:;JSF ManagedBean";是View使用的类;JPA实体";是MVC模式中的模型。

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

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

企业应用程序体系结构的模式目录提供了对DTO的简明解释,这里有更多我觉得很有启发性的参考文献:

  • 如何处理J2EE和设计模式
  • 如何在JSF+Spring+Hibernate中使用DTO
  • 数据传输对象的利弊Martin Fowler对DTO的描述
  • Martin Fowler解释DTO的问题。显然,它们很早就被滥用了截至2004年

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

我对实体最大的用例是,这样我就不会在DTO中乱丢运行时变量或为方便起见添加的方法(例如显示名称/值或后计算值)

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

这确实是一个偏好,你想投入多少严格的清洁架构

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

在您的体系结构中有一个层分离的优势,即使很小,并且有对象"变形";当它们穿过层时。这种去耦允许您用最小的更改来替换软件中的任何层,只需更新2个对象和您的all集之间的字段映射。

如果这两个对象具有相同的成员。。。那么,这就是Apache Commons BeanUtils.copyProperties()的作用;)

其他人已经告诉您DTO的好处,在这里我将讨论如何解决维护多个DTO版本对象的麻烦。我开发了一个库beanKnife来自动生成dto。它将创建一个新的类基础-原来的pojo。您可以筛选继承的特性、修改现有特性或添加新特性。您所需要的只是编写一个配置类,库将为您做剩下的事情。配置支持继承功能,因此您可以提取公共部分来简化配置。以下是的示例

@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);

最新更新