对于项目而言, 我们一般会有DAO->Service->Controller分层设计, 这些层次体现了每层的作用, 而层次之间的数据传递对象设计很少被提及, 下面是一个相对完整的数据转换过程:
Table层--(DO对象)-->DAO层--(DO对象)-->Service层--(DTO对象)-->Controller层--(VO对象)-->Web Template层

DO(domain object) 领域对象, 也有一种叫法是 entity object, 个人不推荐使用 entity object这个叫法, 因为Relationship 表也可以有DO 类. 
1. 一般DO类和数据表是一一对应, 属性和字段也是一一对应的.
2. 对于 relationship 表, 一般也需要有一个对应的 DO 类.
3. DO 类中不应该包含复杂的逻辑, 仅仅是一些简单的 setter() 和 getter() 方法.
4. 比如 user 表有一个 deptId 字段, user entity 类必须有一个 deptId 属性, 不推荐有一个对应的 dept Entity 对象, 当然更不应该直接包含 dept entity 对象的属性, 比如 deptName 等.
5. 多个 DO 类之间的关系和数据模型一样, 是满足第三范式.
6. DO类名: 以DO作为后缀, 或者不加DO这样的后缀.

DTO(Data Transfer Object) 数据传输对象:
1. 用于"跨进程或远程"传输, 对象的序列化/反序列化的网络开销较大, 这时就需要加入 DTO 对象, 典型的使用场景是用来封装Rest API接口. 如果是一个单体应用, 专门维护一套DTO类, 成本和收益相比, 引入DTO意义就不大了.
2. DTO 是一个贫血对象, 它不应包含"业务"处理逻辑, 主要是一些属性和getter和setter访问器, 也可包含一些属性重组逻辑.
3. 多个 DTO 类之间的关系一般不再满足第三范式, 而是反范式.
4. DTO类名: 应该以DTO作为后缀.
5. 用于解耦实体对象的存储层和上层, 这包含下面的好处:
(1): 隐藏部分底层表的属性, 以减少网络传输的代价.
(2): 在存储层和上层增加一个隔离, 屏蔽相互之间的影响.
(3): 用来可封装多个DTO对象, 比如user DTO对象可以包含一个Dept DTO对象; 或者将Dept DTO某些常用属性直接flatten到User DTO对象上, 方便上层的使用.  所以, 一般情况下DTO类的数量要比DO类要少. 
(4): 如果没有DTO, 很多时候 Controller 层不得不直接最低层的 Entity 类, 跨层数据对象依赖将使得分层设计大打折扣.

VO(View Object/Value object)视图对象:
专门用于展现层(比如页面展现等).
对于一般的项目, VO 和 DTO 属性基本一致, 没有必要再维护一套VO类.
在前后端分离的大背景下, 即使是大型项目, 也没有必要再维护一套VO类.

Pojo(plain old java object) 普通java对象:
上述的DO/DTO/VO对象都属于Pojo对象. 阿里巴巴规范中有如下要求:
1. Pojo 类属性必须使用包装数据类型, 而不是基本数据类型, 理由是: 数据库中由可能是null值, 如果使用基本类型, 由可能导致自动拆箱异常.
2. 不能为任何属性设定默认值. 理由是: 强制使用者在使用时显式赋值, 任何NPE问题都应由使用者来保证.
3. Pojo 类都必须实现 toString() 方法, 可以使用 IDE 的 source/generate toString() 功能
4. Pojo 类都必须实现 Serializable 接口.

下图能形象展现 DO 类和 DTO 类的区别:

下图以基于角色的权限管理模块为例, 展现了 table/DO对象/DAO层/Service层 对应关系:
(1) table 是采用范式建模, 共三个实体表, 两个关系表.
(2) pojo和table直接对应,
(3) 在Dao层重点关注实体的操作, 所以就只有三个dao对象, 关系表的维护退化到实体操作中.
(4) 在service层, 是面向顶层使用, 重点考虑的是使用如何方便, 一般也都和Dao的类数量一致.

=====================================
object-object mapping framework 清单
=====================================
正如ORM过程, 我们可以手写代码, 也可以使用MyBatis这样的ORM 工具, 对于DO/DTO的转换也是一样, 可以手写代码转换, 也可以使用object-object mapping framework完成自动转换, 当然也能用在对象之间需要深度拷贝的时候.

当前活跃的框架清单:
https://github.com/mapstruct/mapstruct
https://github.com/DozerMapper/dozer
https://github.com/orika-mapper/orika
https://github.com/modelmapper/modelmapper

性能对比:
https://github.com/arey/java-object-mapper-benchmark
https://www.baeldung.com/java-performance-mapping-frameworks

综合考虑性能/流行度/项目活跃程度, mapstruct 明显领先, 不过我这里更推荐使用 orika-mapper, 原因是:
1. 使用 mapstruct的话, IDE中需要引入 mapstruct 编译插件, 我们自己负责编写mapper interface, mapstruct 负责生成mapper实现类的class文件, 工程化成本较高, 可控性较差.
2. orika-mapper 相对更容易推广, mapping 逻辑可控性较强, 性能也算不错.

=====================================
orika-mapper 使用
=====================================
pom.xml 引入依赖

<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>${orika.version}</version>
</dependency>

--------------------------------
简单示例:
--------------------------------
Orika 要求 Pojo 类必须是public级别,否则会在运行时报错 java.lang.IllegalAccessError.

只要src/target属性名一致, Orika 能自动完成所有的属性的深拷贝, 甚至是 List<T> 这样的属性.  需要说明的是, 默认情况下定义的映射规则是双向的, 也就是说, 如果从 target object 也能得到 src object.

//准备 src 对象
PersonSource source = new PersonSource(); //生成 MapperFactory 对象, 然后使用 MapperFactory 对象注册 src/target 映射关系, 本例 src/target 的属性名完全一致, 所以不需要再设置映射关系.
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build(); // MapperFacade 对象负责对象之间的映射
MapperFacade mapper = mapperFactory.getMapperFacade(); //完成DO->DTO的映射
PersonDest destination = mapper.map(source, PersonDest.class);

--------------------------------
更复杂的示例:
--------------------------------
如果src/target属性名不一致, 或者要重组DO属性, 这时需要自定义src/target的映射规则.

//自定义 src/target class的映射规则
mapperFactory.classMap( Source.class, Destination.class)
.field(......) //src/target 双向映射
.fieldAToB(......) //src->target的单向映射
.fieldBToA(......) //target->src的单向映射
.exclude(......) //不考虑指定的属性
.byDefault()
.register();

mapperFactory 除了负责注册src/target 映射规则, 还可以 mapperFactory.getConverterFactory().registerConverter()来注册自定义类型转换器, 比如要将 Date 类型转换为 String类型, 实际项目中, 一般不需要自定义类型转换器, 因为 json 框架也有这样的功能.

//定义一个类型转换器 MyConverter
public class MyConverter extends CustomConverter<Date,MyDate>{} //注册类型转换器
mapperFactory.getConverterFactory().registerConverter(new MyConverter()); // 然后仍然通过 mapper 完成src/target对象的深度拷贝.
MapperFacade mapper = mapperFactory.getMapperFacade();

=====================================
参考
=====================================
https://cloud.tencent.com/developer/article/1110666
http://tech.dianwoda.com/2017/11/04/gao-xing-neng-te-xing-feng-fu-de-beanying-she-gong-ju-orika/?utmsource=tuicool&utmmedium=referral

DTO/DO等POJO对象的使用场景和 orika-mapper 框架的使用的更多相关文章

  1. POJO对象

    POJO(Plain Old Java Objects)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称. 使用POJO名称是为了避免和 EJB混淆起来, 而且简 ...

  2. jface databinding:部分实现POJO对象的监测

    在前一篇博文<jface databinding/PojoBindable实现对POJO对象的支持  >中,已经知道直接对POJO对象进行修改,是不能被绑定的UI组件知道的,在上一篇文章中 ...

  3. 【SpringMVC】SpringMVC系列7之POJO 对象绑定请求参数值

      7.POJO 对象绑定请求参数值 7.1.概述 Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值.而且支持级联属性.如:dept.deptId.dept ...

  4. 自动封装Servlet HttpServletRequest请求成为一个POJO对象

    自己写了个小工具类,将Servlet里面的HttpServletRequest请求封装成为一个POJO对象,可以复习一下Java的反射原理,开发中这个没什么用,毕竟都用MVC框架,框架都自带这种功能, ...

  5. Hibernate(二)——POJO对象的操作

    POJO对象其实就是我们的实体,这篇博客总结一下框架对POJO对象对应数据库主键的生成策略,和一些对POJO对象的简单增删改查的操作.  一,Hibernate框架中主键的生成策略有三种方式: 1,数 ...

  6. 使用 POJO 对象绑定请求参数

    概述 Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值并且支持级联属性.这一特性在日常开发过程中使用频率比较高,开发效率也高,本文主要对 POJO 对象绑定 ...

  7. 漫淡面向对象——POJO对象

    产品或者服务由数据存储和数据计算组成.pojo对象就是用于数据存储.一旦确定后,整个应用或者产品的数据来源就确定.比如一个页面或者功能需要使用什么数据就可以快速找到对应的对象或者通过对象的关系找出来. ...

  8. Hibernate三种状态;query查询;ResultTransformer转换为pojo对象;能够将query语句写在xml中;Criteria查询;ProjectionList总和/f分组等函数

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u010026901/article/details/24256091 Session操作过程中的po ...

  9. mybatis由浅入深day01_8输出映射_8.1resultType输出类型(8.1.1输出简单类型_8.1.2输出pojo对象和pojo列表_8.1.3输出hashmap)

    8 输出映射 8.1 resultType(输出类型) 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功. 如果查询出来的列名和pojo中的属性名全 ...

随机推荐

  1. 如何制作中文Javadoc包,并导入到Eclipse

    原理:使用chm转换工具将chm文件转换为zip文件,导入eclipse中即可. 准备 JDK1.9 API 中文 谷歌翻译版:http://www.pc6.com/softview/SoftView ...

  2. How-to: Do Real-Time Log Analytics with Apache Kafka, Cloudera Search, and Hue

    Cloudera recently announced formal support for Apache Kafka. This simple use case illustrates how to ...

  3. vue中的watch方法 实时同步存储数据

    watch 监视模式里面有个独特的方法handler 注意要加上deep: true.deep为true时,当对象的key值改变时也监听 当值发生改变被watch监视到触发了事件 开始执行handle ...

  4. java 下载word freemaker

    网上有很多优质的博文了,这里这篇博客就是记录一下字自己,写demo的历程,坑和收获 在java程序中下载word 有6中方式,此处省略(嘻嘻),不过大家公认的是 freemaker 和 PageOff ...

  5. Oracle 执行计划(三)-------表连接方式

    SQL FOR TESTING: create table qcb_student_test( student_id number, student_name varchar2(20), studen ...

  6. EF Core 多对多配置

    1.配置2个数据表 T_Authors ,T_Books 2.新建控制台项目,安装EF驱动 PM> Install-Package Pomelo.EntityFrameworkCore.Mysq ...

  7. win10下ElasticSearch5.5.1与head、Kibana、X-Pack、SQL、IK、PINYIN插件的配置安装

    ElasticSearch5.5.1与插件的配置安装 Elasticsearch5.5.1安装: 下载地址https://www.elastic.co/cn/downloads/elasticsear ...

  8. iOS 封装SDK以及封装时bundle文件的处理

    这篇教程的主要目的是解释怎么样在你的iOS工程中创建并使用一个SDK,俗称.a文件. 环境:xcode 9.0 创建一个静态库工程 打开Xcode,点击File\New\Project, 选择iOS\ ...

  9. 毕业季,我的Linux求职之路

    秋招终于告一段落了,本硕的七年求学之路也快画上了句号.回首求职的这一段日子,痛苦并快乐着.感谢所有陪伴着我走过这一段路程的同学,所有的辛酸都值得铭记.求职的过程中在网上看了很多的求职经验,现在想写一篇 ...

  10. 比sun.misc.Encoder()/Decoder()的base64更高效的mxBase64算法

    package com.mxgraph.online; import java.util.Arrays; /** A very fast and memory efficient class to e ...