`

浅析VO、DTO、DO、PO的概念、区别和用处

阅读更多

概念:

VO View Object ): 视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。

DTO Data Transfer Object ): 数据传输对象,这个概念来源于 J2EE 的设计模式,原来的目的是为了 EJB 的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。

DO Domain Object ): 领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。

PO Persistent Object ): 持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应 PO 的一个(或若干个)属性。  

 

VO DTO 的区别

        大家可能会有个疑问(在笔者参与的项目中,很多程序员也有相同的疑惑):既然 DTO 是展示层与服务层之间传递数据的对象,为什么还需要一个 VO 呢?对!对于绝大部分的应用场景来说, DTO VO 的属性值基本是一致的,而且他们通常都是 POJO ,因此没必要多此一举,但不要忘记这是实现层面的思维,对于设计层面来说,概念上还是应该存在 VO DTO ,因为两者有着本质的区别, DTO 代表服务层需要接收的数据和返回的数据,而 VO 代表展示层需要显示的数据。

        用一个例子来说明可能会比较容易理解:例如服务层有一个 getUser 的方法返回一个系统用户,其中有一个属性是 gender( 性别 ) ,对于服务层来说,它只从语义上定义: 1- 男性, 2- 女性, 0- 未 指定,而对于展示层来说,它可能需要用“帅哥”代表男性,用“美女”代表女性,用“秘密”代表未指定。说到这里,可能你还会反驳,在服务层直接就返回“帅 哥美女”不就行了吗?对于大部分应用来说,这不是问题,但设想一下,如果需求允许客户可以定制风格,而不同风格对于“性别”的表现方式不一样,又或者这个 服务同时供多个客户端使用(不同门户),而不同的客户端对于表现层的要求有所不同,那么,问题就来了。再者,回到设计层面上分析,从职责单一原则来看,服 务层只负责业务,与具体的表现形式无关,因此,它返回的 DTO ,不应该出现与表现形式的耦合。

        理论归理论,这到底还是分析设计层面的思维,是否在实现层面必须这样做呢?一刀切的做法往往会得不偿失,下面我马上会分析应用中如何做出正确的选择。

 

VO DTO 的应用

        上面只是用了一个简单的例子来说明 VO DTO 在概念上的区别,本节将会告诉你如何在应用中做出正确的选择。

        在以下才场景中,我们可以考虑把 VO DTO 二合为一(注意:是实现层面):

  • 当需求非常清晰稳定,而且客户端很明确只有一个的时候,没有必要把 VO DTO 区分开来,这时候 VO 可以退隐,用一个 DTO 即可,为什么是 VO 退隐而不是 DTO ?回到设计层面,服务层的职责依然不应该与展示层耦合,所以,对于前面的例子,你很容易理解, DTO 对于“性别”来说,依然不能用“帅哥美女”,这个转换应该依赖于页面的脚本(如 JavaScript )或其他机制( JSTL EL CSS
  •   即使客户端可以进行定制,或者存在多个不同的客户端,如果客户端能够用某种技术(脚本或其他机制)实现转换,同样可以让 VO 退隐

 

以下场景需要优先考虑 VO DTO 并存:

  • 上述场景的反面场景
  • 因为某种技术原因,比如某个框架(如 Flex )提供自动把 POJO 转换为 UI 中某些 Field 时,可以考虑在实现层面定义出 VO ,这个权衡完全取决于使用框架的自动转换能力带来的开发和维护效率提升与设计多一个 VO 所多做的事情带来的开发和维护效率的下降之间的比对。
  • 如果页面出现一个“大视图”,而组成这个大视图的所有数据需要调用多个服务,返回多个 DTO 来组装(当然,这同样可以通过服务层提供一次性返回一个大视图的 DTO 来取代,但在服务层提供一个这样的方法是否合适,需要在设计层面进行权衡)。

 

DTO DO 的区别

        首先是概念上的区别, DTO 是展示层和服务层之间的数据传输对象(可以认为是两者之间的协议),而 DO 是对现实世界各种业务角色的抽象,这就引出了两者在数据上的区别,例如 UserInfo User (对于 DTO DO 的命名规则,请参见笔者前面的一篇博文),对于一个 getUser 方法来说,本质上它永远不应该返回用户的密码,因此 UserInfo 至少比 User 少一个 password 的数据。而在领域驱动设计中,正如第一篇系列文章所说, DO 不是简单的 POJO ,它具有领域业务逻辑。

 

DTO DO 的应用

        从上一节的例子中,细心的读者可能会发现问题:既然 getUser 方法返回的 UserInfo 不应该包含 password ,那么就不应该存在 password 这个属性定义,但如果同时有一个 createUser 的方法,传入的 UserInfo 需要包含用户的 password ,怎么办?在设计层面,展示层向服务层传递的 DTO 与服务层返回给展示层的 DTO 在概念上是不同的,但在实现层面,我们通常很少会这样做(定义两个 UserInfo ,甚至更多),因为这样做并不见得很明智,我们完全可以设计一个完全兼容的 DTO ,在服务层接收数据的时候,不该由展示层设置的属性(如订单的总价应该由其单价、数量、折扣等决定),无论展示层是否设置,服务层都一概忽略,而在服务层返回数据时,不该返回的数据(如用户密码),就不设置对应的属性。

        对于 DO 来说,还有一点需要说明:为什么不在服务层中直接返回 DO 呢?这样可以省去 DTO 的编码和转换工作,原因如下:

  • 两者在本质上的区别可能导致彼此并不一一对应,一个 DTO 可能对应多个 DO ,反之亦然,甚至两者存在多对多的关系。
  • DO 具有一些不应该让展示层知道的数据
  •   DO 具有业务方法,如果直接把 DO 传递给展示层,展示层的代码就可以绕过服务层直接调用它不应该访问的操作,对于基于 AOP 拦截服务层来进行访问控制的机制来说,这问题尤为突出,而在展示层调用 DO 的业务方法也会因为事务的问题,让事务难以控制。
  •   对于某些 ORM 框架(如 Hibernate )来说,通常会使用“延迟加载”技术,如果直接把 DO 暴露给展示层,对于大部分情况,展示层不在事务范围之内( Open session in view 在大部分情况下不是一种值得推崇的设计),如果其尝试在 Session 关闭的情况下获取一个未加载的关联对象,会出现运行时异常(对于 Hibernate 来说,就是 LazyInitiliaztionException )。
  • 从设计层面来说,展示层依赖于服务层,服务层依赖于领域层,如果把 DO 暴露出去,就会导致展示层直接依赖于领域层,这虽然依然是单向依赖,但这种跨层依赖会导致不必要的耦合。

 

对于 DTO 来说,也有一点必须进行说明,就是 DTO 应该是一个“扁平的二维对象”,举个例子来说明:如果 User 会关联若干个其他实体(例如 Address Account Region 等),那么 getUser() 返回的 UserInfo ,是否就需要把其关联的对象的 DTO 都一并返回呢?如果这样的话,必然导致数据传输量的大增,对于分布式应用来说,由于涉及数据在网络上的传输、序列化和反序列化,这种设计更不可接受。如果 getUser 除了要返回 User 的基本信息外,还需要返回一个 AccountId AccountName RegionId RegionName ,那么,请把这些属性定义到 UserInfo 中,把一个“立体”的对象树“压扁”成一个“扁平的二维对象”,笔者目前参与的项目是一个分布式系统,该系统不管三七二十一,把一个对象的所有关联对象都转换为相同结构的 DTO 对象树并返回,导致性能非常的慢。

 

 

DO PO 的区别

        DO PO 在绝大部分情况下是一一对应的, PO 是只含有 get/set 方法的 POJO ,但某些场景还是能反映出两者在概念上存在本质的区别:

  • DO 在某些场景下不需要进行显式的持久化,例如利用策略模式设计的商品折扣策略,会衍生出折扣策略的接口和不同折扣策略实现类,这些折扣策略实现类可以算是 DO ,但它们只驻留在静态内存,不需要持久化到持久层,因此,这类 DO 是不存在对应的 PO 的。
  • 同样的道理,某些场景下, PO 也没有对应的 DO ,例如老师 Teacher 和学生 Student 存在多对多的关系,在关系数据库中,这种关系需要表现为一个中间表,也就对应有一个 TeacherAndStudentPO PO ,但这个 PO 在业务领域没有任何现实的意义,它完全不能与任何 DO 对应上。这里要特别声明,并不是所有多对多关系都没有业务含义,这跟具体业务场景有关,例如:两个 PO 之间的关系会影响具体业务,并且这种关系存在多种类型,那么这种多对多关系也应该表现为一个 DO ,又如:“角色”与“资源”之间存在多对多关系,而这种关系很明显会表现为一个 DO ——“权限”。
  • 某些情况下,为了某种持久化策略或者性能的考虑,一个 PO 可能对应多个 DO ,反之亦然。例如客户 Customer 有其联系信息 Contacts ,这里是两个一对一关系的 DO ,但可能出于性能的考虑(极端情况,权作举例),为了减少数据库的连接查询操作,把 Customer Contacts 两个 DO 数据合并到一张数据表中。反过来,如果一本图书 Book ,有一个属性是封面 cover ,但该属性是一副图片的二进制数据,而某些查询操作不希望把 cover 一并加载,从而减轻磁盘 IO 开销,同时假设 ORM 框架不支持属性级别的延迟加载,那么就需要考虑把 cover 独立到一张数据表中去,这样就形成一个 DO 对应对个 PO 的情况。
  • PO 的某些属性值对于 DO 没有任何意义,这些属性值可能是为了解决某些持久化策略而存在的数据,例如为了实现“乐观锁”, PO 存在一个 version 的属性,这个 version 对于 DO 来说是没有任何业务意义的,它不应该在 DO 中存在。同理, DO 中也可能存在不需要持久化的属性。

 

DO PO 的应用

        由于 ORM 框架的功能非常强大而大行其道,而且 JavaEE 也推出了 JPA 规范,现在的业务应用开发,基本上不需要区分 DO PO PO 完全可以通过 JPA Hibernate Annotations/hbm 隐藏在 DO 之中。虽然如此,但有些问题我们还必须注意:

  • 对于 DO 中不需要持久化的属性,需要通过 ORM 显式的声明,如:在 JPA 中,可以利用 @Transient 声明。
  • 对于 PO 中为了某种持久化策略而存在的属性,例如 version ,由于 DO PO 合并了,必须在 DO 中声明,但由于这个属性对 DO 是没有任何业务意义的,需要让该属性对外隐藏起来,最常见的做法是把该属性的 get/set 方法私有化,甚至不提供 get/set 方法,但对于 Hibernate 来说,这需要特别注意,由于 Hibernate 从数据库读取数据转换为 DO 时,是利用反射机制先调用 DO 的空参数构造函数构造 DO 实例,然后再利用 JavaBean 的规范反射出 set 方法来为每个属性设值,如果不显式声明 set 方法,或把 set 方法设置为 private ,都会导致 Hibernate 无法初始化 DO ,从而出现运行时异常,可行的做法是把属性的 set 方法设置为 protected
  •   对于一个 DO 对应多个 PO ,或者一个 PO 对应多个 DO 的场景,以及属性级别的延迟加载, Hibernate 都提供了很好的支持,请参考 Hibnate 的相关资料。

分享到:
评论

相关推荐

    po vo dto bo to

    ### Java中的PO、VO、TO、BO、DAO与POJO详解 #### 一、概述 在Java企业级应用开发中,经常会遇到各种类型的对象,如PO、VO、TO、BO、DAO以及POJO等。这些对象各有侧重,在系统架构的不同层次扮演着不同的角色。...

    java术语(PO/POJO/VO/BO/DAO/DTO)

    在Java开发中,我们经常会遇到各种各样...例如,PO和DAO一起用于数据持久化,BO处理业务逻辑,而VO和DTO则专注于数据的传输和展示。了解这些术语,对于提升代码质量、提高团队沟通效率以及优化系统设计都具有重要意义。

    JAVA中的POJO、VO、PO、DO、DTO都是什么?有什么区别?

    以下是关于POJO、VO、PO、DO、DTO的详细解释及其区别。 1. POJO(Plain Old Java Object):POJO是一个通用术语,指没有特定框架限制的简单Java对象。它通常包含了业务逻辑和数据属性,不包含任何特定框架的注解或...

    VO DTO 实体类的区别 java

    java简单基础 需要的了解一下 我们大家还是有必要去区分的

    VO / DTO / BO / ORM DAO entity DO PO/ POJO(分层领域模型规约)整理

    通过对VO、DTO、BO、ORM、DAO、Entity、DO、PO 和 POJO 的深入分析,我们可以看出这些概念在不同的层次和场景下发挥着各自的作用。正确地使用这些模型能够帮助开发者更好地组织代码,提高系统的可维护性和扩展性。在...

    J2EE基础知识之DTO,VO,PO,DO等定义

    J2EE基础知识之DTO,VO,PO,DO等定义J2EE基础知识之DTO,VO,PO,DO等定义J2EE基础知识之DTO,VO,PO,DO等定义

    Java中 PO VO BO DTO DAO 和 POJO 关系图

    Java中 PO VO BO DTO DAO 和 POJO 关系图

    扩展MyBatisPlus代码生成器实现自定义源码生成,可生成前端页面、vo对象、dto对象等代码

    然而,对于VO对象、DTO对象以及前端Vue页面等非预置的代码生成需求,就需要我们对默认的代码生成器进行扩展和定制。 在MyBatisPlus 3.5.3版本中,代码生成器主要有两种类:`AutoGenerator`和`FastAutoGenerator`。`...

    视图对象(VO、DTO)的应用!

    在实际开发中,我们可以通过一些自动化工具,如Dozer、MapStruct等,来简化VO和DTO之间的数据映射过程,减少手动编写转换代码的工作量。同时,为了提高效率和减少内存占用,还可以考虑使用流式API处理大数据量的DTO...

    vo bo po dto dao区别

    本人以前搞不懂这些o的区别,特意查找资料总结了一下,希望也可以帮到其他人

    Java Web开发 之VO、PO、DTO等收集

    J2EE开发人员必须知道 Java Web开发中VO、PO、DTO、POJO代表含义。

    java中PO、VO、BO、POJO、DAO、DTO、TO、QO、Bean、conn的理解

    在 Java 中,PO、VO、BO、POJO、DAO、DTO、TO、QO 等概念都是 O/R Mapping 中的重要概念。它们之间的关系是:BO 封装业务逻辑,调用 DAO 方法,结合 PO 和 VO 进行业务操作。DAO 中包含了各种数据库的操作方法,通过...

    Java的(PO,VO,TO,BO,DAO,POJO)解释

    VO和PO相似,但VO更像是抽象出的业务对象。VO在Web上传递中也可以和DTO(数据传输对象)混淆,但它们之间有一些细微的区别。 TO(Transfer Object)数据传输对象 TO是Java中的数据传输对象,在应用程序不同关系之间...

    别再用 BeanUtils 了,这款 PO VO DTO 转换神器不香么?.zip

    计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料计算机技术、IT咨询、人工智能AI理论介绍,学习...

    Java的几种对象(PO-VO-DAO-BO-POJO)解释

    在O/R Mapping的上下文中,VO和PO具有以下特点: 1. **VO vs PO**: - **VO**(值对象):通常用new关键字创建,并由垃圾回收器(GC)回收。它是业务逻辑使用的对象,主要目的是为数据提供存储位置。 - **PO**...

    java实现相同属性名称及相似类型的pojo、dto、vo等互转操作

    Java 实现相同属性名称及相似类型的 POJO、DTO、VO 等互转操作 Java 实现相同属性名称及相似类型的 POJO、DTO、VO 等互转操作是 Java 编程中一个常见的问题。POJO、DTO、VO 等都是 Java 中常用的类别,但是它们之间...

    eclipse插件,根据数据库表自动生成DTO(pojo)插件

    在Java开发中,数据传输对象(DTO)和持久化对象(POJO)是常见的概念,它们用于在不同层之间传递数据。手动创建这些类可能会耗费大量时间,特别是在处理大量数据库表时。因此,"eclipse插件,根据数据库表自动生成...

    FreeMarker模板包括dto、dao、rowmapper、bo、service

    FreeMarker通用模板。... 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据

    POBOVODTOPOJODAO.zip_dto_java dto dao_java vo_qovod

    PO可以严格对应数据库表,一张表对映一个PO。... VO:value object值对象、view object视图对象 PO:持久对象 QO:查询对象 DAO:数据访问对象——同时还有DAO模式 DTO:数据传输对象——同时还有DTO模式

Global site tag (gtag.js) - Google Analytics