JavaRush /Java Blogu /Random-AZ /Mapstruct nədir və onu SpringBoot proqramlarında vahid sı...
Roman Beekeeper
Səviyyə

Mapstruct nədir və onu SpringBoot proqramlarında vahid sınağı üçün necə düzgün konfiqurasiya etmək olar

Qrupda dərc edilmişdir

Fon

Hər kəsə salam, əziz dostlarım və oxucularım! Məqaləni yazmazdan əvvəl bir az məlumat... Bu yaxınlarda burada telegram kanalımda qısaca təsvir etdiyim Mapstruct kitabxanası ilə işləmək problemi ilə qarşılaşdım . Şərhlərdə yazı ilə bağlı problem həll olundu, əvvəlki layihədəki həmkarım bu işdə kömək etdi. Bundan sonra bu mövzuda bir məqalə yazmaq qərarına gəldim, lakin təbii ki, dar bir baxış keçirməyəcəyik və əvvəlcə sürətlənməyə, Mapstruct-un nə olduğunu və nə üçün lazım olduğunu başa düşməyə çalışacağıq və real bir nümunədən istifadə edəcəyik. əvvəllər yaranmış vəziyyəti və onu necə həll edəcəyini təhlil edin. Buna görə də, hər şeyi praktikada yaşamaq üçün məqaləni oxumaqla paralel olaraq bütün hesablamaları etməyi şiddətlə tövsiyə edirəm. Başlamazdan əvvəl telegram kanalıma abunə olun , fəaliyyətlərimi orada toplayıram, Java və ümumiyyətlə İT-də inkişaf haqqında fikirlər yazıram. Abunə oldunuz? Əla! Yaxşı, indi gedək! Mapstruct nədir və onu SpringBoot proqramlarında vahid sınağı üçün necə düzgün konfiqurasiya etmək olar.  1-1 hissə

Mapstruct, tez-tez sual?

Sürətli növ təhlükəsiz lobya xəritələri üçün kod generatoru. İlk işimiz Mapstruct-un nə olduğunu və ona nə üçün ehtiyacımız olduğunu anlamaqdır. Ümumiyyətlə, bu barədə rəsmi internet saytında oxuya bilərsiniz. Saytın əsas səhifəsində suallara üç cavab var: bu nədir? Nə üçün? Necə? Biz də bunu etməyə çalışacağıq:

Bu nədir?

Mapstruct, interfeyslər vasitəsilə təsvir edilən konfiqurasiyalar əsasında yaradılan koddan istifadə edərək bəzi obyektlərin obyektlərini digər obyektlərin obyektlərinə xəritələşdirməyə (xəritə, ümumiyyətlə, onlar həmişə belə deyirlər: xəritə, xəritə və s.) kömək edən kitabxanadır.

Nə üçün?

Əksər hallarda biz çox qatlı proqramlar (verilənlər bazası ilə işləmək üçün təbəqə, biznes məntiqi təbəqəsi, tətbiqin xarici dünya ilə qarşılıqlı əlaqəsi üçün təbəqə) inkişaf etdiririk və hər bir təbəqənin məlumatların saxlanması və emalı üçün öz obyektləri var. . Və bu məlumatları bir varlıqdan digərinə köçürməklə təbəqədən təbəqəyə köçürmək lazımdır. Bu yanaşma ilə işləməmişlər üçün bu bir az mürəkkəb görünə bilər. Məsələn, Tələbə verilənlər bazası üçün bir qurumumuz var. Bu obyektin məlumatları biznes məntiqi (xidmətlər) səviyyəsinə keçdikdə, məlumatları Student sinfindən StudentModel sinfinə köçürməliyik. Sonra, biznes məntiqi ilə bütün manipulyasiyalardan sonra məlumatların kənarda buraxılması lazımdır. Bunun üçün bizdə StudentDto sinfi var. Əlbəttə ki, biz StudentModel sinfindən məlumatları StudentDto-ya ötürməliyik. Köçürüləcək üsulların hər dəfə əllə yazılması çox əmək tələb edir. Üstəlik bu, kod bazasında saxlanılmalı olan əlavə koddur. Səhv edə bilərsiniz. Mapstruct isə bu cür metodları kompilyasiya mərhələsində yaradır və onları yaradılan mənbələrdə saxlayır.

Necə?

Annotasiyalardan istifadə. Biz sadəcə olaraq kitabxanaya bu interfeysdəki metodların bir obyektdən digərinə tərcümə etmək üçün istifadə oluna biləcəyini bildirən əsas Mapper annotasiyası olan annotasiya yaratmalıyıq. Tələbələr haqqında daha əvvəl dediyim kimi, bizim vəziyyətimizdə bu, məlumatları bir təbəqədən digərinə ötürmək üçün bir neçə üsula malik olan StudentMapper interfeysi olacaq:
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;
}
Bu siniflər üçün xəritəçi yaradırıq (bundan sonra nəyi və hara köçürmək istədiyimizi təsvir edən interfeys adlandıracağıq):
@Mapper
public interface StudentMapper {
   StudentModel toModel(StudentDTO dto);
   Student toEntity(StudentModel model);
   StudentModel toModel(Student entity);
   StudentDTO toDto(StudentModel model);
}
Bu yanaşmanın gözəlliyi ondadır ki, əgər müxtəlif siniflərdə sahələrin adları və növləri eynidirsə (bizim vəziyyətimizdə olduğu kimi), onda Mapstruct üçün parametrlər kompilyasiya mərhələsində StudentMapper interfeysi əsasında lazımi tətbiqi yaratmaq üçün kifayətdir. tərcümə edəcək. Beləliklə, artıq aydın oldu, elə deyilmi? Gəlin daha da irəli gedək və Spring Boot proqramında işi təhlil etmək üçün real nümunədən istifadə edək.

Spring Boot və Mapstruct işinin nümunəsi

Bizə lazım olan ilk şey Spring Boot layihəsi yaratmaq və ona Mapstruct əlavə etməkdir. Bu mövzuda GitHub-da depolar üçün şablonları olan bir təşkilatım var və Spring Boot üçün başlanğıc onlardan biridir. Bunun əsasında yeni layihə yaradırıq: Sonra layihəniMapstruct nədir və onu SpringBoot proqramlarında vahid sınağı üçün necə düzgün konfiqurasiya etmək olar.  1-2 hissə alırıq . Bəli, dostlar, layihəni faydalı hesab etdinizsə, ona bir ulduz verin, ona görə də biləcəyəm ki, bunu boş yerə etmirəm. Bu layihədə işdə aldığım və Telegram kanalımdakı bir yazıda təsvir etdiyim bir vəziyyəti açıqlayacağıq . Bilməyənlər üçün vəziyyəti qısaca təsvir edəcəyəm: xəritəçilər üçün testlər yazarkən (yəni əvvəllər haqqında danışdığımız interfeys tətbiqləri üçün) testlərin mümkün qədər tez keçməsini istəyirik. Xəritəçəkənlərlə ən sadə seçim testi işləyərkən SpringBootTest annotasiyasından istifadə etməkdir ki, bu da Spring Boot tətbiqinin bütün ApplicationContext-i götürəcək və testin içinə test üçün lazım olan xəritəçini yeridəcək. Ancaq bu seçim resurs tələb edir və daha çox vaxt tələb edir, ona görə də bizim üçün uyğun deyil. Sadəcə olaraq istədiyiniz xəritəçini yaradan və onun metodlarının gözlədiyimiz kimi işlədiyini yoxlayan vahid testi yazmalıyıq. Daha sürətli işləmək üçün niyə testlərə ehtiyacınız var? Testlər uzun müddət çəkirsə, bütün inkişaf prosesini ləngidir. Testlər yeni kodu keçməyincə, bu kod düzgün hesab edilə bilməz və sınaqdan keçirilməyəcək, yəni istehsala qəbul edilməyəcək və bu, tərtibatçının işi tamamlamaması deməkdir. Deyəsən, işləməsi şübhə doğurmayan bir kitabxana üçün niyə test yazmalısınız? Və yenə də bir test yazmalıyıq, çünki xəritəçini necə düzgün təsvir etdiyimizi və onun gözlədiyimizi edib-etmədiyini yoxlayırıq. İlk öncə işimizi asanlaşdırmaq üçün pom.xml-ə başqa bir asılılıq əlavə edərək layihəmizə Lombok əlavə edək:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <scope>provided</scope>
</dependency>
Layihəmizdə model siniflərdən (biznes məntiqi ilə işləmək üçün istifadə olunan) xarici dünya ilə ünsiyyət üçün istifadə etdiyimiz DTO siniflərinə keçməmiz lazım gələcək. Sadələşdirilmiş versiyamızda sahələrin dəyişmədiyini və xəritəçilərimizin sadə olacağını güman edəcəyik. Amma istək olarsa, Mapstruct ilə necə işləmək, onu necə konfiqurasiya etmək və onun üstünlüklərindən necə yararlanmaq barədə daha ətraflı məqalə yazmaq olardı. Ancaq sonra, çünki bu məqalə olduqca uzun olacaq. Tutaq ki, bir tələbəmiz var, onun iştirak etdiyi mühazirə və mühazirəçilərin siyahısı var. Model paketi yaradaq . Buna əsaslanaraq sadə bir model yaradacağıq:
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;
}
onun mühazirələri
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
və mühazirəçilər
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
Və model paketinin yanında dto paketi yaradın :
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;
}
mühazirələr
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
və mühazirəçilər
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
İndi mühazirə modelləri toplusunu DTO mühazirələri toplusuna çevirəcək mapper yaradaq. Ediləcək ilk şey Mapstruct-u layihəyə əlavə etməkdir. Bunun üçün biz onların rəsmi internet saytından istifadə edəcəyik , orada hər şey təsvir edilmişdir. Yəni yaddaşımıza bir asılılıq və plagin əlavə etməliyik (yaddaşın nə olduğu ilə bağlı sualınız varsa, buyurun, Maddə1Maddə2 ):
<dependency>
   <groupId>org.mapstruct</groupId>
   <artifactId>mapstruct</artifactId>
   <version>1.4.2.Final</version>
</dependency>
və <build/> blokunda yaddaşda. bizdə hələ yoxdur:
<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>
Sonra, dtomodelin yanında mapper paketi yaradaq . Daha əvvəl göstərdiyimiz siniflərə əsasən, daha beş xəritəçi yaratmalı olacaqsınız:
  • Mapper LectureModel <-> LectureDTO
  • Mapper List<LectureModel> <-> List<LectureDTO>
  • Mapper LecturerModel <-> LecturerDTO
  • Mapper List<LecturerModel> <-> List<LecturerDTO>
  • Mapper StudentModel <-> StudentDTO
Get:

LectureMapper

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

MühazirəçiMapper

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

MühazirəçiListMapper

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

StudentMapper

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);
}
Ayrı-ayrılıqda qeyd etmək lazımdır ki, xəritəçilərdə biz başqa xəritəçilərə müraciət edirik. Bu , StudentMapper-də olduğu kimi, Mapper annotasiyasındakı istifadə sahəsi vasitəsilə həyata keçirilir :
@Mapper(componentModel = "spring", uses = {LectureListMapper.class, LecturerListMapper.class})
Burada mühazirələrin siyahısını və mühazirəçilərin siyahısını düzgün xəritələşdirmək üçün iki xəritəçidən istifadə edirik. İndi kodumuzu tərtib etməliyik və orada nə və necə olduğunu görməliyik. Bu, mvn clean compile əmrindən istifadə etməklə edilə bilər . Lakin məlum oldu ki, xəritəçilərimizin Mapstruct tətbiqlərini yaratarkən, xəritəçi tətbiqləri sahələrin üzərinə yazılmayıb. Niyə? Məlum oldu ki, Lombokdan Data annotasiyasını götürmək mümkün deyil. Və nəsə etmək lazım idi... Ona görə də yazıda yeni bölməmiz var.

Lombok və Mapstruct-u birləşdirin

Bir neçə dəqiqəlik axtarışdan sonra məlum oldu ki, Lombok və Mapstruct-u müəyyən bir şəkildə birləşdirməliyik. Mapstruct sənədlərində bu barədə məlumat var . Mapstruct-dan tərtibatçılar tərəfindən təklif olunan nümunəni araşdırdıqdan sonra pom.xml-imizi yeniləyək: Ayrı-ayrı versiyalar əlavə edək:
​​<properties>
   <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
   <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
Çatışmayan asılılığı əlavə edək:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok-mapstruct-binding</artifactId>
   <version>${lombok-mapstruct-binding.version}</version>
</dependency>
Gəlin tərtibçi plaginimizi yeniləyək ki, Lombok və Mapstruct-u birləşdirə bilsin:
<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>
Bundan sonra hər şey düzəlməlidir. Layihəmizi yenidən tərtib edək. Bəs Mapstruct-un yaratdığı sinifləri haradan tapa bilərsiniz? Onlar yaradılan mənbələrdədir: ${projectDir}/target/generated-sources/annotations/ Mapstruct nədir və onu SpringBoot proqramlarında vahid sınağı üçün necə düzgün konfiqurasiya etmək olar.  1-3 hissə İndi Mapstruct yazısından məyusluğumu başa düşməyə hazırıq, gəlin xəritəçilər üçün testlər yaratmağa çalışaq.

Xəritəçəkənlərimiz üçün testlər yazırıq

İnteqrasiya testi yaratdığımız halda xəritəçilərdən birini sınaqdan keçirəcək tez və sadə bir test yaradacağam və onun tamamlanma vaxtı barədə narahat olmayın:

LectureMapperTest

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());
   }
}
Burada SpringBootTest annotasiyasından istifadə edərək biz bütün proqram kontekstini işə salırıq və ondan Autowired annotasiyasından istifadə edərək sınaq üçün lazım olan sinfi çıxarırıq. Test yazmağın sürəti və asanlığı baxımından bu çox yaxşıdır. Test uğurla keçdi, hər şey yaxşıdır. Amma biz başqa yolla gedəcəyik və xəritəçi üçün vahid testi yazacağıq, məsələn, 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());
   }
}
Mapstruct-un yaratdığı tətbiqlər layihəmizlə eyni sinifdə olduğundan, biz onları testlərimizdə asanlıqla istifadə edə bilərik. Hər şey əla görünür - annotasiya yoxdur, biz ən sadə şəkildə ehtiyac duyduğumuz sinfi yaradırıq və budur. Amma testi həyata keçirəndə biz başa düşəcəyik ki, o, qəzaya uğrayacaq və konsolda NullPointerException olacaq... Bunun səbəbi LectureListMapper mapper-in tətbiqi belə görünür:
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;
   }
}
NPE-ə (NullPointerException üçün qısa) baxsaq, onu lectureMapper dəyişənindən alırıq , bu da işə salınmayıb. Amma bizim tətbiqimizdə dəyişəni işə sala biləcəyimiz konstruktor yoxdur. Mapstruct-un xəritəçini bu şəkildə həyata keçirməsinin səbəbi məhz budur! Baharda siz bir neçə yolla dərslərə lobya əlavə edə bilərsiniz, yuxarıda göstərildiyi kimi onları Autowired annotasiyası ilə birlikdə sahəyə yeridə bilərsiniz və ya konstruktor vasitəsilə yeridə bilərsiniz. Testin icra müddətini optimallaşdırmaq lazım olanda özümü işdə belə problemli vəziyyətdə tapdım. Bununla bağlı heç nə edilə bilməyəcəyini düşündüm və ağrılarımı Telegram kanalımda tökdüm. Və sonra şərhlərdə mənə kömək etdilər və inyeksiya strategiyasını fərdiləşdirməyin mümkün olduğunu söylədilər. Mapper interfeysində iki dəyəri olan InjectionStrategy adını qəbul edən injectionStrategy sahəsi var: FIELDCONSTRUCTOR . İndi bunu bilərək, gəlin bu parametri xəritəçilərimizə əlavə edək; mən bunu LectureListMapper istifadə edərək nümunə olaraq göstərəcəyəm :
@Mapper(componentModel = "spring", uses = LectureMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface LectureListMapper {
   List<LectureModel> toModelList(List<LectureDTO> dtos);
   List<LectureDTO> toDTOList(List<LectureModel> models);
}
Əlavə etdiyim hissəni qalın hərflərlə vurğuladım. Bütün digərləri üçün bu seçimi əlavə edək və layihəni yenidən kompilyasiya edək ki, xəritəçilər yeni sətirlə yaradılsın. Bunu etdikdən sonra LectureListMapper üçün xəritəçinin tətbiqinin necə dəyişdiyini görək (lazım olan hissə qalın hərflərlə vurğulanır):
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;
   }
}
İndi Mapstruct konstruktor vasitəsilə mapper inyeksiyasını həyata keçirdi. Məhz buna nail olmağa çalışdıq. İndi testimiz tərtib etməyi dayandıracaq, onu yeniləyək və əldə edək:
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());
   }
}
İndi testi keçirsək, hər şey gözlənildiyi kimi işləyəcək, çünki LectureListMapperImpl-də biz ona lazım olan LectureMapper-i keçirik... Qələbə! Sizin üçün çətin deyil, amma məmnunam: Dostlar, hər şey həmişəki kimidir, mənim GitHub hesabıma , Telegram hesabıma abunə olun . Orada fəaliyyətimin nəticələrini yerləşdirirəm, həqiqətən faydalı şeylər var) Sizi xüsusilə telegram kanalının müzakirə qrupuna qoşulmağa dəvət edirəm . Elə olur ki, kimin texniki sualı olsa, orada cavab ala bilər. Bu format hamı üçün maraqlıdır, kimin nə bildiyini oxuyub təcrübə qazana bilərsiniz.

Nəticə

Bu yazının bir hissəsi olaraq Mapstruct kimi zəruri və tez-tez istifadə olunan məhsulla tanış olduq. Bunun nə olduğunu, niyə və necə olduğunu anladıq. Həqiqi bir nümunədən istifadə edərək, nəyin edilə biləcəyini və necə dəyişdirilə biləcəyini hiss etdik. Biz həmçinin konstruktor vasitəsilə lobya inyeksiyasının necə qurulacağına baxdıq ki, xəritəçilərin düzgün sınaqdan keçirilməsi mümkün olsun. Mapstruct-dan olan həmkarlar öz məhsullarının istifadəçilərinə xəritəçilərin necə inyeksiya ediləcəyini dəqiq seçmək imkanı verdilər, buna görə biz onlara, şübhəsiz ki, təşəkkür edirik. AMMA, Spring-in konstruktor vasitəsilə lobya yeritməyi tövsiyə etməsinə baxmayaraq, Mapstruct-dan olan uşaqlar standart olaraq sahəyə inyeksiya təyin etdilər. Niyə belədir? Cavab yoxdur. Düşünürəm ki, bizim bilmədiyimiz səbəblər ola bilər və buna görə də bunu belə etdilər. Və onlardan öyrənmək üçün onların rəsmi məhsul deposunda GitHub məsələsi yaratdım .
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION