JavaRush /Java Blog /Random-TL /Ano ang Mapstruct at kung paano ito i-configure nang maay...

Ano ang Mapstruct at kung paano ito i-configure nang maayos para sa pagsubok ng unit sa mga application ng SpringBoot

Nai-publish sa grupo

Background

Kumusta sa lahat, mahal kong mga kaibigan at mambabasa! Bago namin isulat ang artikulo, isang maliit na background... Nakatagpo ako kamakailan ng isang problema sa pagtatrabaho sa Mapstruct library , na maikling inilarawan ko sa aking telegram channel dito . Ang problema sa post ay nalutas sa mga komento; ang aking kasamahan mula sa nakaraang proyekto ay tumulong dito. Ano ang Mapstruct at kung paano ito i-configure nang maayos para sa pagsubok ng unit sa mga application ng SpringBoot.  Bahagi 1 - 1Pagkatapos nito, nagpasya akong magsulat ng isang artikulo sa paksang ito, ngunit siyempre hindi kami kukuha ng isang makitid na pananaw at susubukan munang makakuha ng bilis, maunawaan kung ano ang Mapstruct at kung bakit ito kinakailangan, at gamit ang isang tunay na halimbawa ay gagawin namin. pag-aralan ang sitwasyon na lumitaw kanina at kung paano ito lutasin. Samakatuwid, masidhi kong inirerekumenda na gawin ang lahat ng mga kalkulasyon na kahanay sa pagbabasa ng artikulo upang maranasan ang lahat sa pagsasanay. Bago tayo magsimula, mag-subscribe sa aking telegram channel , kinokolekta ko ang aking mga aktibidad doon, sumulat ng mga saloobin tungkol sa pag-unlad sa Java at IT sa pangkalahatan. Naka-subscribe? Malaki! Well, ngayon tayo na!

Mapstruct, faq?

Isang code generator para sa mabilis na uri-safe bean mappings. Ang aming unang gawain ay upang malaman kung ano ang Mapstruct at kung bakit namin ito kailangan. Sa pangkalahatan, maaari mong basahin ang tungkol dito sa opisyal na website. Sa pangunahing pahina ng site mayroong tatlong sagot sa mga tanong: ano ito? Para saan? Paano? Susubukan din naming gawin ito:

Ano ito?

Ang Mapstruct ay isang library na tumutulong sa pagmamapa (mapa, sa pangkalahatan, iyon ang lagi nilang sinasabi: mapa, mapa, atbp.) na mga bagay ng ilang entity sa mga object ng iba pang entity gamit ang nabuong code batay sa mga configuration na inilalarawan sa pamamagitan ng mga interface.

Para saan?

Para sa karamihan, bumuo kami ng mga multi-layer na application (isang layer para sa pagtatrabaho sa database, isang layer ng business logic, isang layer para sa pakikipag-ugnayan ng application sa labas ng mundo) at ang bawat layer ay may sarili nitong mga object para sa pag-iimbak at pagproseso ng data . At ang data na ito ay kailangang ilipat mula sa layer patungo sa layer sa pamamagitan ng paglilipat mula sa isang entity patungo sa isa pa. Para sa mga hindi pa nakagawa sa diskarteng ito, maaaring mukhang medyo kumplikado ito. Halimbawa, mayroon kaming entity para sa database ng Mag-aaral. Kapag napunta ang data ng entity na ito sa business logic (services) layer, kailangan naming ilipat ang data mula sa Student class papunta sa StudentModel class. Susunod, pagkatapos ng lahat ng mga manipulasyon sa lohika ng negosyo, ang data ay kailangang ilabas sa labas. At para dito mayroon kaming klase ng StudentDto. Siyempre, kailangan nating ipasa ang data mula sa klase ng StudentModel sa StudentDto. Ang pagsulat sa pamamagitan ng kamay sa bawat oras na ang mga pamamaraan na ililipat ay labor-intensive. Dagdag pa ito ay dagdag na code sa base ng code na kailangang mapanatili. Maaari kang magkamali. At ang Mapstruct ay bumubuo ng mga ganitong pamamaraan sa yugto ng pagsasama-sama at iniimbak ang mga ito sa mga nabuong mapagkukunan.

Paano?

Paggamit ng mga anotasyon. Kailangan lang naming gumawa ng anotasyon na mayroong pangunahing anotasyon ng Mapper na nagsasabi sa library na ang mga pamamaraan sa interface na ito ay maaaring gamitin upang magsalin mula sa isang bagay patungo sa isa pa. Tulad ng sinabi ko kanina tungkol sa mga mag-aaral, sa aming kaso ito ang magiging interface ng StudentMapper, na magkakaroon ng ilang mga pamamaraan para sa paglilipat ng data mula sa isang layer patungo sa isa pa:
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;
}
Para sa mga klaseng ito, lumikha kami ng isang mapper (pagkatapos nito ay kung ano ang tatawagin naming interface, na naglalarawan kung ano ang gusto naming ilipat at kung saan):
@Mapper
public interface StudentMapper {
   StudentModel toModel(StudentDTO dto);
   Student toEntity(StudentModel model);
   StudentModel toModel(Student entity);
   StudentDTO toDto(StudentModel model);
}
Ang kagandahan ng diskarte na ito ay kung ang mga pangalan at uri ng mga patlang ay pareho sa iba't ibang mga klase (tulad ng sa aming kaso), kung gayon ang mga setting para sa Mapstruct ay sapat na upang makabuo ng kinakailangang pagpapatupad batay sa interface ng StudentMapper sa yugto ng compilation, na kung saan magsasalin. Kaya naging mas malinaw na, tama ba? Pumunta pa tayo at gumamit ng totoong halimbawa para pag-aralan ang trabaho sa isang Spring Boot na application.

Isang halimbawa ng Spring Boot at Mapstruct na gumagana

Ang unang bagay na kailangan namin ay lumikha ng isang Spring Boot na proyekto at magdagdag ng Mapstruct dito. Para sa bagay na ito, mayroon akong isang organisasyon sa GitHub na may mga template para sa mga repositoryo at isang simula para sa Spring Boot ay isa sa mga ito. Batay dito, lumikha kami ng isang bagong proyekto: Ano ang Mapstruct at kung paano ito i-configure nang maayos para sa pagsubok ng unit sa mga application ng SpringBoot.  Bahagi 1 - 2Susunod, makuha namin ang proyekto . Oo, mga kaibigan, bigyan ang proyekto ng isang bituin kung nakita mong kapaki-pakinabang ito, upang malaman ko na hindi ko ito ginagawa nang walang kabuluhan. Sa proyektong ito, ibubunyag namin ang isang sitwasyon na natanggap ko sa trabaho at inilarawan sa isang post sa aking Telegram channel . Sa madaling sabi, ilalarawan ko ang sitwasyon para sa mga hindi alam: kapag sumulat kami ng mga pagsubok para sa mga mapper (iyon ay, para sa mga pagpapatupad ng interface na napag-usapan namin kanina), gusto naming pumasa ang mga pagsubok nang mabilis hangga't maaari. Ang pinakasimpleng opsyon sa mga mapper ay ang paggamit ng SpringBootTest annotation kapag nagpapatakbo ng pagsubok, na kukunin ang buong ApplicationContext ng Spring Boot application at iniksyon ang mapper na kailangan para sa pagsubok sa loob ng pagsubok. Ngunit ang pagpipiliang ito ay masinsinang mapagkukunan at tumatagal ng mas maraming oras, kaya hindi ito angkop para sa amin. Kailangan nating magsulat ng unit test na lumilikha lamang ng nais na mapper at sinusuri kung ang mga pamamaraan nito ay gumagana nang eksakto tulad ng inaasahan natin. Bakit kailangan mo ng mga pagsubok upang tumakbo nang mas mabilis? Kung ang mga pagsubok ay tumatagal ng mahabang panahon, pinapabagal nito ang buong proseso ng pag-unlad. Hanggang sa maipasa ng mga pagsubok ang bagong code, ang code na ito ay hindi maituturing na tama at hindi kukunin para sa pagsubok, na nangangahulugang hindi ito dadalhin sa produksyon at ibig sabihin ay hindi pa natapos ng developer ang trabaho. Tila, bakit sumulat ng isang pagsubok para sa isang silid-aklatan na ang operasyon ay walang pag-aalinlangan? Gayunpaman, kailangan nating magsulat ng isang pagsubok, dahil sinusubok natin kung gaano natin inilarawan nang tama ang mapper at kung ginagawa nito ang inaasahan natin. Una sa lahat, upang gawing mas madali ang ating trabaho, idagdag natin ang Lombok sa ating proyekto sa pamamagitan ng pagdaragdag ng isa pang dependency sa pom.xml:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <scope>provided</scope>
</dependency>
Sa aming proyekto, kakailanganin naming lumipat mula sa mga klase ng modelo (na ginagamit upang gumana sa lohika ng negosyo) sa mga klase ng DTO, na ginagamit namin upang makipag-usap sa labas ng mundo. Sa aming pinasimple na bersyon, ipagpalagay namin na ang mga patlang ay hindi nagbabago at ang aming mga mapper ay magiging simple. Ngunit, kung may pagnanais, posibleng magsulat ng mas detalyadong artikulo sa kung paano magtrabaho sa Mapstruct, kung paano i-configure ito, at kung paano samantalahin ang mga benepisyo nito. Ngunit pagkatapos, dahil ang artikulong ito ay magiging mahaba. Sabihin na nating may estudyante tayong may listahan ng mga lecture at lecturer na pinapasukan niya. Gumawa tayo ng modelong pakete . Batay dito, gagawa kami ng isang simpleng modelo:
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;
}
kanyang mga lecture
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
at mga lecturer
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
At lumikha ng isang pakete ng dto sa tabi ng pakete ng modelo :
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;
}
mga lecture
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
at mga lecturer
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
Ngayon, gumawa tayo ng mapper na magsasalin ng isang koleksyon ng mga modelo ng lecture sa isang koleksyon ng mga lecture ng DTO. Ang unang bagay na dapat gawin ay magdagdag ng Mapstruct sa proyekto. Upang gawin ito, gagamitin namin ang kanilang opisyal na website , ang lahat ay inilarawan doon. Iyon ay, kailangan naming magdagdag ng isang dependency at isang plugin sa aming memorya (kung mayroon kang mga tanong tungkol sa kung ano ang memorya, narito ka, Artikulo1 at Artikulo2 ):
<dependency>
   <groupId>org.mapstruct</groupId>
   <artifactId>mapstruct</artifactId>
   <version>1.4.2.Final</version>
</dependency>
at sa memorya sa <build/> block. na hindi pa natin nakukuha:
<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>
Susunod, gumawa tayo ng mapper package sa tabi ng dto at model . Batay sa mga klase na ipinakita namin kanina, kakailanganin mong lumikha ng lima pang mapper:
  • Mapper LectureModel <-> LectureDTO
  • Listahan ng Mapper<LectureModel> <-> Listahan<LectureDTO>
  • Mapper LecturerModel <-> LecturerDTO
  • Listahan ng Mapper<LecturerModel> <-> Listahan<LecturerDTO>
  • Mapper StudentModel <-> StudentDTO
Pumunta:

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

LecturerMapper

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

LecturerListMapper

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);
}
Dapat pansinin nang hiwalay na sa mga mapper ay tinutukoy namin ang iba pang mga mapper. Ginagawa ito sa pamamagitan ng field ng mga gamit sa anotasyon ng Mapper, tulad ng ginagawa sa StudentMapper:
@Mapper(componentModel = "spring", uses = {LectureListMapper.class, LecturerListMapper.class})
Dito ay gumagamit kami ng dalawang mapper upang wastong mapa ang listahan ng mga lecture at ang listahan ng mga lecturer. Ngayon kailangan naming i-compile ang aming code at tingnan kung ano ang naroroon at kung paano. Magagawa ito gamit ang mvn clean compile command . Ngunit, tulad ng nangyari, kapag lumilikha ng mga pagpapatupad ng Mapstruct ng aming mga mapper, hindi na-overwrite ng mga pagpapatupad ng mapper ang mga field. Bakit? Lumalabas na hindi posibleng kunin ang Data annotation mula sa Lombok. At may kailangang gawin... Samakatuwid, mayroon kaming bagong seksyon sa artikulo.

Pag-uugnay ng Lombok at Mapstruct

Pagkatapos ng ilang minuto ng paghahanap, lumabas na kailangan naming ikonekta ang Lombok at Mapstruct sa isang tiyak na paraan. Mayroong impormasyon tungkol dito sa dokumentasyon ng Mapstruct . Pagkatapos suriin ang halimbawang iminungkahi ng mga developer mula sa Mapstruct, i-update natin ang ating pom.xml: Magdagdag tayo ng hiwalay na mga bersyon:
​​<properties>
   <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
   <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
Idagdag natin ang nawawalang dependency:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok-mapstruct-binding</artifactId>
   <version>${lombok-mapstruct-binding.version}</version>
</dependency>
At i-update natin ang aming compiler plugin upang maikonekta nito ang Lombok at 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>
Pagkatapos nito, dapat gumana ang lahat. Buuin natin muli ang ating proyekto. Ngunit saan mo mahahanap ang mga klase na nabuo ng Mapstruct? Nasa mga nabuong-source ang mga ito: ${projectDir}/target/generated-sources/annotation/ Ano ang Mapstruct at kung paano ito i-configure nang maayos para sa pagsubok ng unit sa mga application ng SpringBoot.  Bahagi 1 - 3 Ngayong handa na tayong matanto ang aking pagkabigo mula sa post ng Mapstruct, subukan nating gumawa ng mga pagsubok para sa mga mappers.

Nagsusulat kami ng mga pagsubok para sa aming mga mapper

Gagawa ako ng mabilis at simpleng pagsubok na susubok sa isa sa mga mapper sa kaso kung saan gumagawa kami ng integration test at huwag mag-alala tungkol sa oras ng pagkumpleto nito:

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());
   }
}
Dito, gamit ang SpringBootTest annotation, inilunsad namin ang buong applicationContext at mula rito, gamit ang Autowired annotation, kinukuha namin ang klase na kailangan namin para sa pagsubok. Mula sa punto ng view ng bilis at kadalian ng pagsulat ng isang pagsubok, ito ay napakahusay. Matagumpay na pumasa ang pagsubok, maayos ang lahat. Ngunit pupunta tayo sa ibang paraan at magsulat ng isang unit test para sa isang mapper, halimbawa, 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());
   }
}
Dahil ang mga pagpapatupad na binubuo ng Mapstruct ay nasa parehong klase ng aming proyekto, madali naming magagamit ang mga ito sa aming mga pagsubok. Mukhang maganda ang lahat - walang anotasyon, ginagawa namin ang klase na kailangan namin sa pinakasimpleng paraan at iyon lang. Ngunit kapag pinatakbo namin ang pagsubok, mauunawaan namin na ito ay mag-crash at magkakaroon ng NullPointerException sa console... Ito ay dahil ang pagpapatupad ng LectureListMapper mapper ay mukhang:
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;
   }
}
Kung titingnan natin ang NPE (maikli para sa NullPointerException), nakukuha natin ito mula sa variable na lectureMapper , na lumalabas na hindi nasimulan. Ngunit sa aming pagpapatupad wala kaming isang tagapagbuo kung saan maaari naming simulan ang variable. Ito mismo ang dahilan kung bakit ipinatupad ng Mapstruct ang mapper sa ganitong paraan! Sa Spring, maaari kang magdagdag ng mga beans sa mga klase sa maraming paraan, maaari mong i-inject ang mga ito sa pamamagitan ng isang field kasama ang Autowired annotation, tulad ng ginawa sa itaas, o maaari mong i-inject ang mga ito sa pamamagitan ng isang constructor. Natagpuan ko ang aking sarili sa isang problemang sitwasyon sa trabaho kapag kailangan kong i-optimize ang oras ng pagpapatupad ng pagsubok. Naisip ko na walang magagawa tungkol dito at ibinuhos ang aking sakit sa aking Telegram channel. At pagkatapos ay tinulungan nila ako sa mga komento at sinabi na posible na ipasadya ang diskarte sa pag-iniksyon. Ang interface ng Mapper ay may field ng injectionStrategy , na tinatanggap lang ang pangalan ng InjectionStrategy , na may dalawang value: FIELD at CONSTRUCTOR . Ngayon, alam na natin ito, idagdag natin ang setting na ito sa ating mga mapper; Ipapakita ko ito gamit ang LectureListMapper bilang isang halimbawa :
@Mapper(componentModel = "spring", uses = LectureMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface LectureListMapper {
   List<LectureModel> toModelList(List<LectureDTO> dtos);
   List<LectureDTO> toDTOList(List<LectureModel> models);
}
Ni-highlight ko ng bold ang bahaging idinagdag ko. Idagdag natin ang opsyong ito para sa lahat ng iba pa at muling i-compile ang proyekto upang ang mga mapper ay mabuo gamit ang isang bagong linya. Kapag tapos na ito, tingnan natin kung paano nagbago ang pagpapatupad ng mapper para sa LectureListMapper (naka-highlight sa bold ang bahaging kailangan natin):
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;
   }
}
At ngayon ang Mapstruct ay nagpatupad ng mapper injection sa pamamagitan ng constructor. Ito ay eksakto kung ano ang sinusubukan naming makamit. Ngayon ang aming pagsubok ay hihinto sa pag-compile, i-update natin ito at makuha ang:
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());
   }
}
Ngayon, kung patakbuhin natin ang pagsubok, gagana ang lahat gaya ng inaasahan, dahil sa LectureListMapperImpl pumasa tayo sa LectureMapper na kailangan nito... Tagumpay! Hindi ito mahirap para sa iyo, ngunit nalulugod ako: Mga kaibigan, lahat ay gaya ng dati, mag-subscribe sa aking GitHub account , sa aking Telegram account . Doon ko nai-post ang mga resulta ng aking mga aktibidad, may mga talagang kapaki-pakinabang na bagay) Lalo kitang inaanyayahan na sumali sa grupo ng talakayan ng telegrama channel . Nagkataon na kung ang isang tao ay may teknikal na tanong, maaari silang makakuha ng sagot doon. Ang format na ito ay kawili-wili para sa lahat, maaari mong basahin kung sino ang nakakaalam kung ano at makakuha ng karanasan.

Konklusyon

Bilang bahagi ng artikulong ito, nakilala namin ang isang kinakailangan at madalas na ginagamit na produkto bilang Mapstruct. Inisip namin kung ano ito, bakit at paano. Gamit ang isang tunay na halimbawa, naramdaman namin kung ano ang maaaring gawin at kung paano ito mababago. Tiningnan din namin kung paano i-set up ang pag-iniksyon ng mga beans sa pamamagitan ng constructor, upang maging posible na maayos na masuri ang mga mapper. Pinahintulutan ng mga kasamahan mula sa Mapstruct ang mga user ng kanilang produkto na pumili nang eksakto kung paano mag-inject ng mga mapper, kung saan walang alinlangan kaming nagpapasalamat sa kanila. PERO, sa kabila ng katotohanang inirerekomenda ng Spring ang pag-inject ng beans sa pamamagitan ng constructor, ang mga lalaki mula sa Mapstruct ay nagtakda ng iniksyon sa field bilang default. Bakit ganon? Walang sagot. Sa palagay ko, maaaring may mga dahilan na hindi natin alam, at iyon ang dahilan kung bakit nila ito ginawa sa paraang ito. At upang malaman mula sa kanila, gumawa ako ng isyu sa GitHub sa kanilang opisyal na imbakan ng produkto.
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION