Verzamelingen in kaart brengen met MapStruct

1. Overzicht

In deze zelfstudie bekijken we hoe u verzamelingen objecten in kaart kunt brengen met MapStruct.

Omdat in dit artikel al wordt uitgegaan van een basiskennis van MapStruct, moeten beginners eerst onze beknopte handleiding voor MapStruct bekijken.

2. Verzamelingen in kaart brengen

Over het algemeen, het in kaart brengen van verzamelingen met MapStruct werkt op dezelfde manier als voor eenvoudige typen.

In principe moeten we een eenvoudige interface of abstracte klasse maken en de toewijzingsmethoden declareren. Op basis van onze verklaringen genereert MapStruct automatisch de toewijzingscode. Typisch, de gegenereerde code loopt door de broncollectie, converteert elk element naar het doeltype en neemt ze allemaal op in de doelcollectie.

Laten we een eenvoudig voorbeeld bekijken.

2.1. Mapping lijsten

Laten we eerst voor ons voorbeeld een eenvoudige POJO beschouwen als de toewijzingsbron voor onze mapper:

openbare klasse Medewerker {private String firstName; private String achternaam; // constructeur, getters en setters} 

Het doel is een eenvoudige DTO:

openbare klasse EmployeeDTO {private String firstName; private String achternaam; // getters en setters}

Laten we vervolgens onze mapper definiëren:

@Mapper openbare interface EmployeeMapper {List map (List medewerkers); } 

Laten we tot slot eens kijken naar de code MapStruct die is gegenereerd op basis van onze WerknemerMapper koppel:

public class EmployeeMapperImpl implementeert EmployeeMapper {@Override public List map (List workers) {if (workers == null) {return null; } Lijstlijst = nieuwe ArrayList (workers.size ()); voor (Medewerker werknemer: werknemers) {list.add (employeeToEmployeeDTO (werknemer)); } retourlijst; } beschermde EmployeeDTO employeeToEmployeeDTO (Employee werknemer) {if (employee == null) {return null; } EmployeeDTO employeeDTO = nieuwe EmployeeDTO (); employeeDTO.setFirstName (employee.getFirstName ()); employeeDTO.setLastName (employee.getLastName ()); terugkeer medewerkerDTO; }} 

Er is een belangrijk ding om op te merken. Specifiek, MapStruct heeft voor ons automatisch de mapping gegenereerd van Werknemer naar MedewerkerDTO.

Er zijn gevallen waarin dit niet mogelijk is. Laten we bijvoorbeeld zeggen dat we onze Werknemer model naar het volgende model:

openbare klasse EmployeeFullNameDTO {private String fullName; // getter en setter}

Als we in dit geval de toewijzingsmethode van een Lijst van Werknemer naar een Lijst van WerknemerVolledigeNaamDTO we ontvangen een compileerfout of waarschuwing zoals:

Waarschuwing: (11, 31) java: niet-toegewezen doeleigenschap: "fullName". Toewijzing van collectie-element "com.baeldung.mapstruct.mappingCollections.model.Employee-medewerker" naar "com.baeldung.mapstruct.mappingCollections.dto.EmployeeFullNameDTO employeeFullNameDTO".

In feite betekent dit dat MapStruct kon de mapping niet automatisch voor ons genererenin dit geval. Daarom moeten we handmatig de toewijzing tussen Werknemer en WerknemerVolledigeNaamDTO.

Gezien deze punten, laten we het handmatig definiëren:

@Mapper openbare interface EmployeeFullNameMapper {List map (List workers); standaard EmployeeFullNameDTO-kaart (Employee-werknemer) {EmployeeFullNameDTO employeeInfoDTO = nieuwe EmployeeFullNameDTO (); employeeInfoDTO.setFullName (employee.getFirstName () + "" + employee.getLastName ()); terugkeer medewerkerInfoDTO; }}

De gegenereerde code gebruikt de methode die we hebben gedefinieerd om de elementen van de bron in kaart te brengen Lijst naar het doel Lijst.

Dit geldt ook in het algemeen. Als we een methode hebben gedefinieerd die het type bronelement toewijst aan het type doelelement, zal MapStruct deze gebruiken.

2.2. Toewijzingssets en kaarten

Het in kaart brengen van sets met MapStruct werkt op dezelfde manier als met lijsten. Laten we bijvoorbeeld zeggen dat we een Set van Werknemer instanties naar een Set van MedewerkerDTO gevallen.

Zoals eerder hebben we een mapper nodig:

@Mapper openbare interface EmployeeMapper {Set map (Set medewerkers); }

En MapStruct genereert de juiste code:

public class EmployeeMapperImpl implementeert EmployeeMapper {@Override public Set map (Set workers) {if (workers == null) {return null; } Set set = new HashSet (Math.max ((int) (workers.size () / .75f) + 1, 16)); voor (Werknemer werknemer: werknemers) {set.add (werknemerToEmployeeDTO (werknemer)); } retour set; } beschermde EmployeeDTO employeeToEmployeeDTO (Employee werknemer) {if (employee == null) {return null; } EmployeeDTO employeeDTO = nieuwe EmployeeDTO (); employeeDTO.setFirstName (employee.getFirstName ()); employeeDTO.setLastName (employee.getLastName ()); terugkeer medewerkerDTO; }}

Hetzelfde geldt voor kaarten. Laten we overwegen dat we een Kaart naar een Kaart.

Vervolgens kunnen we dezelfde stappen volgen als hiervoor:

@Mapper openbare interface EmployeeMapper {Map map (Map idEmployeeMap); }

En MapStruct doet zijn werk:

public class EmployeeMapperImpl implementeert EmployeeMapper {@Override public Map map (Map idEmployeeMap) {if (idEmployeeMap == null) {return null; } Map map = nieuwe HashMap (Math.max ((int) (idEmployeeMap.size () / .75f) + 1, 16)); voor (java.util.Map.Entry entry: idEmployeeMap.entrySet ()) {String key = entry.getKey (); EmployeeDTO waarde = employeeToEmployeeDTO (entry.getValue ()); map.put (sleutel, waarde); } terugkeer kaart; } beschermde EmployeeDTO employeeToEmployeeDTO (Employee werknemer) {if (employee == null) {return null; } EmployeeDTO employeeDTO = nieuwe EmployeeDTO (); employeeDTO.setFirstName (employee.getFirstName ()); employeeDTO.setLastName (employee.getLastName ()); terugkeer medewerkerDTO; }}

3. Strategieën voor het in kaart brengen van collecties

Vaak moeten we gegevenstypen in kaart brengen die een ouder-kindrelatie hebben. Meestal hebben we een datatype (ouder) met als veld een Verzameling van een ander gegevenstype (kind).

Voor dergelijke gevallen MapStruct biedt een manier om te kiezen hoe u de kinderen instelt of toevoegt aan het bovenliggende type. In het bijzonder de @Mapper annotatie heeft een collectionMappingStrategy attribuut dat kan zijn ACCESSOR_ONLY, SETTER_PREFERRED, ADDER_PREFERRED of TARGET_IMMUTABLE.

Al deze waarden verwijzen naar de manier waarop de kinderen moeten worden ingesteld of toegevoegd aan het bovenliggende type. De standaardwaarde is ACCESSOR_ONLY, wat betekent dat alleen accessors kunnen worden gebruikt om de Verzameling van kinderen.

Deze optie is handig wanneer het setter voor de Verzameling veld is niet beschikbaar, maar we hebben een opteller. Een ander geval waarin dit nuttig is, is wanneer de Verzameling is onveranderlijk op het bovenliggende type. Meestal komen we deze gevallen tegen in gegenereerde doeltypes.

3.1. ACCESSOR_ONLY Inzamelingsstrategie

Laten we een voorbeeld nemen om beter te begrijpen hoe dit werkt.

Laten we voor ons voorbeeld een Bedrijf class als onze toewijzingsbron:

Public class Company {private List-medewerkers; // getter en setter}

En het doel voor onze mapping zal een eenvoudige DTO zijn:

openbare klasse CompanyDTO {privélijstmedewerkers; openbare lijst getEmployees () {terugkeer werknemers; } public void setEmployees (lijst werknemers) {this.employees = werknemers; } public void addEmployee (EmployeeDTO employeeDTO) {if (werknemers == null) {werknemers = nieuwe ArrayList (); } workers.add (employeeDTO); }}

Merk op dat we zowel de setter, setMedewerkers, en de opteller, werknemer toevoegen, beschikbaar. Ook, voor de opteller zijn wij verantwoordelijk voor de initialisatie van de collectie.

Laten we nu zeggen dat we een Bedrijf naar een BedrijfDTO. Dan hebben we, zoals eerder, een mapper nodig:

@Mapper (gebruikt = EmployeeMapper.class) openbare interface CompanyMapper {CompanyDTO-kaart (Company-bedrijf); }

Merk op dat we het WerknemerMapper en de standaard collectionMappingStrategy.

Laten we nu eens kijken naar de code die MapStruct heeft gegenereerd:

openbare klasse CompanyMapperImpl implementeert CompanyMapper {privé finale EmployeeMapper employeeMapper = Mappers.getMapper (EmployeeMapper.class); @Override public CompanyDTO map (Company company) {if (company == null) {return null; } CompanyDTO companyDTO = nieuwe CompanyDTO (); companyDTO.setEmployees (employeeMapper.map (company.getEmployees ())); retourbedrijfDTO; }}

Zoals te zien is, MapStruct gebruikt de setter, setEmployees, om de Lijst van MedewerkerDTO gevallen. Dit gebeurt omdat we hier de standaard gebruiken collectionMappingStrategy,ACCESSOR_ONLY.

MapStruct vond ook een methode die een Lijst naar een Lijst in WerknemerMapper en hergebruikt het.

3.2. ADDER_PREFERRED Inzamelingsstrategie

Laten we daarentegen eens kijken naar we gebruikten ADDER_PREFERRED net zo collectionMappingStrategy:

@Mapper (collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, gebruikt = EmployeeMapper.class) openbare interface CompanyMapperAdderPreferred {CompanyDTO-kaart (bedrijfsbedrijf); }

Nogmaals, we willen het WerknemerMapper. Echter, we moeten expliciet een methode toevoegen die een enkele Werknemer aan een MedewerkerDTO eerste:

@Mapper openbare interface EmployeeMapper {EmployeeDTO-kaart (Employee-werknemer); Lijstkaart (lijst werknemers); Set map (Set medewerkers); Map map (Map idEmployeeMap); }

Dit komt doordat MapStruct de adder zal gebruiken om toe te voegen MedewerkerDTO instanties naar het doel BedrijfDTO bijvoorbeeld een voor een:

openbare klasse CompanyMapperAdderPreferredImpl implementeert CompanyMapperAdderPreferred {privé finale EmployeeMapper employeeMapper = Mappers.getMapper (EmployeeMapper.class); @Override public CompanyDTO map (Company company) {if (company == null) {return null; } CompanyDTO companyDTO = nieuwe CompanyDTO (); if (company.getEmployees ()! = null) {voor (Werknemer werknemer: company.getEmployees ()) {companyDTO.addEmployee (employeeMapper.map (werknemer)); }} retourneer companyDTO; }}

Als de opteller niet beschikbaar was, zou de setter zijn gebruikt.

We kunnen een volledige beschrijving vinden van alle strategieën voor het in kaart brengen van collecties in de referentiedocumentatie van MapStruct.

4. Implementatietypes voor het verzamelen van doelen

MapStruct ondersteunt verzamelingsinterfaces als doeltypes voor toewijzingsmethoden.

In dit geval worden enkele standaardimplementaties gebruikt in de gegenereerde code. Bijvoorbeeld de standaardimplementatie voor Lijst is ArrayList zoals kan worden opgemerkt uit onze voorbeelden hierboven.

We kunnen de volledige lijst met interfaces die MapStruct ondersteunt en de standaardimplementaties die het voor elke interface gebruikt, vinden in de referentiedocumentatie.

5. Conclusie

In dit artikel hebben we onderzocht hoe u collecties in kaart kunt brengen met MapStruct.

We hebben eerst gekeken hoe we verschillende soorten collecties in kaart kunnen brengen. Vervolgens hebben we gezien hoe we mappers voor ouder-kindrelaties kunnen aanpassen met behulp van strategieën voor het in kaart brengen van verzamelingen.

Onderweg hebben we de belangrijkste punten en dingen benadrukt waarmee u rekening moet houden bij het in kaart brengen van collecties met MapStruct.

Zoals gewoonlijk is de volledige code beschikbaar op GitHub.