本文共 10987 字,大约阅读时间需要 36 分钟。
这篇文章是本人在阅读Dozer官方文档(5.5.1版本,官网已经一年多没更新了)的过程中,整理下来我认为比较基础的应用场景。
本文中提到的例子应该能覆盖JavaBean映射的大部分场景,希望对你有所帮助。
Dozer是什么?
Dozer是一个JavaBean映射工具库。
它支持简单的属性映射,复杂类型映射,双向映射,隐式显式的映射,以及递归映射。
它支持三种映射方式:注解、API、XML。
它是开源的,遵从
maven方式
如果你的项目使用maven,添加以下依赖到你的pom.xml即可:
net.sf.dozer dozer 5.4.0
非maven方式
如果你的项目不使用maven,那就只能发扬不怕苦不怕累的精神了。
使用Dozer需要引入Dozer的jar包以及其依赖的第三方jar包。
Dozer有插件可以在Eclipse中使用(不知道是否好用,反正我没用过)
插件地址:
将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的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>
对应,聪明的你,猜也猜出来了吧。 org.zp.notes.spring.common.dozer.vo.NotSameAttributeA org.zp.notes.spring.common.dozer.vo.NotSameAttributeB name value
在src/test/resources
目录下添加spring/spring-dozer.xml
文件。
Dozer与Spring的整合很便利,你只需要声明一个DozerBeanMapperFactoryBean
,
mappingFiles
,DozerBeanMapperFactoryBean
会加载这些规则。 spring-dozer.xml文件范例
classpath*:dozer/dozer-mapping.xml
至此,万事具备,你只需要自动装配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支持以下数据类型转换(都是双向的)
原型(int、long等)和原型包装类(Integer、Long)
原型和定制的包装
原型包装类和包装类
原型和原型
复杂类型和复杂类型
字符串和原型
字符串和原型包装类
字符串和有字符串构造器的复杂类型(类)
字符串和Map
集合和集合
集合和数组
Map和复杂类型
Map和定制Map类型
枚举和枚举
这些时间相关的常见类可以互换:java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp, java.util.Calendar, java.util.GregorianCalendar
字符串和支持Date/Calendar的对象
如果一个对象的toString()方法返回的是一个代表long型的时间数值(单位:ms),就可以和任何支持Date/Calendar的对象转换。
在前面的简单例子中,我们体验了一把Dozer的映射流程。但是两个类进行映射,有很多复杂的情况,相应的,你也需要一些更复杂的配置。
Dozer有三种映射配置方式:
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
标记和目标类中对应的属性就可以了。
@Testpublic 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
,但是在未来的发布版本会提供其他的注解功能,那就敬请期待吧(再次吐槽一下:一年多没更新了)。
个人觉得这种方式比较麻烦,不推荐,也不想多做介绍,就是这么任性。
需要强调的是:如果两个类的所有属性都能很好的互转,可以你中有我,我中有你,不分彼此,那么就不要画蛇添足的在xml中去声明映射规则了。
Dozer会自动映射属性名相同的属性,所以不必添加在xml文件中。
one onePrime
字符串在和日期进行映射时,允许用户指定日期的格式。
格式的设置分为三个作用域级别:
属性级别
对当前属性有效(这个属性必须是日期字符串)
dateString dateObject
类级别
对这个类中的所有日期相关的属性有效
org.dozer.vo.TestObject org.dozer.vo.TestObjectPrime dateString dateObject
全局级别
对整个文件中的所有日期相关的属性有效。
MM/dd/yyyy HH:mm org.dozer.vo.TestObject org.dozer.vo.TestObjectPrime dateString dateObject
Dozer可以自动处理以下类型的双向转换。
使用hint
如果使用泛型或数组,没有必要使用hint。
如果不使用泛型或数组。在处理集合或数组之间的转换时,你需要用hint
指定目标列表的数据类型。
若你不指定hint
,Dozer将认为目标集合和源集合的类型是一致的。
使用Hints的范例:
hintList hintList org.dozer.vo.TheFirstSubClassPrime
累计映射和非累计映射(Cumulative vs. Non-Cumulative List Mapping)
如果你要转换的目标类已经初始化,你可以选择让Dozer添加或更新对象到你的集合中。
而这取决于relationship-type
配置,默认是累计。
它的设置有作用域级别:
non-cumulative
hintList hintList org.dozer.vo.TheFirstSubClass org.dozer.vo.TheFirstSubClassPrime
移动孤儿(Removing Orphans)
这里的孤儿是指目标集合中存在,但是源集合中不存在的元素。
你可以使用remove-orphans
开关来选择是否移除这样的元素。
srcList destList
所谓深度映射,是指允许你指定属性的属性(比如一个类的属性本身也是一个类)。举例来说
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;}
映射规则
org.zp.notes.spring.common.dozer.vo.Source org.zp.notes.spring.common.dozer.vo.Dest info info.content
就像任何团体都有捣乱分子,类之间转换时也有想要排除的因子。
如何在做类型转换时,自动排除一些属性,Dozer提供了几种方法,这里只介绍一种比较通用的方法。
更多详情参考。
field-exclude可以排除不需要映射的属性。
fieldToExclude fieldToExclude
注:本文的映射方式,无特殊说明,都是双向映射的。
有的场景可能希望转换过程不可逆,即单向转换。
单向转换可以通过使用one-way
来开启
类级别
org.dozer.vo.TestObjectFoo org.dozer.vo.TestObjectFooPrime oneFoo oneFooPrime
属性级别
org.dozer.vo.TestObjectFoo2 org.dozer.vo.TestObjectFooPrime2 oneFoo2 oneFooPrime2 oneFoo3.prime oneFooPrime3
全局配置用来设置全局的配置信息。此外,任何定制转换都是在这里定义的。
全局配置都是可选的。
<date-format>
表示日期格式<stop-on-errors>
错误处理开关<wildcard>
通配符<trim-strings>
裁剪字符串开关MM/dd/yyyy HH:mm true true false org.dozer.vo.TestCustomConverterObject another.type.to.Associate
全局配置的作用是帮助你少配置一些参数,如果个别类的映射规则需要变更,你可以mapping中覆盖它。
覆盖的范例如下
如果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中引用定制的映射规则
引用定制的映射规则也是分级的,你可以酌情使用。
org.dozer.vo.CustomDoubleObject java.lang.Double org.dozer.vo.TestCustomConverterHashMapObject org.dozer.vo.TestCustomConverterHashMapPrimeObject
org.dozer.vo.SimpleObj org.dozer.vo.SimpleObjPrime2 field1 field1Prime
Dozer支持映射规则的继承机制。
属性如果有着相同的名字则不需要在xml中配置,除非使用了hint
我们来看一个例子
org.dozer.vo.SuperClass org.dozer.vo.SuperClassPrime superAttribute superAttr org.dozer.vo.SubClass org.dozer.vo.SubClassPrime attribute attributePrime org.dozer.vo.SubClass2 org.dozer.vo.SubClassPrime2 attribute2 attributePrime2
在上面的例子中SubClass、SubClass2是SuperClass的子类;
SubClassPrime和SubClassPrime2是SuperClassPrime的子类。
superAttribute和superAttr的映射规则会被子类所继承,所以不必再重复的在子类中去声明。
|
转载地址:http://wtnqa.baihongyu.com/