這篇文章是本人在閱讀Dozer官方文檔(5.5.1版本斗这,官網(wǎng)已經(jīng)一年多沒更新了)的過程中,整理下來我認(rèn)為比較基礎(chǔ)的應(yīng)用場景。
本文中提到的例子應(yīng)該能覆蓋JavaBean映射的大部分場景经瓷,希望對你有所幫助翠储。
概述
Dozer是什么?
Dozer是一個JavaBean映射工具庫绘雁。
它支持簡單的屬性映射,復(fù)雜類型映射援所,雙向映射庐舟,隱式顯式的映射,以及遞歸映射住拭。
它支持三種映射方式:注解挪略、API、XML滔岳。
它是開源的杠娱,遵從Apache 2.0 協(xié)議
安裝
引入jar包
maven方式
如果你的項目使用maven,添加以下依賴到你的pom.xml即可:
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.4.0</version>
</dependency>
非maven方式
如果你的項目不使用maven谱煤,那就只能發(fā)揚(yáng)不怕苦不怕累的精神了摊求。
使用Dozer需要引入Dozer的jar包以及其依賴的第三方j(luò)ar包。
Eclipse插件
Dozer有插件可以在Eclipse中使用(不知道是否好用刘离,反正我沒用過)
插件地址: http://dozer.sourceforge.net/eclipse-plugin
使用
將Dozer引入到工程中后室叉,我們就可以來小試一番了。
實踐出真知硫惕,先以一個最簡單的例子來展示Dozer映射的處理過程茧痕。
準(zhǔn)備
我們先準(zhǔn)備兩個要互相映射的類
NotSameAttributeA.java
public class NotSameAttributeA {
private long id;
private String name;
private Date date;
// 省略getter/setter
}
NotSameAttributeB.java
public class NotSameAttributeB {
private long id;
private String value;
private Date date;
// 省略getter/setter
}
這兩個類存在屬性名不完全相同的情況:name 和 value。
Dozer的配置
為什么要有映射配置?
如果要映射的兩個對象有完全相同的屬性名恼除,那么一切都很簡單踪旷。
只需要直接使用Dozer的API即可:
Mapper mapper = new DozerBeanMapper();
DestinationObject destObject =
mapper.map(sourceObject, DestinationObject.class);
但實際映射時,往往存在屬性名不同的情況缚柳。
所以埃脏,你需要一些配置來告訴Dozer應(yīng)該轉(zhuǎn)換什么,怎么轉(zhuǎn)換秋忙。
注:官網(wǎng)著重建議:在現(xiàn)實應(yīng)用中彩掐,最好不要每次映射對象時都創(chuàng)建一個Mapper
實例來工作,這樣會產(chǎn)生不必要的開銷灰追。如果你不使用IoC容器(如:spring)來管理你的項目堵幽,那么狗超,最好將Mapper
定義為單例模式。
映射配置文件
在src/test/resources
目錄下添加dozer/dozer-mapping.xml
文件朴下。
<mapping>
標(biāo)簽中允許你定義<class-a>
和<class-b>
努咐,對應(yīng)著相互映射的類。
<field>
標(biāo)簽里定義要映射的特殊屬性殴胧。需要注意<a>
和<class-a>
對應(yīng)渗稍,<b>
和<class-b>
對應(yīng),聰明的你团滥,猜也猜出來了吧竿屹。
<?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 date-format="yyyy-MM-dd">
<class-a>org.zp.notes.spring.common.dozer.vo.NotSameAttributeA</class-a>
<class-b>org.zp.notes.spring.common.dozer.vo.NotSameAttributeB</class-b>
<field>
<a>name</a>
<b>value</b>
</field>
</mapping>
</mappings>
與Spring整合
配置 DozerBeanMapperFactoryBean
在src/test/resources
目錄下添加spring/spring-dozer.xml
文件。
Dozer與Spring的整合很便利灸姊,你只需要聲明一個DozerBeanMapperFactoryBean
拱燃,
將所有的dozer映射配置文件作為屬性注入到mappingFiles
,
DozerBeanMapperFactoryBean
會加載這些規(guī)則力惯。
spring-dozer.xml文件范例
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
default-autowire="byName" default-lazy-init="false">
<bean id="mapper" class="org.dozer.spring.DozerBeanMapperFactoryBean">
<property name="mappingFiles">
<list>
<value>classpath*:dozer/dozer-mapping.xml</value>
</list>
</property>
</bean>
</beans>
自動裝配
至此碗誉,萬事具備,你只需要自動裝配mapper
父晶。
RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/spring-dozer.xml"})
@TransactionConfiguration(defaultRollback = false)
public class DozerTest extends TestCase {
@Autowired
Mapper mapper;
@Test
public void testNotSameAttributeMapping() {
NotSameAttributeA src = new NotSameAttributeA();
src.setId(007);
src.setName("邦德");
src.setDate(new Date());
NotSameAttributeB desc = mapper.map(src, NotSameAttributeB.class);
Assert.assertNotNull(desc);
}
}
運(yùn)行一下單元測試哮缺,綠燈通過。
Dozer支持的數(shù)據(jù)類型轉(zhuǎn)換
Dozer可以自動做數(shù)據(jù)類型轉(zhuǎn)換诱建。當(dāng)前蝴蜓,Dozer支持以下數(shù)據(jù)類型轉(zhuǎn)換(都是雙向的)
-
Primitive to Primitive Wrapper
原型(int、long等)和原型包裝類(Integer俺猿、Long)
-
Primitive to Custom Wrapper
原型和定制的包裝
-
Primitive Wrapper to Primitive Wrapper
原型包裝類和包裝類
-
Primitive to Primitive
原型和原型
-
Complex Type to Complex Type
復(fù)雜類型和復(fù)雜類型
-
String to Primitive
字符串和原型
-
String to Primitive Wrapper
字符串和原型包裝類
-
String to Complex Type if the Complex Type contains a String constructor
字符串和有字符串構(gòu)造器的復(fù)雜類型(類)
-
String to Map
字符串和Map
-
Collection to Collection
集合和集合
-
Collection to Array
集合和數(shù)組
-
Map to Complex Type
Map和復(fù)雜類型
-
Map to Custom Map Type
Map和定制Map類型
-
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
這些時間相關(guān)的常見類可以互換: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.
字符串和支持Date/Calendar的對象
-
**Objects containing a toString() method that produces a long representing time in (ms) to any supported Date/Calendar object. **
如果一個對象的toString()方法返回的是一個代表long型的時間數(shù)值(單位:ms)茎匠,就可以和任何支持Date/Calendar的對象轉(zhuǎn)換。
Dozer的映射配置
在前面的簡單例子中押袍,我們體驗了一把Dozer的映射流程诵冒。但是兩個類進(jìn)行映射,有很多復(fù)雜的情況谊惭,相應(yīng)的汽馋,你也需要一些更復(fù)雜的配置。
Dozer有三種映射配置方式:
- 注解方式
- API方式
- XML方式
用注解來配置映射
Dozer 5.3.2版本開始支持注解方式配置映射(只有一個注解:@Mapping
)圈盔”荆可以應(yīng)對一些簡單的映射處理,復(fù)雜的就玩不轉(zhuǎn)了驱敲。
看一下@Mapping
的聲明就可以知道铁蹈,這個注解只能用于元素和方法。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface Mapping {
String value() default "";
}
讓我們來試試吧:
TargetBean.java
public class SourceBean {
private Long id;
private String name;
@Mapping("binaryData")
private String data;
@Mapping("pk")
public Long getId() {
return this.id;
}
//其余getter/setter方法略
}
TargetBean.java
public class TargetBean {
private String pk;
private String name;
private String binaryData;
//getter/setter方法略
}
定義了兩個相互映射的Java類众眨,只需要在源類中用@Mapping
標(biāo)記和目標(biāo)類中對應(yīng)的屬性就可以了握牧。
@Test
public void testAnnotationMapping() {
SourceBean src = new SourceBean();
src.setId(7L);
src.setName("邦德");
src.setData("00000111");
TargetBean desc = mapper.map(src, TargetBean.class);
Assert.assertNotNull(desc);
}
測試一下容诬,綠燈通過。
官方文檔說沿腰,雖然當(dāng)前版本(文檔的版本對應(yīng)Dozer 5.5.1)僅支持@Mapping
览徒,但是在未來的發(fā)布版本會提供其他的注解功能,那就敬請期待吧(再次吐槽一下:一年多沒更新了)颂龙。
用API來配置映射
個人覺得這種方式比較麻煩习蓬,不推薦,也不想多做介紹措嵌,就是這么任性友雳。
用XML來配置映射
需要強(qiáng)調(diào)的是:如果兩個類的所有屬性都能很好的互轉(zhuǎn),可以你中有我铅匹,我中有你,不分彼此饺藤,那么就不要畫蛇添足的在xml中去聲明映射規(guī)則了包斑。
屬性名不同時的映射(Basic Property Mapping)
Dozer會自動映射屬性名相同的屬性,所以不必添加在xml文件中涕俗。
<field>
<a>one</a>
<b>onePrime</b>
</field>
字符串和日期映射(String to Date Mapping)
字符串在和日期進(jìn)行映射時罗丰,允許用戶指定日期的格式。
格式的設(shè)置分為三個作用域級別:
屬性級別
對當(dāng)前屬性有效(這個屬性必須是日期字符串)
<field>
<a date-format="MM/dd/yyyy HH:mm:ss:SS">dateString</a>
<b>dateObject</b>
</field>
類級別
對這個類中的所有日期相關(guān)的屬性有效
<mapping date-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>
全局級別
對整個文件中的所有日期相關(guān)的屬性有效再姑。
<mappings>
<configuration>
<date-format>MM/dd/yyyy HH: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>
</mappings>
集合和數(shù)組映射(Collection and Array Mapping)
Dozer可以自動處理以下類型的雙向轉(zhuǎn)換萌抵。
- List to List
- List to Array
- Array to Array
- Set to Set
- Set to Array
- Set to List
使用hint
如果使用泛型或數(shù)組,沒有必要使用hint元镀。
如果不使用泛型或數(shù)組绍填。在處理集合或數(shù)組之間的轉(zhuǎn)換時,你需要用hint
指定目標(biāo)列表的數(shù)據(jù)類型栖疑。
若你不指定hint
讨永,Dozer將認(rèn)為目標(biāo)集合和源集合的類型是一致的。
使用Hints的范例:
<field>
<a>hintList</a>
<b>hintList</b>
<b-hint>org.dozer.vo.TheFirstSubClassPrime</b-hint>
</field>
累計映射和非累計映射(Cumulative vs. Non-Cumulative List Mapping)
如果你要轉(zhuǎn)換的目標(biāo)類已經(jīng)初始化遇革,你可以選擇讓Dozer添加或更新對象到你的集合中卿闹。
而這取決于relationship-type
配置,默認(rèn)是累計萝快。
它的設(shè)置有作用域級別:
- 全局級
<mappings>
<configuration>
<relationship-type>non-cumulative</relationship-type>
</configuration>
</mappings>
- 類級別
<mappings>
<mapping relationship-type="non-cumulative">
<!-- 省略 -->
</mapping>
</mappings>
- 屬性級別
<field relationship-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>
移動孤兒(Removing Orphans)
這里的孤兒是指目標(biāo)集合中存在锻霎,但是源集合中不存在的元素。
你可以使用remove-orphans
開關(guān)來選擇是否移除這樣的元素揪漩。
<field remove-orphans="true">
<a>srcList</a>
<b>destList</b>
</field>
深度映射(Deep Mapping)
所謂深度映射旋恼,是指允許你指定屬性的屬性(比如一個類的屬性本身也是一個類)。舉例來說
Source.java
public class Source {
private long id;
private String info;
}
Dest.java
public class Dest {
private long id;
private Info info;
}
public class Info {
private String content;
}
映射規(guī)則
<mapping>
<class-a>org.zp.notes.spring.common.dozer.vo.Source</class-a>
<class-b>org.zp.notes.spring.common.dozer.vo.Dest</class-b>
<field>
<a>info</a>
<b>info.content</b>
</field>
</mapping>
排除屬性(Excluding Fields)
就像任何團(tuán)體都有搗亂分子氢拥,類之間轉(zhuǎn)換時也有想要排除的因子蚌铜。
如何在做類型轉(zhuǎn)換時锨侯,自動排除一些屬性,Dozer提供了幾種方法冬殃,這里只介紹一種比較通用的方法囚痴。
更多詳情參考官網(wǎng)。
field-exclude可以排除不需要映射的屬性审葬。
<field-exclude>
<a>fieldToExclude</a>
<b>fieldToExclude</b>
</field-exclude>
單向映射(One-Way Mapping)
注:本文的映射方式深滚,無特殊說明,都是雙向映射的涣觉。
有的場景可能希望轉(zhuǎn)換過程不可逆痴荐,即單向轉(zhuǎn)換。
單向轉(zhuǎn)換可以通過使用one-way
來開啟
類級別
<mapping type="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>
全局配置(Global Configuration)
全局配置用來設(shè)置全局的配置信息官册。此外生兆,任何定制轉(zhuǎn)換都是在這里定義的。
全局配置都是可選的膝宁。
-
<date-format>
表示日期格式 -
<stop-on-errors>
錯誤處理開關(guān) -
<wildcard>
通配符 -
<trim-strings>
裁剪字符串開關(guān)
<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 always bi-directional -->
<converter type="org.dozer.converters.TestCustomConverter" >
<class-a>org.dozer.vo.TestCustomConverterObject</class-a>
<class-b>another.type.to.Associate</class-b>
</converter>
</custom-converters>
</configuration>
全局配置的作用是幫助你少配置一些參數(shù)鸦难,如果個別類的映射規(guī)則需要變更,你可以mapping中覆蓋它员淫。
覆蓋的范例如下
<mapping date-format="MM-dd-yyyy HH:mm:ss">
<!-- 省略 -->
</mapping>
<mapping wildcard="false">
<!-- 省略 -->
</mapping>
<mapping stop-on-errors="false">
<!-- 省略 -->
</mapping>
<mapping trim-strings="true">
<!-- 省略 -->
</mapping>
定制轉(zhuǎn)換(Custom Converters)
如果Dozer默認(rèn)的轉(zhuǎn)換規(guī)則不能滿足實際需要合蔽,你可以選擇定制轉(zhuǎn)換。
定制轉(zhuǎn)換通過配置XML來告訴Dozer如何去轉(zhuǎn)換兩個指定的類介返。當(dāng)Dozer轉(zhuǎn)換這兩個指定類的時候拴事,會調(diào)用你的映射規(guī)則去替換標(biāo)準(zhǔn)映射規(guī)則。
為了讓Dozer識別圣蝎,你必須實現(xiàn)org.dozer.CustomConverter
接口刃宵。否則,Dozer會拋異常捅彻。
具體做法:
(1) 創(chuàng)建一個類實現(xiàn)org.dozer.CustomConverter
接口组去。
public class TestCustomConverter 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("Converter TestCustomConverter "
+ "used incorrectly. Arguments passed in were:"
+ destination + " and " + source);
}
}
(2) 在xml中引用定制的映射規(guī)則
引用定制的映射規(guī)則也是分級的,你可以酌情使用步淹。
- 全局級
<?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>
<!-- 總是雙向轉(zhuǎn)換的 -->
<custom-converters>
<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 mapping everything 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>
- 屬性級
<mapping>
<class-a>org.dozer.vo.SimpleObj</class-a>
<class-b>org.dozer.vo.SimpleObjPrime2</class-b>
<field custom-converter=
"org.dozer.converters.TestCustomConverter">
<a>field1</a>
<b>field1Prime</b>
</field>
</mapping>
映射的繼承(Inheritance Mapping)
Dozer支持映射規(guī)則的繼承機(jī)制从隆。
屬性如果有著相同的名字則不需要在xml中配置,除非使用了hint
我們來看一個例子
<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>
在上面的例子中SubClass缭裆、SubClass2是SuperClass的子類键闺;
SubClassPrime和SubClassPrime2是SuperClassPrime的子類。
superAttribute和superAttr的映射規(guī)則會被子類所繼承澈驼,所以不必再重復(fù)的在子類中去聲明驹愚。