How do beanautils copy lists?

Keywords: Programming Java Database Spring JDK

Background

We may often transform data between DO, Model, VO layer data:

  1. Entity corresponds to the data structure of persistence layer (generally the mapping model of database table);
  2. The Model corresponds to the data structure of the business layer;
  3. VO is the data structure that the Controller interacts with the client.

For example: the user information (table mapping model) queried from the database is UserDO, but we need to pass it to the client is UserVO. In this case, we need to assign the attributes of the UserDO instance to the UserVO instance one by one.

A large number of attributes may be the same or different between these data structures.

2, Data copy

2.1 data model

  • UserDO.jav

    @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;
    }
  • Note: the sex type of the last field of UserDO.java and UserVO.java is different, which are Integer/String

    2.2. Regular use - bean utils

    Spring provides the org.springframework.beans.BeanUtils class for quick assignment.

    For example, we copy the UserDO.java found in the database to 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);
    }

    Log printing:

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

    By printing the results, we can find that: except for the different types of sex, other values are copied successfully.

    2.3. Set copy

    What we just copied is an object, but sometimes we want to copy a group of UerDO.java. When it is a collection, we can't assign values directly. If this logic is also followed, it is as follows:

    The log is printed as follows:

    .... userVOList:[]

    Through the log, we can find that the direct copy collection is invalid, so how to solve it?

    3, Set copy

    3.1 violent copy (not recommended)

    Will need to copy the collection traversal, violent copy.

    Test method

    @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);
    }

    • Copy result

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

    Although this method can be solved, it is not elegant at all, especially the trouble of writing.

    3.2 elegant copy (recommended in this article)

    Encapsulate org.springframework.beans.BeanUtils through the functional interface of JDK 8

    • Define a functional interface

    The default method can be included in the functional interface. Here we define the default callback method.

    @FunctionalInterface
    public interface BeanCopyUtilCallBack <S, T> {

        /**
         * Define default callback method
         * @param t
         * @param s
         */
        void callBack(S t, T s);
    }

    • Encapsulate a data copying tool class BeanCopyUtil.java

    public class BeanCopyUtil extends BeanUtils {

        /**
         * Copy of collection data
         * @param sources: Data source class
         * @param target: Target class:: new(eg: UserVO::new)
         * @return
         */
        public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {
            return copyListProperties(sources, target, null);
        }


        /**
         * Copy of collection data with callback function (customizable field copy rule)
         * @param sources: Data source class
         * @param target: Target class:: new(eg: UserVO::new)
         * @param callBack: Callback function
         * @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.callBack(source, t);
                }
            }
            return list;
        }
    }

    • Simple copy test

    @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);
    }

    Print results:

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

    Through the above methods, we basically implement the copy of the collection, but from the returned results we can find that: fields with different properties cannot be copied. Therefore, we need to use the callback method just defined to implement the custom transformation.

    • Gender enumeration class

    public enum SexEnum {
        UNKNOW("Not set up",0),
        MEN("Schoolboy", 1),
        WOMAN("Girl student",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;
        }
    }

    • Copy with callback function

    @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) ->{
            // Here you can define specific transformation rules
            userVO.setSex(SexEnum.getDescByCode(userDO.getSex()).getDesc());
        });
        log.info("userVOList:{}",userVOList);
    }

    Print results:

    ... userVOList:[UserVO(userId=1, userName=Van, age=18, sex=Schoolboy), UserVO(userId=2, userName=VanVan, age=20, sex=Girl student)]

    It can be found from the printing results that the sex of Integer type in UserDO.java is copied to UserVO.java and becomes a boy / girl of String type.

    If you want to get learning materials, please comment on [learning] below the comment

    By Van Ou fa

    Link: https://juejin.im/post/5e1b18b1f265da3e140fa377

    Posted by smook on Wed, 15 Jan 2020 00:22:10 -0800