DTO/DO等POJO对象的使用场景和 orika-mapper 框架的使用
对于项目而言, 我们一般会有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 框架的使用的更多相关文章
- POJO对象
POJO(Plain Old Java Objects)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称. 使用POJO名称是为了避免和 EJB混淆起来, 而且简 ...
- jface databinding:部分实现POJO对象的监测
在前一篇博文<jface databinding/PojoBindable实现对POJO对象的支持 >中,已经知道直接对POJO对象进行修改,是不能被绑定的UI组件知道的,在上一篇文章中 ...
- 【SpringMVC】SpringMVC系列7之POJO 对象绑定请求参数值
7.POJO 对象绑定请求参数值 7.1.概述 Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值.而且支持级联属性.如:dept.deptId.dept ...
- 自动封装Servlet HttpServletRequest请求成为一个POJO对象
自己写了个小工具类,将Servlet里面的HttpServletRequest请求封装成为一个POJO对象,可以复习一下Java的反射原理,开发中这个没什么用,毕竟都用MVC框架,框架都自带这种功能, ...
- Hibernate(二)——POJO对象的操作
POJO对象其实就是我们的实体,这篇博客总结一下框架对POJO对象对应数据库主键的生成策略,和一些对POJO对象的简单增删改查的操作. 一,Hibernate框架中主键的生成策略有三种方式: 1,数 ...
- 使用 POJO 对象绑定请求参数
概述 Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值并且支持级联属性.这一特性在日常开发过程中使用频率比较高,开发效率也高,本文主要对 POJO 对象绑定 ...
- 漫淡面向对象——POJO对象
产品或者服务由数据存储和数据计算组成.pojo对象就是用于数据存储.一旦确定后,整个应用或者产品的数据来源就确定.比如一个页面或者功能需要使用什么数据就可以快速找到对应的对象或者通过对象的关系找出来. ...
- Hibernate三种状态;query查询;ResultTransformer转换为pojo对象;能够将query语句写在xml中;Criteria查询;ProjectionList总和/f分组等函数
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u010026901/article/details/24256091 Session操作过程中的po ...
- mybatis由浅入深day01_8输出映射_8.1resultType输出类型(8.1.1输出简单类型_8.1.2输出pojo对象和pojo列表_8.1.3输出hashmap)
8 输出映射 8.1 resultType(输出类型) 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功. 如果查询出来的列名和pojo中的属性名全 ...
随机推荐
- IIS出现The specified module could not be found的解决方法
1.打开IIS 信息服务,在左侧找到自己的计算机,点右键,选择属性,在主属性中选编辑,打开“目录安全性”选项卡,单击“匿名访问和验证控制”里的“编辑”按钮,在弹出的对话框中确保只选中了“匿名访问 ...
- Django-CRM项目学习(三)-stark的页面展示
开始今日份整理,主要为stark组件的增删改以及model_form组件的使用以及form组件的回顾 1.list_display_link功能 功能:客制化字段进行跳转到指定的页面(编辑页面) 1. ...
- B. Creating the Contest(水题)
直接水过 #include<iostream> #include<algorithm> using namespace std; ; int a[maxn]; int n, u ...
- 11-page分页原理
创建一个分页对象PageBean<T>来存储分页信息+实体信息, 客户端请求时传递分页信息, 服务端将实体信息+分页信息放进分页对象返回给客户端. 实例如下: listStudent.js ...
- SkylineGlobe 7.0.1 & 7.0.2版本Web开发 如何实现土方量计算
土方量计算,或者叫填挖方计算,体积计算,Skyline在很早的版本中就提供了这个的功能. 目前的软件版本,不仅仅可以对地形修改对象进行土方量计算,还可以在FLY工程中导入DEM数字高程模型数据,计算不 ...
- WPF防止界面卡死并显示加载中效果
原文:WPF防止界面卡死并显示加载中效果 网上貌似没有完整的WPF正在加载的例子,所以自己写了一个,希望能帮到有需要的同学 前台: <Window x:Class="WpfApplic ...
- HTML5新增特性
1. 语义化标签 2. 增强型表单 (1)新的表单输入类型 (2)新表单元素 (3)新表单属性 3. 视频和音频 4. Canvas绘图(图形.路径.文本.渐变.图像) 5. SVG绘图 (与Canv ...
- 用es6类封装的图片预加载技术!
1.HTML <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...
- docker(三) 镜像和容器常用命令
一.镜像操作 1.搜索镜像 可以直接在Docker Hub 直接搜索镜像,当然也可以使用命令来搜索. docker search 名称 docker search tomcat 2.拉取镜像 dock ...
- 一.从零认识XAML
一.XMAL的简单了解 XAML读做zaml,,它是WPF中专门用于设计UI的语言,它简单易懂,结构清晰.是一种声明式语言,当你见到一个标签时,就意味着声明了一个对象:对象之间的层级关系要么是并列,要 ...