DO、DTO和VO分层设计的好处

 

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

在平常的项目开发中,VO对应于页面上须要显示的数据(表单),DO对应于数据库中存储的数据(数据表),DTO对应于除两者以外须要进行传递的数据。前端

不少人可能对VO和DTO并非那么熟悉,相反对DO却比较熟悉,那是由于在不少项目中因为种种缘由咱们只使用了DO,缘由可能有如下几种:java

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

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

三、了解DO\DTO\VO之间的区别,可是懒得用。数据库

那么,这里,博主再啰嗦一下为何要引入这么多概念,为何我要建议你们在本身的项目中使用这些实体对象。安全

为何不能只用一个DO

咱们来看这样一个例子。假如咱们的项目中由User这样一个实体。咱们在建立User表的时候通常包含一下属性:app

针对这个实体,咱们一般须要建立一个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 ==  null return 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方法看看还能不能把属性值转换过来,博主已经测试过了,是不能的。

相关文章
相关标签/搜索