这篇文章是本人在阅读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源码地址

Dozer 使用小结的更多相关文章

  1. 对象转换利器之Dozer

    什么是Dozer Dozer是一个Java对象转换工具,可以在JavaBean和JavaBean之间进行递归数据复制,并且适应不同复杂的类型.Dozer会直接将名称相同的属性进行复制,属性名不同或者有 ...

  2. 从零开始编写自己的C#框架(26)——小结

    一直想写个总结,不过实在太忙了,所以一直拖啊拖啊,拖到现在,不过也好,有了这段时间的沉淀,发现自己又有了小小的进步.哈哈...... 原想框架开发的相关开发步骤.文档.代码.功能.部署等都简单的讲过了 ...

  3. Python自然语言处理工具小结

    Python自然语言处理工具小结 作者:白宁超 2016年11月21日21:45:26 目录 [Python NLP]干货!详述Python NLTK下如何使用stanford NLP工具包(1) [ ...

  4. java单向加密算法小结(2)--MD5哈希算法

    上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...

  5. iOS--->微信支付小结

    iOS--->微信支付小结 说起支付,除了支付宝支付之外,微信支付也是我们三方支付中最重要的方式之一,承接上面总结的支付宝,接下来把微信支付也总结了一下 ***那么首先还是由公司去创建并申请使用 ...

  6. iOS 之UITextFiled/UITextView小结

    一:编辑被键盘遮挡的问题 参考自:http://blog.csdn.net/windkisshao/article/details/21398521 1.自定方法 ,用于移动视图 -(void)mov ...

  7. K近邻法(KNN)原理小结

    K近邻法(k-nearst neighbors,KNN)是一种很基本的机器学习方法了,在我们平常的生活中也会不自主的应用.比如,我们判断一个人的人品,只需要观察他来往最密切的几个人的人品好坏就可以得出 ...

  8. scikit-learn随机森林调参小结

    在Bagging与随机森林算法原理小结中,我们对随机森林(Random Forest, 以下简称RF)的原理做了总结.本文就从实践的角度对RF做一个总结.重点讲述scikit-learn中RF的调参注 ...

  9. Bagging与随机森林算法原理小结

    在集成学习原理小结中,我们讲到了集成学习有两个流派,一个是boosting派系,它的特点是各个弱学习器之间有依赖关系.另一种是bagging流派,它的特点是各个弱学习器之间没有依赖关系,可以并行拟合. ...

随机推荐

  1. 令人血脉喷张的animate.css

    都说html5.css3是大局是潮流,尽管css3也不难,学起来比较简单,但我还是喜欢他的轻量级集大成者的animate.css框架,初学的小伙伴们加油啦 下面我分析一下animate.css框架里面 ...

  2. Everything实用技巧随手记

    Everything是一款非常好用的即时搜索软件,针对文件名目录名搜索,并且支持通配符.正则表达式,胜过windows自带搜索N倍. 在使用中有些许不便,比如搜索结果中出现很多快捷方式,或者搜索到不想 ...

  3. nginx www解析失败问题解决

    nginx www解析失败: nginx代理IIS下域名时 xxxx.xxx可以解析 但www.xxxx.xxx解析失败 IIS增加ip解析:配置下127.0.0.1就可以解析了.

  4. Java web 开发填坑记 2 -如何正确的创建一个Java Web 项目

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/72566261 本文出自[赵彦军的博客] Java web 开发填坑记 1-如何正确 ...

  5. 13 款惊艳的 Node.js 框架——第2部分

    [编者按]本文作者为 Peter Wayner,主要介绍13款至精至简的 Node.js 框架,帮助你简化高速网站.丰富 API 以及实时应用的开发流程.本文系国内 ITOM 管理平台 OneAPM ...

  6. innodb compressed 表碰到的问题,BUG么?

    innodb compressed 表碰到的问题 (摘自 http://papaisadba.puyu.me/?p=239) compressed innodb 表是MySQL 5.5开始提供的功能, ...

  7. winform调用jar包

    因为工作需要,需要做一个数据上传的程序,客户规定的是:数据接口采用http连接,采用JSON-RPC轻量级远程调用协议.所以决定用winform做一个管理界面(其中还包括其他的功能),java完成数据 ...

  8. for之于while的优势

    前言 for与while各有功效,下面就只列举for之于while的优势有哪些 优势 1.循环中提供了特殊的机会来将变量的作用域最小化.(无论是传统的还是for-each形式的)for循环,都允许声明 ...

  9. python爬虫_入门_翻页

    写出来的爬虫,肯定不能只在一个页面爬,只要要爬几个页面,甚至一个网站,这时候就需要用到翻页了 其实翻页很简单,还是这个页面http://bbs.fengniao.com/forum/10384633. ...

  10. 自定义上传控件(兼容IE8)

    上传控件是 <input type="file"/> 而实际开发过程中,都会自定义一个控件,因为这个控件本身难看,而且不同浏览器效果不一样. 如IE8显示如下: 谷歌浏 ...