Dozer 使用小結


目錄

這篇文章是本人在閱讀Dozer官方文檔(5.5.1版本,官網已經一年多沒更新了)的過程中,整理下來我認為比較基礎的應用場景。

本文中提到的例子應該能覆蓋JavaBean映射的大部分場景,希望對你有所幫助。

概述

Dozer是什么?

Dozer是一個JavaBean映射工具庫。

它支持簡單的屬性映射,復雜類型映射,雙向映射,隱式顯式的映射,以及遞歸映射。

它支持三種映射方式:注解、API、XML。

它是開源的,遵從Apache 2.0 協議

安裝

引入jar包

maven方式

如果你的項目使用maven,添加以下依賴到你的pom.xml即可:

<dependency> <groupId>net.sf.dozer</groupId> <artifactId>dozer</artifactId> <version>5.4.0</version> </dependency>

非maven方式

如果你的項目不使用maven,那就只能發揚不怕苦不怕累的精神了。

使用Dozer需要引入Dozer的jar包以及其依賴的第三方jar包。

Eclipse插件

Dozer有插件可以在Eclipse中使用(不知道是否好用,反正我沒用過)

插件地址: http://dozer.sourceforge.net/eclipse-plugin

使用

將Dozer引入到工程中后,我們就可以來小試一番了。

實踐出真知,先以一個最簡單的例子來展示Dozer映射的處理過程。

准備

我們先准備兩個要互相映射的類

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應該轉換什么,怎么轉換。

注:官網着重建議:在現實應用中,最好不要每次映射對象時都創建一個Mapper實例來工作,這樣會產生不必要的開銷。如果你不使用IoC容器(如:spring)來管理你的項目,那么,最好將Mapper定義為單例模式。

映射配置文件

src/test/resources目錄下添加dozer/dozer-mapping.xml文件。
<mapping>標簽中允許你定義<class-a><class-b>,對應着相互映射的類。
<field>標簽里定義要映射的特殊屬性。需要注意<a><class-a>對應,<b><class-b>對應,聰明的你,猜也猜出來了吧。

<?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會加載這些規則。

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); } }

運行一下單元測試,綠燈通過。

Dozer支持的數據類型轉換

Dozer可以自動做數據類型轉換。當前,Dozer支持以下數據類型轉換(都是雙向的)

  • 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

    復雜類型和復雜類型

  • String to Primitive

    字符串和原型

  • String to Primitive Wrapper

    字符串和原型包裝類

  • String to Complex Type if the Complex Type contains a String constructor

    字符串和有字符串構造器的復雜類型(類)

  • String to Map

    字符串和Map

  • Collection to Collection

    集合和集合

  • Collection to Array

    集合和數組

  • Map to Complex Type

    Map和復雜類型

  • 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

    這些時間相關的常見類可以互換: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型的時間數值(單位:ms),就可以和任何支持Date/Calendar的對象轉換。

Dozer的映射配置

在前面的簡單例子中,我們體驗了一把Dozer的映射流程。但是兩個類進行映射,有很多復雜的情況,相應的,你也需要一些更復雜的配置。

Dozer有三種映射配置方式:

  • 注解方式
  • API方式
  • XML方式

用注解來配置映射

Dozer 5.3.2版本開始支持注解方式配置映射(只有一個注解:@Mapping)。可以應對一些簡單的映射處理,復雜的就玩不轉了。

看一下@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標記和目標類中對應的屬性就可以了。

@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); }

測試一下,綠燈通過。

官方文檔說,雖然當前版本(文檔的版本對應Dozer 5.5.1)僅支持@Mapping,但是在未來的發布版本會提供其他的注解功能,那就敬請期待吧(再次吐槽一下:一年多沒更新了)。

用API來配置映射

個人覺得這種方式比較麻煩,不推薦,也不想多做介紹,就是這么任性。

用XML來配置映射

需要強調的是:如果兩個類的所有屬性都能很好的互轉,可以你中有我,我中有你,不分彼此,那么就不要畫蛇添足的在xml中去聲明映射規則了。

屬性名不同時的映射(Basic Property Mapping)

Dozer會自動映射屬性名相同的屬性,所以不必添加在xml文件中。

<field> <a>one</a> <b>onePrime</b> </field>

字符串和日期映射(String to Date Mapping)

字符串在和日期進行映射時,允許用戶指定日期的格式。

格式的設置分為三個作用域級別:

屬性級別

對當前屬性有效(這個屬性必須是日期字符串)

<field> <a date-format="MM/dd/yyyy HH:mm:ss:SS">dateString</a> <b>dateObject</b> </field>

類級別

對這個類中的所有日期相關的屬性有效

<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>

全局級別

對整個文件中的所有日期相關的屬性有效。

<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>

集合和數組映射(Collection and Array Mapping)

Dozer可以自動處理以下類型的雙向轉換。

  • List to List
  • List to Array
  • Array to Array
  • Set to Set
  • Set to Array
  • Set to List

使用hint

如果使用泛型或數組,沒有必要使用hint。

如果不使用泛型或數組。在處理集合或數組之間的轉換時,你需要用hint指定目標列表的數據類型。

若你不指定hint,Dozer將認為目標集合和源集合的類型是一致的。

使用Hints的范例:

<field> <a>hintList</a> <b>hintList</b> <b-hint>org.dozer.vo.TheFirstSubClassPrime</b-hint> </field> 

累計映射和非累計映射(Cumulative vs. Non-Cumulative List Mapping)

如果你要轉換的目標類已經初始化,你可以選擇讓Dozer添加或更新對象到你的集合中。

而這取決於relationship-type配置,默認是累計。

它的設置有作用域級別:

  • 全局級
<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)

這里的孤兒是指目標集合中存在,但是源集合中不存在的元素。

你可以使用remove-orphans開關來選擇是否移除這樣的元素。

<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; }

映射規則

<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)

就像任何團體都有搗亂分子,類之間轉換時也有想要排除的因子。

如何在做類型轉換時,自動排除一些屬性,Dozer提供了幾種方法,這里只介紹一種比較通用的方法。

更多詳情參考官網

field-exclude可以排除不需要映射的屬性。

<field-exclude> <a>fieldToExclude</a> <b>fieldToExclude</b> </field-exclude>

單向映射(One-Way Mapping)

注:本文的映射方式,無特殊說明,都是雙向映射的。

有的場景可能希望轉換過程不可逆,即單向轉換。

單向轉換可以通過使用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)

全局配置用來設置全局的配置信息。此外,任何定制轉換都是在這里定義的。

全局配置都是可選的。

  • <date-format>表示日期格式
  • <stop-on-errors>錯誤處理開關
  • <wildcard>通配符
  • <trim-strings>裁剪字符串開關
<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>

全局配置的作用是幫助你少配置一些參數,如果個別類的映射規則需要變更,你可以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> 

定制轉換(Custom Converters)

如果Dozer默認的轉換規則不能滿足實際需要,你可以選擇定制轉換。

定制轉換通過配置XML來告訴Dozer如何去轉換兩個指定的類。當Dozer轉換這兩個指定類的時候,會調用你的映射規則去替換標准映射規則。

為了讓Dozer識別,你必須實現org.dozer.CustomConverter接口。否則,Dozer會拋異常。

具體做法:

(1) 創建一個類實現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中引用定制的映射規則

引用定制的映射規則也是分級的,你可以酌情使用。

  • 全局級
<?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> <!-- 總是雙向轉換的 --> <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支持映射規則的繼承機制。

屬性如果有着相同的名字則不需要在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的映射規則會被子類所繼承,所以不必再重復的在子類中去聲明。

參考

Dozer官方文檔 | Dozer源碼地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM