最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot如何進行對象復(fù)制的實踐

 更新時間:2021年09月24日 09:40:25   作者:飄渺Jam  
本文主要介紹了SpringBoot 如何進行對象復(fù)制,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

首先我們看看為什么需要對象復(fù)制?

為什么需要對象復(fù)制

image-20210906145134173

如上,是我們平時開發(fā)中最常見的三層MVC架構(gòu)模型,編輯操作時Controller層接收到前端傳來的DTO對象,在Service層需要將DTO轉(zhuǎn)換成DO,然后在數(shù)據(jù)庫中保存。查詢操作時Service層查詢到DO對象后需要將DO對象轉(zhuǎn)換成VO對象,然后通過Controller層返回給前端進行渲染。

這中間會涉及到大量的對象轉(zhuǎn)換,很明顯我們不能直接使用getter/setter復(fù)制對象屬性,這看上去太low了。想象一下你業(yè)務(wù)邏輯中充斥著大量的getter&setter,代碼評審時老鳥們會如何笑話你?

image-20210716084136689

所以我們必須要找一個第三方工具來幫我們實現(xiàn)對象轉(zhuǎn)換。

看到這里有同學(xué)可能會問,為什么不能前后端都統(tǒng)一使用DO對象呢?這樣就不存在對象轉(zhuǎn)換呀?

設(shè)想一下如果我們不想定義 DTO 和 VO,直接將 DO 用到數(shù)據(jù)訪問層、服務(wù)層、控制層和外部訪問接口上。此時該表刪除或則修改一個字段,DO 必須同步修改,這種修改將會影響到各層,這并不符合高內(nèi)聚低耦合的原則。通過定義不同的 DTO 可以控制對不同系統(tǒng)暴露不同的屬性,通過屬性映射還可以實現(xiàn)具體的字段名稱的隱藏。不同業(yè)務(wù)使用不同的模型,當一個業(yè)務(wù)發(fā)生變更需要修改字段時,不需要考慮對其它業(yè)務(wù)的影響,如果使用同一個對象則可能因為 “不敢亂改” 而產(chǎn)生很多不優(yōu)雅的兼容性行為。

對象復(fù)制工具類推薦

對象復(fù)制的類庫工具有很多,除了常見的Apache的BeanUtils,Spring的BeanUtils,Cglib BeanCopier,還有重量級組件MapStruct,Orika,DozerModelMapper等。

如果沒有特殊要求,這些工具類都可以直接使用,除了Apache的BeanUtils。原因在于Apache BeanUtils底層源碼為了追求完美,加了過多的包裝,使用了很多反射,做了很多校驗,所以導(dǎo)致性能較差,并在阿里巴巴開發(fā)手冊上強制規(guī)定避免使用 Apache BeanUtils。

強制規(guī)定避免使用 Apache BeanUtils

至于剩下的重量級組件,綜合考慮其性能還有使用的易用性,我這里更推薦使用Orika。Orika底層采用了javassist類庫生成Bean映射的字節(jié)碼,之后直接加載執(zhí)行生成的字節(jié)碼文件,在速度上比使用反射進行賦值會快很多。

國外大神 baeldung 已經(jīng)對常見的組件性能進行過詳細測試,大家可以通過 https://www.baeldung.com/java-performance-mapping-frameworks 查看。

Orika基本使用

要使用Orika很簡單,只需要簡單四步:

引入依賴

<dependency>
  <groupId>ma.glasnost.orika</groupId>
  <artifactId>orika-core</artifactId>
  <version>1.5.4</version>
</dependency>

構(gòu)造一個MapperFactory

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();  

注冊字段映射

mapperFactory.classMap(SourceClass.class, TargetClass.class)  
   .field("firstName", "givenName")
   .field("lastName", "sirName")
   .byDefault()
   .register();

當字段名在兩個實體不一致時可以通過.field()方法進行映射,如果字段名都一樣則可省略,byDefault()方法用于注冊名稱相同的屬性,如果不希望某個字段參與映射,可以使用exclude方法。

進行映射

MapperFacade mapper = mapperFactory.getMapperFacade();

SourceClass source = new SourceClass();  
// set some field values
...
// map the fields of 'source' onto a new instance of PersonDest
TargetClass target = mapper.map(source, TargetClass.class);  

經(jīng)過上面四步我們就完成了SourceClass到TargetClass的轉(zhuǎn)換。至于Orika的其他使用方法大家可以參考 http://orika-mapper.github.io/orika-docs/index.html

看到這里,肯定有粉絲會說:你這推薦的啥玩意呀,這個Orika使用也不簡單呀,每次都要這先創(chuàng)建MapperFactory,建立字段映射關(guān)系,才能進行映射轉(zhuǎn)換。

別急,我這里給你準備了一個工具類OrikaUtils,你可以通過文末github倉庫獲取。

它提供了五個公共方法:

image-20210903151829872

分別對應(yīng):

  • 字段一致實體轉(zhuǎn)換
  • 字段不一致實體轉(zhuǎn)換(需要字段映射)
  • 字段一致集合轉(zhuǎn)換
  • 字段不一致集合轉(zhuǎn)換(需要字段映射)
  • 字段屬性轉(zhuǎn)換注冊

接下來我們通過單元測試案例重點介紹此工具類的使用。

Orika工具類使用文檔

先準備兩個基礎(chǔ)實體類,Student,Teacher。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private String id;
    private String name;
    private String email;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private String id;
    private String name;
    private String emailAddress;
}

TC1,基礎(chǔ)實體映射

/**
 * 只拷貝相同的屬性
 */
@Test
public void convertObject(){
  Student student = new Student("1","javadaily","jianzh5@163.com");
  Teacher teacher = OrikaUtils.convert(student, Teacher.class);
  System.out.println(teacher);
}

輸出結(jié)果:

Teacher(id=1, name=javadaily, emailAddress=null)

此時由于屬性名不一致,無法映射字段email。

TC2,實體映射 - 字段轉(zhuǎn)換

/**
 * 拷貝不同屬性
 */
@Test
public void convertRefObject(){
  Student student = new Student("1","javadaily","jianzh5@163.com");

  Map<String,String> refMap = new HashMap<>(1);
  //map key 放置 源屬性,value 放置 目標屬性
  refMap.put("email","emailAddress");
  Teacher teacher = OrikaUtils.convert(student, Teacher.class, refMap);
  System.out.println(teacher);
}

輸出結(jié)果:

Teacher(id=1, name=javadaily, emailAddress=jianzh5@163.com)

此時由于對字段做了映射,可以將email映射到emailAddress。注意這里的refMap中key放置的是源實體的屬性,而value放置的是目標實體的屬性,不要弄反了。

TC3,基礎(chǔ)集合映射

/**
  * 只拷貝相同的屬性集合
  */
@Test
public void convertList(){
  Student student1 = new Student("1","javadaily","jianzh5@163.com");
  Student student2 = new Student("2","JAVA日知錄","jianzh5@xxx.com");
  List<Student> studentList = Lists.newArrayList(student1,student2);

  List<Teacher> teacherList = OrikaUtils.convertList(studentList, Teacher.class);

  System.out.println(teacherList);
}

輸出結(jié)果:

[Teacher(id=1, name=javadaily, emailAddress=null), Teacher(id=2, name=JAVA日知錄, emailAddress=null)]

此時由于屬性名不一致,集合中無法映射字段email。

TC4,集合映射 - 字段映射

/**
 * 映射不同屬性的集合
 */
@Test
public void convertRefList(){
  Student student1 = new Student("1","javadaily","jianzh5@163.com");
  Student student2 = new Student("2","JAVA日知錄","jianzh5@xxx.com");
  List<Student> studentList = Lists.newArrayList(student1,student2);

  Map<String,String> refMap = new HashMap<>(2);
  //map key 放置 源屬性,value 放置 目標屬性
  refMap.put("email","emailAddress");

  List<Teacher> teacherList = OrikaUtils.convertList(studentList, Teacher.class,refMap);

  System.out.println(teacherList);
}

輸出結(jié)果:

[Teacher(id=1, name=javadaily, emailAddress=jianzh5@163.com), Teacher(id=2, name=JAVA日知錄, emailAddress=jianzh5@xxx.com)]

也可以通過這樣映射:

Map<String,String> refMap = new HashMap<>(2);
refMap.put("email","emailAddress");
List<Teacher> teacherList = OrikaUtils.classMap(Student.class,Teacher.class,refMap)
        .mapAsList(studentList,Teacher.class);

TC5,集合與實體映射

有時候我們需要將集合數(shù)據(jù)映射到實體中,如Person類

@Data
public class Person {
    private List<String> nameParts;
}

現(xiàn)在需要將Person類nameParts的值映射到Student中,可以這樣做

/**
 * 數(shù)組和List的映射
 */
@Test
public void convertListObject(){
   Person person = new Person();
   person.setNameParts(Lists.newArrayList("1","javadaily","jianzh5@163.com"));

    Map<String,String> refMap = new HashMap<>(2);
    //map key 放置 源屬性,value 放置 目標屬性
    refMap.put("nameParts[0]","id");
    refMap.put("nameParts[1]","name");
    refMap.put("nameParts[2]","email");

    Student student = OrikaUtils.convert(person, Student.class,refMap);
    System.out.println(student);
}

輸出結(jié)果:

Student(id=1, name=javadaily, email=jianzh5@163.com)

TC6,類類型映射

有時候我們需要類類型對象映射,如BasicPerson類

@Data
public class BasicPerson {
    private Student student;
}

現(xiàn)在需要將BasicPerson映射到Teacher

/**
 * 類類型映射
 */
@Test
public void convertClassObject(){
    BasicPerson basicPerson = new BasicPerson();
    Student student = new Student("1","javadaily","jianzh5@163.com");
    basicPerson.setStudent(student);

    Map<String,String> refMap = new HashMap<>(2);
    //map key 放置 源屬性,value 放置 目標屬性
    refMap.put("student.id","id");
    refMap.put("student.name","name");
    refMap.put("student.email","emailAddress");

    Teacher teacher = OrikaUtils.convert(basicPerson, Teacher.class,refMap);
    System.out.println(teacher);
}

輸出結(jié)果:

Teacher(id=1, name=javadaily, emailAddress=jianzh5@163.com)

TC7,多重映射

有時候我們會遇到多重映射,如將StudentGrade映射到TeacherGrade

@Data
public class StudentGrade {
    private String studentGradeName;
    private List<Student> studentList;
}

@Data
public class TeacherGrade {
    private String teacherGradeName;
    private List<Teacher> teacherList;
}

這種場景稍微復(fù)雜,Student與Teacher的屬性有email字段不相同,需要做轉(zhuǎn)換映射;StudentGrade與TeacherGrade中的屬性也需要映射。

/**
 * 一對多映射
 */
@Test
public void convertComplexObject(){
  Student student1 = new Student("1","javadaily","jianzh5@163.com");
  Student student2 = new Student("2","JAVA日知錄","jianzh5@xxx.com");
  List<Student> studentList = Lists.newArrayList(student1,student2);

  StudentGrade studentGrade = new StudentGrade();
  studentGrade.setStudentGradeName("碩士");
  studentGrade.setStudentList(studentList);

  Map<String,String> refMap1 = new HashMap<>(1);
  //map key 放置 源屬性,value 放置 目標屬性
  refMap1.put("email","emailAddress");
  OrikaUtils.register(Student.class,Teacher.class,refMap1);


  Map<String,String> refMap2 = new HashMap<>(2);
  //map key 放置 源屬性,value 放置 目標屬性
  refMap2.put("studentGradeName", "teacherGradeName");
  refMap2.put("studentList", "teacherList");


  TeacherGrade teacherGrade = OrikaUtils.convert(studentGrade,TeacherGrade.class,refMap2);
  System.out.println(teacherGrade);
}

多重映射的場景需要根據(jù)情況調(diào)用OrikaUtils.register()注冊字段映射。

輸出結(jié)果:

TeacherGrade(teacherGradeName=碩士, teacherList=[Teacher(id=1, name=javadaily, emailAddress=jianzh5@163.com), Teacher(id=2, name=JAVA日知錄, emailAddress=jianzh5@xxx.com)])

TC8,MyBaits plus分頁映射

如果你使用的是mybatis的分頁組件,可以這樣轉(zhuǎn)換

public IPage<UserDTO> selectPage(UserDTO userDTO, Integer pageNo, Integer pageSize) {
  Page page = new Page<>(pageNo, pageSize);
  LambdaQueryWrapper<User> query = new LambdaQueryWrapper();
  if (StringUtils.isNotBlank(userDTO.getName())) {
    query.like(User::getKindName,userDTO.getName());
  }
  IPage<User> pageList = page(page,query);
  // 實體轉(zhuǎn)換 SysKind轉(zhuǎn)化為SysKindDto
  Map<String,String> refMap = new HashMap<>(3);
  refMap.put("kindName","name");
  refMap.put("createBy","createUserName");
  refMap.put("createTime","createDate");
  return pageList.convert(item -> OrikaUtils.convert(item, UserDTO.class, refMap));
}

小結(jié)

在MVC架構(gòu)中肯定少不了需要用到對象復(fù)制,屬性轉(zhuǎn)換的功能,借用Orika組件,可以很簡單實現(xiàn)這些功能。本文在Orika的基礎(chǔ)上封裝了工具類,進一步簡化了Orika的操作,希望對各位有所幫助。

到此這篇關(guān)于SpringBoot如何進行對象復(fù)制的實踐的文章就介紹到這了,更多相關(guān)SpringBoot 對象復(fù)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java生成申請單序列號的實現(xiàn)方法

    java生成申請單序列號的實現(xiàn)方法

    申請單序列號一般要求根據(jù)一定的規(guī)則生成后幾位連續(xù)的字符串,下面是我項目中使用的生成序列號的代碼,其中用到了鎖機制,有需要的朋友可以參考一下
    2014-01-01
  • SpringBoot應(yīng)用部署到Tomcat中無法啟動的解決方法

    SpringBoot應(yīng)用部署到Tomcat中無法啟動的解決方法

    這篇文章主要介紹了SpringBoot應(yīng)用部署到Tomcat中無法啟動的解決方法,需要的朋友可以參考下
    2017-09-09
  • SpringBoot快速入門及起步依賴解析(實例詳解)

    SpringBoot快速入門及起步依賴解析(實例詳解)

    SpringBoot?是由?Pivotal?團隊提供的全新框架,其設(shè)計目的是用來簡化?Spring?應(yīng)用的初始搭建以及開發(fā)過程,這篇文章主要介紹了SpringBoot快速入門及起步依賴解析,需要的朋友可以參考下
    2022-10-10
  • Java編程實現(xiàn)獲取mp3時長及播放mp3文件的方法

    Java編程實現(xiàn)獲取mp3時長及播放mp3文件的方法

    這篇文章主要介紹了Java編程實現(xiàn)獲取mp3時長及播放mp3文件的方法,涉及java基于jaudiotagger與jl包對MP3音頻文件屬性操作及音頻播放相關(guān)操作技巧,并提供了相關(guān)jar包的本站下載,需要的朋友可以參考下
    2018-02-02
  • java中線程的sleep()方法和yield()方法的區(qū)別

    java中線程的sleep()方法和yield()方法的區(qū)別

    本文主要介紹了java中線程的sleep()方法和yield()方法的區(qū)別,Thread類的sleep()方法使線程休眠指定時間,不釋放鎖,而yield()提示調(diào)度器當前線程愿意讓出CPU資源,不保證立即切換線程,感興趣的可以了解一下
    2024-10-10
  • Java Stream流零基礎(chǔ)教程

    Java Stream流零基礎(chǔ)教程

    Java8的另一大亮點Stream,它與java.io包里的InputStream和OutputStream是完全不同的概念,下面這篇文章主要給大家介紹了關(guān)于Java8中Stream詳細使用方法的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • java 8如何自定義收集器(collector)詳解

    java 8如何自定義收集器(collector)詳解

    這篇文章主要給大家介紹了關(guān)于java 8如何自定義收集器(collector)的相關(guān)資料,文中介紹的非常詳細,對大家具有一定的參考學(xué)習價值,需要的朋友們下面來一起看看吧。
    2017-06-06
  • Java9中操作和查詢本地進程信息的示例詳解

    Java9中操作和查詢本地進程信息的示例詳解

    這篇文章主要為大家詳細介紹了Java9中操作和查詢本地進程信息的相關(guān)知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習一下
    2024-03-03
  • Java8深入學(xué)習之熟透Optional

    Java8深入學(xué)習之熟透Optional

    這篇文章主要給大家介紹了關(guān)于Java8深入學(xué)習之熟透Optional的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習或者使用Java8具有一定的參考學(xué)習價值,需要的朋友們下面來一起學(xué)習學(xué)習吧
    2019-09-09
  • java中自帶有并發(fā)屬性的List總結(jié)

    java中自帶有并發(fā)屬性的List總結(jié)

    java中有很多l(xiāng)ist,但是原生支持并發(fā)的并不多,那么java中的并發(fā)list到底有哪些呢?下面小編就來給大家介紹一下ArrayList、CopyOnWriteArrayList、ConcurrentLinkedDeque這幾個吧
    2023-09-09

最新評論

彰化市| 若羌县| 张家界市| 梅州市| 蓝山县| 攀枝花市| 环江| 桐庐县| 屏山县| 安乡县| 安阳市| 鹤山市| 塔河县| 昌都县| 九龙坡区| 梧州市| 望江县| 喀什市| 资兴市| 新河县| 茂名市| 准格尔旗| 丽江市| 湘潭市| 杭锦后旗| 天门市| 博乐市| 东港市| 南岸区| 开化县| 罗定市| 且末县| 屯留县| 越西县| 阿勒泰市| 东明县| 赫章县| 鲜城| 温州市| 日土县| 漳州市|