【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 映射方法
 *
 * <p>这个类的 Get(Function) 方法对应的 Set(BiConsumer)方法 的映射</p>
 *
 * @param <Target> 设置属性的目标对象的类型
 * @param <Source> 获取属性的来源对象的类型
 */
public class FunctionMap<Target, Source> {
    /**
     * 当前所属构建器
     */
    private final PropertyMapCopyBuilder<Target> owner;

    /**
     * Get 方法对应要设置的 Set 方法的映射
     */
    private final Map<Function, BiConsumer> functionMap = new HashMap<>();

    /**
     * 对 Get 数据进行处理为 Set 所需的数据的方法
     */
    private final Map<Function, Function> handlerMap = new HashMap<>();

    protected FunctionMap(PropertyMapCopyBuilder<Target> owner) {
        this.owner = owner;
    }

    /**
     * 添加方法映射
     *
     * @param consumer Set 方法
     * @param function Get 方法
     * @return 返回当前对象以便链式调用
     */
    public <Result> FunctionMap<Target, Source> add(BiConsumer<Target, Result> consumer, Function<Source, Result> function) {
        functionMap.put(function, consumer);
        return this;
    }

    /**
     * 添加方法映射
     *
     * @param consumer     Set 方法
     * @param function     Get 方法
     * @param handler      对 Get 数据进行处理为 Set 所需的数据的方法
     * @param <Result>     数据结果类型
     * @param <SourceType> 数据来源类型
     */
    public <Result, SourceType> FunctionMap<Target, Source> add(BiConsumer<Target, Result> consumer, Function<Source, SourceType> function, Function<SourceType, Result> handler) {
        functionMap.put(function, consumer);
        handlerMap.put(function, handler);
        return this;
    }

    /**
     * 获取这个类的方法映射
     *
     * @param clazz 数据来源的类的类型
     * @param <S>   获取数据的来源对象的类型
     */
    public <S> FunctionMap<Target, S> map(Class<S> clazz) {
        return owner.map(clazz);
    }

    /**
     * 构建出来 EntityConvertBuilder 对象
     */
    public PropertyMapCopyBuilder<Target> build() {
        return owner;
    }

    /**
     * 对属性进行赋值
     *
     * @param target 设置属性的目标对象
     * @param source 获取属性的来源对象
     */
    public void copy(Target target, Source source) {
        BiConsumer<Target, Object> consumer;
        Function<Source, 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 {
                consumer.accept(target, function.apply(source));
            }
        }
    }

}

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

PropertyMapCopyBuilder

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

/**
 * <p>数据属性的映射构建器</p>
 *
 * <p>创建一个构造器实例,添加每个类对应的复制到对应的属性的方法。使用构造器的{@link FunctionMap#map(Class)}方法获取数据来源类的映射
 * 对象,然后调用{@link FunctionMap#add(BiConsumer, Function)}方法添加映射的属性方法。添加完成之后使用{@link FunctionMap#build()}
 * 方法构建出构建器对象。</p>
 *
 * <p>使用{@link PropertyMapCopyBuilder}对象的{@link PropertyMapCopyBuilder#copy(Object, Object...)}方法将数据复制到目标对象上,
 * 或使用{@link PropertyMapCopyBuilder#create(Object...)}方法创建出来实例对象,将属性复制到这个对象身上。</p>
 *
 * <p>示例:</p>
 *
 * <pre>{@code
 * final PropertyMapCopyBuilder<UserDTO> builder = PropertyCopyBuilder.instance(UserDTO.class)
 *      .map(UserPO.class)
 *      .add(UserDTO::setId, UserPO::getId)
 *      .add(UserDTO::setUsername, UserPO::getUsername)
 *      .map(RolePO.class)
 *      .add(UserDTO::setRolename, RolePO::getName)
 *      .add(UserDTO::setPermission, RolePO::getPermission)
 *      .build();
 * 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 <Target> 要复制到的目标对象的类型
 * @author zhangxuetu
 * @date 2022-11-24
 * @since 1.8
 */
public class PropertyMapCopyBuilder<Target> {

    /**
     * 项目中的类对应的{@link PropertyMapCopyBuilder},这个 map 已经包含有这个 Class,则不重新创建新的{@link PropertyMapCopyBuilder}
     */
    private final static Map<Class, PropertyMapCopyBuilder> BUILDER_MAP = new HashMap<>();

    /**
     * 转换到的类
     */
    private final Class<Target> clazz;

    /**
     * 不同的类的方法可以设置的值
     */
    private final Map<Class, FunctionMap> classToFunctionMap = new HashMap<>();

    private PropertyMapCopyBuilder(Class<Target> clazz) {
        this.clazz = clazz;
    }

    /**
     * 创建个新的 Builder
     *
     * @param clazz 类型
     * @param <T>   这个类的类型
     * @return 返回新建的{@link PropertyMapCopyBuilder},这个创建出来的{@link PropertyMapCopyBuilder}与
     * {@link PropertyMapCopyBuilder#instance(Class)}方式创建出来的不同,这个每次都要设置类映射的属性,然后{@link FunctionMap#build()}
     * 进行构建出来{@link PropertyMapCopyBuilder}才能正常使用
     */
    public static <T> PropertyMapCopyBuilder<T> create(Class<T> clazz) {
        return new PropertyMapCopyBuilder<>(clazz);
    }

    /**
     * 实例化一个构建器单例,如果已经创建过这个类的 {@link PropertyMapCopyBuilder},则直接返回创建过的 {@link PropertyMapCopyBuilder}
     *
     * @param clazz 转换的类
     * @param <T>   这个类的类型
     */
    public static <T> PropertyMapCopyBuilder<T> instance(Class<T> clazz) {
        if (BUILDER_MAP.containsKey(clazz)) {
            return BUILDER_MAP.get(clazz);
        }
        final PropertyMapCopyBuilder<T> builder = new PropertyMapCopyBuilder<>(clazz);
        BUILDER_MAP.put(clazz, builder);
        return builder;
    }

    /**
     * 调用这个方法开始添加Set方法映射的Get方法的那个类
     *
     * @param clazz    类的类型
     * @param <Source> 获取数据的对象的类型
     */
    public <Source> FunctionMap<Target, Source> map(Class<Source> clazz) {
        if (classToFunctionMap.containsKey(clazz)) {
            return classToFunctionMap.get(clazz);
        } else {
            final FunctionMap<Target, Source> functionMap = new FunctionMap<>(this);
            classToFunctionMap.put(clazz, functionMap);
            return functionMap;
        }
    }

    /**
     * 复制属性到这个对象上
     *
     * @param target  设置属性的目标对象
     * @param getters 获取数据的对象
     */
    public void copy(Target target, Object... getters) {
        for (Object getter : getters) {
            // 获取这个类的 FunctionMap 对象记录着映射的方法
            final FunctionMap<Target, Object> functionMap = classToFunctionMap.get(getter.getClass());
            functionMap.copy(target, getter);
        }
    }

    /**
     * 根据这个几个对象创建出来目标类型的对象
     *
     * @param getters 获取数据的对象列表
     */
    public Target create(Object... getters) {
        Target target = null;
        try {
            target = clazz.getDeclaredConstructor().newInstance();
            copy(target, getters);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return target;
    }

}

创建一个测试类,测试一下功能

CopyPropertyTest

public class CopyPropertyTest {

    public static void main(String[] args) {
        // 实例化一个属性映射构建器
        final PropertyMapCopyBuilder<UserDTO> builder = PropertyMapCopyBuilder.instance(UserDTO.class)
                .map(UserPO.class)
                .add(UserDTO::setId, UserPO::getId)
                .add(UserDTO::setUsername, UserPO::getUsername)
                .map(RolePO.class)
                .add(UserDTO::setRoleName, RolePO::getName)
                .add(UserDTO::setPermission, RolePO::getPermission)
                .map(TagPO.class)
                // 添加第三个参数进行处理数据,比如这里将 Integer 改为了 Boolean 类型的数据
                .add(UserDTO::setShow, TagPO::getTagIsShow, (v) -> v == null ? null : (v != 0))
                .build();

        final UserPO userPO1 = new UserPO(null, "zhangsan", "123456", "15511112222");
        final RolePO rolePO1 = new RolePO(1, "管理员", "/user,/log");
        final UserDTO userDTO1 = builder.create(userPO1, rolePO1);
        System.out.println(userDTO1);

        final UserPO userPO2 = new UserPO(2, "zhaosi", "45555", "1454888888");
        final RolePO rolePO2 = new RolePO(5, "admin", "/user,/log");
        final TagPO tagPO = new TagPO(1, "c2xaDk3l", 1);
        final UserDTO userDTO2 = builder.create(userPO2, rolePO2, tagPO);
        System.out.println(userDTO2);


    }


    //==================================================
    //              实体类
    //==================================================

    public static class TagPO {
        private Integer tagId;
        private String tagCode;
        private Integer tagIsShow;

        public TagPO() {
        }

        public TagPO(Integer tagId, String tagCode, Integer tagIsShow) {
            this.tagId = tagId;
            this.tagCode = tagCode;
            this.tagIsShow = tagIsShow;
        }

        public Integer getTagId() {
            return tagId;
        }

        public void setTagId(Integer tagId) {
            this.tagId = tagId;
        }

        public String getTagCode() {
            return tagCode;
        }

        public void setTagCode(String tagCode) {
            this.tagCode = tagCode;
        }

        public Integer getTagIsShow() {
            return tagIsShow;
        }

        public void setTagIsShow(Integer tagIsShow) {
            this.tagIsShow = tagIsShow;
        }
    }

    public static class UserPO {
        private Integer id;
        private String username;
        private String password;
        private String phone;

        public UserPO() {
        }

        public UserPO(Integer id, String username, String password, String phone) {
            this.id = id;
            this.username = username;
            this.password = password;
            this.phone = phone;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getPhone() {
            return phone;
        }

        public void setPhone(String phone) {
            this.phone = phone;
        }

        @Override
        public String toString() {
            return "UserPO{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    ", phone='" + phone + '\'' +
                    '}';
        }
    }

    public static class UserDTO {
        private Integer id;
        private String username;
        private String phone;
        private String roleName;
        private String permission;
        private Boolean show;

        public UserDTO() {
        }

        public UserDTO(Integer id, String username, String phone, String roleName, String permission, Boolean show) {
            this.id = id;
            this.username = username;
            this.phone = phone;
            this.roleName = roleName;
            this.permission = permission;
            this.show = show;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPhone() {
            return phone;
        }

        public void setPhone(String phone) {
            this.phone = phone;
        }

        public String getRoleName() {
            return roleName;
        }

        public void setRoleName(String roleName) {
            this.roleName = roleName;
        }

        public String getPermission() {
            return permission;
        }

        public void setPermission(String permission) {
            this.permission = permission;
        }

        public Boolean getShow() {
            return show;
        }

        public void setShow(Boolean show) {
            this.show = show;
        }

        @Override
        public String toString() {
            return "UserDTO{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", phone='" + phone + '\'' +
                    ", roleName='" + roleName + '\'' +
                    ", permission='" + permission + '\'' +
                    ", show=" + show +
                    '}';
        }
    }

    public static class RolePO {
        private Integer id;
        private String name;
        private String permission;

        public RolePO() {
        }

        public RolePO(Integer id, String name, String permission) {
            this.id = id;
            this.name = name;
            this.permission = permission;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getPermission() {
            return permission;
        }

        public void setPermission(String permission) {
            this.permission = permission;
        }

        @Override
        public String toString() {
            return "RolePO{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", permission='" + permission + '\'' +
                    '}';
        }
    }

}

输出结果

UserDTO{id=null, username='zhangsan', phone='null', roleName='管理员', permission='/user,/log', show=null}
UserDTO{id=2, username='zhaosi', phone='null', roleName='admin', permission='/user,/log', show=true}

可以看到,因为 phone 的属性没有设置映射,所以没有被赋值,其他的都对应数据来源对象映射到的方法的值

发表评论