序言
现有对象拷贝,使用的是spring的BeanUtils或者直接JSON转换,效率比较低,在大数据量情况下会占用大量资源(性能对比)。因此,引入orika,提高效率。
简介
引入
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.4.1</version>
</dependency>
基本概念
MapperFactory用于注册字段映射,配置转换器,自定义映射器等,而我们主要使用的为字段映射这个特性,在自定义映射时就是用MapperFactory来匹配特殊字段。
MapperFacade相当于spring的BeanUtils,负责对象间的映射,也就是实现对象拷贝所使用的类。
跳过的坑
1、 OrikaMapper根据get/set方法获取属性,那么对于使用@Data注解的实体类中,最好不要出现类似于pName的属性。因为@Data注解会将这类属性的get/set方法写成getPName/setPName,在映射时可能会报“pName does not belong to OrikaTestBO”的错误。相类似的,这个错误在后端与前端交互时也会出现,因此最好不使用这一结构命名。
使用
这里注意,以下所说的简单指的是所需映射对象属性全为基本数据类型,若包含自定义对象或者数组,则需要使用自定义映射,否则这些属性会置为null。另外,orika是以属性名称来匹配的,对于非对应属性需要确保没有重名。
source:
/**
* 测试对象
* @author 心随
*/
@Data
public class OrikaTestBO {
private Integer id;
private String name;
private BigDecimal price;
private OrikaTestSonBO orikaTestSonBO;
private String extraBO;
}
/**
* orika 测试子对象
* @author 心随
*/
@Data
public class OrikaTestSonBO {
private Integer id;
private String name;
private BigDecimal price;
}
destination:
/**
* orika测试对象
* @author 心随
*/
@Data
public class OrikaTestDTO {
private Integer id;
private String name;
private BigDecimal price;
private OrikaTestSonDTO orikaTestSonDTO;
private String extraDTO;
}
/**
* orika测试子对象
* @author 心随
*/
@Data
public class OrikaTestSonDTO {
private Integer id;
private String name;
private BigDecimal price;
}
init:
//source初始化
public static OrikaTestBO init(){
OrikaTestBO orikaTestBO = new OrikaTestBO();
orikaTestBO.setId(1);
orikaTestBO.setName("zzh");
orikaTestBO.setPrice(BigDecimal.TEN);
OrikaTestSonBO orikaTestSonBO = new OrikaTestSonBO();
orikaTestSonBO.setId(2);
orikaTestSonBO.setName("xxx");
orikaTestSonBO.setPrice(BigDecimal.ONE);
orikaTestBO.setOrikaTestSonBO(orikaTestSonBO);
return orikaTestBO;
}
简单object
public static OrikaTestDTO singleMap(){
OrikaTestBO orikaTestBO = init();
System.out.println(JSON.toJSONString(orikaTestBO));
OrikaTestDTO orikaTestDTO = OrikaMapperUtils.map(orikaTestBO,OrikaTestDTO.class);
System.out.println(JSON.toJSONString(orikaTestDTO));
return orikaTestDTO;
}
结果
{"extraBO":"啦啦啦啦","id":1,"name":"zzh","orikaTestSonBO":{"id":2,"name":"xxx","price":1},"price":10}
{"id":1,"name":"zzh","price":10}
简单list
public static List<OrikaTestDTO> listMap(){
List<OrikaTestBO> sourceList = new ArrayList<>();
sourceList.add(init());
sourceList.add(init());
System.out.println(JSON.toJSONString(sourceList));
List<OrikaTestDTO> destinationList = OrikaMapperUtils.mapList(sourceList,OrikaTestBO.class,OrikaTestDTO.class);
System.out.println(JSON.toJSONString(destinationList));
return destinationList;
}
结果
[{"extraBO":"啦啦啦啦","id":1,"name":"zzh","orikaTestSonBO":{"id":2,"name":"xxx","price":1},"price":10},{"extraBO":"啦啦啦啦","id":1,"name":"zzh","orikaTestSonBO":{"id":2,"name":"xxx","price":1},"price":10}]
[{"id":1,"name":"zzh","price":10},{"id":1,"name":"zzh","price":10}]
自定义
public static OrikaTestDTO specialMap(){
OrikaTestBO orikaTestBO = init();
System.out.println(JSON.toJSONString(orikaTestBO));
//配置特殊化映射
MapperFactory factory = new DefaultMapperFactory.Builder().build();
factory.classMap(OrikaTestBO.class,OrikaTestDTO.class)
.field("orikaTestSonBO","orikaTestSonDTO")
.byDefault()
.register();
factory.classMap(OrikaTestSonBO.class, OrikaTestSonDTO.class)
.byDefault()
.register();
//映射类
MapperFacade mapper = factory.getMapperFacade();
OrikaTestDTO orikaTestDTO = mapper.map(orikaTestBO,OrikaTestDTO.class);
System.out.println(JSON.toJSONString(orikaTestDTO));
return orikaTestDTO;
}
结果
{"extraBO":"啦啦啦啦","id":1,"name":"zzh","orikaTestSonBO":{"id":2,"name":"xxx","price":1},"price":10}
{"id":1,"name":"zzh","orikaTestSonDTO":{"id":2,"name":"xxx","price":1},"price":10}
工具类
public class OrikaMapperUtils {
private static MapperFacade mapper;
static {
MapperFactory factory = new DefaultMapperFactory.Builder().build();
mapper = factory.getMapperFacade();
}
private OrikaMapperUtils() {
}
/**
* 简单的复制出新类型对象.
* <p>
* 内部实现是通过source.getClass() 获得源Class
*/
public static <S, D> D map(S source, Class<D> destinationClass) {
return mapper.map(source, destinationClass);
}
/**
* 极致性能的复制出新类型对象.
* <p>
* 预先通过{@link OrikaMapperUtils#getType(Class)} 静态获取并缓存Type类型,在此处传入
*/
public static <S, D> D map(S source, Type<S> sourceType, Type<D> destinationType) {
return mapper.map(source, sourceType, destinationType);
}
/**
* 简单的复制出新对象列表到ArrayList
* <p>
* 不建议使用{@link MapperFacade#mapAsList(Object[], Class)}} 接口, sourceClass需要在遍历每一个元素的时候反射,实在有点慢
*/
public static <S, D> List<D> mapList(Iterable<S> sourceList, Class<S> sourceClass, Class<D> destinationClass) {
return mapper.mapAsList(sourceList, TypeFactory.valueOf(sourceClass), TypeFactory.valueOf(destinationClass));
}
/**
* 极致性能的复制出新类型对象到ArrayList.
* <p>
* 预先通过{@link OrikaMapperUtils#getType(Class)} 静态获取并缓存Type类型,在此处传入
*/
public static <S, D> List<D> mapList(Iterable<S> sourceList, Type<S> sourceType, Type<D> destinationType) {
return mapper.mapAsList(sourceList, sourceType, destinationType);
}
/**
* 简单复制出新对象列表到数组.
* <p>
* 内部实现是通过source.getComponentType() 获得源Class
*
* @param destination 要复制到的目标数组
* @param source 待复制的源数据数组
* @param destinationClass 要复制到的目标数组数据元素Class
*/
public static <S, D> D[] mapArray(final D[] destination, final S[] source, final Class<D> destinationClass) {
return mapper.mapAsArray(destination, source, destinationClass);
}
/**
* 极致性能的复制出新类型对象到数组.
* <p>
* 需要预先通过{@link OrikaMapperUtils#getType(Class)} 静态获取并缓存转换所需Type类型,在此处传入
*
* @param destination 要复制到的目标数组
* @param source 待复制的源数据数组
* @param sourceType 待复制的源数据数组实例类型
* @param destinationType 要复制到的目标数组类型
*/
public static <S, D> D[] mapArray(D[] destination, S[] source, Type<S> sourceType, Type<D> destinationType) {
return mapper.mapAsArray(destination, source, sourceType, destinationType);
}
/**
* 预先获取orika转换所需要的{@link Type},避免每次复制都做转换.
*/
public static <E> Type<E> getType(final Class<E> rawType) {
return TypeFactory.valueOf(rawType);
}
}