• 【Java】DTO、PO、VO 类相互转换的工具


    DTO、PO、VO 相互转换的工具

    这里我使用一个 Builder 将要赋值的属性映射添加进去,然后使用的时候直接传入对应的类对象即可

    这个工具需要创建两个类,一个 FunctionMap 用于记录“被赋值的类”对应的“获取值的类”的方法映射

    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.function.BiConsumer;
    import java.util.function.Function;
    
    /**
     * Function 映射方法
     *
     * 

    这个类的 Get(Function) 方法对应的 Set(BiConsumer)方法 的映射

    * * @param 设置属性的目标对象的类型 * @param 获取属性的来源对象的类型 * @author zhangxuetu * @date 2022-11-24 * @since 1.8 */
    public class FunctionMap<T, S> { /** * 当前所属构建器 */ private final PropertyMapBuilder<T> owner; /** * Get 方法对应要设置的 Set 方法的映射 */ private final Map<Function, BiConsumer> functionMap = new HashMap<>(); /** * 对 Get 数据进行处理为 Set 所需的数据的方法 */ private final Map<Function, Function> handlerMap = new HashMap<>(); protected FunctionMap(PropertyMapBuilder<T> owner) { this.owner = owner; } /** * 添加方法映射 * * @param consumer Set 方法 * @param function Get 方法 * @return 返回当前对象以便链式调用 */ public <R> FunctionMap<T, S> add(BiConsumer<T, R> consumer, Function<S, R> function) { functionMap.put(function, consumer); return this; } /** * 添加方法映射 * * @param consumer Set 方法 * @param function Get 方法 * @param handler 对 Get 数据进行处理为 Set 所需的数据的方法 * @param 数据结果类型 * @param 数据来源类型 */ public <R, SourceType> FunctionMap<T, S> add(BiConsumer<T, R> consumer, Function<S, SourceType> function, Function<SourceType, R> handler) { functionMap.put(function, consumer); handlerMap.put(function, handler); return this; } /** * 获取这个类的方法映射 * * @param clazz 数据来源的类的类型 * @param 获取数据的来源对象的类型 */ public <S> FunctionMap<T, S> from(Class<S> clazz) { return owner.from(clazz); } /** * 对属性进行赋值 * * @param target 设置属性的目标对象 * @param source 获取属性的来源对象 */ public void copy(T target, S source) { BiConsumer<T, Object> consumer; Function<S, Object> function; Function<Object, Object> convert; for (Map.Entry<Function, BiConsumer> entry : functionMap.entrySet()) { function = entry.getKey(); consumer = entry.getValue(); if (handlerMap.containsKey(function)) { // 这个方法存在有转换方法,则进行转换 convert = handlerMap.get(function); consumer.accept(target, convert.apply(function.apply(source))); } else { // 要注意值类型的问题,比如如果是值为 null 的 Integer 类型向 int 类型赋值会报 NullPoint 异常 consumer.accept(target, function.apply(source)); } } } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    第二个类用于构建类的所有的映射的类的方法

    PropertyMapBuilder

    import java.lang.reflect.InvocationTargetException;
    import java.util.*;
    import java.util.function.BiConsumer;
    import java.util.function.Function;
    
    /**
     * 

    数据属性的映射构建器

    * *

    创建一个构造器实例,添加每个类对应的赋值到对应的属性的方法。使用构造器的{@link FunctionMap#from(Class)}方法获取数据来源类的映射 * 对象,然后调用{@link FunctionMap#add(BiConsumer, Function)}方法添加映射的属性方法。

    * *

    使用{@link PropertyMapBuilder}对象的{@link PropertyMapBuilder#copy(Object, Object...)}方法将数据赋值到目标对象上, * 或使用{@link PropertyMapBuilder#create(Object...)}方法创建出来实例对象,将属性赋值到这个对象身上。

    * *

    示例:

    * *
    {@code
     * // 这个 builder 一般在一个 Config 类中配置好,然后使用 PropertyMapBuilder.to(UserDTO.class).create 方法或 PropertyMapBuilder.to(UserDTO.class).copy 方法进行使用
     * final PropertyMapCopyBuilder builder = PropertyMapBuilder.to(UserDTO.class)
     *      .from(UserPO.class)
     *      .add(UserDTO::setId, UserPO::getId)
     *      .add(UserDTO::setUsername, UserPO::getUsername)
     *      .from(RolePO.class)
     *      .add(UserDTO::setRolename, RolePO::getName)
     *      .add(UserDTO::setPermission, RolePO::getPermission);
     * final UserPO userPO = new UserPO(2, "zhangsan", "123456", "1567778889");
     * final RolePO rolePO = new RolePO(1, "admin", "/user,/log");
     * final UserDTO userDTO = builder.create(userPO, rolePO);
     * System.out.println(userDTO);
     * }
    * *

    可以预先创建一个PropertyMapConfig属性映射配置类,用以预先设置好要映射的类的属性

    * * @param 要赋值到的目标对象的类型 * @author zhangxuetu * @date 2022-11-24 * @since 1.8 */
    public class PropertyMapBuilder<T> { /** * 项目中的类对应的{@link PropertyMapBuilder},这个 map 已经包含有这个 Class,则不重新创建新的{@link PropertyMapBuilder} */ private final static Map<Class, PropertyMapBuilder> BUILDER_MAP = new HashMap<>(); /** * 转换到的类 */ private final Class<T> clazz; /** * 不同的类的方法可以设置的值 */ private final Map<Class, FunctionMap> classToFunctionMap = new HashMap<>(); private PropertyMapBuilder(Class<T> clazz) { this.clazz = clazz; } /** * 转换到这个类型。 * *

    实例化一个构建器单例,如果已经创建过这个类的 {@link PropertyMapBuilder},则直接返回创建过的 {@link PropertyMapBuilder}

    * * @param clazz 最终要转换赋值的类 * @param 这个类的类型 */
    public static <T> PropertyMapBuilder<T> to(Class<T> clazz) { if (BUILDER_MAP.containsKey(clazz)) { return BUILDER_MAP.get(clazz); } final PropertyMapBuilder<T> builder = new PropertyMapBuilder<>(clazz); BUILDER_MAP.put(clazz, builder); return builder; } /** * *

    调用这个方法开始添加Set方法映射的Get方法的那个类

    * * @param clazz 类的类型 * @param 获取数据的对象的类型 */
    public <S> FunctionMap<T, S> from(Class<S> clazz) { if (classToFunctionMap.containsKey(clazz)) { return classToFunctionMap.get(clazz); } else { final FunctionMap<T, S> functionMap = new FunctionMap<>(this); classToFunctionMap.put(clazz, functionMap); return functionMap; } } /** * 赋值属性到这个对象上 * * @param target 设置属性的目标对象 * @param getters 获取数据的对象 */ public void copy(T target, Object... getters) { for (Object getter : getters) { if (getter != null) { // 获取这个类的 FunctionMap 对象记录着映射的方法 final FunctionMap<T, Object> functionMap = classToFunctionMap.get(getter.getClass()); functionMap.copy(target, getter); } } } /** * 根据这个几个对象创建出来目标类型的对象 * * @param getters 获取数据的对象列表 */ public T create(Object... getters) { T target = null; try { target = clazz.getDeclaredConstructor().newInstance(); copy(target, getters); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { e.printStackTrace(); } return target; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127

    通过上面对象我们可以利用他们做属性映射工具类

    import cn.hutool.json.JSONArray;
    import cn.hutool.json.JSONUtil;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Component;
    
    import java.time.LocalDateTime;
    import java.time.ZoneId;
    import java.time.format.DateTimeFormatter;
    import java.util.Date;
    import java.util.List;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    
    
    /**
     * 实体类转换器
     *
     * 

    通过{@link PropertyMapBuilder}映射Setter和Getter方法之后进行使用下面的方法转换创建对应类型的对象

    * *

    例如转换为一个{@link UserVo}类型的对象:

    * *
    
     *     UserPO userPo = userMapper.getUserById(userId);
     *     RolePO rolePo = roleMapper.getRoleByUserId(userId);
     *     return EntityCreator.convertUserVo(userPo, rolePo)
     * 
    * * @author zhangxuetu * @date 2022-12-07 * @since 1.8 */
    @Component public class EntityConvertor { static { // AssetsPO + AssetsTypePO = AssetsVO PropertyMapBuilder.to(AssetsVo.class) .from(AssetsPo.class) .add(AssetsVo::setId, AssetsPo::getId) .add(AssetsVo::setAssetsId, AssetsPo::getAssetsId) .add(AssetsVo::setAssetsName, AssetsPo::getAssetsName) .add(AssetsVo::setAssetsModel, AssetsPo::getAssetsModel) .add(AssetsVo::setAssetsDesc, AssetsPo::getAssetsDesc) .add(AssetsVo::setTagCode, AssetsPo::getTagCode, (v) -> "".equals(v) ? null : v) .add(AssetsVo::setAssetsId, AssetsPo::getAssetsId) .from(AssetsTypePo.class) .add(AssetsVo::setAssetsTypeId, AssetsTypePo::getAssetsTypeId) .add(AssetsVo::setAssetsTypeName, AssetsTypePo::getAssetsTypeName); // Dto 转 Po PropertyMapBuilder.to(AssetsPo.class) .from(AssetsDto.class) .add(AssetsPo::setId, AssetsDto::getId) .add(AssetsPo::setAssetsId, AssetsDto::getAssetsId) .add(AssetsPo::setAssetsName, AssetsDto::getAssetsName) .add(AssetsPo::setAssetsTypeId, AssetsDto::getAssetsTypeId) .add(AssetsPo::setAssetsModel, AssetsDto::getAssetsModel) .add(AssetsPo::setTagCode, AssetsDto::getTagCode, (v) -> "".equals(v) ? null : v) .add(AssetsPo::setAssetsWorkRoomId, AssetsDto::getRoomId) .add(AssetsPo::setAssetsDesc, AssetsDto::getAssetsDesc); // AssetsTypePO => AssetsTypeVO PropertyMapBuilder.to(AssetsTypeVo.class) .from(AssetsTypePo.class) .add(AssetsTypeVo::setAssetsTypeId, AssetsTypePo::getAssetsTypeId) .add(AssetsTypeVo::setAssetsTypeName, AssetsTypePo::getAssetsTypeName) .add(AssetsTypeVo::setAssetsTypeIndoorImage, AssetsTypePo::getAssetsTypeIndoorImage, FileConfig::getImageUrl) .add(AssetsTypeVo::setAssetsTypeOutdoorImage, AssetsTypePo::getAssetsTypeOutdoorImage, FileConfig::getImageUrl) .add(AssetsTypeVo::setAssetsTypeDesc, AssetsTypePo::getAssetsTypeDesc) .add(AssetsTypeVo::setAssetsTypeCreateTime, AssetsTypePo::getAssetsTypeCreateTime); // TagPO + AssetsPO = TagVO PropertyMapBuilder.to(TagVo.class) .from(TagPo.class) .add(TagVo::setTagId, TagPo::getTagId) .add(TagVo::setTagTypeId, TagPo::getTagTypeId) .add(TagVo::setTagCode, TagPo::getTagCode, (v) -> "".equals(v) ? null : v) .add(TagVo::setTagQrCode, TagPo::getTagQrCode) .add(TagVo::setTagName, TagPo::getTagName) .add(TagVo::setSignalFrequency, (tagPo) -> { if (tagPo == null) { return ""; } String indoor = tagPo.getTagIndoorSignalFreq(); String outdoor = tagPo.getTagOutdoorSignalFreq(); if (indoor == null || outdoor == null) { return ""; } indoor = indoor.replace("/", ""); outdoor = outdoor.replace("/", ""); return indoor + "/" + outdoor; }) .from(AssetsPo.class) .add(TagVo::setAssetsIncrId, AssetsPo::getId) .add(TagVo::setAssetsId, AssetsPo::getAssetsId) .add(TagVo::setAssetsName, AssetsPo::getAssetsName); } /** * 将这个列表项的类型转换成另一种类型 * * @param list 数据列表 * @param convert 转换的引用的方法。一般都是使用当前类的其他方法配合使用,也可以对数据额外处理,例如返回 second 字段值的末尾追加一个 s * 字符后的值:(v) -> v.getSecond() + "s"。 * @param 目标类型 * @param 转换成的类型 * @return 返回转换后的类型的列表 */ public static <T, S> List<S> convertList(List<T> list, Function<T, S> convert) { return list.stream().map(convert).collect(Collectors.toList()); } /** * 转为这个类型的对象 * * @param tClass 转换到的对象类型 * @param froms 从这些来源对象中获取对应映射方法的数据 * @return 返回转换的类型 */ public static <T> T to(Class<T> tClass, Object... froms) { return PropertyMapBuilder.to(tClass).create(froms); } public static AssetsVo convertAssetsVo(AssetsPo assetsPo, AssetsTypePo assetsTypePo, String bindArea, String bindAreaId) { AssetsVo assetsVo = to(AssetsVo.class, assetsPo, assetsTypePo); assetsVo.setAssetsBindArea(bindArea); assetsVo.setAssetsBindAreaId(bindAreaId); return assetsVo; } public static AssetsPo convertAssetsPo(AssetsExcelData assetsExcelData, int assetsTypeId, String roomId) { AssetsPo assetsPo = to(AssetsPo.class, assetsExcelData); assetsPo.setAssetsWorkRoomId(roomId); assetsPo.setAssetsTypeId(assetsTypeId); assetsPo.setAssetsWorkStatus(0); return assetsPo; } public static AssetsTypeVo convertAssetsTypeVo(AssetsTypePo assetsTypePo) { return to(AssetsTypeVo.class, assetsTypePo); } public static TagVo convertTagVo(TagPo tagPo, AssetsPo assetsPo) { TagVo tagVo = to(TagVo.class, tagPo, assetsPo); tagVo.setTagIsBind(assetsPo != null ? 1 : 0); return tagVo; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151

    直接通过上面的 EntityConvertor 对象里的方法传入对应所需参数即可创建出转换后的对象

  • 相关阅读:
    面试官:Spring中都应用了哪些设计模式?
    PDE求格林函数
    我花16块得到了一个永久的微型服务器
    2022.9.25-----leetcode.788
    (四)Alian 的 Spring Cloud Ebean自动生成Query Bean
    求职攻略| 硬核公司的硬件笔试题长什么样?先来5道选择题
    双碳目标下基于“遥感+”集成技术的碳储量、碳排放、碳循环、温室气体等多领域监测与模拟实践
    DVWA-impossible代码审计
    minio文件上传
    vue3 知识点(二)
  • 原文地址:https://blog.csdn.net/qq_37280924/article/details/128017148