Prestaties van Java Mapping Frameworks

1. Invoering

Om grote Java-applicaties te maken die uit meerdere lagen zijn samengesteld, moeten meerdere modellen worden gebruikt, zoals persistentiemodel, domeinmodel of zogenaamde DTO's. Door meerdere modellen voor verschillende toepassingslagen te gebruiken, moeten we een manier bieden om bonen in kaart te brengen.

Als u dit handmatig doet, kan er snel veel standaardcode worden gemaakt en kan dit veel tijd in beslag nemen. Gelukkig voor ons zijn er meerdere frameworks voor objecttoewijzing voor Java.

In deze zelfstudie gaan we de prestaties van de meest populaire Java-mappingframeworks vergelijken.

2. Kaders in kaart brengen

2.1. Bulldozer

Dozer is een mapping-framework dat recursie gebruikt om gegevens van het ene object naar het andere te kopiëren. Het framework kan niet alleen eigenschappen tussen de bonen kopiëren, maar kan ook automatisch tussen verschillende typen converteren.

Om het Dozer-framework te gebruiken, moeten we een dergelijke afhankelijkheid aan ons project toevoegen:

 com.github.dozermapper dozer-core 6.5.0 

Meer informatie over het gebruik van het Dozer-framework vindt u in dit artikel.

De documentatie van het framework is hier te vinden.

2.2. Orika

Orika is een bean-to-bean mapping-framework dat recursief gegevens van het ene object naar het andere kopieert.

Het algemene werkprincipe van de Orika is vergelijkbaar met die van Dozer. Het belangrijkste verschil tussen de twee is het feit dat Orika maakt gebruik van bytecode-generatie. Hierdoor kunnen snellere mappers worden gegenereerd met minimale overhead.

Om het te gebruiken,we moeten een dergelijke afhankelijkheid aan ons project toevoegen:

 ma.glasnost.orika orika-core 1.5.4 

Meer gedetailleerde informatie over het gebruik van de Orika vind je in dit artikel.

De feitelijke documentatie van het framework is hier te vinden.

2.3. MapStruct

MapStruct is een codegenerator die automatisch bean-mapper-klassen genereert.

MapStruct heeft ook de mogelijkheid om te converteren tussen verschillende gegevenstypen. Meer informatie over het gebruik ervan vindt u in dit artikel.

Om MapStructvoor ons project moeten we de volgende afhankelijkheid opnemen:

 org.mapstruct mapstruct 1.3.1.Final 

De documentatie van het framework is hier te vinden.

2.4. ModelMapper

ModelMapper is een raamwerk dat tot doel heeft objecttoewijzing te vereenvoudigen door te bepalen hoe objecten aan elkaar worden toegewezen op basis van conventies. Het biedt type-veilige en refactoring-veilige API.

Meer informatie over het framework is te vinden in de documentatie.

Om ModelMapper in ons project op te nemen, moeten we de volgende afhankelijkheid toevoegen:

 org.modelmapper modelmapper 2.3.8 

2.5. JMapper

JMapper is het mappingraamwerk dat tot doel heeft een gebruiksvriendelijke, hoogwaardige mapping tussen Java Beans te bieden.

Het raamwerk heeft tot doel het DRY-principe toe te passen met behulp van annotaties en relationele mapping.

Het framework maakt verschillende configuratiemogelijkheden mogelijk: op annotatie, XML of API.

Meer informatie over het framework is te vinden in de documentatie.

Om JMapper in ons project op te nemen, moeten we zijn afhankelijkheid toevoegen:

 com.googlecode.jmapper-framework jmapper-core 1.6.1.CR2 

3. TestenModel

Om mapping goed te kunnen testen, hebben we bron- en doelmodellen nodig. We hebben twee testmodellen gemaakt.

De eerste is gewoon een simpele POJO met één Draad veld, dit stelde ons in staat om frameworks in eenvoudigere gevallen te vergelijken en te controleren of er iets verandert als we meer gecompliceerde bonen gebruiken.

Het eenvoudige bronmodel ziet er als volgt uit:

openbare klasse SourceCode {String-code; // getter en setter}

En de bestemming is vrij gelijkaardig:

openbare klasse DestinationCode {String-code; // getter en setter}

Het real-life voorbeeld van source bean ziet er zo uit:

openbare klasse SourceOrder {privé String orderFinishDate; privé PaymentType paymentType; particuliere korting korting; privé DeliveryData deliveryData; privégebruiker bestellenGebruiker; privélijst bestelde producten; privéwinkelaanbodWinkel; privé int orderId; privé OrderStatus-status; privé LocalDate orderDate; // standaard getters en setters}

En de doelklasse ziet er als volgt uit:

openbare klasse Bestelling {privé gebruiker ordergebruiker; privélijst bestelde producten; privé OrderStatus orderStatus; privé LocalDate orderDate; privé LocalDate orderFinishDate; privé PaymentType paymentType; particuliere korting korting; privé int shopId; privé DeliveryData deliveryData; privéwinkelaanbodWinkel; // standaard getters en setters}

De hele modelstructuur is hier te vinden.

4. Omvormers

Om het ontwerp van de testopstelling te vereenvoudigen, hebben we de Converter koppel:

openbare interface Converter {Order converteren (SourceOrder sourceOrder); DestinationCode converteren (SourceCode sourceCode); }

En al onze aangepaste mappers zullen deze interface implementeren.

4.1. OrikaConverter

Orika maakt volledige API-implementatie mogelijk, dit vereenvoudigt het maken van de mapper aanzienlijk:

openbare klasse OrikaConverter implementeert Converter {private MapperFacade mapperFacade; openbare OrikaConverter () {MapperFactory mapperFactory = nieuwe DefaultMapperFactory .Builder (). build (); mapperFactory.classMap (Order.class, SourceOrder.class) .field ("orderStatus", "status"). byDefault (). register (); mapperFacade = mapperFactory.getMapperFacade (); } @Override public Order convert (SourceOrder sourceOrder) {return mapperFacade.map (sourceOrder, Order.class); } @Override openbare DestinationCode converteren (SourceCode sourceCode) {return mapperFacade.map (sourceCode, DestinationCode.class); }}

4.2. DozerConverter

Dozer vereist XML-toewijzingsbestand, met de volgende secties:

  com.baeldung.performancetests.model.source.SourceOrder com.baeldung.performancetests.model.destination.Order status bestelstatus    com.baeldung.performancetests.model.source.SourceCode com.baeldung.performancetests.model.destination.DestinationCode 

Nadat we de XML-toewijzing hebben gedefinieerd, kunnen we deze gebruiken vanuit code:

openbare klasse DozerConverter implementeert Converter {private final Mapper mapper; openbare DozerConverter () {this.mapper = DozerBeanMapperBuilder.create () .withMappingFiles ("dozer-mapping.xml") .build (); } @Override public Order convert (SourceOrder sourceOrder) {return mapper.map (sourceOrder, Order.class); } @Override openbare DestinationCode converteren (SourceCode sourceCode) {return mapper.map (sourceCode, DestinationCode.class); }}

4.3. MapStructConverter

De MapStruct-definitie is vrij eenvoudig omdat het volledig is gebaseerd op het genereren van code:

@Mapper openbare interface MapStructConverter breidt Converter uit {MapStructConverter MAPPER = Mappers.getMapper (MapStructConverter.class); @Mapping (source = "status", target = "orderStatus") @Override Order converteren (SourceOrder sourceOrder); @Override DestinationCode converteren (SourceCode sourceCode); }

4.4. JMapperConverter

JMapperConverter vereist meer werk. Na het implementeren van de interface:

openbare klasse JMapperConverter implementeert Converter {JMapper realLifeMapper; JMapper simpleMapper; openbare JMapperConverter () {JMapperAPI api = nieuwe JMapperAPI () .add (JMapperAPI.mappedClass (Order.class)); realLifeMapper = nieuwe JMapper (Order.class, SourceOrder.class, api); JMapperAPI simpleApi = nieuwe JMapperAPI () .add (JMapperAPI.mappedClass (DestinationCode.class)); simpleMapper = nieuwe JMapper (DestinationCode.class, SourceCode.class, simpleApi); } @Override public Order convert (SourceOrder sourceOrder) {return (Order) realLifeMapper.getDestination (sourceOrder); } @Override openbare DestinationCode converteren (SourceCode sourceCode) {return (DestinationCode) simpleMapper.getDestination (sourceCode); }}

We moeten ook toevoegen @JMap annotaties op elk veld van de doelklasse. JMapper kan ook niet zelfstandig tussen enum-typen converteren en vereist dat we aangepaste toewijzingsfuncties maken:

@JMapConversion (van = "paymentType", naar = "paymentType") openbare PaymentType-conversie (com.baeldung.performancetests.model.source.PaymentType-type) {PaymentType paymentType = null; schakelaar (type) {case CARD: paymentType = PaymentType.CARD; breken; geval CASH: paymentType = PaymentType.CASH; breken; geval TRANSFER: paymentType = PaymentType.TRANSFER; breken; } return paymentType; }

4.5. ModelMapperConverter

ModelMapperConverter vereist dat we alleen de klassen leveren die we in kaart willen brengen:

openbare klasse ModelMapperConverter implementeert Converter {private ModelMapper modelMapper; openbare ModelMapperConverter () {modelMapper = nieuwe ModelMapper (); } @Override public Order convert (SourceOrder sourceOrder) {return modelMapper.map (sourceOrder, Order.class); } @Override openbare DestinationCode converteren (SourceCode sourceCode) {return modelMapper.map (sourceCode, DestinationCode.class); }}

5. Eenvoudig testen van modellen

Voor de prestatietests kunnen we Java Microbenchmark Harness gebruiken, meer informatie over het gebruik ervan is te vinden in dit artikel.

We hebben voor elk een aparte benchmark gemaakt Converter met specificeren BenchmarkMode naar Mode.Alles.

5.1. Gemiddelde tijd

JMH retourneerde de volgende resultaten voor de gemiddelde looptijd (hoe minder hoe beter):

Framework naamGemiddelde looptijd (in ms per operatie)
MapStruct10 -5
JMapper10 -5
Orika0.001
ModelMapper0.001
Bulldozer0.002

Deze benchmark laat duidelijk zien dat zowel MapStruct als JMapper de beste gemiddelde werktijden hebben.

5.2. Doorvoer

In deze modus retourneert de benchmark het aantal bewerkingen per seconde. We hebben de volgende resultaten ontvangen (meer is beter) :

Framework naamDoorvoer (in bewerkingen per ms)
MapStruct133719
JMapper106978
Orika1800
ModelMapper978
Bulldozer471

In doorvoermodus was MapStruct de snelste van de geteste frameworks, met JMapper een goede tweede.

5.3. SingleShotTime

Met deze modus kan de tijd van een enkele bewerking van het begin tot het einde worden gemeten. De benchmark leverde het volgende resultaat op (minder is beter):

Framework naamSingle Shot-tijd (in ms per operatie)
JMapper0.015
MapStruct0.450
Bulldozer2.094
Orika2.898
ModelMapper4.837

Hier zien we dat JMapper een beter resultaat oplevert dan MapStruct.

5.4. Proeftijd

In deze modus kunt u de tijd van elke bewerking bemonsteren. De resultaten voor drie verschillende percentielen zien er als volgt uit:

Monstertijd (in milliseconden per bewerking)
Framework naamp0,90p0.999p1.0
JMapper10-40.0012.6
MapStruct10-40.0013
Orika0.0010.0104
ModelMapper0.0020.0153.2
Bulldozer0.0030.02125

Alle benchmarks hebben aangetoond dat MapStruct en JMapper beide goede keuzes zijn, afhankelijk van het scenario.

6. Testen van modellen in de praktijk

Voor de prestatietests kunnen we Java Microbenchmark Harness gebruiken, meer informatie over het gebruik ervan is te vinden in dit artikel.

We hebben voor elk een aparte benchmark gemaakt Converter met specificeren BenchmarkMode naar Mode.Alles.

6.1. Gemiddelde tijd

JMH retourneerde de volgende resultaten voor de gemiddelde looptijd (minder is beter):

Framework naamGemiddelde looptijd (in ms per operatie)
MapStruct10 -4
JMapper10 -4
Orika0.004
ModelMapper0.059
Bulldozer0.103

6.2. Doorvoer

In deze modus retourneert de benchmark het aantal bewerkingen per seconde. Voor elk van de mappers hebben we de volgende resultaten ontvangen (meer is beter):

Framework naamDoorvoer (in bewerkingen per ms)
JMapper7691
MapStruct7120
Orika281
ModelMapper19
Bulldozer10

6.3. SingleShotTime

Met deze modus kunt u de tijd van een enkele bewerking van het begin tot het einde meten. De benchmark leverde de volgende resultaten op (minder is beter):

Framework naamSingle Shot-tijd (in ms per operatie)
JMapper0.253
MapStruct0.532
Bulldozer9.495
ModelMapper16.288
Orika18.081

6.4. Proeftijd

In deze modus kunt u de tijd van elke bewerking bemonsteren. Bemonsteringsresultaten worden opgesplitst in percentielen, we presenteren resultaten voor drie verschillende percentielen p0.90, p0.999, en p1.00:

Monstertijd (in milliseconden per bewerking)
Framework naamp0,90p0.999p1.0
JMapper10-30.00864
MapStruct10-30.01068
Orika0.0060.27832
ModelMapper0.0832.39897
Bulldozer0.1464.526118

Hoewel de exacte resultaten van het eenvoudige voorbeeld en het praktijkvoorbeeld duidelijk verschillend waren, volgen ze min of meer dezelfde trend. In beide voorbeelden zagen we een nauwe strijd tussen JMapper en MapStruct om de eerste plaats.

6.5. Conclusie

Op basis van de real-life modeltesten die we in deze sectie hebben uitgevoerd, kunnen we zien dat de beste prestatie duidelijk bij JMapper hoort, hoewel MapStruct een goede tweede is. In dezelfde tests zien we dat Dozer consequent onderaan onze resultatentabel staat, behalve SingleShotTime.

7. Samenvatting

In dit artikel hebben we prestatietests uitgevoerd van vijf populaire Java-bean mapping-frameworks: ModelMapper, MapStruct, Orika, Dozer en JMapper.

Zoals altijd zijn codevoorbeelden te vinden op GitHub.


$config[zx-auto] not found$config[zx-overlay] not found