基於SSH2框架構建JavaEE應用程序(2) – JAVA編程語言程序開發技術文章

四、數據傳輸、數據模型與Dozer
數據傳輸是程序員實現各種功能時刻需要考慮的問題,從數據模型的建立,到數據模型的轉換,從數據的合法性驗證,到數據類型的轉化,我們要時刻小心,精心設計與組織。數據模型與數據傳輸可簡單可復雜,完全取決於設計者的經驗與意圖,當然,項目的規模也是我們應該考慮的因素,一個小型項目實在沒必要將問題復雜化。


 


 


我們首先考慮數據從視圖(View)傳輸到數據庫(DB)的數據模型變化過程。用戶從界面輸入數據,此時,數據毫無結構可言,是零散的、無組織的,數據提交到控制器後,一方面考慮到OOP的嚴謹性,另一方面考慮到數據的封裝,會將零散的無組織的數據封裝成Value Object(簡稱VO)對象,VO就是JavaBean,並傳送到業務類中,在數據模型比較復雜的情況下,業務類的方法參數和返回值都應該是VO或VO的集合,VO轉換成PO(Persistence Object)後傳送到DAO,DAO調用Hibernate API持久化數據。簡單來說,業務類對外暴露的數據模型是VO,DAO對外暴露的數據模型是PO。


 


 


數據從數據庫傳遞到視圖層的數據模型變化恰恰相反,Hibernate將數據庫中的記錄轉換成PO,PO傳遞給業務類後轉換成VO,VO被傳送到視圖層進行顯示處理。



 



以上轉換過程如下圖:


 


為什麼同時需要PO和VO?前面說過,這不是必須的,如果項目比較小,直接使用PO就行瞭。但PO有如下缺點:


 


 


v PO反應瞭數據庫的物理模型,向外暴露PO存在一定的風險;


v PO過於僵化,無法適應變化莫測的業務需求;


v PO在持久化狀態下與數據庫同步,可能導致數據意外修改;


v PO將導致程序缺乏健壯性。


 


 


而VO沒有PO的缺點,相對而言,VO更加靈活,能適應各種需求的變化,下面是PO和VO的區別:


 


 


v VO是用new關鍵字創建,由GC回收的。PO則是向數據庫中添加新數據時創建,刪除數據庫中數據時削除的。並且它隻能存活在一個數據庫連接中,斷開連接即被銷毀;


v VO是值對象,精確點講它是業務對象,是存活在業務層的,是業務邏輯使用的,它存活的目的就是為數據提供一個生存的地方。PO則是有狀態的,每個屬性代表其當前的狀態。它是物理數據的對象表示。使用它,可以使我們的程序與物理數據解耦,並且可以簡化對象數據與物理數據之間的轉換;


v VO的屬性是根據當前業務的不同而不同的,也就是說,它的每一個屬性都一一對應當前業務邏輯所需要的數據的名稱。PO的屬性是跟數據庫表的字段一一對應的;


v PO一般隻有一個,但對應的VO可能有多個。


 


 


我們舉一個簡單的例子來說明VO與PO的區別:比如要實現用戶註冊與登陸的功能,在物理模型中創建一個用戶表,字段分別為用戶ID(標識列)、用戶名、密碼、註冊日期等等,定義PO時應該有這四個字段的映射屬性。現在,我們來實現用戶註冊這一功能,為瞭接收用戶輸入的註冊信息,必須定義VO,註冊用戶需要輸入的信息有:用戶名、密碼1、密碼2,這正好是VO的屬性。而實現用戶登陸功能時,用戶需要輸入的信息隻有用戶名和密碼,所以,該VO的屬性隻有兩個:用戶名、密碼。可以看出,PO側重於物理模型,而VO則更關註用戶的實際需求。如下圖所示:


 



因為數據傳輸是雙向的,所以VO和PO之間存在相互轉換的問題,這會給編程帶來麻煩,我們總是要通過getter方法取出數據,再通過setter方法給對方屬性賦值,在屬性很多的情況下,讓人深感繁瑣,而Dozer工具能簡化這個問題。


Dozer(http://dozer.sourceforge.net/)是一個JavaBean映射工具,能實現對象屬性值之間的相互賦值,Dozer支持簡單類型映射、復合類型映射、雙向映射以及遞歸映射,默認情況下,Dozer能實現類型相同、名字相同的屬性之間的賦值,如果屬性名不同,則必須在xml(dozerBeanMapping.xml)配置文件中指定,如果不必為JavaBean的每個屬性賦值,也可以在xml中指定。Xml的定義可以參考http://dozer.sourceforge.net/dtd/dozerbeanmapping.dtd,典型的結構如下:


 


 


<?xml version=”1.0″ encoding=”UTF-8″?>


<!DOCTYPE mappings PUBLIC “-//DOZER//DTD MAPPINGS//EN”


“http://dozer.sourceforge.net/dtd/dozerbeanmapping.dtd”>


<mappings>


<configuration>


<stop-on-errors>false</stop-on-errors>


<date-format>MM/dd/yyyy HH:mm</date-format>


<wildcard>true</wildcard>


</configuration>


<mapping>


<class-a>com.denny_blue.dozerdemo.Book</class-a>


<class-b>com.denny_blue.dozerdemo.CookBook</class-b>


<field>


<a>name</a>


<b>bookName</b>


</field>


<field>


<a>author</a>


<b>author</b>


</field>


</mapping>


</mappings>


 


 


<class-a>指定所要復制的源對象,<class-b>復制的目標對象,<a>源對象的屬性名, <b>目標對象的屬性名。wildcard默認為true,在此時默認對所有屬性進行map,如果為false,則隻對在xml文件中配置的屬性進行map。


 


 


其中,Book和CookBook類分別定義如下:


 


 


public class Book{


public Book(){


 


}


public void setAuthor(String author) {


this.author = author;


}


public String getAuthor() {


return (this.author);


}


public void setName(String name){


this.name=name;


}


public String getName(){


return this.name;


}


}



public class CookBook {


private String bookName;


private String author;



public CookBook(){}


public String getBookName() {


return (this.bookName);


}


public void setBookName(String bookName) {


this.bookName = bookName;


}


public String getAuthor() {


return (this.author);


}


public void setAuthor(String author) {


this.author = author;


}


}


 


 


以下是測試代碼:


 


 


Book book1=new Book();


book1.setAuthor(“dennis”);


book1.setName(“dozer demo”);


DozerBeanMapper mapper=new DozerBeanMapper();


book2=(Book)mapper.map(book1,com.denny_blue.dozerdemo.Book.class);


CookBook cookBook=new CookBook();


List myMappingFiles = new ArrayList();


myMappingFiles.add(“dozerBeanMapping.xml”);


mapper.setMappingFiles(myMappingFiles);


cookBook=(CookBook)mapper.map(book1,CookBook.class);


System.out.println(“cookBooks name:”+   cookBook.getBookName()+”     cookBooks author:”+


  cookBook.getAuthor());


}


 


 


通過mapper.setMappingFiles()設置映射文件,可以添加多個配置文件,也可以把所有的映射寫在一個配置文件裡面。這裡介紹的隻是最基本的使用方法,為瞭實現Dozer的模塊化應用,我專門寫瞭一個VoPoConverter類簡化Dozer的調用。


 


 


package com.aptech.util;



import java.util.ArrayList;


import java.util.List;



import org.dozer.DozerBeanMapper;


import org.dozer.Mapper;



/**


 * VO和PO相互轉換的類


 */


public class VoPoConverter {



/**


 * VO和PO之間相互轉換,將源對象的同名屬性復制目標對象中


 * 前提:源對象和目標對象都必須存在


 * @param src 源對象


 * @param desc 目標對象


 */


public static void copyProperties(Object src, Object desc){


if(src == null) return;



Mapper mapper = new DozerBeanMapper();


mapper.map(src, desc);


}



/**


 * VO和PO之間相互轉換,先創建對象,再將源對象的同名屬性復制目標對象中


 * @param <T> 目標類型


 * @param src 源對象


 * @param descType

發佈留言