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

6 篇文章 0 订阅
1 篇文章 0 订阅

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

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

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


import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * Function 映射方法
 *
 * <p>这个类的 Get(Function) 方法对应的 Set(BiConsumer)方法 的映射</p>
 *
 * @param <T> 设置属性的目标对象的类型
 * @param <S> 获取属性的来源对象的类型
 * @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 <R>          数据结果类型
     * @param <SourceType> 数据来源类型
     */
    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 <S>   获取数据的来源对象的类型
     */
    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));
            }
        }
    }

}

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

PropertyMapBuilder

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

/**
 * <p>数据属性的映射构建器</p>
 *
 * <p>创建一个构造器实例,添加每个类对应的赋值到对应的属性的方法。使用构造器的{@link FunctionMap#from(Class)}方法获取数据来源类的映射
 * 对象,然后调用{@link FunctionMap#add(BiConsumer, Function)}方法添加映射的属性方法。</p>
 *
 * <p>使用{@link PropertyMapBuilder}对象的{@link PropertyMapBuilder#copy(Object, Object...)}方法将数据赋值到目标对象上,
 * 或使用{@link PropertyMapBuilder#create(Object...)}方法创建出来实例对象,将属性赋值到这个对象身上。</p>
 *
 * <p>示例:</p>
 *
 * <pre>{@code
 * // 这个 builder 一般在一个 Config 类中配置好,然后使用 PropertyMapBuilder.to(UserDTO.class).create 方法或 PropertyMapBuilder.to(UserDTO.class).copy 方法进行使用
 * final PropertyMapCopyBuilder<UserDTO> 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);
 * }</pre>
 *
 * <p>可以预先创建一个<code>PropertyMapConfig</code>属性映射配置类,用以预先设置好要映射的类的属性</p>
 *
 * @param <T> 要赋值到的目标对象的类型
 * @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;
    }

    /**
     * 转换到这个类型。
     *
     * <p>实例化一个构建器单例,如果已经创建过这个类的 {@link PropertyMapBuilder},则直接返回创建过的 {@link PropertyMapBuilder}</p>
     *
     * @param clazz 最终要转换赋值的类
     * @param <T>   这个类的类型
     */
    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;
    }

    /**
     *
     * <p>调用这个方法开始添加Set方法映射的Get方法的那个类</p>
     *
     * @param clazz 类的类型
     * @param <S>   获取数据的对象的类型
     */
    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;
    }

}

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

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;


/**
 * 实体类转换器
 *
 * <p>通过{@link PropertyMapBuilder}映射Setter和Getter方法之后进行使用下面的方法转换创建对应类型的对象</p>
 *
 * <p>例如转换为一个{@link UserVo}类型的对象:</p>
 *
 * <pre><code>
 *     UserPO userPo = userMapper.getUserById(userId);
 *     RolePO rolePo = roleMapper.getRoleByUserId(userId);
 *     return EntityCreator.convertUserVo(userPo, rolePo)
 * </code></pre>
 *
 * @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 <T>     目标类型
     * @param <S>     转换成的类型
     * @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;
    }
	
}

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

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值