Orika JavaBean映射工具使用

京东云开发者
• 阅读 324

作者:京东零售 张宾

1.Orika是什么?

Orika是一个简单、快速的JavaBean拷贝框架,它能够递归地将数据从一个JavaBean复制到另一个JavaBean,这在多层应用开发中是非常有用的。

2.为什么要引入Orika?

工作中,我们经常需要将对象转换成不同的形式以适应不同的api,或者在不同业务层中传输对象而不同分层的对象存在不同的格式,因此我们需要编写映射代码将对象中的属性值从一种类型转换成另一种类型。

Orika用于简化多层之间的对象映射,避免苦苦挣扎于手工编码和基于反射的映射。Orika关注尽可能地自动化,同时根据需要提供配置和扩展实现定制。

3.项目常用javabean映射技术

Java反射:性能问题、无法解决嵌套javabean映射复制

手工映射:硬编码、代码量大

序列化方式:通过json、xml的序列化和反序列化方式,字段参数不相同时,需要硬编码赋值

4.常用bean映射工具

BeanUtils:apache的BeanUtils和spring的BeanUtils中拷贝方法的原理都是先用jdk中 java.beans.Introspector类的getBeanInfo()方法获取对象的属性信息及属性get/set方法,接着使用反射(Method的invoke(Object obj, Object... args))方法进行赋值。apache支持名称相同但类型不同的属性的转换,spring支持忽略某些属性不进行映射,他们都设置了缓存保存已解析过的BeanInfo信息。

BeanCopier:cglib的BeanCopier采用了不同的方法:它不是利用反射对属性进行赋值,而是直接使用ASM的MethodVisitor直接编写各属性的get/set方法(具体过程可见BeanCopier类的generateClass(ClassVisitor v)方法)生成class文件,然后进行执行。由于是直接生成字节码执行,所以BeanCopier的性能较采用反射的BeanUtils有较大提高。

Orika:底层采用了javassist类库生成Bean映射的字节码,之后直接加载执行生成的字节码文件,因此在速度上比使用反射进行赋值会快很多。Orika支持递归映射,将映射嵌套类直到用“简单”类型完成映射。它还包含故障保险,以正确处理正在尝试映射的对象中的递归引用。

5.如何使用Orika?

5.1 maven依赖

ma.glasnost.orika

orika-core

1.5.2

5.2 两个javabean字段名称相同映射

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

//字段名相同映射

mapperFactory.classMap(Person.class, PersonInfo.class).byDefault().register();

MapperFacade mapperFacade = mapperFactory.getMapperFacade();

Person person = new Person();

person.setFirstName("张");

person.setLastName("三");

PersonInfo personInfo = mapperFacade.map(person, PersonInfo.class);

输出结果:

Orika JavaBean映射工具使用

5.3 两个javabean字段名称不相同映射

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

mapperFactory.classMap(Person.class, PersonDesc.class)

.field("firstName", "givenName").field("lastName", "sirName")

.byDefault() .register();

MapperFacade mapperFacadeDesc = mapperFactory.getMapperFacade();

Person personNew = new Person();

personNew.setFirstName("王");

personNew.setLastName("五");

PersonDesc personDesc = mapperFacadeDesc.map(personNew, PersonDesc.class);

输出结果:

Orika JavaBean映射工具使用

5.4 数组和List映射javabean

public static class PersonNameList {

private List nameList;

public PersonNameList(List nameList) {

this.nameList = nameList;

}

//getter/setter方法

}

mapperFactory.classMap(PersonNameList.class, Person.class)

.field("nameList[0]", "firstName")

.field("nameList[1]", "lastName").register();

List nameParts = Arrays.asList(new String[]{"李", "四"});

Person person = mapperFactory.getMapperFacade().map(new PersonNameList(nameParts), Person.class);

输出结果:

Orika JavaBean映射工具使用

5.5 相同类型javabean复制(原型模式)

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

mapperFactory.classMap(Person.class, Person.class).byDefault().register();

Person person = new Person();

person.setFirstName("张");

person.setLastName("三");

Person personNew = mapperFactory.getMapperFacade().map(person, Person.class);

输出结果:

Orika JavaBean映射工具使用

5.6 嵌套javabean映射

mapperFactory.classMap(Flight.class, Flight.class).byDefault().register();

Cabin cabinY = new Cabin("Y", "8", 1200);

Cabin cabinF = new Cabin("C", "6", 1900);

Cabin cabinC = new Cabin("F", "A", 2400);

Flight flight = new Flight("CA1831", Lists.newArrayList(cabinY, cabinF, cabinC));

Flight flightCopy = mapperFactory.getMapperFacade().map(flight, Flight.class);

System.out.println("原对象:flight=" + JSON.toJSONString(flight));

System.out.println("新对象:flightCopy=" + JSON.toJSONString(flightCopy));

flightCopy.setFlightNo("MU5183");

flightCopy.getCabins().remove(0);

System.out.println("修改航变号和删除舱位:flightCopy=" + JSON.toJSONString(flightCopy)); System.out.println("原对象:flight=" + JSON.toJSONString(flight));

输出结果:

Orika JavaBean映射工具使用

5.7 List类型映射

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

mapperFactory.classMap(Cabin.class, CabinDesc.class).byDefault().register();

Cabin cabinY = new Cabin("Y", "8", 1200);

Cabin cabinF = new Cabin("C", "6", 1900);

Cabin cabinC = new Cabin("F", "A", 2400);

List cabinList = Lists.newArrayList(cabinY, cabinF, cabinC);

List cabinDescList = mapperFactory.getMapperFacade().mapAsList(cabinList, CabinDesc.class);

System.out.println("cabinList = " + JSON.toJSONString(cabinList));

System.out.println("cabinDescList = " + JSON.toJSONString(cabinDescList));

输出结果

Orika JavaBean映射工具使用

5.8 自定义数据类型转化器(String to Date)

继承接口CustomConverter类,覆盖实现convert方法

public static class StringToDateConverter extends CustomConverter<String, Date> {

private String dateFormat;

public StringToDateConverter(String dateFormat) {

this.dateFormat = dateFormat;

}

public Date convert(String s, Type<? extends Date> type, MappingContext mappingContext) {

try {

return DateUtils.parseDate(s, dateFormat);

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

}

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

mapperFactory.getConverterFactory()

.registerConverter(new StringToDateConverter("yyyy-MM-dd"));

mapperFactory.classMap(ArrayMapper.PersonNameList.class, Person.class)

.field("nameList[0]", "firstName").field("nameList[1]", "lastName")

.field("nameList[2]", "birthDate").register();

List nameParts = Arrays.asList(new String[]{"李", "四", "1989-10-11"});

Person person = mapperFactory.getMapperFacade().map(new ArrayMapper.PersonNameList(nameParts), Person.class);

System.out.println("nameParts = " + JSON.toJSONString(nameParts));

System.out.println("person = " + JSON.toJSONStringWithDateFormat(person,"yyyy-MM-dd"));

输出结果:

Orika JavaBean映射工具使用

6. 各映射工具的性能测试

每种方法先预热执行20次,而后再执行100000次获取每次执行的平均时间

Orika JavaBean映射工具使用

7.参考文献

官网:http://orika-mapper.github.io/orika-docs/intro.html

其他:https://www.jianshu.com/p/5daf68dc5758

http://tech.dianwoda.com/2017/11/04/gao-xing-neng-te

-xing-feng-fu-de-beanying-she-gong-ju-orika/

本文正在参加「金石计划」

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Easter79 Easter79
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_