2016年10月23日 20:11:03

阅读数:10646

Java中 VO、 PO、DO、DTO、 BO、 QO、DAO、POJO的概念中介绍过Java中的各种模型概念。
在这里简单再总结一下:

在日常的项目开发中,VO对应于页面上需要显示的数据(表单),DO对应于数据库中存储的数据(数据表),DTO对应于除二者之外需要进行传递的数据。

很多人可能对VO和DTO并不是那么熟悉,相反对DO却比较熟悉,那是因为在很多项目中由于种种原因我们只使用了DO,原因可能有以下几种:

1、项目太小,对于一种业务实体,封装成一个DO就够了。

2、并不熟悉DTO、VO,更不知道他们之间的区别。

3、了解DO\DTO\VO之间的区别,但是懒得用。

那么,这里,博主再啰嗦一下为什么要引入这么多概念,为什么我要建议大家在自己的项目中使用这些实体对象。

为什么不能只用一个DO

我们来看这样一个例子。假如我们的项目中由User这样一个实体。我们在创建User表的时候一般包含一下属性:

针对这个实体,我们通常需要创建一个DO类,用来封装这个User实体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public
class  UserDO {
    private
Integer id;                 //唯一主键
    private
Date createdTime;           //创建时间
    private
Date updatedTime;           //最后更新时间
    private
String name;                //姓名
    private
Integer age;                //年龄
    private
String gender;              //性别
    private
String address;             //住址
    private
String password;            //密码
    private
String nickName;            //昵称
    private
Date birthday;              //生日
    private
String politicalStatus;     //政治面貌,1表示群众,2表示团员,3表示党员,4表示其他,100表示未知
    private
Integer companyId;          //公司的ID
    private
Integer status;             //数据状态,1表示可用,0表示不可用
 
    //setter and getter
}

然后,在代码中,从DAO一直到前端展示,我们都通过这个UserDO类的对象来进行数据传输。这样做会有什么问题嘛?

  • 不需要的字段也会传递到前端页面。

    • 如password、createdTime、updatedTime和status这几个字段我们可能在前端根本不需要展示,但是这些字段有可能也会被传递到前端(除非我们在SQL查询的时候没有查询出对应的字段)。这不仅使数据的传输量增大,还可能有安全性问题。
  • 某些字段需要转换,但是无法支持。
    • 对于上面例子中的政治面貌字段,我们在数据库中存储的是数字,但是在前端页面我要展示的是中文描述。这种情况只能在前端通过if/else的方式来分情况展示。
  • 某些字段要展示,但是并不希望出现在数据库中
    • 在User表中我们只保存了这个用户的companyId,需要同时查询company表来查询出该公司的更多详细信息。对于User对象,如果我们想在前端同时展示他所属的公司,希望通过一次查询全都查出来怎么办?有几个简单的方案,第一个是让UserDO中包含一个Company类的属性,通过这个属性来传递。另外一种是把我们希望传到前端的Company的属性也写到UserDO中。但是,如果真的这么做了,那UserDO还能被称作DO了吗?

还有很多问题,这这里就不详细介绍了。

可见,使用一个DO从头用到尾(从数据库到前端页面)并不是一种好的设计。

如何正确的使用DO、DTO、VO

对于上面的例子,我们可以将他设计成以下类。由于模型并不复杂,这里只需要再引入VO就可以了。

UserDO已经和数据库字段一一对应了,这里不需要修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public
class  UserDO {
    private
Integer id;                 //唯一主键
    private
Date createdTime;           //创建时间
    private
Date updatedTime;           //最后更新时间
    private
String name;                //姓名
    private
Integer age;                //年龄
    private
String gender;              //性别
    private
String address;             //住址
    private
String password;            //密码
    private
String nickName;            //昵称
    private
Date birthday;              //生日
    private
String education;           //学历
    private
String politicalStatus;     //政治面貌,1表示群众,2表示团员,3表示党员,4表示其他,100表示未知
    private
Integer companyId;          //公司的ID
    private
Integer status;             //数据状态,1表示可用,0表示不可用
 
    //setter and getter
}

引入UserVO,用于封装传递到前端需要展示的字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public
class  UserVO {
    private
Integer id;                 //唯一主键
    private
String name;                //姓名
    private
Integer age;                //年龄
    private
String gender;              //性别
    private
String address;             //住址
    private
String nickName;            //昵称
    private
Date birthday;              //生日
    private
String education;           //学历
    private
String politicalStatus;     //政治面貌,群众、团员、党员等
    private
Company company;            //公司
 
    //setter and getter
}

UserVO中只包含了展示所需要的字段,并不需要展示的字段在这里不需要包含。

在引入了这个类之后,我们就可在进行数据库查询的时候使用UserDO,然后再需要传递到前端的时候把DO转换成VO。

总结

看到这里,你可能已经发现,UserDO和UserVO中包含了大量的相同字段。难道真的有必要再单独设计个VO嘛?我可以明确告诉你的是,当你的系统越来越大,表中的字段越来越多的时候,使用DO\DTO\VO等概念进行分层处理是绝对有好处的。至于如何进行有效的在不同的实体类间进行转换是我接下来要介绍的。

优雅的将DO转换成VO

Dozer 是一个对象转换工具。

Dozer可以在JavaBean到JavaBean之间进行递归数据复制,并且这些JavaBean可以是不同的复杂的类型。
所有的mapping,Dozer将会很直接的将名称相同的fields进行复制,如果field名不同,或者有特别的对应要求,则可以在xml中进行定义。

前面我们介绍的DO\DTO\VO不就是JavaBean嘛。正好可以使用Dozer进行转换呀。
除了使用Dozer,当然你还由其他选择:

典型的解决方案就是手动拷贝,弊端很明显,代码中充斥大量Set 和Get方法,真正的业务被埋藏值与值的拷贝之中。

另一种方案就是使用BeanUtil,但BeanUtil不够很好的灵活性,又时候还不得不手动拷贝。Dozer可以灵活的对对象进行转换,且使用简单。

Dozer 支持的转换类型

Primitive 基本数据类型 , 后面带 Wrapper 是包装类 Complex Type 是复杂类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
•   Primitive to Primitive Wrapper
•   Primitive to Custom Wrapper
•   Primitive Wrapper to Primitive Wrapper
•   Primitive to Primitive
•   Complex Type to Complex Type
•   String to Primitive
•   String to Primitive Wrapper
•   String to Complex Type if
the Complex Type contains a String constructor
•   String 到复杂类型 , 如果复杂类型包含一个 String 类型的构造器
•   String to Map
•   Collection to Collection
•   Collection to Array
•   Map to Complex Type
•   Map to Custom Map Type
•   Enum to Enum
•   Each of these can be mapped to one another: java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp, java.util.Calendar, java.util.GregorianCalendar
•   String to any of the supported Date/Calendar Objects.
•   Objects containing a toString() method that produces a long
representing time in (ms) to any supported Date/Calendar object.

在普通Java项目中使用Dozer

在pom.xml中增加依赖

1
2
3
4
5
<dependency>
  <groupId>net.sf.dozer</groupId>
  <artifactId>dozer</artifactId>
  <version>5.5.1</version>
</dependency>

使用Dozer进行类转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public
class  Main {
 
    public
static  void  main(String[] args) {
        DozerBeanMapper mapper = new
DozerBeanMapper();
        UserDO userDO = new
UserDO();
        userDO.setName("hollis");
        userDO.setAddress("hz");
        userDO.setAge(25);
        userDO.setCompanyId(1);
        userDO.setBirthday(new
Date());
        userDO.setGender("male");
        userDO.setEducation("1");
        userDO.setNickName("hollis");
        userDO.setPoliticalStatus("3");
        UserVO userVO = (UserVO) mapper.map(userDO, UserVO.class);
        System.out.println(userVO);
    }
}

特殊的字段转换
在使用mapper进行转换前,设置一个或多个mapping文件

1
2
3
List myMappingFiles = new
ArrayList();   
myMappingFiles.add("dozer-mapping.xml");   
mapper.setMappingFiles(myMappingFiles);

配置dozer-mapping.xml文件

数据类型不一致,或者名称不相同或者有级联关系等情况下,可以通过文件dozer-mapping.xml来进行定制Bean的转换

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0"
encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net
 
http://dozer.sourceforge.net/schema/beanmapping.xsd">
 
    <mapping>
        <class-a>com.hollis.lab.UserDO</class-a>
        <class-b>com.hollis.lab.UserVO</class-b>
    </mapping>
</mappings>

在JavaWeb项目中使用Dozer

在pom.xml中增加依赖

1
2
3
4
5
<dependency>
  <groupId>net.sf.dozer</groupId>
  <artifactId>dozer</artifactId>
  <version>5.5.1</version>
</dependency>

使用Spring集成dozer

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0"
encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="baseMapper"
class="org.dozer.spring.DozerBeanMapperFactoryBean">
        <property name="mappingFiles">
            <list>
                <value>classpath:mapping/dozer-mapping.xml</value>
            </list>
        </property>
    </bean>
</beans>

使用baseMapper进行Bean的转换

1
2
3
4
5
6
7
8
@Autowired
private
Mapper baseMapper;
private
UserVO doToVo(UserDO userDO){
    if(userDO == nullreturn
null;
    UserVO vo = baseMapper.map(userDO, UserVO.getClass());
    if(userDO.getCompanyId != null) getCompany(vo);
    return
vo;
}

通过以上的代码加配置,我们就实现了从DO转换到VO的部分操作,之所以说是部分操作,是因为我们在dozer-mapping.xml并没有做多余的配置,只是使用dozer将DO中和VO中共有的属性转换了过来。对于其他的类型不同或者名称不同等的转换可以参考官网例子通过设置dozer-mapping.xml文件来实现。

上面还有一个getCompany()没有实现。这个方法其实就是通过companyId查询出company实体然后在赋值给UserVO中的company属性。

在使用了dozer之后,我们可以把UserDO中的部分属性赋值到UserVO中,其实,转化的过程是通过调用UserDO中的getter方法和UserVO中的setter方法来实现的。读者可以做个实验,对于UserVO中的部分属性不写Setter方法看看还能不能把属性值转换过来,博主已经测试过了,是不能的。

DO、DTO和VO分层设计的好处的更多相关文章

  1. VO(DTO)模式在架构设计中是否需要

    DTO(VO):也就是一般意义上的VO,封装后的对象.一般用于Web层—Service层间的数据传输入. PO:也就是一般概念上的Domain Object,如hibernate 中的Entity.一 ...

  2. JavaWeb单体项目的分层设计与实现

    1.概述 为什么要把一个完整的项目(Project)按层拆分成多个模块(Module)? 1)使项目层次更加的清晰: 2)提高代码的复用性: 3)细化分工: 4)解耦. 是不是听起来很高大尚,今天就简 ...

  3. ABP分层设计

    ABP分层设计 一.为什么要分层 分层架构是所有架构的鼻祖,分层的作用就是隔离,不过,我们有时候有个误解,就是把层和程序集对应起来,就比如简单三层架构中,在你的解决方案中,一般会有三个程序集项目:XX ...

  4. 什么是JavaBean、bean? 什么是POJO、PO、DTO、VO、BO ? 什么是EJB、EntityBean?

    什么是JavaBean.bean?  什么是POJO.PO.DTO.VO.BO ?  什么是EJB.EntityBean?   前言:   在Java开发中经常遇到这些概念问题,有的可能理解混淆,有的 ...

  5. linux设备驱动的分层设计思想--input子系统及RTC

    转自:linux设备驱动的分层设计思想 宋宝华 http://blog.csdn.net/21cnbao/article/details/5615493 1.1 设备驱动核心层和例化 在面向对象的程序 ...

  6. Java中PO、DO、DTO、 VO、 BO、POJO 、DAO、TO的概念

    1.  PO(persistant object) 持久对象 在 O/R 映射的时候出现的概念,如果没有 O/R 映射,没有这个概念存在了. 通常对应数据模型 ( 数据库 ), 本身还有部分业务逻辑的 ...

  7. Web自动化框架之五一套完整demo的点点滴滴(excel功能案例参数化+业务功能分层设计+mysql数据存储封装+截图+日志+测试报告+对接缺陷管理系统+自动编译部署环境+自动验证false、error案例)

    标题很大,想说的很多,不知道从那开始~~直接步入正题吧 个人也是由于公司的人员的现状和项目的特殊情况,今年年中后开始折腾web自动化这块:整这个原因很简单,就是想能让自己偷点懒.也让减轻一点同事的苦力 ...

  8. robot framework 使用四:分层设计和截图以及注意事项

    再说一下眼下的主要环境信息和版本号: 操作系统:win7 64位 python版本号:2.7.6 RIDE版本号:1.2.3 selenium2library:1.5.0 selenium:2.40. ...

  9. RSF 分布式 RPC 服务框架的分层设计

    RSF 是个什么东西? 一个高可用.高性能.轻量级的分布式服务框架.支持容灾.负载均衡.集群.一个典型的应用场景是,将同一个服务部署在多个Server上提供 request.response 消息通知 ...

随机推荐

  1. Python抓取天气信息并存储原来这么简单

    我们计划抓取的数据:杭州的天气信息 实现数据抓取的逻辑:使用python 请求 URL,会返回对应的 HTML 信息,我们解析 html,获得自己需要的数据.(很简单的逻辑) 第一步:创建 Pytho ...

  2. Python 字符编码及其文件操作

    本章节内容导航: 1.字符编码:人识别的语言与机器机器识别的语言转化的媒介. 2.字符与字节:字符占多少个字节,字符串转化 3.文件操作:操作硬盘中的一块区域:读写操作 注:浅拷贝与深拷贝 用法: d ...

  3. CentOS_7升级系统内核

    最近,在虚拟机中安装docker成功之后,尝试运行docker run hello-world时出现以下错误: $ sudo docker run hello-world Unable to find ...

  4. poj-2516(最小费用流)

    题意:有n个商店,每个商店有k种货物,每个货物需要a[n][k]个,有m个仓库,每个仓库也有k种货物,每个货物有b[m][k]个,然后k个矩阵,每个矩阵都是n*m的,第i行第j列表示从仓库j到商店i每 ...

  5. 设置QPushbutton按钮背景、鼠标滑过状态、鼠标点击后状态用法

    1.1当要设置QPushbutton按钮背景,字体颜色,鼠标滑过状态,鼠标单击后状态时,可以用QSS来设置,具体的代码如下:     QPushButton *allSelect = new QPus ...

  6. matplotlib绘图总结

    本文作为学习过程中对matplotlib一些常用知识点的整理,方便查找. 类MATLAB API 最简单的入门是从类 MATLAB API 开始,它被设计成兼容 MATLAB 绘图函数. from p ...

  7. MAC OS进阶必看——这10个技巧让你秒变MAC达人

    文章内容及图片来源于:什么值得买,如果涉及版权问题,请联系作者删除 文章收录于:风云社区(提供上千款各类mac软件的下载) 使用mac系统也有好几个年头,出色的办公效率以及越来越广的兼容性让mac成为 ...

  8. CMDB服务器管理系统【s5day89】:采集资产之整合资产

    1.业务逻辑单独写 1.代码目录结构 2.client.py from src.plugins import PluginManager class BaseClient(object): def p ...

  9. React 记录(5)

    React文档:https://www.reactjscn.com/docs/state-and-lifecycle.html 慢慢学习:对照教程文档,逐句猜解,截图 React官网:https:// ...

  10. 跨站请求伪造 | ajax

    一.简介 django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成.而对于django中设置防跨站请求伪造功 ...