BeanUtils 如何拷贝 List?

1、背景

咱们在DOModelVO层数据间可能常常转换数据:java

  1. Entity对应的是持久层数据结构(通常是数据库表的映射模型);
  2. Model 对应的是业务层的数据结构;
  3. VO 就是Controller和客户端交互的数据结构。

例如:数据库查询出来的用户信息(表的映射模型)是UserDO,可是咱们须要传递给客户端的是UserVO,这时候就须要把UserDO实例的属性一个一个赋值到UserVO实例中。git

在这些数据结构之间很大一部分属性均可能会相同,也可能不一样。github

2、数据拷贝

2.一、数据模型

  • UserDO.java
@Data
public class UserDO {

    private Long userId;
    private String userName;
    private Integer age;
    private Integer sex;
    public UserDO() {

    }

    public UserDO(Long userId, String userName, Integer age, Integer sex) {
        this.userId = userId;
        this.userName = userName;
        this.age = age;
        this.sex = sex;
    }
}
复制代码
  • UserVO.java
@Data
public class UserVO {
    private Long userId;
    private String userName;
    private Integer age;
    private String sex;
}
复制代码

注意: UserDO.javaUserVO.java 最后一个字段sex类型不同,分别是:Integer/Stringspring

2.二、常规使用-BeanUtils

Spring 提供了 org.springframework.beans.BeanUtils 类进行快速赋值。数据库

例如:咱们把数据库查询出来的UserDO.java 拷贝到 UserVO.java数据结构

@Test
public void commonCopy() {
    UserDO userDO = new UserDO(1L, "Van", 18, 1);
    UserVO userVO = new UserVO();
    BeanUtils.copyProperties(userDO, userVO);
    log.info("userVO:{}",userVO);
}
复制代码

日志打印:函数

.... userVO:UserVO(userId=1, userName=Van, age=18, sex=null)
复制代码

经过打印结果咱们能够发现:除了类型不一样的sex,其余数值都成功拷贝。工具

2.三、集合拷贝

刚刚拷贝的是一个对象,可是有时候咱们想拷贝一组UerDO.java,是一个集合的时候就不能这样直接赋值了。若是还按照这种逻辑,以下:post

@Test
public void listCopyFalse() {
    List<UserDO> userDOList = new ArrayList();
    userDOList.add(new UserDO(1L, "Van", 18, 1));
    userDOList.add(new UserDO(2L, "VanVan", 18, 2));
    List<UserVO> userVOList = new ArrayList();
    BeanUtils.copyProperties(userDOList, userVOList);
    log.info("userVOList:{}",userVOList);
}
复制代码

日志打印以下:测试

.... userVOList:[]
复制代码

经过日志能够发现,直接拷贝集合是无效的,那么怎么解决呢?

3、集合拷贝

3.一、暴力拷贝(不推荐)

将须要拷贝的集合遍历,暴力拷贝。

  • 测试方式
@Test
public void listCopyCommon() {
    List<UserDO> userDOList = new ArrayList();
    userDOList.add(new UserDO(1L, "Van", 18, 1));
    userDOList.add(new UserDO(2L, "VanVan", 20, 2));
    List<UserVO> userVOList = new ArrayList();
    userDOList.forEach(userDO ->{
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userDO, userVO);
        userVOList.add(userVO);
    });
    log.info("userVOList:{}",userVOList);
}
复制代码
  • 拷贝结果
.... userVOList:[UserVO(userId=1, userName=Van, age=18, sex=null), UserVO(userId=2, userName=VanVan, age=20, sex=null)]
复制代码

虽然该方式能够解决,可是一点都不优雅,特别是写起来麻烦。

3.二、优雅拷贝(本文推荐

经过JDK 8 的函数式接口封装org.springframework.beans.BeanUtils

  • 定义一个函数式接口

函数式接口里是能够包含默认方法,这里咱们定义默认回调方法。

@FunctionalInterface
public interface BeanCopyUtilCallBack <S, T> {

    /** * 定义默认回调方法 * @param t * @param s */
    void callBack(S t, T s);
}
复制代码
  • 封装一个数据拷贝工具类 BeanCopyUtil.java
public class BeanCopyUtil extends BeanUtils {

    /** * 集合数据的拷贝 * @param sources: 数据源类 * @param target: 目标类::new(eg: UserVO::new) * @return */
    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {
        return copyListProperties(sources, target, null);
    }


    /** * 带回调函数的集合数据的拷贝(可自定义字段拷贝规则) * @param sources: 数据源类 * @param target: 目标类::new(eg: UserVO::new) * @param callBack: 回调函数 * @return */
    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target, BeanCopyUtilCallBack<S, T> callBack) {
        List<T> list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T t = target.get();
            copyProperties(source, t);
            list.add(t);
            if (callBack != null) {
                // 回调
                callBack.callBack(source, t);
            }
        }
        return list;
    }
}
复制代码
  • 简单拷贝测试
@Test
public void listCopyUp() {
    List<UserDO> userDOList = new ArrayList();
    userDOList.add(new UserDO(1L, "Van", 18, 1));
    userDOList.add(new UserDO(2L, "VanVan", 20, 2));
    List<UserVO> userVOList = BeanCopyUtil.copyListProperties(userDOList, UserVO::new);
    log.info("userVOList:{}",userVOList);
}
复制代码

打印结果:

.... userVOList:[UserVO(userId=1, userName=Van, age=18, sex=null), UserVO(userId=2, userName=VanVan, age=20, sex=null)]
复制代码

经过如上方法,咱们基本实现了集合的拷贝,可是从返回结果咱们能够发现:属性不一样的字段没法拷贝。因此,咱们这里须要借助刚定义的回调方法实现自定义转换。

  • 性别枚举类
public enum SexEnum {
    UNKNOW("未设置",0),
    MEN("男生", 1),
    WOMAN("女生",2),

    ;
    private String desc;
    private int code;

    SexEnum(String desc, int code) {
        this.desc = desc;
        this.code = code;
    }

    public static SexEnum getDescByCode(int code) {
        SexEnum[] typeEnums = values();
        for (SexEnum value : typeEnums) {
            if (code == value.getCode()) {
                return value;
            }
        }
        return null;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}
复制代码
  • 带回调函数的拷贝
@Test
public void listCopyUpWithCallback() {
    List<UserDO> userDOList = new ArrayList();
    userDOList.add(new UserDO(1L, "Van", 18, 1));
    userDOList.add(new UserDO(2L, "VanVan", 20, 2));
    List<UserVO> userVOList = BeanCopyUtil.copyListProperties(userDOList, UserVO::new, (userDO, userVO) ->{
        // 这里能够定义特定的转换规则
        userVO.setSex(SexEnum.getDescByCode(userDO.getSex()).getDesc());
    });
    log.info("userVOList:{}",userVOList);
}
复制代码

打印结果:

... userVOList:[UserVO(userId=1, userName=Van, age=18, sex=男生), UserVO(userId=2, userName=VanVan, age=20, sex=女生)]
复制代码

经过打印结果能够发现,UserDO.javaInteger类型的sex复制到UserVO.java成了String类型的男生/女生。

4、技术交流

Github 源码

  1. 风尘博客:https://www.dustyblog.cn
  2. 风尘博客-掘金
  3. 风尘博客-博客园
  4. Github
  5. 公众号
    风尘博客
相关文章
相关标签/搜索