1.為什么要映射
一個映射的框架在一個分層的體系架構中非常有用,特別是你在創建一個抽象的分層去包裝一些特殊數據的變化 vs 這些數據傳輸到其它層(外部服務的數據對象、領域的數據對象、數據傳輸對象、內部服務數據對象)。因此一個映射框架非常適合於使用在映射器類型的類中,負責將數據從一個數據對象映射到另一個數據對象。
對於分布式系統,一個幅作用就在不同的系統中需要傳輸各自領域的數據對象,通常情況下,你不想把內部領域數據對象直接暴露給外部,同時也不想把外部數據對象暴露地內部。
數據對象之間的映射已經用傳統手工編碼值對象來解決裝配(或轉換器)之間的數據對象復制,大多數程序員會開發一些的自定義映射框架和花費無數個小時和成千上萬行代碼映射與他們不同的數據對象
一個通用的映射框架解決了這些問題,dozer是一個開源的映射框架,健壯、靈活、可重用性和可配置的。
數據對象映射是分層的面向服務的體系結構的一個重要組成部分,仔細選擇你所使用的層映射,不走極端,因為做數據映射有時間和性能成本的限制。
1.1並行對象層次
為什么應用程序應該技術並行對象層次的體系,有幾個不同原因如下:
和外部代碼整合
序列化的要求
框架的整合
層次體系分離
在某些情況下你不直接控制就可以有效的保護您的代碼庫經常變化的對象層次結構, Dozer服務建立了應用程序和外部對象之間映射橋梁。映射是用反射的方式來進行的不會讓你修改你的API。例如,如果對象從數據類型到字符串類型的轉換,而代碼可以繼續運行,因為所有的這些轉換都是自動進行的。
有些框架施加了序列化的限制,不允許發送任何java對象,其中最流行的框架就是Google Web Toolkit(GWT)框架,限制開發人員只能發送編譯為javascipt和序列化標記的對象。Dozer框架有助於豐富的領域模型轉換到表示層的模型,這剛好滿足了GWT序列化的要求。
Dozer完美地整合了像Apache XmlBeans和JAXB框架,這有助於提供工廠類型、有助於領域模型和Xml對象的轉換,以同樣的方式作為普通對象到對象的映射。
在一個復雜的企業應用中,通常是有價值的獨立設計幾個層次開始,這些層次中每一個都依賴它們自己的抽象層。一個典型的簡單例子就是表示層、領域層和持久層,每一個層次都有它們自己的java Beans數據模型,並且不是所有的數據都要傳到體系的高層。例如相同的領域層對象根據表示層的需求會有不同的映射。
2.使用前的准備
2.1 下載以下的部分
下載Dozer並且解壓文檔
把dozer.jar添加到你項目的類路徑
添加第三方運行的jar包到你的項目類路徑
如果你使用Maven,把下面這些依賴復制粘貼到你的項目中:
2.2 第一個映射
對於第一個映射,我們假設兩個數據對象具有相同的屬性名稱
Mappermapper = new DozerBeanMapper();
DestinationObjectdestObject = mapper.map(sourceObject,DestinationObject.class);
執行這個Dozer映射以后,新的目標對象的實例將會有源對象屬性的值,如果映射的屬性的數據類型不相同,Dozer映射引擎將會自動執行數據類型轉換,到此為止,我已經完成了第一個Dozer映射,以下的部分,我們將會用XML文件來映射。
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.4.0</version>
</dependency>
ApacheIvy用戶可以復制粘貼下面一行
<dependencyorg="net.sf.dozer" name="dozer" rev="5.4.0"/>
2.3 通過XML文件指示用戶映射文件
如果兩個不同類型的數據對象要映射,但這兩個數據對象有不同的屬性名稱,你需要添加一個類映射到你的XML映射文件中,這些映射文件在使用的過程中被Dozer映射引擎解析。
當你把源數據對象的復制到目的數據對象的過程中,Dozer將會自動執行所有數據類型轉換。Dozer映射引擎是又向的,如果你已經在XML文件中配制了從源對象類型到目標對象類型的映射,你就不需要再配制從目標對象類型到源對象類型的映射
提示:有相同名稱的屬性不用在XML映射文件中指示,Dozer會自動映射從源對象到目標對象中所有具有相同名稱的屬性。
<mapping>
<class-a>yourpackage.yourSourceClassName</class-a>
<class-b>yourpackage.yourDestinationClassName</class-b>
<field>
<a>yourSourceFieldName</a>
<b>yourDestinationFieldName</b>
</field>
</mapping>
完整的Dozer映射的XML文件會下面這樣的,自定義映射部分包含映射選項的更多信息,這樣使用你的映射更加復雜。
<?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">
<configuration>
<stop-on-errors>true</stop-on-errors>
<date-format>MM/dd/yyyyHH:mm</date-format>
<wildcard>true</wildcard>
</configuration>
<mapping>
<class-a>yourpackage.yourSourceClassName</class-a>
<class-b>yourpackage.yourDestinationClassName</class-b>
<field>
<A>yourSourceFieldName</A>
<B>yourDestinationFieldName</B>
</field>
</mapping>
othercustom class mappings would go here.......
</mappings>
2.4 Dozer和依賴注入框架
Dozer不獨立於任何一個已存在的依賴注入框架, 然而,最常用的目標是技術最典型的應用包裝,查一查Spring手冊,你會發現,Dozer框架已經集成到Spring框架中了。
3.使用
3.1普通使用
運行Dozer需要如下:
Dozer使用SLF4J來記錄日志
Dozer需要第三方Jar包
所在第三方Jar包在運行時都要添加到運行程序的類路徑中去
Dozer.jar一定要添加到你的類路徑中去。
3.1.1 Dozer Bean映射
在我們開始使用XML映射文件之前,讓我們看一個使用Dozer簡單的例子,Dozer映射的實現有一個方法叫map,這個方法有兩具參數,源數據對象,要么一個目標數據對象,要么一個目標數據類型。映射之后,將會返回映射的目標對象。
Mapper mapper = new DozerBeanMapper();
DestinationObject destObject = mapper.map(sourceObject,DestinationObject.class);
or
DestinationObject destObject = newDestinationObject();
mapper.map(sourceObject, destObject);
注意:在真實的項目開發中,不推薦你在每一次映射的過程中,都要創建一個新的Mapper實例,典型的做法是在每一個啟動的VM中創建一個Mapper實例,如果你不用IOC框架,你可以定義Mapper的singleton=”true”,DozerBeanMapperSingletonWrapper是一個方便的類,已經在Dozer.jar中提供。
Dozer操作有兩種模式:隱式模式和顯示模式。隱式模式是默認激活的並試圖為你解決映射:相同的屬性名做映射,如果有更多的映射需要,你可以使用XML映射文件、注解或相關的API。
顯式模式假設沒有映射應該執行或“猜”,除非你指定專門的映射。這樣代碼量比隱式模式要多,但有時,你想完全控制整個映射過程,顯式模式是一個不錯的選擇。隱式和顯示模式的開關是“wildcard”。
3.1.2注入定制的映射文件
Dozer的xml映射文件不會自動地被Dozer引擎執行,一個定制的XML映射文件需要被注入到Mapper的實現類中(org.dozer.DozerBeanMapper).這個類技術set方法和構造函數。
最好使用像spring一樣的IOC框架,另一種方法是,用編程的方式把這些映射的配制文件注入到Mapper中,下面是用編程的方法來創建一個Bean mapper,注意,不推薦這種方法來創建bean mapper.每一次被映射的時候都會被創建,如果你使用這種方法,用單例就可以了。
List myMappingFiles = new ArrayList();
myMappingFiles.add("dozerBeanMapping.xml");
myMappingFiles.add("someOtherDozerBeanMappings.xml");
DozerBeanMapper mapper = newDozerBeanMapper();
mapper.setMappingFiles(myMappingFiles);
DestinationObject destObject = mapper.map(sourceObject, DestinationObject.class);
注意:Mapper實例應該被配制為單例,你應該用如下的方法來配制mapper,這樣你就不用在每次映射的時候都要加載和重新初始化映射文件。這樣做是不高效的,也是沒必要的,DozerBeanMapper類是線程安全的。下面是dozer的Mapper bean被配制到Spring中的例子,spring相關的配制文件如下:
<bean id="mapper"class="org.dozer.DozerBeanMapper">
<property name="mappingFiles">
<list>
<value>dozer-global-configuration.xml</value>
<value>dozer-bean-mappings.xml</value>
<value>more-dozer-bean-mappings.xml</value>
</list>
</property>
</bean>
3.1.3 Dozer Bean的映射單例的包裝
有一個方法可以來配制DozerBeanMapperSingletonWrapper,以便來使用你的映射文件。使用一個映射文件:一個文件叫做dozerBeanMapping.xml如果在你們類路徑下,將會被加載。你可以在{dozer.home}/mappings目錄下找到這樣一個簡單的文件。這個映射文件定義了java類和它們屬性之間的所有關系,自定義的映射部分詳細地定制了XMl映射部分。如下的例子顯示如何使用DozerBeanMapperSingletonWrapper:
Mapper mapper = DozerBeanMapperSingletonWrapper.getInstance();
DestinationObject destObject = mapper.map(sourceObject,DestinationObject.class);
or
Mapper mapper = DozerBeanMapperSingletonWrapper.getInstance();
DestinationObject destObject = newDestinationObject();
mapper.map(sourceObject,destObject);
4.通過標注映射
使用dozer的缺點之一是Xml。自從推土機開始Xmlhype期間超過五年前,是顯而易見的選擇。之后,Java 5注釋使我們和新行業接受的配置行為是特定於域的語言風格。用於支持在形式的映射提供了API,但由於版本5.3.2dozer開始提供注釋的支持.
使用注釋的顯而易見的原因是為了避免重復字段和方法在你的名字映射代碼。注釋可以把在映射屬性本身從而減少的數量代碼。然而在某些情況下當注釋應該避免,甚至無法使用。一些是以下幾點:
1. 你正在映射文的類,不在你的控制范圍內,而在dozer庫中;
2. 映射是相當復雜,需要很多的配制;
在第一種情況下你可以映射或第三方dto和JAXB生成實體不可能把注釋放進去。在第二種情況下有選擇的將大量的多行注釋或隔離某些重復的實體名稱的映射代碼。過多標注bean可能有閱讀和理解的困難。
注意:注解支持Dozer是實驗性的,並且還不支持技術復雜的映射。然而,在最簡單的映射情況下,用注解還是比XmL配制和API方便。
這個相方非常地簡單,我可以把@Mapping標注放在屬性的getter方法或直接放在屬性上,如果Dozer發現,這是一個雙向的映射,這就意味着,放一次注解,就可以創建雙向的映射,類型映射(例如String-long)將會自動進行,全局定制的轉換也可以被解析,注釋只能工作在轉換對象在默認通配的情況下,下面是一個演示的例子:
public class SourceBean {
privateLong id;
privateString name;
@Mapping("binaryData")
privateString data;
@Mapping("pk")
publicLong getId() {
return this.id;
}
public String getName() {
return this.name;
}
}
public class TargetBean {
private String pk;
private String name;
private String binaryData;
public void setPk(String pk) {
this.pk = pk;
}
public void setName(String name) {
this.name = name;
}
}
5.通過API進行映射
import staticorg.dozer.loader.api.FieldsMappingOptions.*;
import staticorg.dozer.loader.api.TypeMappingOptions.*;
...
BeanMappingBuilder builder = newBeanMappingBuilder() {
protectedvoid configure() {
mapping(Bean.class, Bean.class, TypeMappingOptions.oneWay(),
mapId("A"),
mapNull(true)
).exclude("excluded").fields("src", "dest",
copyByReference(),
collectionStrategy(true,
RelationshipType.NON_CUMULATIVE),
hintA(String.class),
hintB(Integer.class),
FieldsMappingOptions.oneWay(),
useMapId("A"),
customConverterId("id")
).fields("src", "dest",
customConverter("org.dozer.CustomConverter")
);
}
};
構造的builder對象被傳到DozerBeanMapper實例中,可以添加多個Builder對象
DozerBeanMappermapper = new DozerBeanMapper();
mapper.addMapping(builder);
最后不要忘記了對FieldsMappingOptions和TypeMappingOptins類的靜態引入。
6.通過XML進行映射
在兩個要被映射的對象類型或名稱都一樣的時候,用XML映射文件進行映射是一個不錯的選擇,你只需要添加一個類映射的到你的XML配制文件中,XML配制文件在Dozer執行的時候被加載。
Dozer在做映射的時候,默認執行所有類型轉換,Dozer映射引擎是雙向的。下面就是一個映射文件的例子:
<?xml version="1.0"encoding="UTF-8"?>
<mappingsxmlns="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>org.dozer.vo.TestObject</class-a>
<class-b>org.dozer.vo.TestObjectPrime</class-b>
<field>
<a>one</a>
<b>onePrime</b>
</field>
</mapping>
<mapping wildcard="false">
<class-a>org.dozer.vo.TestObjectFoo</class-a>
<class-b>org.dozer.vo.TestObjectFooPrime</class-b>
<field>
<a>oneFoo</a>
<b>oneFooPrime</b>
</field>
</mapping>
</mappings>
一個映射元素都有多個映射元素,伴隨着類級別的映射聲明和域級別的映射,wildcard屬性在默認的情況是true,這意味着那將自動地嘗試映射兩個類中的每一個屬性,當wildcard屬性被設置為false的時候,僅僅映射顯示定義的映射域。
重要:屬性相同的不必要在XML配置文件中指出,Dozer將會自動映射所有名稱相同的屬性到目標對象中。
Dozer將搜索整個類路徑中尋找指定的文件,通常可接受的方式是在你的程序中指定你映射文件。你還可以從應用外部加載XMl映射配置文件。
從5.4.0以后,Dozer可以從提供的流中加載XMl映射文件
6.1兩個類之間的映射
下面是兩個類之間映射的例子,注意:當兩上類之間的屬性名稱都相同的時候,顯式的XML配置文件不是不必要的,定制的XMl配置文件只是在你需要定制屬性之間的映射的時候才需要。
<mappings>
<mapping>
<class-a>org.dozer.vo.TestObject</class-a>
<class-b>org.dozer.vo.TestObjectPrime</class-b>
<!-- Any custom field mapping xml would go here -->
</mapping>
</mappings>
這些映射都是雙向的,你不需要定義一個XML配制映射從TestObjectPrime到TestObject,如果這兩個類有引用的復雜類型需要類型轉換,你將要定義它們之間的映射,Dozer將會遞歸地映射兩個類對象,類型轉換也是自動進行的,Dozer也支持非屬性映射,如果提供的兩個類不能映射,它就簡單地嘗試映射有相同名稱的屬性。
6.2基本屬性映射
6.2.1隱式屬性映射
匹配屬性的名稱是自動進行的,有相同名稱的屬性可以不在XMl配置文件中配置。
6.2.2簡單映射(雙向)
如果兩個屬性有不同的名稱,那么他們可以被映射如下:
<field>
<a>one</a>
<b>onePrime</b>
</field>
6.2.3數據類型轉換
數據類型轉換是通過Dozer引擎自動進行的,當前Dozer支持以下的數據類型轉換(都是雙向的)
l Primitive to PrimitiveWrapper
l Primitive to Custom Wrapper
l Primitive Wrapper toPrimitive Wrapper
l Primitive to Primitive
l Complex Type to ComplexType
l String to Primitive
l String to Primitive Wrapper
l String to Complex Type ifthe Complex Type contains a String constructor
l String to Map
l Collection to Collection
l Collection to Array
l Map to Complex Type
l Map to Custom Map Type
l Enum to Enum
l Each of these can be mappedto one another: java.util.Date, java.sql.Date,java.sql.Time,java.sql.Timestamp, java.util.Calendar,java.util.GregorianCalendar
l String to any of thesupported Date/Calendar Objects.
l Objects containing atoString() method that produces a long representing time in (ms) to any supportedDate/Calendar object.
6.2.4遞歸映射(雙向)
Dozer支持類級別的遞歸映射,如果你的映射類中有一些作為屬性的復雜的類型定義,Dozer將會在映射文件中尋找類級別的映射,如果沒有找到這些類之間的映射,Dozer將會映射復雜類型中具有同名稱的屬性的值。
6.3字符串到日期的映射
從日期格式對於到字符串之間的映射可以在屬性級別指定,類型轉換可以自動進行。
<field>
<a date-format="MM/dd/yyyyHH:mm:ss:SS">dateString</a>
<b>dateObject</b>
</field>
一個默認的日期格式也可以在類級別的映射中指定,這個默認的日期格式將會被應用到所有屬性的映射中,除非它在屬性級別中重寫了。
<mappingdate-format="MM-dd-yyyy HH:mm:ss">
<class-a>org.dozer.vo.TestObject</class-a>
<class-b>org.dozer.vo.TestObjectPrime</class-b>
<field>
<a>dateString</a>
<b>dateObject</b>
</field>
</mapping>
也可以在頂級的映射中指定默認的日期的映射格式。這個默認的日期格式將被用於所有屬性的映射中,除非在低級別重寫了。
<mappings>
<configuration>
<date-format>MM/dd/yyyyHH:mm</date-format>
</configuration>
<mapping wildcard="true">
<class-a>org.dozer.vo.TestObject</class-a>
<class-b>org.dozer.vo.TestObjectPrime</class-b>
<field>
<a>dateString</a>
<b>dateObject</b>
</field>
</mapping>
<mapping>
<class-a>org.dozer.vo.SomeObject</class-a>
<class-b>org.dozer.vo.SomeOtherObject</class-b>
<field>
<a>srcField</a>
<b>destField</b>
</field>
</mapping>
</mappings>
6.4 Enum之間的映射
從一個枚舉值到另一個枚舉值的映射
<field>
<a>status</a>
<b>statusPrime</b>
</field>
enum Status {
PROCESSING, SUCCESS, ERROR
}
public classUserGroup {
private Status status;
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}
enum StatusPrime {
PROCESSING, SUCCESS, ERROR
}
public classUserGroupPrime {
private StatusPrime statusPrime;
public StatusPrime getStatusPrime() {
return statusPrime;
}
public void setStatusPrime(StatusPrime statusPrime) {
this.statusPrime = statusPrime;
}
}
6.5容器之間的映射
6.5.1容器和數組之間的映射
Dozer會自動的在所有容器之間進行映射以及執行所有類型之間的轉換。源容器中的每一個元素都映射到目標容易中,Hints被用來指示目標容易中被創建對象的類型,下面就是Dozer支持的容器之間的映射:
l List to List
l List to Array
l Array to Array
l Set to Set
l Set to Array
l Set to List
6.5.1.1 為容器映射提供提示
如果是使用JDK1.5以后的泛型,Hints是沒有必要的,因為我們在開發的過程中都是使用的JDK1.6以上的版本,因此針對Hints就不作詳細的說明,例子如下:
<field>
<a>hintList</a>
<b>hintList</b>
<b-hint>org.dozer.vo.TheFirstSubClassPrime</b-hint>
</field>
下面是針對容器之間映射作的一個總結,Arrays,Sets,Lists,下面給出了當hints給出或沒有給出的時候,具體發生的:
• List to List
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
• Array to List
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
n If hint is speficied: Destlist will contain objects that match dest hint(s) type
• List to Array
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest array will contain data types defined by the array
n If hint is speficied: Destlist will contain objects that match dest hint(s) type (only if Object
• Array to Array
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest array will contain data types defined by the array
n If hint is speficied: Destlist will contain objects that match dest hint(s) type (only if Object
• Set to Set
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
n If hint is speficied: Destlist will contain objects that match dest hint(s) type
l Array to Set
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
n If hint is speficied: Destlist will contain objects that match dest hint(s) type
l Set to Array
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest array will contain data types defined by the array
n If hint is speficied: Destlist will contain objects that match dest hint(s) type (only if Object
l List to Set
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
n If hint is speficied: Destlist will contain objects that match dest hint(s) type
l Set to List
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
n If hint is speficied: Destlist will contain objects that match dest hint(s) type
6.5.1.2 使用Jdk1.5的泛型來實現容器之間的映射
如果使用JDK1.5以上的泛型,就可以不用指定Hints了,從容器/數組到容器/數組的轉換,Dozer在運行時會決定。
public classUserGroup {
private Set<User> users;
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> aUsers) {
users = aUsers;
}
}
public classUserGroupPrime {
private List<UserPrime> users;
public List<UserPrime> getUsers() {
return users;
}
public void setUsers(List<UserPrime> aUsers) {
users = aUsers;
}
}
6.6.1.3 對象數組到List的轉換(雙向的)
當從一個對象數組到List轉換的時候,默認的List集合會包含和對象數組相同的數據:
<!-- changing anInteger [] to List and back again -->
<field>
<a>arrayForLists</a>
<b>listForArray</b>
</field>
使用一個提示來為數據類型轉換,因一個提示被指定,目標的List將會包含String類型的元素而不是Integer類型的元素。
<!-- changing anInteger [] to List and back again -->
<field>
<a>arrayForLists</a>
<b>listForArray</b>
<b-hint>java.lang.String</b-hint>
</field>
6.5.1.4 原始類型的數組到原始類型的數組的映射(雙向)
當從一個對象數組到另一個對象數組進行轉換,默認的情況下,映射之后,目標數組和源數據包含相同的數據
<!-- convertingint[] to int [] by name only -->
<field>
<a>anArray</a>
<b>theMappedArray</b>
</field>
6.5.1.5 類積和非類積List映射(雙向的)
如果你在映射一個已經初始化的類,Dozer將會添加或更新對象到List中。如果你要映射Set、List或Array中已經包含有對象元素,在映射的過程中,將會調用contains()方法來決定是添加還是更新。這個決定是使用relationship-type屬性來判斷,默認值是cumulative。Relatinship-type可以被指定在屬性級別,類級別和全局配置級別。
全局配置級別:
<mappings>
<configuration>
<relationship-type>non-cumulative</relationship-type>
</configuration>
</mappings>
類級別:
<mappings>
<mappingrelationship-type="non-cumulative">
<class-a>org.dozer.vo.TestObject</class-a>
<class-b>org.dozer.vo.TestObjectPrime</class-b>
<field>
<a>someList</a>
<b>someList</b>
</field>
</mapping>
</mappings>
屬性級別:
<!-- objects willalways be added to an existing List -->
<fieldrelationship-type="cumulative">
<a>hintList</a>
<b>hintList</b>
<a-hint>org.dozer.vo.TheFirstSubClass</a-hint>
<b-hint>org.dozer.vo.TheFirstSubClassPrime</b-hint>
</field>
<!-- objects willupdated if already exist in List,
added if they arenot present -->
<fieldrelationship-type="non-cumulative">
<a>unequalNamedList</a>
<b>theMappedUnequallyNamedList</b>
</field>
6.5.1.5 移除孤獨數據
孤獨數據是那些存在於目標容器而不存在於源容器中,Dozer將會移除這些孤獨數據通過調用Remove方法在實際目標容器中,為了絕定這些孤獨元素元素是否清除,Dozer使用了contains()方法去檢查結果集中是否包含孤獨元素,默認的設置是為false;
<!-- orphanobjects will always be removed from an existing
destination List-->
<fieldremove-orphans="true">
<a>srcList</a>
<b>destList</b>
</field>
6.6Map支持的屬性映射
6.6.1Map到Map映射
Dozer能夠映射一個java.util.Map到java.util.Map。如果Map中包含復雜的類型,它將會用遞歸的方法進行映射,如果目標Map中已經包含了元素,那么在映射的時候,就是添加元素到目標Map中。
<mapping>
<class-a>org.dozer.vo.map.MapToMap</class-a>
<class-b>org.dozer.vo.map.MapToMapPrime</class-b>
<field>
<a>standardMapWithHint</a>
<b>standardMapWithHint</b>
<a-hint>org.dozer.vo.TestObject</a-hint>
<b-hint>org.dozer.vo.TestObjectPrime</b-hint>
</field>
</mapping>
6.6.2 映射一個屬性級的屬性到一個java.util.Map或一個定制的Map有唯一的Get/Set方法
Dozer支持在一個類屬性級別映射一個map支持的屬性。這個map要么是實現了java.util.Map接口或是一個擁有唯一Get/Set方法定制的Map。
在下面這個例子中,屬性A是一個基本的String類型,它被映射到屬性B,屬性B是一個HashMap,HashMap中的關系字key將是一個String類型的“StringProperty”,並且value是就是存儲在屬性A中。
<mapping>
<class-a>org.dozer.vo.map.PropertyToMap</class-a>
<class-b>org.dozer.vo.map.MapToProperty</class-b>
<field>
<a>stringProperty</a>
<b>hashMap</b>
</field>
</mapping>
下面這個例子是屬性A是String類型的,它被映射到屬性B,屬性B是一個HashMap。HashMap中的Key將會是“myStringProprty”,value將會是屬性A的值,注意:屬性A必須有一個唯一的setter方法。
<mapping>
<class-a>org.dozer.vo.map.PropertyToMap</class-a>
<class-b>org.dozer.vo.map.MapToProperty</class-b>
<field>
<aset-method="addStringProperty2">stringProperty2</a>
<bkey="myStringProperty">hashMap</b>
</field>
</mapping>
下面這個例子是屬性A是一個基本的String類型,它被映射到屬性B中,屬性B是一個定制的map,map中的key是“myCustomProperty”,value就是屬性A的值,注意屬性B有一個唯一的getter和setter方法名,如果你實用一個定制的map,你一定要顯示的設置map的Get/Set方法名,如果你使用定制的map是實現了一個接口或一個抽象類,目標的中hints也要被設置。
<mapping>
<class-a>org.dozer.vo.map.PropertyToMap</class-a>
<class-b>org.dozer.vo.map.MapToProperty</class-b>
<field>
<a>stringProperty3</a>
<bmap-get-method="getValue" map-set-method="putValue"
key="myCustomProperty">customMap</b>
</field>
<field>
<a>stringProperty4</a>
<b map-get-method="getValue" map-set-method="putValue"
key="myCustomNullProperty">nullCustomMap</b>
<b-hint>org.dozer.vo.map.CustomMap</b-hint>
</field>
<field>
<a>stringProperty5</a>
<bmap-get-method="getValue"
map-set-method="putValue">customMap</b>
</field>
</mapping>
6.6.3 映射一個類性級的屬性到一個java.util.Map或一個定制的Map有唯一的Get/Set方法
Dozer也可以直接映射一個復雜類型的對象到一個java.util.Map或一個定制的map中,下面這個例子顯示地聲明一個復合類型的對象(PropertyToMap)到一java.util.Map的映射,當做這樣的映射的時候,你需要為mapping顯示地定義一個唯一的map-id。這被用來區分那一個mapping在運行的時候被調用,PropertyToMap中每一個屬性將會被映射到java.util.Map中。沒必要顯示的定義這些映射,排除的屬性映射可以用來在運行的時候排除屬性,如果屬性的名稱和定制map中的key不一樣的時候,一定要設置定制map的key。
第二個例子顯示怎樣建立一個定制的map對象,唯一的不同之處是需不需要定義map-set-method和map-get-method的值,這關系到java.util.Map的get()和put()方法。
<mappingmap-id="myTestMapping">
<class-a>org.dozer.vo.map.PropertyToMap</class-a>
<class-b>java.util.Map</class-b>
<field>
<aset-method="addStringProperty2">stringProperty2</a>
<bkey="myStringProperty">this</b>
</field>
<field-exclude>
<a>excludeMe</a>
<b>this</b>
</field-exclude>
</mapping>
<mappingmap-id="myCustomTestMapping">
<class-a>org.dozer.vo.map.PropertyToMap</class-a>
<class-b map-set-method="putValue"map-get-method="getValue">
org.dozer.vo.map.CustomMap
</class-b>
<field>
<aset-method="addStringProperty2">stringProperty2</a>
<bkey="myStringProperty">this</b>
</field>
<field-exclude>
<a>excludeMe</a>
<b>this</b>
</field-exclude>
</mapping>
下面這個例子顯示使用這些映射,注意這些屬性映射引用了一個map-id,第一個屬性映射將會使用myTestMapping定義mapping
<mapping>
<class-a>org.dozer.vo.map.MapTestObject</class-a>
<class-b>org.dozer.vo.map.MapTestObjectPrime</class-b>
<field map-id="myTestMapping">
<a>propertyToMap</a>
<b>propertyToMapMap</b>
</field>
<field map-id="myTestMapping">
<a>propertyToMapToNullMap</a>
<b>nullPropertyToMapMap</b>
<b-hint>java.util.HashMap</b-hint>
</field>
<field map-id="myCustomTestMapping">
<a>propertyToCustomMap</a>
<b>propertyToCustomMapMap</b>
</field>
</mapping>
在類級別,map支持的映射也能夠被當作一個標准的映射,對於Dozer來說,有一個新的API,除源類的目標類之外,你可以傳一個map引用的id.
// Example 1
PropertyToMap ptm =new PropertyToMap();
ptm.setStringProperty("stringPropertyValue");
ptm.addStringProperty2("stringProperty2Value");
Map map =Mapper.map(ptm, HashMap.class, "myTestMapping");
// Example 2
CustomMap customMap= mapper.map(ptm, CustomMap.class,
"myCustomTestMapping");
// Example 3
CustomMap custom =new CustomMap();
custom.putValue("myKey","myValue");
Mapper.map(ptm,custom, "myCustomTestMapping");
// Example 4 - MapBack
Map map = newHashMap();
map.put("stringProperty","stringPropertyValue");
PropertyToMapproperty = mapper.map(map, PropertyToMap.class,
"myTestMapping");
assertEquals("stringPropertyValue",property.getStringProperty());
6.7索引映射
索引映射就是支持需要索引進行讀和寫的映射
<mapping>
<class-a>org.dozer.vo.Individuals</class-a>
<class-b>org.dozer.vo.FlatIndividual</class-b>
<field>
<a>usernames[0]</a>
<b>username1</b>
</field>
<field>
<a>usernames[1]</a>
<b>username2</b>
</field>
<field>
<a>individual.username</a>
<b>username2</b>
</field>
<field>
<a>secondNames[1]</a>
<b>secondName1</b>
</field>
<field>
<a>secondNames[2]</a>
<b>secondName2</b>
</field>
<field>
<a>aliases.otherAliases[0]</a>
<b>primaryAlias</b>
</field>
</mapping>
6.8深度映射
6.8.1深度屬性映射
嘗試屬性映射是有可能的。一個例子就你有一個擁有一個String類型屬性的對象,其它的對象也有一個String類型的屬性,但是它在對象圖的更深層次。在下面例子中,DestDeepObje 有一個嵌套的屬性一個對象里面需要被映射,hints類型在深度屬性映射中是被支持的,這個屬性”copy-by-reference”被設置為one-way,並且relationship-type也可以被使用。
<mapping>
<class-a>org.dozer.vo.deep.SrcDeepObj</class-a>
<class-b>org.dozer.vo.deep.DestDeepObj</class-b>
<field>
<a>srcNestedObj.src1</a>
<b>dest1</b>
</field>
<field>
<a>srcNestedObj.src2</a>
<b>dest2</b>
</field>
<field>
<a>srcNestedObj.srcNestedObj2.src5</a>
<b>dest5</b>
</field>
<field><!-- java.util.List to java.util.List-->
<a>srcNestedObj.hintList</a>
<b>hintList</b>
<a-hint>java.lang.String</a-hint>
<b-hint>java.lang.Integer</b-hint>
</field>
<field>
<a>srcNestedObj.hintList2</a>
<b>hintList2</b>
<a-hint>org.dozer.vo.TheFirstSubClass</a-hint>
<b-hint>org.dozer.vo.TheFirstSubClassPrime</b-hint>
</field>
<field copy-by-reference="true">
<a>srcNestedObj.hintList3</a>
<b>hintList3</b>
</field>
</mapping>
6.8.2深度索引映射
<field>
<a>offSpringName</a>
<b>pets[1].offSpring[2].petName</b>
</field>
6.9排除的字段映射
Dozer通過使用field-exclude標簽來技術排除字段,Dozer的排除字段也支持one-way屬性
<field-exclude>
<a>fieldToExclude</a>
<b>fieldToExclude</b>
</field-exclude>
<field-excludetype="one-way">
<a>fieldToExclude</a>
<b>fieldToExclude</b>
</field-exclude>
6.9.1 wildcard 排除默認的映射字段
在mapping標簽中有一個屬性wildcard用為控制默認的映射是否被執行,默認的值是true,例如:
<mappingwildcard="false">
<class-a>org.dozer.vo.AnotherTestObject</class-a>
<class-b>org.dozer.vo.AnotherTestObjectPrime</class-b>
<field>
<a>field1</a>
<b>field1</b>
</field>
</mapping>
這個例子只會影射field1字段的值,如果兩個類中均有field2屬性,也不會被自動映射
6.9.2排除映射Null值
你可以繞過null值的映射,如果這個被指定,源對象的值為空的時候,目標字段的映射也會被繞過,目標字段的Setter方法也不會被調用,它可以在Mapping級別或類級別,例如:
<mappingmap-null="false">
<class-a>org.dozer.vo.AnotherTestObject</class-a>
<class-b>org.dozer.vo.AnotherTestObjectPrime</class-b>
<field>
<a>field4</a>
<b>to.one</b>
</field>
</mapping>
<mapping>
<class-a>org.dozer.vo.AnotherTestObject</class-a>
<class-b map-null="false">org.dozer.vo.AnotherTestObjectPrime
</class-b>
<field>
<a>field4</a>
<b>to.one</b>
</field>
</mapping>
6.9.3排除映射空值
你還可繞過空字段的映射,如果被指定,並且源目標所對應的字段為空的時候,到目標字段的映射將會被繞過,並且目標字段所對應的Setter方法不會被執行,它可以在Mapping級別或類級別上指示,例如:
<mapping map-empty-string="false">
<class-a>org.dozer.vo.AnotherTestObject</class-a>
<class-b>org.dozer.vo.AnotherTestObjectPrime</class-b>
<field>
<a>field4</a>
<b>to.one</b>
</field>
</mapping>
<mapping>
<class-a>org.dozer.vo.AnotherTestObject</class-a>
<class-b map-empty-string="false">
org.dozer.vo.AnotherTestObjectPrime
</class-b>
<field>
<a>field4</a>
<b>to.one</b>
</field>
</mapping>
6.10單向映射
單向映射只需要在mapping或field標簽加上type為“one-way”的屬性,也可用在field-exclude標簽上
<mappingtype="one-way">
<class-a>org.dozer.vo.TestObjectFoo</class-a>
<class-b>org.dozer.vo.TestObjectFooPrime</class-b>
<field>
<a>oneFoo</a>
<b>oneFooPrime</b>
</field>
</mapping>
<mapping>
<class-a>org.dozer.vo.TestObjectFoo2</class-a>
<class-b>org.dozer.vo.TestObjectFooPrime2</class-b>
<field type="one-way">
<a>oneFoo2</a>
<b>oneFooPrime2</b>
</field>
<field type="one-way">
<a>oneFoo3.prime</a>
<b>oneFooPrime3</b>
</field>
</mapping>
<field-excludetype="one-way"">
<a>fieldToExclude</a>
<b>fieldToExclude</b>
</field-exclude>
6.11基於上下文的映射
基於於上下文的映射可以指示使用map-id屬性,Dozer也支持嵌套的上下文映射通過在字段級別指示一個map-id屬性:
<mappingmap-id="caseA">
<class-a>org.dozer.vo.context.ContextMapping</class-a>
<class-b>org.dozer.vo.context.ContextMappingPrime</class-b>
<field-exclude>
<a>loanNo</a>
<b>loanNo</b>
</field-exclude>
<field map-id="caseC">
<a>contextList</a>
<b>contextList</b>
<b-hint>org.dozer.vo.context.ContextMappingNestedPrime
</b-hint>
</field>
</mapping>
<mappingmap-id="caseB">
<class-a>org.dozer.vo.context.ContextMapping</class-a>
<class-b>org.dozer.vo.context.ContextMappingPrime</class-b>
</mapping>
<mappingmap-id="caseC">
<class-a>org.dozer.vo.context.ContextMappingNested</class-a>
<class-b>org.dozer.vo.context.ContextMappingNestedPrime</class-b>
<field-exclude>
<a>loanNo</a>
<b>loanNo</b>
</field-exclude>
</mapping>
<mapping map-id="caseD">
<class-a>org.dozer.vo.context.ContextMappingNested</class-a>
<class-b>org.dozer.vo.context.ContextMappingNestedPrime</class-b>
</mapping>
</mappings>
為了使用指定的上下文映射,我們在調用的時候,只要指定map-id屬性就ok啦!
ContextMappingPrimecmpA =
mapper.map(cm,ContextMappingPrime.class, "caseA");
6.12全局配置
6.12.1全局配置
全局配置塊是用來設置全局映射的默認設置,任何一個用戶自定義的類型轉換都可在全局配置中指定,全局配置也是可選的。Dozer支持多個配置文件,但是僅一個配置文件的全局配置就會慣穿所有配置文件,我們推薦一個唯一全局配置文件和多個分離的映射文件,隱式配置文件將會繼承全局配置的默認值,下面是一個簡單的全局配置塊
<configuration>
<date-format>MM/dd/yyyy HH:mm</date-format>
<stop-on-errors>true</stop-on-errors>
<wildcard>true</wildcard>
<trim-strings>false</trim-strings>
<custom-converters> <!-- these are alwaysbi-directional -->
<convertertype="org.dozer.converters.TestCustomConverter" >
<class-a>org.dozer.vo.TestCustomConverterObject</class-a>
<class-b>another.type.to.Associate</class-b>
</converter>
</custom-converters>
</configuration>
6.12.2 重寫wildcards屬性
每個單獨的映射部分都可以設置自己的wildcard策略,即使有一個全局的wildcard策略,下面的映射部分不會允許wildcards
<mappingwildcard="false">
<class-a>org.dozer.vo.SpringBean</class-a>
<class-b>org.dozer.vo.SpringBeanPrime</class-b>
<field>
<a>anAttributeToMap</a>
<b>anAttributeToMapPrime</b>
</field>
</mapping>
6.12.3 重寫日期格式
每一個單獨的映射部分都可以設置自己的獨立的日期格式,例如:
<!-- Override toplevel date format default -->
<mappingdate-format="MM-dd-yyyy HH:mm:ss">
<class-a>org.dozer.vo.TestObject</class-a>
<class-b>org.dozer.vo.TestObjectPrime</class-b>
<field>
<a>one</a>
<b>onePrime</b>
</field>
</mapping>
6.12.4 重寫錯誤處理信息
可以針對每一個單獨的映射,重寫錯誤處理信息,例如:
<!-- Override toplevel defaults -->
<mappingstop-on-errors="false">
<class-a>org.dozer.vo.TestObject</class-a>
<class-b>org.dozer.vo.TestObjectPrime</class-b>
<field>
<a>one</a>
<b>onePrime</b>
</field>
</mapping>
6.12.5 重寫空字符串的策略
可以為每個單獨的映射,重寫一個空字符串的策略
<!-- Override toplevel defaults -->
<mappingtrim-strings="true">
<class-a>org.dozer.vo.TestObject</class-a>
<class-b>org.dozer.vo.TestObjectPrime</class-b>
<field>
<a>one</a>
<b>onePrime</b>
</mapping>
6.13自定義轉換
自定義轉換被用來執行在兩個對象之間自定義的映射,在全局配置部分,你可添加一些XML來告訴Dozer在類A和類B之間進行映射時,啟用自定義轉換的類型,當一個自定義轉換在類A和類B之間被聯合指定時,Dozer將會調用自定義轉換去執行數據映射,而不是去調用標准的映射邏輯。
為了讓Dozer能夠識別自定義轉換類型,自定義轉換一定要實現org.dozer.CustomConverter接口,否則會拋出一個異常。
自定義轉換類型在所有的配置文件中都啟作用,這意味着,你可以在一個文件中定義一次自定義轉換,然后這個自定義轉換會應用到其它所有的與類A和類B之間的映射轉換,下面是一個自定義轉換的例子:
<?xmlversion="1.0" encoding="UTF-8"?>
<mappingsxmlns="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">
<configuration>
<custom-converters> <!-- these are alwaysbi-directional -->
<converter type="org.dozer.converters.TestCustomConverter">
<class-a>org.dozer.vo.CustomDoubleObject</class-a>
<class-b>java.lang.Double</class-b>
</converter>
<!-- You are responsible for mappingeverything between
ClassA and ClassB -->
<converter type="org.dozer.converters.TestCustomHashMapConverter">
<class-a>
org.dozer.vo.TestCustomConverterHashMapObject
</class-a>
<class-b>
org.dozer.vo.TestCustomConverterHashMapPrimeObject
</class-b>
</converter>
</custom-converters>
</configuration>
</mappings>
自定義類型轉換也可以用在單獨的字段級別上,在下面的例子中,Dozer將會調用自定義的轉換來執行字段映射。
<mapping>
<class-a>org.dozer.vo.SimpleObj</class-a>
<class-b>org.dozer.vo.SimpleObjPrime2</class-b>
<field custom-converter=
"org.dozer.converters.StringAppendCustomConverter">
<a>field1</a>
<b>field1Prime</b>
</field>
</mapping>
自定義的轉換實例可以被重復地使用在單獨的字段級別上。在下面的例子中,Dozer將會調用定義的轉換來執行字段映射:
<mapping>
<class-a>org.dozer.vo.SimpleObj</class-a>
<class-b>org.dozer.vo.SimpleObjPrime2</class-b>
<fieldcustom-converter-id="CustomConverterWithId">
<a>field1</a>
<b>field1Prime</b>
</field>
</mapping>
如果你想在自定義轉換注入到Dozer之前做一些操作,你需要把自定義轉換的實例注入到DozerBeanMapper中, 如果你不想用Spring,它們也可以用編程的方式被設置到DozerBeanMapper中:
<?xmlversion="1.0" encoding="UTF-8"?>
<!DOCTYPE beansPUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-lazy-init="false">
<bean id="org.dozer.Mapper"class="org.dozer.DozerBeanMapper">
<property name="mappingFiles">
<list>
<value>systempropertymapping1.xml</value>
<value>dozerBeanMapping.xml</value>
<value>injectedCustomConverter.xml</value>
</list>
</property>
<propertyname="customConvertersWithId">
<map>
<entry key="CustomConverterWithId1"
ref="configurableConverterBeanInstance1"/>
<entry key="CustomConverterWithId2"
ref="configurableConverterBeanInstance2"/>
</map>
</property>
</bean>
</beans>
注意:當源類型的值為空的時候,自定義轉換也會被調用,於是,你需要在自定義類型的實現中顯示地處理空值。
public classTestCustomConverter implements CustomConverter {
public Object convert(Object destination, Object source,
Class destClass, Class sourceClass) {
if (source == null) {
return null;
}
CustomDoubleObject dest = null;
if (source instanceof Double) {
// check to see if the object already exists
if (destination == null) {
dest = new CustomDoubleObject();
} else {
dest = (CustomDoubleObject) destination;
}
dest.setTheDouble(((Double)source).doubleValue());
return dest;
} else if (source instanceof CustomDoubleObject) {
double sourceObj = ((CustomDoubleObject)source).getTheDouble();
return new Double(sourceObj);
} else {
throw new MappingException("ConverterTestCustomConverter "
+ "used incorrectly. Arguments passed inwere:"
+ destination + " and " + source);
}
}
}
自定義類型也可被注入到DozerBeanMapper中,如果你需要在Dozer使用之間做一些操作。
<?xmlversion="1.0" encoding="UTF-8"?>
<!DOCTYPE beansPUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beansdefault-lazy-init="false">
<bean id="org.dozer.Mapper"class="org.dozer.DozerBeanMapper">
<property name="mappingFiles">
<list>
<value>systempropertymapping1.xml</value>
<value>dozerBeanMapping.xml</value>
<value>injectedCustomConverter.xml</value>
</list>
</property>
<property name="customConverters">
<list>
<ref bean="customConverterTest"/>
</list>
</property>
</bean>
<!-- custom converter -->
<bean id="customConverterTest"
class="org.dozer.converters.InjectedCustomConverter">
<property name="injectedName">
<value>injectedName</value>
</property>
</bean>
</beans>
6.13.1 支持數組類型
可以為數據類型指定自定義轉換。例如,如果你想用一個自定義轉換來映射一個object數組到一個String類型的對象,可以用下面的映射處理規則:在解析映射文件的時候,Dozer通常調用用ClassLoader.loadClass()。對於數組,java希望類的名稱是如下的格式…【Lorg.dozer.vo.SimpleObj】。
<converter type="org.dozer.converters.StringAppendCustomConverter">
<class-a>[Lorg.dozer.vo.SimpleObj;</class-a>
<class-b>java.lang.String</class-b>
</converter>
6.13.2 支持原始類型
可以為原始的類型指定自定義轉換器。當定義自定義類型的時候,使用原始類型的包裝類。在下面的例子中,當在SomeObject和整型的原始類型做映射的時候,Dozer將會調用指定的自定義轉換器。注意,當在SomeObect和Integer做映射的時候,也會調用這個自定義的轉換器。
<convertertype="somePackage.SomeCustomConverter" >
<class-a>somePackage.SomeObject</class-a>
<class-b>java.lang.Integer</class-b>
</converter>
6.13.3 配置自定義轉換器
定義一個自定義轉換器,能夠通過配置參數被配置到映射中,在這種情況下,你應該實現ConfigurableCustomConverter接口,而不是普通的自定義轉換器。可配置的轉換器已經增加了額外的屬性用來提供運行進的參數,參數提供的方式是通過使用custom-converter-param屬性。
<mapping>
<class-a>org.dozer.vo.BeanA</class-a>
<class-b>org.dozer.vo.BeanB</class-b>
<fieldcustom-converter="org.dozer.converters.MathOperationConverter"
custom-converter-param="+">
<a>amount</a>
<b>amount</b>
</field>
</mapping>
當有很多相同行為的情況下,可配置的轉換器可以被用到,它可以被參數化,但是聯合的數據太高了而不能實現簡單的自定義轉換了類型。
public class MathOperationConverterimplements ConfigurableCustomConverter {
public Object convert(Object destinationFieldValue,
Object sourceFieldValue,
Class destinationClass,
Class sourceClass, String param) {
Integer source = (Integer) sourceFieldValue;
Integer destination = (Integer)destinationFieldValue;
if ("+".equals(param)) {
return destination.intValue + source.intValue();
}
if ("-".equals(param)) {
return destination.intValue - source.intValue();
}
}
}
6.13.4新的自定義轉換器API
當提供大量靈活自定義轉換器API來描述以上情況是相當低級別的抽象,原因就在於轉換器,編碼不好理解並且不容易重復地使用到其它的Dozer映射中。然而,正常的情況應該是這樣的:相同的轉換邏輯應該調用同一個地方,而不是映射框架的bean。
Dozer最新版本有一個新的 –cleaner API為了定義用戶自定義類型轉換器,當帶來更多控制執行流時,它給你更多的明顯API,下面的實例表明在使用新的API時更簡單。
public classNewDozerConverter extends DozerConverter<String, Boolean> {
public NewDozerConverter() {
super(String.class, Boolean.class);
}
public Boolean convertTo(String source, Booleandestination) {
if ("yes".equals(source)) {
return Boolean.TRUE;
} else if ("no".equals(source)) {
return Boolean.FALSE;
}
throw new IllegalStateException("Unknownvalue!");
}
public String convertFrom(Boolean source, Stringdestination) {
if (Boolean.TRUE.equals(source)) {
return "yes";
} else if (Boolean.FALSE.equals(source)) {
return "no";
}
throw new IllegalStateException("Unknown value!");
}
}
6.13.5 數據結構轉換器
有這些情況:需要執行可編程的數據結構轉換,從一個list中復制每一個奇數據元素到一個map中作為key,每一個偶數元素作為value;在這種情況下依賴通常Dozer映射支持單獨數據時,需要定義數據結構轉換,對於這種情況,有可能要使用MapperAware接口,注入到當前的mapper實例中,並且是在自定義轉換器外面。
public static classConverter extends DozerConverter <List, Map> implements MapperAware {
private Mapper mapper;
public Converter() {
super(List.class, Map.class);
}
public Map convertTo(List source, Map destination) {
Map originalToMapped = new HashMap();
for (Source item : source) {
Target mappedItem = mapper.map(item,Target.class);
originalToMapped.put(item, mappedItem);
}
return originalToMapped;
}
<...>
public void setMapper(Mapper mapper) {
this.mapper = mapper;
}
}
6.14自定義Bean工廠
在映射到目標數據對象的過程中,可以配置Dozer使用自定義的bean工廠來創建目標對象的實例。而默認的情況下,Dozer用默認的構造函數來創建新目標對象的實例,這在大部分情況下,用戶使用它是很方便的,便是如果你需要更靈活的方式,你可以指定自己的bean工廠來實例這些數據對象。
自定義的bean工廠一定要實現org.dozer.BeanFactory接口,在默認的情況下,Dozer映射引擎將會使用目標對象類的名稱來調用工廠去實例化。
public interfaceBeanFactory {
public Object createBean(Object source, ClasssourceClass,
String targetBeanId);
}
接着,在自己的Dozer映射文件中,你僅僅需要指示一個bean-factory.xml對於任何你想使用的自定義工廠。
在下面的例子中,SimpleCustomBeanFactory將會用來創建任何InsideTestObjectPrime數據對象的實例。
<mapping>
<class-a>com.example.vo.InsideTestObject</class-a>
<class-bbean-factory="com.example.factories.SomeCustomBeanFactory">
com.example.vo.InsideTestObjectPrime
</class-b>
</mapping>
如果你的工廠創建bean實例是基於不同的id,而不是類名稱,你可以指示一個factory-bean-id屬性,在運行時,指示的factory-bean-id將會被傳遞到工廠中,而不是類名。
<mapping>
<class-a>com.example.vo.InsideTestObject</class-a>
<class-b bean-factory="com.example.factories.SomeCustomBeanFactory"
factory-bean-id="someBeanLookupId">
com.example.vo.InsideTestObjectPrime
</class-b>
</mapping>
6.14.1 指示默認的工廠
另外,在Dozer的任何一個映射配置文件中,可以指示默認的bean 工廠,這個默認的bean工廠可以用到任何的映射中。
<configuration>
<stop-on-errors>true</stop-on-errors>
<wildcard>true</wildcard>
<bean-factory>com.example.factories.SomeDefaultBeanFactory
</bean-factory>
</configuration>
也可以映射級別指定bean 工廠,下面這個指定的工廠將會用在class-a和class-b之間的映射
<mappingbean-factory="com.example.factories.SomeCustomBeanFactory">
<class-a>com.example.vo.TestObject</class-a>
<class-b>com.example.vo.TestObjectPrime</class-b>
</mapping>
6.14.2 spring工廠的注入
Bean工廠可以通過Spring或相似的控制反轉技術注入到Dozer框架中
<beans>
<bean id="org.dozer.Mapper"class="org.dozer.DozerBeanMapper">
<property name="mappingFiles">
<list>
<value>systempropertymapping1.xml</value>
<value>dozerBeanMapping.xml</value>
</list>
</property>
<property name="factories">
<map>
<!-- the key matches the name of the factoryin the
dozerBeanMapping.xml file -->
<entry key="org.dozer.factories.SampleCustomBeanFactory">
<refbean="sampleCustomBeanFactory"/>
</entry>
<!-- more factories can be supplied withadditional
entry's -->
</map>
</property>
</bean>
<bean id="sampleCustomBeanFactory"
class="org.dozer.factories.SampleCustomBeanFactory"/>
</beans>
通過以bean的定義你的工廠,就可以把它注入到DozerBeanMapper類的實例中
6.15自定義創建方法
可以在Dozer中使用自定義的靜態創建方法來在映射期間創建目標對象的實例,它可以在字段級別或類級別設置
<mapping>
<class-acreate-method="someCreateMethod">
org.dozer.vo.InsideTestObject
</class-a>
<class-b>org.dozer.vo.InsideTestObjectPrime</class-b>
<field>
<a>label</a>
<b>labelPrime</b>
</field>
</mapping>
<mapping>
<class-a>org.dozer.vo.TestObject</class-a>
<class-b>org.dozer.vo.TestObjectPrime</class-b>
<field>
<a>createMethodType</a>
<bcreate-method="someCreateMethod">createMethodType</b>
</field>
</mapping>
用靜態工廠方法引用不同的類的實例也是可以的,這個是通過給出類的全名稱和方法名稱,並用點號分開來實現:
<bcreate-method="org.dozer.factory.Factory.create">field</b>
6.16自定義Get/Set方法
6.16.1映射字段不用get()和set()方法
被使用的屬性是可訪問的,就表明可以直接訪問那個字段,Dozer可以訪問沒有getter和setter方法的字段
<field>
<a>fieldAccessible</a>
<bis-accessible="true">fieldAccessible</b>
</field>
6.16.2 get()和set()方法都是雙向的
對於一些beans,可能有可能有非正統的getter和setter方法,Dozer支持用戶指定特定的setter和getter方法。為在在這種情況下,實現一個雙向映射,下面就是一個很好的例子,A中的源字段就指定了一個自定義的setter和getter方法來使用屬性。
<field>
<a set-method="placeValue"get-method="buildValue">value</a>
<b>value</b>
</field>
在這種情況下,我們調用addIntegerToList()方法把一個String映射到ArrayList,注意我們定義的是單身的映射,因為我們不能把一個ArrayList映射到一個String。
<!-- we can notmap a ArrayList to a String,
hence the one-waymapping -->
<fieldtype="one-way">
<a>integerStr</a>
<bset-method="addIntegerToList">integerList</b>
</field>
6.16.3 重載set()方法(雙向)
有時候set()方法也可以被重載,為了區分,我們可以添加一個類型作為一個參數。
<field>
<a>overloadGetField</a>
<b set-method="setOverloadSetField(java.util.Date)">
overloadSetField
</b>
</field>
6.16.4 遍歷方法映射(雙向)
Dozer也支持在映射級別遍歷方法,在下面的例子中,appleComputers這個List集合將會被遍歷到,並且每一個對象,方法addComputer都會被調用,任何一個字段被type=iterater標記都需要一個hint提示,get()方法能夠返回一個Array,List或Iterator。
<field>
<a>appleComputers</a>
<b set-method="addComputer"type="iterate">computers</b>
<b-hint>org.dozer.vo.AppleComputer</b-hint>
</field>
下面的例子中,兩個屬性都有遍歷的方法:
<field>
<a set-method="addCar"get-method="myIterateCars" type="iterate">
iterateCars
</a>
<b set-method="addIterateCar"type="iterate">iterateCars</b>
<a-hint>org.dozer.vo.Car</a-hint>
<b-hint>org.dozer.vo.Car</b-hint>
</field>
6.17通過引用來復制對象映射
Dozer支持通過一個對象的引用來復制一個對象,沒有這樣的對象轉換完成。這個方法可以使一個對象降低分配的數量,但是僅僅是在java bean對象被垃圾收集轉換以后才可以應用,這種方法通常推薦在性能調優的映射過程,確保兩個對象類型是相同的,否則運行時會出現異常,默認的值為false
<fieldcopy-by-reference="true">
<a>copyByReference</a>
<b>copyByReferencePrime</b>
</field>
在類級別也支持這種,在全局配置中定義你要用引用進行復制的對象類型即可
<configuration>
...
<copy-by-references>
<copy-by-reference>
org.dozer.vo.NoExtendBaseObjectGlobalCopyByReference
</copy-by-reference>
</copy-by-references>
</configuration>
在類級別上,wildcard表達式也是允許的,通過引用復制通過掩碼被應用,這個掩碼可以包括許多wildcard(*)字符。
<configuration>
...
<copy-by-references>
<copy-by-reference>
org.dozer.vo.*
</copy-by-reference>
<copy-by-reference>
org.dozer.*.vo.*DTO
</copy-by-reference>
</copy-by-references>
</configuration>
通過字段映射可以映射對象自身,在下面的例子中,SimpleAccount被映射到Address,也被映射到Account,假設Address是Account的一個屬性,我們怎么樣把SimpleAccount上的值映射到那個屬性,可以用this關鍵來表示映射整個源對象。
<mapping>
<class-a>org.dozer.vo.self.SimpleAccount</class-a>
<class-b>org.dozer.vo.self.Account</class-b>
<field>
<a>this</a>
<b>address</b>
</field>
</mapping>
<mapping>
<class-a>org.dozer.vo.self.SimpleAccount</class-a>
<class-b>org.dozer.vo.self.Address</class-b>
<field>
<a>streetName</a>
<b>street</b>
</field>
</mapping>
6.18繼承映射
在使用基類屬性的時候,可以減少映射,相同字段名是沒有必要在XML配置文件中指出,除非有hints。如果你在映射兩個了類的時候,也要映射兩個超類的相關屬性,你可能偏向於在子類中重新生成基類對象的映射,下面就是一個很好的例子。
<mapping>
<class-a>org.dozer.vo.SubClass</class-a>
<class-b>org.dozer.vo.SubClassPrime</class-b>
<field>
<!-- this is the same for all sub classes-->
<a>superAttribute</a>
<b>superAttr</b>
</field>
<field>
<a>attribute2</a>
<b>attributePrime2</b>
</field>
</mapping>
<mapping>
<class-a>org.dozer.vo.SubClass2</class-a>
<class-b>org.dozer.vo.SubClassPrime2</class-b>
<field>
<!-- this is the same for all sub classes-->
<a>superAttribute</a>
<b>superAttr</b>
</field>
<field>
<a>attribute2</a>
<b>attributePrime2</b>
</field>
</mapping>
在這兩個映射中,有一些字段是來自同一個超類,但是我們不得不在每一次子類映射的時候都要重復映射超類中的屬性。有一個更好的方法可以單獨映射基類,這可以針對每一個基類,特別是在大型的層次結構中,假設基類的名字在xml文件中是可以重視的。
<mapping>
<class-a>org.dozer.vo.SuperClass</class-a>
<class-b>org.dozer.vo.SuperClassPrime</class-b>
<field>
<a>superAttribute</a>
<b>superAttr</b>
</field>
</mapping>
<mapping>
<class-a>org.dozer.vo.SubClass</class-a>
<class-b>org.dozer.vo.SubClassPrime</class-b>
<field>
<a>attribute</a>
<b>attributePrime</b>
</field>
</mapping>
<mapping>
<class-a>org.dozer.vo.SubClass2</class-a>
<class-b>org.dozer.vo.SubClassPrime2</class-b>
<field>
<a>attribute2</a>
<b>attributePrime2</b>
</field>
</mapping>
轉載:https://blog.csdn.net/whhahyy/article/details/48594657