这四种对象属性拷贝方式,你都知道吗?(第三种)
一、Orika
1.1、 简介:
Orika 是 Java Bean 映射框架,可以实现从一个对象递归拷贝数据至另一个对象。它的优点是:名字相同类型不同也能直接复制。
1.2、所需依赖
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.5.4</version>
</dependency>
1.3、映射工具类
使用枚举实现的单例模式创建一个映射工具类,便于测试。
public enum MapperUtils {
/**
* 实例
*/
INSTANCE;
/**
* 默认字段工厂
*/
private static final MapperFactory MAPPER_FACTORY = new DefaultMapperFactory.Builder().build();
/**
* 默认字段实例
*/
private static final MapperFacade MAPPER_FACADE = MAPPER_FACTORY.getMapperFacade();
/**
* 默认字段实例集合
*/
private static Map<String, MapperFacade> CACHE_MAPPER_FACADE_MAP = new ConcurrentHashMap<>();
/**
* 映射实体(默认字段)
*
* @param toClass 映射类对象
* @param data 数据(对象)
* @return 映射类对象
*/
public <E, T> E map(Class<E> toClass, T data) {
return MAPPER_FACADE.map(data, toClass);
}
/**
* 映射实体(自定义配置)
*
* @param toClass 映射类对象
* @param data 数据(对象)
* @param configMap 自定义配置
* @return 映射类对象
*/
public <E, T> E map(Class<E> toClass, T data, Map<String, String> configMap) {
MapperFacade mapperFacade = this.getMapperFacade(toClass, data.getClass(), configMap);
return mapperFacade.map(data, toClass);
}
/**
* 映射集合(默认字段)
*
* @param toClass 映射类对象
* @param data 数据(集合)
* @return 映射类对象
*/
public <E, T> List<E> mapAsList(Class<E> toClass, Collection<T> data) {
return MAPPER_FACADE.mapAsList(data, toClass);
}
/**
* 映射集合(自定义配置)
*
* @param toClass 映射类
* @param data 数据(集合)
* @param configMap 自定义配置
* @return 映射类对象
*/
public <E, T> List<E> mapAsList(Class<E> toClass, Collection<T> data, Map<String, String> configMap) {
T t = data.stream().findFirst().orElseThrow(() -> new ExceptionInInitializerError("映射集合,数据集合为空"));
MapperFacade mapperFacade = this.getMapperFacade(toClass, t.getClass(), configMap);
return mapperFacade.mapAsList(data, toClass);
}
/**
* 获取自定义映射
*
* @param toClass 映射类
* @param dataClass 数据映射类
* @param configMap 自定义配置
* @return 映射类对象
*/
private <E, T> MapperFacade getMapperFacade(Class<E> toClass, Class<T> dataClass, Map<String, String> configMap) {
String mapKey = dataClass.getCanonicalName() + "_" + toClass.getCanonicalName();
MapperFacade mapperFacade = CACHE_MAPPER_FACADE_MAP.get(mapKey);
if (Objects.isNull(mapperFacade)) {
MapperFactory factory = new DefaultMapperFactory.Builder().build();
ClassMapBuilder classMapBuilder = factory.classMap(dataClass, toClass);
configMap.forEach(classMapBuilder::field);
classMapBuilder.byDefault().register();
mapperFacade = factory.getMapperFacade();
CACHE_MAPPER_FACADE_MAP.put(mapKey, mapperFacade);
}
return mapperFacade;
}
}
- 这个工具类中主要有四个方法:
- map(Class toClass, T data):普通的映射实体,主要映射名称相同(类型可以不同)的字段;
- map(Class toClass, T data, Map<String, String> configMap):自定义配置的映射,映射名称不同时,自定义映射对应名称;
- mapAsList(Class toClass, Collection data):普通的集合的映射;
- mapAsList(Class toClass, Collection data, Map<String, String> configMap):自定义的集合映射,自定义映射对应名称。
1.4简单测试
- 拷贝名称相同类型可不同的属性
@Test
public void normalCopy() {
// 模拟查询出数据
UserDO userDO = DataUtil.createData();
log.info("拷贝前:userDO:{}", userDO);
// 第一个参数:源对象, 第二个参数:目标对象,第三个参数:是否使用自定义转换器(下面会介绍),下同
UserDTO userDTO = MapperUtils.INSTANCE.map(UserDTO.class, userDO);;
log.info("拷贝后:userDTO:{}", userDTO);
}
- 字段名称不同,带翻译
@Test
public void converterTest() {
// 模拟查询出数据
UserDO userDO = DataUtil.createData();
Map<String, String> config = new HashMap<>();
// 自定义配置(balance 转 balances)
config.put("balance", "balances");
log.info("拷贝前:userDO:{}", userDO);
UserDomain userDomain = MapperUtils.INSTANCE.map(UserDomain.class, userDO, config);
log.info("拷贝后:userDomain:{}", userDomain);
}
- 拷贝集合
@Test
public void beanCopierWithCache() {
List<UserDO> userDOList = DataUtil.createDataList(3);
log.info("拷贝前:userDOList:{}", userDOList);
List<UserDTO> userDTOS = MapperUtils.INSTANCE.mapAsList(UserDTO.class,userDOList);
log.info("拷贝后:userDTOS:{}", userDTOS);
}