JavaRush /Java Blog /Random-ID /Apa itu Mapstruct dan cara mengkonfigurasinya dengan bena...

Apa itu Mapstruct dan cara mengkonfigurasinya dengan benar untuk pengujian unit di aplikasi SpringBoot

Dipublikasikan di grup Random-ID

Latar belakang

Halo semuanya, teman dan pembaca terkasih! Sebelum kita menulis artikel, sedikit latar belakang... Saya baru-baru ini mengalami masalah saat bekerja dengan perpustakaan Mapstruct , yang saya jelaskan secara singkat di saluran telegram saya di sini . Di komentar, masalah postingan telah terpecahkan; rekan saya dari proyek sebelumnya membantu dalam hal ini. Apa itu Mapstruct dan cara mengkonfigurasinya dengan benar untuk pengujian unit di aplikasi SpringBoot.  Bagian 1 - 1Setelah itu saya memutuskan untuk menulis artikel tentang topik ini, namun tentunya kita tidak akan mengambil pandangan sempit dan pertama-tama akan mencoba untuk mengetahui apa itu Mapstruct dan mengapa itu diperlukan, dan dengan menggunakan contoh nyata kita akan melakukannya. menganalisis situasi yang muncul sebelumnya dan cara mengatasinya. Oleh karena itu, saya sangat menyarankan melakukan semua perhitungan secara paralel dengan membaca artikel untuk merasakan semuanya dalam praktik. Sebelum kita mulai, berlangganan saluran telegram saya , saya mengumpulkan aktivitas saya di sana, menulis pemikiran tentang perkembangan di Java dan IT secara umum. Berlangganan? Besar! Nah, sekarang ayo pergi!

Struktur peta, pertanyaan umum?

Generator kode untuk pemetaan kacang aman tipe cepat. Tugas pertama kita adalah mencari tahu apa itu Mapstruct dan mengapa kita membutuhkannya. Secara umum, Anda dapat membacanya di situs resminya. Di halaman utama situs terdapat tiga jawaban atas pertanyaan: apa itu? Untuk apa? Bagaimana? Kami akan mencoba melakukan ini juga:

Apa itu?

Mapstruct adalah perpustakaan yang membantu memetakan (memetakan, secara umum, begitulah yang selalu mereka katakan: memetakan, memetakan, dll.) objek dari beberapa entitas ke objek entitas lain menggunakan kode yang dihasilkan berdasarkan konfigurasi yang dijelaskan melalui antarmuka.

Untuk apa?

Untuk sebagian besar, kami mengembangkan aplikasi multi-layer (lapisan untuk bekerja dengan database, lapisan logika bisnis, lapisan untuk interaksi aplikasi dengan dunia luar) dan setiap lapisan memiliki objeknya sendiri untuk menyimpan dan memproses data. . Dan data ini perlu ditransfer dari lapisan ke lapisan dengan mentransfer dari satu entitas ke entitas lainnya. Bagi mereka yang belum pernah menggunakan pendekatan ini, ini mungkin tampak agak rumit. Misalnya, kita memiliki entitas untuk database Siswa. Ketika data entitas ini masuk ke lapisan logika bisnis (layanan), kita perlu mentransfer data dari kelas Student ke kelas StudentModel. Selanjutnya, setelah semua manipulasi dengan logika bisnis, data perlu dilepaskan ke luar. Dan untuk ini kami memiliki kelas StudentDto. Tentu saja, kita perlu meneruskan data dari kelas StudentModel ke StudentDto. Menulis dengan tangan setiap kali metode yang akan ditransfer membutuhkan banyak tenaga. Ditambah lagi, ini adalah kode tambahan dalam basis kode yang perlu dipertahankan. Anda bisa membuat kesalahan. Dan Mapstruct menghasilkan metode seperti itu pada tahap kompilasi dan menyimpannya di sumber yang dihasilkan.

Bagaimana?

Menggunakan anotasi. Kita hanya perlu membuat anotasi yang memiliki anotasi Mapper utama yang memberi tahu perpustakaan bahwa metode dalam antarmuka ini dapat digunakan untuk menerjemahkan dari satu objek ke objek lainnya. Seperti yang saya katakan sebelumnya tentang siswa, dalam kasus kami ini adalah antarmuka StudentMapper, yang memiliki beberapa metode untuk mentransfer data dari satu lapisan ke lapisan lainnya:
public class Student {
   private Long id;
   private String firstName;
   private String lastName;
   private Integer age;
}

public class StudentDTO {
   private Long id;
   private String firstName;
   private String lastName;
   private Integer age;
}

public class StudentModel {
   private Long id;
   private String firstName;
   private String lastName;
   private Integer age;
}
Untuk kelas-kelas ini kita membuat mapper (selanjutnya kita akan memanggil antarmuka, yang menjelaskan apa yang ingin kita transfer dan ke mana):
@Mapper
public interface StudentMapper {
   StudentModel toModel(StudentDTO dto);
   Student toEntity(StudentModel model);
   StudentModel toModel(Student entity);
   StudentDTO toDto(StudentModel model);
}
Keindahan dari pendekatan ini adalah jika nama dan tipe bidang sama di kelas yang berbeda (seperti dalam kasus kami), maka pengaturan untuk Mapstruct cukup untuk menghasilkan implementasi yang diperlukan berdasarkan antarmuka StudentMapper pada tahap kompilasi, yang mana akan menerjemahkan. Jadi sudah menjadi lebih jelas kan? Mari melangkah lebih jauh dan gunakan contoh nyata untuk menganalisis pekerjaan di aplikasi Spring Boot.

Contoh kerja Spring Boot dan Mapstruct

Hal pertama yang kita perlukan adalah membuat proyek Spring Boot dan menambahkan Mapstruct ke dalamnya. Dalam hal ini, saya memiliki organisasi di GitHub dengan templat untuk repositori dan permulaan untuk Spring Boot adalah salah satunya. Berdasarkan hal tersebut, kita membuat proyek baru: Apa itu Mapstruct dan cara mengkonfigurasinya dengan benar untuk pengujian unit di aplikasi SpringBoot.  Bagian 1 - 2Selanjutnya, kita mendapatkan proyek tersebut . Ya teman-teman, beri bintang pada proyek ini jika menurut Anda bermanfaat, agar saya tahu bahwa saya melakukan ini tidak sia-sia. Dalam proyek ini, kami akan mengungkapkan situasi yang saya terima di tempat kerja dan dijelaskan dalam postingan di saluran Telegram saya . Saya akan menguraikan secara singkat situasinya bagi mereka yang belum mengetahuinya: ketika kita menulis tes untuk pembuat peta (yaitu, untuk implementasi antarmuka yang kita bicarakan sebelumnya), kita ingin tes tersebut lulus secepat mungkin. Opsi paling sederhana dengan pembuat peta adalah dengan menggunakan anotasi SpringBootTest saat menjalankan pengujian, yang akan mengambil seluruh ApplicationContext dari aplikasi Spring Boot dan memasukkan mapper yang diperlukan untuk pengujian ke dalam pengujian. Namun opsi ini membutuhkan banyak sumber daya dan membutuhkan lebih banyak waktu, sehingga tidak cocok untuk kami. Kita perlu menulis pengujian unit yang hanya membuat mapper yang diinginkan dan memeriksa apakah metodenya berfungsi persis seperti yang kita harapkan. Mengapa Anda memerlukan pengujian agar berjalan lebih cepat? Jika pengujian memakan waktu lama, hal ini akan memperlambat keseluruhan proses pengembangan. Sampai pengujian lulus pada kode baru, kode ini tidak dapat dianggap benar dan tidak akan diambil untuk pengujian, yang berarti tidak akan diproduksi dan berarti pengembang belum menyelesaikan pekerjaannya. Tampaknya, mengapa menulis tes untuk perpustakaan yang pengoperasiannya tidak diragukan lagi? Namun kita perlu menulis tes, karena kita menguji seberapa benar kita mendeskripsikan pembuat peta dan apakah ia melakukan apa yang kita harapkan. Pertama-tama, untuk mempermudah pekerjaan kita, mari tambahkan Lombok ke proyek kita dengan menambahkan ketergantungan lain ke pom.xml:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <scope>provided</scope>
</dependency>
Dalam proyek kami, kami perlu mentransfer dari kelas model (yang digunakan untuk bekerja dengan logika bisnis) ke kelas DTO, yang kami gunakan untuk berkomunikasi dengan dunia luar. Dalam versi kami yang disederhanakan, kami akan berasumsi bahwa bidang tidak berubah dan pembuat peta kami akan sederhana. Namun, jika ada keinginan, Anda dapat menulis artikel yang lebih detail tentang cara bekerja dengan Mapstruct, cara mengkonfigurasinya, dan cara memanfaatkan manfaatnya. Tapi karena artikel ini akan cukup panjang. Katakanlah kita mempunyai seorang mahasiswa dengan daftar perkuliahan dan dosen yang dia ikuti. Mari buat paket model . Berdasarkan hal tersebut, kita akan membuat model sederhana:
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

import java.util.List;

@Data
public class StudentDTO {

   private Long id;

   private String name;

   private List<LectureDTO> lectures;

   private List<LecturerDTO> lecturers;
}
kuliahnya
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
dan dosen
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
Dan buat paket dto di sebelah paket model :
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

import java.util.List;

@Data
public class StudentDTO {

   private Long id;

   private String name;

   private List<LectureDTO> lectures;

   private List<LecturerDTO> lecturers;
}
kuliah
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
dan dosen
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
Sekarang mari kita buat mapper yang akan menerjemahkan kumpulan model perkuliahan menjadi kumpulan perkuliahan DTO. Hal pertama yang harus dilakukan adalah menambahkan Mapstruct ke proyek. Untuk melakukan ini, kami akan menggunakan situs resmi mereka , semuanya dijelaskan di sana. Artinya, kita perlu menambahkan satu ketergantungan dan sebuah plugin ke memori kita (jika Anda memiliki pertanyaan tentang apa itu memori, ini dia, Article1 dan Article2 ):
<dependency>
   <groupId>org.mapstruct</groupId>
   <artifactId>mapstruct</artifactId>
   <version>1.4.2.Final</version>
</dependency>
dan di memori di blok <build/>. yang belum kami miliki:
<build>
   <plugins>
       <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>3.5.1</version>
           <configuration>
               <source>1.8</source>
               <target>1.8</target>
               <annotationProcessorPaths>
                   <path>
                       <groupId>org.mapstruct</groupId>
                       <artifactId>mapstruct-processor</artifactId>
                       <version>1.4.2.Final</version>
                   </path>
               </annotationProcessorPaths>
           </configuration>
       </plugin>
   </plugins>
</build>
Selanjutnya, mari buat paket mapper di samping dto dan model . Berdasarkan kelas yang kami tunjukkan sebelumnya, Anda perlu membuat lima pembuat peta lagi:
  • Model Kuliah Mapper <-> LectureDTO
  • Daftar Pemeta<LectureModel> <-> Daftar<LectureDTO>
  • Model Dosen Mapper <-> DosenDTO
  • Daftar Pemeta<LecturerModel> <-> Daftar<LecturerDTO>
  • Pemeta StudentModel <-> StudentDTO
Pergi:

Pemeta Kuliah

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.dto.LecturerDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring")
public interface LectureMapper {
   LectureDTO toDTO(LectureModel model);

   LectureModel toModel(LecturerDTO dto);
}

LectureListMapper

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring", uses = LectureMapper.class)
public interface LectureListMapper {
   List<LectureModel> toModelList(List<LectureDTO> dtos);
   List<LectureDTO> toDTOList(List<LectureModel> models);
}

Pemeta Dosen

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring")
public interface LectureMapper {
   LectureDTO toDTO(LectureModel model);

   LectureModel toModel(LectureDTO dto);
}

Pemeta Daftar Dosen

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LecturerDTO;
import com.github.romankh3.templaterepository.springboot.model.LecturerModel;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring", uses = LecturerMapper.class)
public interface LecturerListMapper {
   List<LecturerModel> toModelList(List<LecturerDTO> dloList);
   List<LecturerDTO> toDTOList(List<LecturerModel> modelList);
}

Pemeta Siswa

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.StudentDTO;
import com.github.romankh3.templaterepository.springboot.model.StudentModel;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring", uses = {LectureListMapper.class, LecturerListMapper.class})
public interface StudentMapper {
   StudentDTO toDTO(StudentModel model);
   StudentModel toModel(StudentDTO dto);
}
Perlu dicatat secara terpisah bahwa dalam pembuat peta kami merujuk pada pembuat peta lain. Hal ini dilakukan melalui bidang penggunaan dalam anotasi Mapper, seperti yang dilakukan di StudentMapper:
@Mapper(componentModel = "spring", uses = {LectureListMapper.class, LecturerListMapper.class})
Disini kita menggunakan dua mapper untuk memetakan daftar perkuliahan dan daftar dosen dengan benar. Sekarang kita perlu mengkompilasi kode kita dan melihat apa yang ada di sana dan bagaimana caranya. Ini dapat dilakukan dengan menggunakan perintah mvn clean kompilasi . Namun, ternyata, saat membuat implementasi Mapstruct pada mapper kami, implementasi mapper tidak menimpa kolom tersebut. Mengapa? Ternyata anotasi Data tidak dapat diambil dari Lombok. Dan sesuatu harus dilakukan... Oleh karena itu, kami memiliki bagian baru di artikel ini.

Menghubungkan Lombok dan Mapstruct

Setelah beberapa menit mencari, ternyata kami perlu menghubungkan Lombok dan Mapstruct dengan cara tertentu. Ada informasi tentang ini di dokumentasi Mapstruct . Setelah memeriksa contoh yang diusulkan oleh pengembang dari Mapstruct, mari perbarui pom.xml kita: Mari tambahkan versi terpisah:
​​<properties>
   <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
   <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
Mari tambahkan ketergantungan yang hilang:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok-mapstruct-binding</artifactId>
   <version>${lombok-mapstruct-binding.version}</version>
</dependency>
Dan mari perbarui plugin compiler kita agar dapat menghubungkan Lombok dan Mapstruct:
<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-compiler-plugin</artifactId>
   <version>3.5.1</version>
   <configuration>
       <source>1.8</source>
       <target>1.8</target>
       <annotationProcessorPaths>
           <path>
               <groupId>org.mapstruct</groupId>
               <artifactId>mapstruct-processor</artifactId>
               <version>${org.mapstruct.version}</version>
           </path>
           <path>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok</artifactId>
               <version>${lombok.version}</version>
           </path>
           <path>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok-mapstruct-binding</artifactId>
               <version>${lombok-mapstruct-binding.version}</version>
           </path>
       </annotationProcessorPaths>
   </configuration>
</plugin>
Setelah ini semuanya akan beres. Mari kita kompilasi proyek kita lagi. Namun di mana Anda dapat menemukan kelas yang dihasilkan Mapstruct? Mereka ada di sumber yang dihasilkan: ${projectDir}/target/generated-sources/annotations/ Apa itu Mapstruct dan cara mengkonfigurasinya dengan benar untuk pengujian unit di aplikasi SpringBoot.  Bagian 1 - 3 Sekarang kita siap untuk menyadari kekecewaan saya dari postingan Mapstruct, mari kita coba membuat tes untuk pembuat peta.

Kami menulis tes untuk pembuat peta kami

Saya akan membuat pengujian cepat dan sederhana yang akan menguji salah satu pembuat peta jika kita membuat pengujian integrasi dan tidak mengkhawatirkan waktu penyelesaiannya:

Tes Pemeta Kuliah

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class LectureMapperTest {

   @Autowired
   private LectureMapper mapperUnderTest;

   @Test
   void shouldProperlyMapModelToDto() {
       //given
       LectureModel model = new LectureModel();
       model.setId(11L);
       model.setName("lecture name");

       //when
       LectureDTO dto = mapperUnderTest.toDTO(model);

       //then
       Assertions.assertNotNull(dto);
       Assertions.assertEquals(model.getId(), dto.getId());
       Assertions.assertEquals(model.getName(), dto.getName());
   }

   @Test
   void shouldProperlyMapDtoToModel() {
       //given
       LectureDTO dto = new LectureDTO();
       dto.setId(11L);
       dto.setName("lecture name");

       //when
       LectureModel model = mapperUnderTest.toModel(dto);

       //then
       Assertions.assertNotNull(model);
       Assertions.assertEquals(dto.getId(), model.getId());
       Assertions.assertEquals(dto.getName(), model.getName());
   }
}
Di sini, dengan menggunakan anotasi SpringBootTest, kami meluncurkan seluruh applicationContext dan darinya, menggunakan anotasi Autowired, kami mengekstrak kelas yang kami perlukan untuk pengujian. Dari segi kecepatan dan kemudahan penulisan tes, ini sangat bagus. Tesnya berhasil, semuanya baik-baik saja. Namun kita akan memilih cara lain dan menulis pengujian unit untuk pembuat peta, misalnya, LectureListMapper...
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.List;

class LectureListMapperTest {

   private final LectureListMapper lectureListMapper = new LectureListMapperImpl();

   @Test
   void shouldProperlyMapListDtosToListModels() {
       //given
       LectureDTO dto = new LectureDTO();
       dto.setId(12L);
       dto.setName("I'm BATMAN!");

       List<LectureDTO> dtos = Collections.singletonList(dto);

       //when
       List<LectureModel> models = lectureListMapper.toModelList(dtos);

       //then
       Assertions.assertNotNull(models);
       Assertions.assertEquals(1, models.size());
       Assertions.assertEquals(dto.getId(), models.get(0).getId());
       Assertions.assertEquals(dto.getName(), models.get(0).getName());
   }
}
Karena implementasi yang dihasilkan Mapstruct berada di kelas yang sama dengan proyek kita, kita dapat dengan mudah menggunakannya dalam pengujian. Semuanya tampak hebat - tanpa anotasi, kami membuat kelas yang kami perlukan dengan cara paling sederhana dan hanya itu. Tetapi ketika kita menjalankan pengujian, kita akan memahami bahwa itu akan crash dan akan ada NullPointerException di konsol... Ini karena implementasi mapper LectureListMapper terlihat seperti:
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Generated(
   value = "org.mapstruct.ap.MappingProcessor",
   date = "2021-12-09T21:46:12+0300",
   comments = "version: 1.4.2.Final, compiler: javac, environment: Java 15.0.2 (N/A)"
)
@Component
public class LectureListMapperImpl implements LectureListMapper {

   @Autowired
   private LectureMapper lectureMapper;

   @Override
   public List<LectureModel> toModelList(List<LectureDTO> dtos) {
       if ( dtos == null ) {
           return null;
       }

       List<LectureModel> list = new ArrayList<LectureModel>( dtos.size() );
       for ( LectureDTO lectureDTO : dtos ) {
           list.add( lectureMapper.toModel( lectureDTO ) );
       }

       return list;
   }

   @Override
   public List<LectureDTO> toDTOList(List<LectureModel> models) {
       if ( models == null ) {
           return null;
       }

       List<LectureDTO> list = new ArrayList<LectureDTO>( models.size() );
       for ( LectureModel lectureModel : models ) {
           list.add( lectureMapper.toDTO( lectureModel ) );
       }

       return list;
   }
}
Jika kita melihat NPE (kependekan dari NullPointerException), kita mendapatkannya dari variabel ceramahMapper , yang ternyata tidak diinisialisasi. Namun dalam implementasinya, kami tidak memiliki konstruktor yang dapat digunakan untuk menginisialisasi variabel. Inilah alasan mengapa Mapstruct mengimplementasikan mapper dengan cara ini! Di Spring, Anda dapat menambahkan kacang ke kelas dengan beberapa cara, Anda dapat memasukkannya melalui bidang bersama dengan anotasi Autowired, seperti yang dilakukan di atas, atau Anda dapat memasukkannya melalui konstruktor. Saya mendapati diri saya berada dalam situasi bermasalah di tempat kerja ketika saya perlu mengoptimalkan waktu pelaksanaan pengujian. Saya pikir tidak ada yang bisa dilakukan untuk mengatasinya dan mencurahkan rasa sakit saya di saluran Telegram saya. Dan kemudian mereka membantu saya dalam komentar dan mengatakan bahwa strategi injeksi dapat disesuaikan. Antarmuka Mapper memiliki bidang injectionStrategy , yang hanya menerima nama InjectionStrategy , yang memiliki dua nilai: FIELD dan CONSTRUCTOR . Sekarang, mengetahui hal ini, mari tambahkan pengaturan ini ke pembuat peta kita; saya akan menunjukkannya menggunakan LectureListMapper sebagai contoh :
@Mapper(componentModel = "spring", uses = LectureMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface LectureListMapper {
   List<LectureModel> toModelList(List<LectureDTO> dtos);
   List<LectureDTO> toDTOList(List<LectureModel> models);
}
Saya menyorot dengan huruf tebal bagian yang saya tambahkan. Mari tambahkan opsi ini untuk semua opsi lainnya dan kompilasi ulang proyek sehingga pembuat peta dihasilkan dengan baris baru. Setelah ini selesai, mari kita lihat bagaimana implementasi mapper untuk LectureListMapper telah berubah (sorot bagian yang kita perlukan dengan huruf tebal):
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Generated(
   value = "org.mapstruct.ap.MappingProcessor",
   date = "2021-12-09T22:25:37+0300",
   comments = "version: 1.4.2.Final, compiler: javac, environment: Java 15.0.2 (N/A)"
)
@Component
public class LectureListMapperImpl implements LectureListMapper {

   private final LectureMapper lectureMapper;

   @Autowired
   public LectureListMapperImpl(LectureMapper lectureMapper) {

       this.lectureMapper = lectureMapper;
   }

   @Override
   public List<LectureModel> toModelList(List<LectureDTO> dtos) {
       if ( dtos == null ) {
           return null;
       }

       List<LectureModel> list = new ArrayList<LectureModel>( dtos.size() );
       for ( LectureDTO lectureDTO : dtos ) {
           list.add( lectureMapper.toModel( lectureDTO ) );
       }

       return list;
   }

   @Override
   public List<LectureDTO> toDTOList(List<LectureModel> models) {
       if ( models == null ) {
           return null;
       }

       List<LectureDTO> list = new ArrayList<LectureDTO>( models.size() );
       for ( LectureModel lectureModel : models ) {
           list.add( lectureMapper.toDTO( lectureModel ) );
       }

       return list;
   }
}
Dan sekarang Mapstruct telah mengimplementasikan injeksi mapper melalui konstruktor. Inilah yang ingin kami capai. Sekarang pengujian kita akan berhenti dikompilasi, mari perbarui dan dapatkan:
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.List;

class LectureListMapperTest {

   private final LectureListMapper lectureListMapper = new LectureListMapperImpl(new LectureMapperImpl());

   @Test
   void shouldProperlyMapListDtosToListModels() {
       //given
       LectureDTO dto = new LectureDTO();
       dto.setId(12L);
       dto.setName("I'm BATMAN!");

       List<LectureDTO> dtos = Collections.singletonList(dto);

       //when
       List<LectureModel> models = lectureListMapper.toModelList(dtos);

       //then
       Assertions.assertNotNull(models);
       Assertions.assertEquals(1, models.size());
       Assertions.assertEquals(dto.getId(), models.get(0).getId());
       Assertions.assertEquals(dto.getName(), models.get(0).getName());
   }
}
Sekarang, jika kita menjalankan pengujian, semuanya akan berjalan seperti yang diharapkan, karena di LectureListMapperImpl kita meneruskan LectureMapper yang dibutuhkan... Kemenangan! Ini tidak sulit bagi Anda, tetapi saya senang: Teman-teman, semuanya seperti biasa, berlangganan akun GitHub saya , ke akun Telegram saya . Di sana saya posting hasil kegiatan saya, ada hal-hal yang bermanfaat sekali) Saya terutama mengundang Anda untuk bergabung dalam grup diskusi saluran telegram . Kebetulan jika seseorang memiliki pertanyaan teknis, mereka bisa mendapatkan jawabannya di sana. Format ini menarik untuk semua orang, Anda dapat membaca entah apa dan mendapatkan pengalaman.

Kesimpulan

Sebagai bagian dari artikel ini, kami berkenalan dengan produk yang diperlukan dan sering digunakan seperti Mapstruct. Kami menemukan apa itu, mengapa dan bagaimana. Dengan menggunakan contoh nyata, kami merasakan apa yang bisa dilakukan dan bagaimana hal itu bisa diubah. Kami juga melihat cara menyiapkan injeksi kacang melalui konstruktor, sehingga memungkinkan untuk menguji pembuat peta dengan benar. Rekan-rekan dari Mapstruct mengizinkan pengguna produk mereka untuk memilih dengan tepat cara menginjeksi pembuat peta, dan kami tentu berterima kasih kepada mereka. TAPI, terlepas dari kenyataan bahwa Spring merekomendasikan untuk menyuntikkan kacang melalui konstruktor, orang-orang dari Mapstruct telah mengatur injeksi melalui bidang secara default. Mengapa demikian? Tidak ada Jawaban. Saya curiga mungkin ada alasan yang tidak kita ketahui, dan itulah mengapa mereka melakukannya dengan cara ini. Dan untuk mengetahuinya, saya membuat masalah GitHub di repositori produk resmi mereka.
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION