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
什么是Dozer Dozer是一个Java对象转换工具,可以在JavaBean和JavaBean之间进行递归数据复制,并且适应不同复杂的类型.Dozer会直接将名称相同的属性进行复制,属性名不同或者有 ...
- 从零开始编写自己的C#框架(26)——小结
一直想写个总结,不过实在太忙了,所以一直拖啊拖啊,拖到现在,不过也好,有了这段时间的沉淀,发现自己又有了小小的进步.哈哈...... 原想框架开发的相关开发步骤.文档.代码.功能.部署等都简单的讲过了 ...
- Python自然语言处理工具小结
Python自然语言处理工具小结 作者:白宁超 2016年11月21日21:45:26 目录 [Python NLP]干货!详述Python NLTK下如何使用stanford NLP工具包(1) [ ...
- java单向加密算法小结(2)--MD5哈希算法
上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...
- iOS--->微信支付小结
iOS--->微信支付小结 说起支付,除了支付宝支付之外,微信支付也是我们三方支付中最重要的方式之一,承接上面总结的支付宝,接下来把微信支付也总结了一下 ***那么首先还是由公司去创建并申请使用 ...
- iOS 之UITextFiled/UITextView小结
一:编辑被键盘遮挡的问题 参考自:http://blog.csdn.net/windkisshao/article/details/21398521 1.自定方法 ,用于移动视图 -(void)mov ...
- K近邻法(KNN)原理小结
K近邻法(k-nearst neighbors,KNN)是一种很基本的机器学习方法了,在我们平常的生活中也会不自主的应用.比如,我们判断一个人的人品,只需要观察他来往最密切的几个人的人品好坏就可以得出 ...
- scikit-learn随机森林调参小结
在Bagging与随机森林算法原理小结中,我们对随机森林(Random Forest, 以下简称RF)的原理做了总结.本文就从实践的角度对RF做一个总结.重点讲述scikit-learn中RF的调参注 ...
- Bagging与随机森林算法原理小结
在集成学习原理小结中,我们讲到了集成学习有两个流派,一个是boosting派系,它的特点是各个弱学习器之间有依赖关系.另一种是bagging流派,它的特点是各个弱学习器之间没有依赖关系,可以并行拟合. ...
随机推荐
- 令人血脉喷张的animate.css
都说html5.css3是大局是潮流,尽管css3也不难,学起来比较简单,但我还是喜欢他的轻量级集大成者的animate.css框架,初学的小伙伴们加油啦 下面我分析一下animate.css框架里面 ...
- Everything实用技巧随手记
Everything是一款非常好用的即时搜索软件,针对文件名目录名搜索,并且支持通配符.正则表达式,胜过windows自带搜索N倍. 在使用中有些许不便,比如搜索结果中出现很多快捷方式,或者搜索到不想 ...
- nginx www解析失败问题解决
nginx www解析失败: nginx代理IIS下域名时 xxxx.xxx可以解析 但www.xxxx.xxx解析失败 IIS增加ip解析:配置下127.0.0.1就可以解析了.
- Java web 开发填坑记 2 -如何正确的创建一个Java Web 项目
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/72566261 本文出自[赵彦军的博客] Java web 开发填坑记 1-如何正确 ...
- 13 款惊艳的 Node.js 框架——第2部分
[编者按]本文作者为 Peter Wayner,主要介绍13款至精至简的 Node.js 框架,帮助你简化高速网站.丰富 API 以及实时应用的开发流程.本文系国内 ITOM 管理平台 OneAPM ...
- innodb compressed 表碰到的问题,BUG么?
innodb compressed 表碰到的问题 (摘自 http://papaisadba.puyu.me/?p=239) compressed innodb 表是MySQL 5.5开始提供的功能, ...
- winform调用jar包
因为工作需要,需要做一个数据上传的程序,客户规定的是:数据接口采用http连接,采用JSON-RPC轻量级远程调用协议.所以决定用winform做一个管理界面(其中还包括其他的功能),java完成数据 ...
- for之于while的优势
前言 for与while各有功效,下面就只列举for之于while的优势有哪些 优势 1.循环中提供了特殊的机会来将变量的作用域最小化.(无论是传统的还是for-each形式的)for循环,都允许声明 ...
- python爬虫_入门_翻页
写出来的爬虫,肯定不能只在一个页面爬,只要要爬几个页面,甚至一个网站,这时候就需要用到翻页了 其实翻页很简单,还是这个页面http://bbs.fengniao.com/forum/10384633. ...
- 自定义上传控件(兼容IE8)
上传控件是 <input type="file"/> 而实际开发过程中,都会自定义一个控件,因为这个控件本身难看,而且不同浏览器效果不一样. 如IE8显示如下: 谷歌浏 ...