Výkon rámcov mapovania Java
1. Úvod
Vytváranie veľkých aplikácií Java zložených z viacerých vrstiev si vyžaduje použitie viacerých modelov, ako je napríklad model vytrvalosti, doménový model alebo takzvané DTO. Používanie viacerých modelov pre rôzne aplikačné vrstvy bude vyžadovať, aby sme poskytli spôsob mapovania medzi fazuľami.
Ak to urobíte manuálne, môžete rýchlo vytvoriť veľa štandardných kódov a stráviť veľa času. Našťastie pre nás existuje viac rámcov na mapovanie objektov pre Javu.
V tomto výučbe budeme porovnávať výkon najpopulárnejších rámcov mapovania Java.
2. Mapovacie rámce
2.1. Buldozér
Dozer je mapovací rámec, ktorý využíva rekurziu na kopírovanie údajov z jedného objektu do druhého. Rámec je schopný nielen kopírovať vlastnosti medzi fazuľami, ale dokáže aj automaticky prevádzať medzi rôznymi typmi.
Aby sme mohli použiť rámec Dozer, musíme do nášho projektu pridať takúto závislosť:
com.github.dozermapper dozer-core 6.5.0
Viac informácií o použití rámca Dozer nájdete v tomto článku.
Dokumentáciu k rámcu nájdete tu.
2.2. Orika
Orika je rámec mapovania fazule na fazuľu, ktorý rekurzívne kopíruje údaje z jedného objektu do druhého.
Všeobecný princíp práce Oriky je podobný ako u Dozera. Hlavným rozdielom medzi nimi je skutočnosť, že Orika používa generovanie bytecode. To umožňuje generovanie rýchlejších mapovačov s minimálnou réžiou.
Ak to chcete použiť,musíme do nášho projektu pridať takúto závislosť:
ma.glasnost.orika orika-core 1.5.4
Podrobnejšie informácie o používaní Oriky nájdete v tomto článku.
Aktuálnu dokumentáciu k rámcu nájdete tu.
2.3. MapStruct
MapStruct je generátor kódu, ktorý automaticky generuje triedy mapovačov fazule.
MapStruct má tiež schopnosť prevádzať medzi rôznymi dátovými typmi. Viac informácií o tom, ako ju používať, nájdete v tomto článku.
Ak chcete pridať MapStructdo nášho projektu musíme zahrnúť nasledujúcu závislosť:
org.mapstruct mapstruct 1.3.1.Final
Dokumentáciu k rámcu nájdete tu.
2.4. ModelMapper
ModelMapper je rámec, ktorého cieľom je zjednodušiť mapovanie objektov určením spôsobu vzájomného mapovania objektov na základe konvencií. Poskytuje typovo bezpečné a refaktoringovo bezpečné API.
Viac informácií o rámci sa nachádza v dokumentácii.
Ak chcete zahrnúť ModelMapper do nášho projektu, musíme pridať nasledujúcu závislosť:
org.modelmapper modelmapper 2.3.8
2.5. JMapper
JMapper je mapovací rámec, ktorého cieľom je poskytnúť ľahko použiteľné a vysoko výkonné mapovanie medzi Java Beans.
Rámec sa zameriava na uplatnenie princípu DRY pomocou anotácií a relačného mapovania.
Rámec umožňuje rôzne spôsoby konfigurácie: anotácie, XML alebo API.
Viac informácií o rámci sa nachádza v jeho dokumentácii.
Ak chcete zahrnúť JMapper do nášho projektu, musíme pridať jeho závislosť:
com.googlecode.jmapper-framework jmapper-core 1.6.1.CR2
3. TestovanieModel
Aby sme mohli mapovanie správne otestovať, musíme mať zdrojové a cieľové modely. Vytvorili sme dva testovacie modely.
Prvý z nich je iba jednoduché POJO s jedným String Toto nám umožnilo v jednoduchších prípadoch porovnávať rámce a skontrolovať, či sa niečo zmení, ak použijeme komplikovanejšie fazule.
Jednoduchý zdrojový model vyzerá takto:
verejná trieda SourceCode {reťazcový kód; // getter a setter}
A jeho cieľ je dosť podobný:
verejná trieda DestinationCode {reťazcový kód; // getter a setter}
Skutočný príklad zdroja fazule vyzerá takto:
verejná trieda SourceOrder {private String orderFinishDate; private PaymentType PaymentType; súkromná zľava na zľavu; súkromné DeliveryData deliveryData; private User orderingUser; súkromný Zoznam objednaných produktov; súkromný obchod offerShop; private int orderId; súkromný stav OrderStatus; private LocalDate orderDate; // štandardné getre a setre}
A cieľová trieda vyzerá takto:
public class Order {private User orderingUser; súkromný Zoznam objednaných produktov; private OrderStatus orderStatus; private LocalDate orderDate; private LocalDate orderFinishDate; private PaymentType PaymentType; súkromná zľava Zľava; private int shopId; súkromné DeliveryData deliveryData; súkromný obchod offerShop; // štandardné getre a setre}
Celú štruktúru modelu nájdete tu.
4. Prevodníky
Pre zjednodušenie návrhu testovacieho nastavenia sme vytvorili Prevádzač rozhranie:
prevodník verejného rozhrania {Prevod objednávky (SourceOrder sourceOrder); Konvertovať cieľový kód (SourceCode sourceCode); }
A toto rozhranie implementujú všetci naši vlastní mapovači.
4.1. OrikaConverter
Orika umožňuje úplnú implementáciu API, čo výrazne zjednodušuje tvorbu mapovača:
verejná trieda OrikaConverter implementuje Converter {private MapperFacade mapperFacade; public OrikaConverter () {MapperFactory mapperFactory = nový 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 public DestinationCode convert (SourceCode sourceCode) {return mapperFacade.map (sourceCode, DestinationCode.class); }}
4.2. DozerConverter
Dozer vyžaduje mapovací súbor XML s nasledujúcimi časťami:
com.baeldung.performancetests.model.source.SourceOrder com.baeldung.performancetests.model.destination.Order status stav objednávky com.baeldung.performancetests.model.source.SourceCode com.baeldung.performancetests.model.destination.DestinationCode
Po definovaní mapovania XML ho môžeme použiť z kódu:
verejná trieda DozerConverter implementuje Converter {private final Mapper mapper; public DozerConverter () {this.mapper = DozerBeanMapperBuilder.create () .withMappingFiles ("dozer-mapping.xml") .build (); } @Override public Order convert (SourceOrder sourceOrder) {return mapper.map (sourceOrder, Order.class); } @Override public DestinationCode convert (SourceCode sourceCode) {return mapper.map (sourceCode, DestinationCode.class); }}
4.3. MapStructConverter
Definícia MapStruct je dosť jednoduchá, pretože je úplne založená na generovaní kódu:
@Mapper verejné rozhranie MapStructConverter rozširuje Converter {MapStructConverter MAPPER = Mappers.getMapper (MapStructConverter.class); @Mapping (source = "status", target = "orderStatus") @Override Prevod objednávky (SourceOrder sourceOrder); @Override DestinationCode convert (SourceCode sourceCode); }
4.4. JMapperConverter
JMapperConverter vyžaduje viac práce. Po implementácii rozhrania:
verejná trieda JMapperConverter implementuje Converter {JMapper realLifeMapper; JMapper simpleMapper; public JMapperConverter () {JMapperAPI api = nový JMapperAPI () .add (JMapperAPI.mappedClass (Order.class)); realLifeMapper = nový JMapper (Order.class, SourceOrder.class, api); JMapperAPI simpleApi = nový JMapperAPI () .add (JMapperAPI.mappedClass (DestinationCode.class)); simpleMapper = nový JMapper (DestinationCode.class, SourceCode.class, simpleApi); } @Override public Order convert (SourceOrder sourceOrder) {return (Order) realLifeMapper.getDestination (sourceOrder); } @Override public DestinationCode convert (SourceCode sourceCode) {return (DestinationCode) simpleMapper.getDestination (sourceCode); }}
Musíme tiež pridať @JMap anotácie ku každému poľu cieľovej triedy. JMapper tiež nemôže sám prevádzať medzi typmi enum a vyžaduje od nás, aby sme vytvorili vlastné mapovacie funkcie:
@JMapConversion (from = "paymentType", to = "PaymentType") verejná konverzia PaymentType (typ com.baeldung.performancetests.model.source.PaymentType) {PaymentType paymentType = null; switch (type) {case CARD: PaymentType = PaymentType.CARD; prestávka; prípad CASH: PaymentType = PaymentType.CASH; prestávka; prípad TRANSFER: PaymentType = PaymentType.TRANSFER; prestávka; } vrátiť PaymentType; }
4.5. ModelMapperConverter
ModelMapperConverter vyžaduje, aby sme poskytovali iba triedy, ktoré chceme mapovať:
verejná trieda ModelMapperConverter implementuje Converter {private ModelMapper modelMapper; public ModelMapperConverter () {modelMapper = nový ModelMapper (); } @Override public Order convert (SourceOrder sourceOrder) {return modelMapper.map (sourceOrder, Order.class); } @Override public DestinationCode convert (SourceCode sourceCode) {return modelMapper.map (sourceCode, DestinationCode.class); }}
5. Jednoduché testovanie modelu
Na testovanie výkonu môžeme použiť Java Microbenchmark Harness, viac informácií o tom, ako ho používať, nájdete v tomto článku.
Pre každú z nich sme vytvorili samostatnú referenčnú hodnotu Prevádzač so špecifikáciou BenchmarkMode do Režim. Všetko.
5.1. Priemerný čas
Spoločnosť JMH vrátila nasledujúce výsledky pre priemerný čas chodu (čím menej, tým lepšie):
Názov rámca | Priemerná doba chodu (v ms na operáciu) |
---|---|
MapStruct | 10 -5 |
JMapper | 10 -5 |
Orika | 0.001 |
ModelMapper | 0.001 |
Buldozér | 0.002 |
Táto referenčná hodnota jasne ukazuje, že MapStruct aj JMapper majú najlepší priemerný pracovný čas.
5.2. Priepustnosť
V tomto režime porovnávacia hodnota vracia počet operácií za sekundu. Dostali sme nasledujúce výsledky (viac je lepšie) :
Názov rámca | Priepustnosť (v operáciách za ms) |
---|---|
MapStruct | 133719 |
JMapper | 106978 |
Orika | 1800 |
ModelMapper | 978 |
Buldozér | 471 |
V režime priepustnosti bol MapStruct najrýchlejší z testovaných rámcov, JMapper bol tesne za ním.
5.3. SingleShotTime
Tento režim umožňuje meranie času jednej operácie od jej začiatku do konca. Referenčná hodnota poskytla nasledujúci výsledok (menej je lepšie):
Názov rámca | Čas jedného záberu (v ms na operáciu) |
---|---|
JMapper | 0.015 |
MapStruct | 0.450 |
Buldozér | 2.094 |
Orika | 2.898 |
ModelMapper | 4.837 |
Tu vidíme, že JMapper vráti lepší výsledok ako MapStruct.
5.4. Čas vzorkovania
Tento režim umožňuje vzorkovanie času každej operácie. Výsledky troch rôznych percentilov vyzerajú takto:
Ukážkový čas (v milisekundách na operáciu) | |||
---|---|---|---|
Názov rámca | 0,90 p | 0,999 | p1.0 |
JMapper | 10-4 | 0.001 | 2.6 |
MapStruct | 10-4 | 0.001 | 3 |
Orika | 0.001 | 0.010 | 4 |
ModelMapper | 0.002 | 0.015 | 3.2 |
Buldozér | 0.003 | 0.021 | 25 |
Všetky kritériá ukázali, že MapStruct a JMapper sú dobrou voľbou v závislosti od scenára.
6. Testovanie modelu v reálnom živote
Na testovanie výkonu môžeme použiť Java Microbenchmark Harness, viac informácií o tom, ako ho používať, nájdete v tomto článku.
Pre každú z nich sme vytvorili samostatnú referenčnú hodnotu Prevádzač so špecifikáciou BenchmarkMode do Režim. Všetko.
6.1. Priemerný čas
Spoločnosť JMH vrátila nasledujúce výsledky pre priemerný čas chodu (menej je lepšie):
Názov rámca | Priemerná doba chodu (v ms na operáciu) |
---|---|
MapStruct | 10 -4 |
JMapper | 10 -4 |
Orika | 0.004 |
ModelMapper | 0.059 |
Buldozér | 0.103 |
6.2. Priepustnosť
V tomto režime benchmark vráti počet operácií za sekundu. Pre každého z mapovačov sme dostali nasledujúce výsledky (viac je lepších):
Názov rámca | Priepustnosť (v operáciách za ms) |
---|---|
JMapper | 7691 |
MapStruct | 7120 |
Orika | 281 |
ModelMapper | 19 |
Buldozér | 10 |
6.3. SingleShotTime
Tento režim umožňuje meranie času jednej operácie od jej začiatku do konca. Referenčná hodnota priniesla nasledujúce výsledky (menej je lepších):
Názov rámca | Čas jedného záberu (v ms na operáciu) |
---|---|
JMapper | 0.253 |
MapStruct | 0.532 |
Buldozér | 9.495 |
ModelMapper | 16.288 |
Orika | 18.081 |
6.4. Čas vzorkovania
Tento režim umožňuje vzorkovanie času každej operácie. Výsledky vzorkovania sú rozdelené na percentily. Uvádzame výsledky pre tri rôzne percentily p0,90, p0,999, a p1,00:
Ukážkový čas (v milisekundách na operáciu) | |||
---|---|---|---|
Názov rámca | 0,90 p | 0,999 | p1.0 |
JMapper | 10-3 | 0.008 | 64 |
MapStruct | 10-3 | 0.010 | 68 |
Orika | 0.006 | 0.278 | 32 |
ModelMapper | 0.083 | 2.398 | 97 |
Buldozér | 0.146 | 4.526 | 118 |
Presné výsledky jednoduchého príkladu a príkladu zo skutočného života boli síce zreteľne odlišné, sledujú však viac-menej rovnaký trend. V obidvoch príkladoch sme videli tesnú súťaž medzi JMapper a MapStruct o najvyššie miesto.
6.5. Záver
Na základe testovania reálneho modelu, ktoré sme vykonali v tejto časti, vidíme, že najlepší výkon jednoznačne patrí JMapperu, hoci MapStruct je tesne za ním. V rovnakých testoch vidíme, že Dozer je až na koniec našej tabuľky výsledkov neustále na konci SingleShotTime.
7. Zhrnutie
V tomto článku sme vykonali testy výkonu piatich populárnych rámcov mapovania fazule Java: ModelMapper, MapStruct, Orika, Dozer a JMapper.
Ako vždy, vzorky kódu nájdete na GitHub.