简介
我们在业务中,经常需要转换对象,来解决各种 POJO,VO,DTO 之间的转换,在不使用工具类的情况下,需要写大量的 Get,Set 方法,代码看起来很臃肿.现在也有很多工具类可以让我们来使用,解决这个问题,常用的有 mapstruct、Spring BeanUtils、Apache BeanUtils、dozer 等,今天要介绍的是 orika
Orika是一个简单、快速的JavaBean拷贝框架,它能够递归地将数据从一个JavaBean复制到另一个JavaBean,这在多层应用开发中是非常有用的。Orika 底层采用了javassist类库生成Bean映射的字节码,之后直接加载执行生成的字节码文件,因此在速度上比使用反射进行赋值会快很多
使用
要使用 orika 实现需要添加他的依赖
<!-- 复制类工具 -->
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.4.6</version>
</dependency>
复制代码
定义两个实体类 user 和 userVo 用来转换
@Data
public class User {
private Integer id;
private String name;
private String phone;
private Integer age;
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
private Date date;
}
复制代码
@Data
public class UserVo {
private Integer id;
private String name;
private String telephone;
private String myAge;
private String date;
private String addr;
private String ip;
}
复制代码
编写一个测试方法 使用 orika
@GetMapping("/testOrika")
public HttpResult testOrika() {
User user = new User();
user.setId(1);
user.setName("哈哈");
user.setPhone("1234567");
user.setAge(18);
user.setDate(DateUtil.date());
System.out.println("user:"+user);
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
UserVo userVo = mapperFactory.getMapperFacade().map(user, UserVo.class);
System.out.println("userVo:"+userVo);
return HttpResult.ok(userVo);
}
复制代码
执行结果
可以看到字段名一致的都可以映射到,并且date类的时间也成功转换成字符串的时间
那么怎么把字段不一致的也映射到对应的字段里呢,毕竟平时不同实体类字段名经常是不一致的
不同字段名映射
@GetMapping("/testOrika")
public HttpResult testOrika() {
User user = new User();
user.setId(1);
user.setName("哈哈");
user.setPhone("1234567");
user.setAge(18);
user.setDate(DateUtil.date());
System.out.println("user:"+user);
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(User.class,UserVo.class)
.field("phone","telephone")
.field("age","myAge")
.byDefault().register();
UserVo userVo = mapperFactory.getMapperFacade().map(user, UserVo.class);
System.out.println("userVo:"+userVo);
return HttpResult.ok(userVo);
}
复制代码
在这里添加了字段的映射 结果:
可以看到不同字段名也映射到了数据
当我们反过来映射数据用 userVo 转换 user 测试
@GetMapping("/testOrika2")
public HttpResult testOrika2() {
UserVo userVo = new UserVo();
userVo.setId(1);
userVo.setName("哈哈");
userVo.setTelephone("123456");
userVo.setMyAge("20");
userVo.setDate("2021-12-01 10:00:00");
userVo.setAddr("xx");
userVo.setIp("xx");
System.out.println("userVo:"+userVo);
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(UserVo.class,User.class)
.field("telephone","phone")
.field("myAge","age")
.byDefault().register();
User user = mapperFactory.getMapperFacade().map(userVo, User.class);
System.out.println("user:"+user);
return HttpResult.ok(user);
}
复制代码
结果:
系统报错了,出现了反射异常 这是由于用 字符串转换时间类型时无法转换导致报错.
如何解决呢,我们可以实现一个自定义转换器,来让他转换时间
实现自定义转换器
public class DateConverter extends BidirectionalConverter<String, Date> {
@Override
public Date convertTo(String str, Type<Date> date) {
return DateUtil.parseDateTime(str);
}
@Override
public String convertFrom(Date date, Type<String> type) {
return DateUtil.formatDateTime(date);
}
}
复制代码
实现 BidirectionalConverter 类 重写他里面的方法,自定义返回结果
改造一下 testOrika2 方法
@GetMapping("/testOrika2")
public HttpResult testOrika2() {
UserVo userVo = new UserVo();
userVo.setId(1);
userVo.setName("哈哈");
userVo.setTelephone("123456");
userVo.setMyAge("20");
userVo.setDate("2021-12-01 10:00:00");
userVo.setAddr("xx");
userVo.setIp("xx");
System.out.println("userVo:"+userVo);
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.getConverterFactory().registerConverter("dateConverter",new DateConverter());
mapperFactory.classMap(UserVo.class,User.class)
.field("telephone","phone")
.field("myAge","age")
.fieldMap("date","date").converter("dateConverter").add()
.byDefault().register();
User user = mapperFactory.getMapperFacade().map(userVo, User.class);
System.out.println("user:"+user);
return HttpResult.ok(user);
}
复制代码
执行结果:
可以看到时间也成功转换了
orika 还可以直接转换集合 更多的方法可以去他的官网了解 orika-mapper.github.io/orika-docs/…
可以自己封装工具类使用
public class BeanCopyUtil {
/**
*
* @param sourceObject 数据源
* @param destinationClass 要转换的数据类型
* @param <S>
* @param <D>
* @return
*/
public static <S,D>D map(S sourceObject, Class<D> destinationClass) {
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
MapperFacade mapperFacade = mapperFactory.getMapperFacade();
return mapperFacade.map(sourceObject,destinationClass);
}
/**
*
* @param sourceObject 数据源
* @param destinationClass 要转换的数据类型
* @param converter 类型转换器
* @param converMap 要转换的字段集合
* @param <S>
* @param <D>
* @return
*/
public static <S,D>D map(S sourceObject, Class<D> destinationClass, BidirectionalConverter converter, Map<String, String> converMap) {
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
if (converter != null && !converMap.isEmpty()) {
mapperFactory.getConverterFactory().registerConverter("dateConverter",new DateConverter());
ClassMapBuilder<?, D> classMapBuilder = mapperFactory.classMap(sourceObject.getClass(), destinationClass);
for (Map.Entry<String, String> entry : converMap.entrySet()) {
classMapBuilder.fieldMap(entry.getKey(),entry.getValue()).converter("dateConverter").add();
}
classMapBuilder.byDefault().register();
}
MapperFacade mapperFacade = mapperFactory.getMapperFacade();
return mapperFacade.map(sourceObject,destinationClass);
}
/**
*
* @param sourceObject 数据源
* @param destinationClass 要转换的数据类型
* @param fieldMap 不同名的字段映射集合
* @param <S>
* @param <D>
* @return
*/
public static <S,D>D map(S sourceObject, Class<D> destinationClass, Map<String, String> fieldMap) {
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
if (!fieldMap.isEmpty()) {
ClassMapBuilder<?, D> classMapBuilder = mapperFactory.classMap(sourceObject.getClass(), destinationClass);
for (Map.Entry<String, String> entry : fieldMap.entrySet()) {
classMapBuilder.field(entry.getKey(),entry.getValue());
}
classMapBuilder.byDefault().register();
}
return mapperFactory.getMapperFacade().map(sourceObject,destinationClass);
}
/***
*
* @param sourceObject 数据源
* @param destinationClass 要转换的数据类型
* @param fieldMap 不同名的字段映射集合
* @param converter 类型转换器
* @param converMap 要转换的字段集合
* @param <S>
* @param <D>
* @return
*/
public static <S,D>D map(S sourceObject, Class<D> destinationClass, Map<String, String> fieldMap, BidirectionalConverter converter, Map<String, String> converMap) {
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
ClassMapBuilder<?, D> classMapBuilder = mapperFactory.classMap(sourceObject.getClass(), destinationClass);
if (!fieldMap.isEmpty()) {
for (Map.Entry<String, String> entry : fieldMap.entrySet()) {
classMapBuilder.field(entry.getKey(),entry.getValue());
}
}
if (converter != null && !converMap.isEmpty()) {
mapperFactory.getConverterFactory().registerConverter("dateConverter",new DateConverter());
for (Map.Entry<String, String> entry : converMap.entrySet()) {
classMapBuilder.fieldMap(entry.getKey(),entry.getValue()).converter("dateConverter").add();
}
}
classMapBuilder.byDefault().register();
return mapperFactory.getMapperFacade().map(sourceObject,destinationClass);
}
}
复制代码