对象转换工具
一、modelmapper 介绍
- 简化对象转换操作
在Java 应用开发中,经常需要在不同层次(如持久层、业务逻辑层、表示层)之间转换对象。例如,将数据库查询得到的实体对象(Entity)转换为适合在网络上传输的数据传输对象(DTO),或者将用户输入的表单对象转换为业务逻辑处理的领域对象(Domain Object)。ModelMapper 能够自动完成大部分属性的映射,减少了手动编写大量属性赋值语句的工作量。
假设存在一个UserEntity类,包含id、username、password、createdAt等属性,以及一个UserDTO类,包含id、username和isAdmin属性。使用 ModelMapper 可以轻松地将UserEntity转换为UserDTO,而不需要为每个属性手动编写转换代码。- 支持复杂对象结构的转换
当处理具有嵌套结构的对象时,ModelMapper 能够递归地进行映射。例如,在一个电商系统中,有Order类包含Customer对象和List,对应的OrderDTO也有类似的嵌套结构。ModelMapper 可以自动处理这种嵌套对象的映射,将Order对象中的Customer和Product相关属性正确地映射到OrderDTO中的相应部分。
二、安装
(一)引入依赖
<!-- 对象转换工具-->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.2.0</version>
</dependency>
(二)添加工具类
需要修改一下包名
package org.github.zuuuyao.common.util;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
* 对象映射转换工具类,转换逻辑属性名完全匹配进行转换
*
* @Description:
* @Time: 2019-11-28 20:18
* @Author: HuangZhangYao
*/
public final class ModelMapperUtil {
public static final ModelMapper MODEL_MAPPER;
static {
MODEL_MAPPER = new ModelMapper();
// 完全类型匹配
//MODEL_MAPPER.getConfiguration().setFullTypeMatchingRequired(true);
// 设置属性匹配规则,设置为最严格匹配,必须属性名相同
MODEL_MAPPER.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
}
/**
* 禁止实列化该类
* @throws InstantiationException 不能实列化
*/
private ModelMapperUtil() throws InstantiationException {
throw new InstantiationException("Tool Class Cannot Be Created Instantiation !");
}
/**
* 将对象转换为指定class类型对象。
* @param source 源对象
* @param targetClass 转换目标类型
* @return 转换后对象
* @param <Target> 目标类型
*/
public static <Target> Target map(Object source, Class<Target> targetClass) {
return source == null ? null : MODEL_MAPPER.map(source, targetClass);
}
/**
* 将源对象中的属性转换到目标对象中,源对象与目标对象属性名相同的则转换
* @param source 源对象
* @param target 目标对象
*/
public static void map(Object source, Object target) {
if (source == null || target == null) {
return;
} else {
MODEL_MAPPER.map(source, target);
}
}
/**
* 将对象转换为指定class类型对象,并对转换后对象进行处理
* @param source 源对象
* @param targetClass 转换目标类型
* @param consumer 对转换后对象进行消费处理
* @return 转换后对象
* @param <Target> 目标类型
*/
public static <Target> Target map(Object source, Class<Target> targetClass, Consumer<Target> consumer) {
Target target = source == null ? null : MODEL_MAPPER.map(source, targetClass);
if (consumer != null) {
consumer.accept(target);
}
return target;
}
/**
* 将对象转换为指定class类型对象,并对转换后对象进行处理
* @param source 源对象
* @param targetClass 转换目标类型
* @param consumer 对转换后对象进行消费处理
* @return 转换后对象
* @param <Source> 源对象类型
* @param <Target> 目标类型
*/
public static <Source, Target> Target map(Source source, Class<Target> targetClass, BiConsumer<Source, Target> consumer) {
Target target = source == null ? null : MODEL_MAPPER.map(source, targetClass);
if (consumer != null) {
consumer.accept(source, target);
}
return target;
}
/**
* 将集合中的元素抓换为指定类型
* @param sourceList 源集合
* @param targetClass 转换目标类型
* @return 转换后的目标类型集合
* @param <TSource> 源类型
* @param <Target> 目标类型
*/
public static <TSource, Target> List<Target> mapList(Collection<TSource> sourceList, Class<Target> targetClass) {
if (sourceList == null) {
return new ArrayList<Target>();
}
ArrayList<Target> targets = new ArrayList<>(sourceList.size());
sourceList.forEach(p -> {
Target item = map(p, targetClass);
targets.add(item);
});
return targets;
}
/**
* 将集合中的元素抓换为指定类型,并对转换后的元素处理
* @param sourceList 源集合
* @param targetClass 转换目标类型
* @param consumer 对转换后元素处理
* @return 转换后的集合
* @param <TSource> 源类型
* @param <Target> 目标类型
*/
public static <TSource, Target> List<Target> mapList(Collection<TSource> sourceList, Class<Target> targetClass, Consumer<Target> consumer) {
if (sourceList == null) {
return new ArrayList<Target>();
}
ArrayList<Target> targets = new ArrayList<>(sourceList.size());
sourceList.forEach(p -> {
Target item = map(p, targetClass);
if (consumer != null) {
consumer.accept(item);
}
targets.add(item);
});
return targets;
}
/**
* 将集合中的元素抓换为指定类型,并对转换后的元素处理
* @param sourceList 源集合
* @param targetClass 转换目标类型
* @param consumer 对转换后元素处理
* @return 转换后的集合
* @param <TSource> 源类型
* @param <Target> 目标类型
*/
public static <TSource, Target> List<Target> mapList(Collection<TSource> sourceList, Class<Target> targetClass, BiConsumer<TSource, Target> consumer) {
if (sourceList == null) {
return new ArrayList<Target>();
}
ArrayList<Target> targets = new ArrayList<>(sourceList.size());
sourceList.forEach(p -> {
Target map = ModelMapperUtil.map(p, targetClass);
if (consumer != null) {
consumer.accept(p, map);
}
targets.add(map);
});
return targets;
}
}
三、使用示例
- 准备 UserEntity 实体
package org.github.zuuuyao.entity.system;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import org.github.zuuuyao.common.base.entity.AbstractBaseEntity;
import org.github.zuuuyao.entity.enums.GenderEnum;
import java.io.Serial;
import java.time.LocalDateTime;
/**
* @Desc 系统用户表
* @Time 2024-07-11 16:31
* @Author HuangZhongYao
*/
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@TableName("sys_user")
public class UserEntity extends AbstractBaseEntity {
@Serial
private static final long serialVersionUID = -4075127738715995785L;
/**
* 用户名
*/
private String username;
/**
* 账号
*/
private String account;
/**
* 密码
*/
private String password;
/**
* 密码盐
*/
private String salt;
/**
* 性别
*/
private GenderEnum gender = GenderEnum.UNKNOWN;
/**
* 手机号
*/
private String phone;
/**
* 头像url
*/
private String avatarUrl;
/**
* 备注
*/
private String remark;
/**
* 最后登录时间
*/
private LocalDateTime lastLoginTime;
/**
* 启用状态
*/
private Boolean enable;
}
- 准备 UserVo 实体
package org.github.zuuuyao.service.user.dto.output;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serial;
import java.time.LocalDateTime;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
import org.github.zuuuyao.common.base.dto.output.BaseOutputIdAndTimeAndOperationDTO;
import org.github.zuuuyao.entity.enums.GenderEnum;
import org.github.zuuuyao.service.role.dto.output.RoleVo;
import org.github.zuuuyao.service.user.model.UserRoleModel;
/**
* @Desc
* @Time 2024-07-16 16:29
* @Author HuangZhongYao
*/
@Getter
@Setter
public class UserVo extends BaseOutputIdAndTimeAndOperationDTO {
@Serial
private static final long serialVersionUID = -7091624991626890336L;
/**
* 用户名
*/
@Schema(description = "用户名")
private String username;
/**
* 账号
*/
@Schema(description = "账号")
private String account;
/**
* 性别枚举
*/
@Schema(description = "性别")
private GenderEnum gender;
/**
* 性别枚举表示值
*/
@Schema(description = "性别枚举表示值")
private int genderEnumValue;
/**
* 性别枚举描述
*/
@Schema(description = "性别枚举描述")
private String genderEnumDesc;
/**
* 手机号
*/
@Schema(description = "手机号")
private String phone;
/**
* 头像url
*/
@Schema(description = "头像url")
private String avatarUrl;
/**
* 备注
*/
@Schema(description = "备注")
private String remark;
/**
* 最后登录时间
*/
@Schema(description = "最后登录时间")
private LocalDateTime lastLoginTime;
/**
* 启用状态
*/
@Schema(description = "启用状态")
private Boolean enable;
/**
* 用户角色列表
*/
@Schema(description = "用户角色列表")
private List<UserRoleModel> roles;
}
(一)单个对象转换
(一)简单转换
public static void main(String[] args) {
// 创建一个用户实体对象
UserEntity admin = UserEntity.builder()
.username("ZuuuYao")
.phone("1888888888")
.account("zy9527")
.avatarUrl("http://xxx.com/avatar.png")
.gender(GenderEnum.MALE)
.remark("管理员")
.enable(Boolean.TRUE)
.build();
// 方式1: 转换为指定类型
UserVo adminDetailsVo = ModelMapperUtil.map(admin, UserVo.class);
// 方式2: 转换到指定对象上
UserVo userVo = new UserVo();
// 将admin中的同名的数据映射到userVo中
ModelMapperUtil.map(admin, userVo);
}
(二)转换时对转换后的对象处理
public static void main(String[] args) {
// 创建一个用户实体对象
UserEntity admin = UserEntity.builder()
.username("ZuuuYao")
.phone("1888888888")
.account("zy9527")
.avatarUrl("http://xxx.com/avatar.png")
.gender(GenderEnum.MALE)
.remark("管理员")
.enable(Boolean.TRUE)
.build();
// 转换为指定类型, 并使用Consumer设置自定义属性
UserVo adminDetailsVo = ModelMapperUtil.map(admin, UserVo.class, target -> {
// 模拟执行逻辑
target.setGenderEnumDesc(target.getGender().getDesc());
target.setGenderEnumValue(target.getGenderEnumValue());
});
}
(二)转换时对转换后的对象、源对象处理
public static void main(String[] args) {
// 创建一个用户实体对象
UserEntity admin = UserEntity.builder()
.username("ZuuuYao")
.phone("1888888888")
.account("zy9527")
.avatarUrl("http://xxx.com/avatar.png")
.gender(GenderEnum.MALE)
.remark("管理员")
.enable(Boolean.TRUE)
.build();
// 转换为指定类型, 并使用Consumer设置自定义属性
UserVo adminDetailsVo = ModelMapperUtil.map(admin, UserVo.class, (source, target) -> {
// source 是 admin
// target 是 转换后的adminDetailsVo
// 模拟执行逻辑
target.setRemark("用户:" + source.getUsername() + ",描述:" + source.getRemark());
});
}
(一)批量转换
(一)简单转换
public static void main(String[] args) {
// stream 生成100个对象
List<UserEntity> userEntityList = Stream.generate(() -> UserEntity.builder()
.username("ZuuuYao")
.phone("1888888888")
.account("zy9527")
.avatarUrl("http://xxx.com/avatar.png")
.gender(GenderEnum.MALE)
.remark("管理员")
.enable(Boolean.TRUE)
.build())
.limit(100)
.toList();
// 转换为指定类型
List<UserVo> userVos = ModelMapperUtil.mapList(userEntityList, UserVo.class);
}
(二)转换时对转换后的对象处理
public static void main(String[] args) {
// stream 生成100个对象
List<UserEntity> userEntityList = Stream.generate(() -> UserEntity.builder()
.username("ZuuuYao")
.phone("18888889898")
.account("zy9527")
.avatarUrl("http://xxx.com/avatar.png")
.gender(GenderEnum.MALE)
.remark("管理员")
.enable(Boolean.TRUE)
.build())
.limit(100)
.toList();
// 转换为指定类型
List<UserVo> userVos = ModelMapperUtil.mapList(userEntityList, UserVo.class, target -> {
// 将手机号脱敏
String phone = target.getPhone();
String desensitizationPhone = phone.substring(0, 3) + "*****" + phone.substring(8);
target.setPhone(desensitizationPhone);
});
}
(二)转换时对转换后的对象、源对象处理
public static void main(String[] args) {
// stream 生成100个对象
List<UserEntity> userEntityList = Stream.generate(() -> UserEntity.builder()
.username("ZuuuYao")
.phone("18888889898")
.account("zy9527")
.avatarUrl("http://xxx.com/avatar.png")
.gender(GenderEnum.MALE)
.remark("管理员")
.enable(Boolean.TRUE)
.build())
.limit(100)
.toList();
// 转换为指定类型
List<UserVo> userVos = ModelMapperUtil.mapList(userEntityList, UserVo.class, (source, target) -> {
// 可以获取到源对象和转换后对象
// 假如源对象中有的属性,目标对象没有可以手动赋值
// target.setA(source.getB());
});
}
四、扩展
可以结合mybatis、JPA
做一些封装
如下:
// 查询结果就转换为UserVo了就不用再转换了,写起来也比较舒服
Page<UserVo> page = userRepository.selectPage(inputDTO.toMybatisPageObject(), queryWrapper, UserVo.class);
完