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. 公衆號
    風塵博客
相關文章
相關標籤/搜索