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方法看看還能不能把屬性值轉換過來,博主已經測試過了,是不能的。

相關文章
相關標籤/搜索