zoukankan      html  css  js  c++  java
  • BeanUtils 如何拷贝 List?

    摘自:https://www.cnblogs.com/vandusty/p/12184769.html

    BeanUtils 如何拷贝 List?

    一、背景

    我们在DOModelVO层数据间可能经常转换数据:

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

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

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

    二、数据拷贝

    2.1、数据模型

    • 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.java 和UserVO.java 最后一个字段sex类型不一样,分别是:Integer/String

    2.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.3、集合拷贝

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

    @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.1、暴力拷贝(不推荐)

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

    • 测试方式
    @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.2、优雅拷贝(本文推荐

    通过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.java 中Integer类型的sex复制到UserVO.java成了String类型的男生/女生。

    四、更多

    Github 源码

    1. 风尘博客:https://www.dustyblog.cn
    2. 风尘博客-掘金
    3. 风尘博客-博客园
    4. Github
    5. 公众号

      风尘博客

    技术交流,欢迎扫一扫!

    风尘博客

     
    分类: Spring
    标签: Spring
  • 相关阅读:
    大作文-学以”成人”
    方案类--博物院整改意见
    归纳概括-我国中小学开展研学旅行活动的特点
    短文-网络新一代
    短评
    讲话稿-文明素养教育主题宣传
    检验用户单点登录方案解决
    Spring @Transactional注解
    RPC-局限于java的RMI
    Redis缓存雪崩、击穿、穿透的问题和解决方式
  • 原文地址:https://www.cnblogs.com/xichji/p/12208597.html
Copyright © 2011-2022 走看看