Sprievodca mapovaním pomocou Dozera

1. Prehľad

Dozer je a Mapovač Java Bean na Java Bean ktorý rekurzívne kopíruje údaje z jedného objektu do druhého, atribút po atribúte.

Knižnica podporuje nielen mapovanie medzi názvami atribútov Java Beans, ale aj automaticky prevádza medzi typmi - ak sú odlišné.

Väčšina scenárov konverzie je podporovaná po vybalení z krabice, ale Dozer to tiež umožňuje určiť vlastné prevody pomocou XML.

2. Jednoduchý príklad

Pre náš prvý príklad predpokladajme, že všetky zdrojové a cieľové dátové objekty majú rovnaké spoločné názvy atribútov.

Toto je najzákladnejšie mapovanie, ktoré môžete s Dozerom urobiť:

public class Zdroj {private String name; súkromný int vek; public Source () {} public Source (názov reťazca, vek int) {this.name = meno; this.age = vek; } // štandardní zakladatelia a zakladatelia}

Potom náš cieľový súbor, Dest.java:

public class Dest {private String name; súkromný int vek; public Dest () {} public Dest (Názov reťazca, int vek) {this.name = meno; this.age = vek; } // štandardní zakladatelia a zakladatelia}

Musíme sa uistiť zahrnúť predvolené alebo nulové konštruktory argumentov, pretože Dozer využíva odraz pod kapotou.

A z dôvodu výkonu urobme nášho mapovača globálnym a vytvorme jeden objekt, ktorý použijeme v našich testoch:

Mapovač DozerBeanMapper; @Before public void before () vyvolá výnimku {mapper = new DozerBeanMapper (); }

Teraz spustíme náš prvý test, ktorý potvrdí, že keď vytvoríme a Zdroj objekt, môžeme ho priamo namapovať na a Dest objekt:

@Test public void givenSourceObjectAndDestClass_whenMapsSameNameFieldsCorrectly_ thenCorrect () {Zdrojový zdroj = nový Zdroj ("Baeldung", 10); Dest dest = mapper.map (zdroj, Dest.class); assertEquals (dest.getName (), "Baeldung"); assertEquals (dest.getAge (), 10); }

Ako vidíme, po mapovaní Dozera bude výsledkom nová inštancia súboru Dest objekt, ktorý obsahuje hodnoty pre všetky polia, ktoré majú rovnaký názov poľa ako Zdroj objekt.

Prípadne namiesto míňania mapovač the Dest triedy, mohli sme práve vytvoriť Dest objekt a prešiel mapovač jeho odkaz:

@Test public void givenSourceObjectAndDestObject_whenMapsSameNameFieldsCorrectly_ thenCorrect () {Zdroj zdroj = nový Zdroj ("Baeldung", 10); Dest dest = nový Dest (); mapovač.mapa (zdroj, cieľ); assertEquals (dest.getName (), "Baeldung"); assertEquals (dest.getAge (), 10); }

3. Nastavenie Maven

Teraz, keď máme základné znalosti o tom, ako funguje Dozer, pridajme k nasledujúcej závislosti pom.xml:

 net.sf.dozer dozer 5.5.1 

Najnovšia verzia je k dispozícii tu.

4. Príklad konverzie dát

Ako už vieme, Dozer dokáže mapovať existujúci objekt na iný, pokiaľ v oboch triedach nájde atribúty s rovnakým názvom.

Nie je to však vždy tak; a teda, ak niektorý z mapovaných atribútov má rôzne dátové typy, mapovací stroj Dozer bude automaticky vykoná konverziu dátového typu.

Pozrime sa na tento nový koncept v akcii:

verejná trieda Source2 {private String id; súkromné ​​dvojité body; public Source2 () {} public Source2 (reťazec id, dvojité body) {this.id = id; this.points = body; } // štandardní zakladatelia a zakladatelia}

A cieľová trieda:

verejná trieda Dest2 {private int id; súkromné ​​int body; public Dest2 () {} public Dest2 (int id, int points) {super (); this.id = id; this.points = body; } // štandardní zakladatelia a zakladatelia}

Všimnite si, že názvy atribútov sú rovnaké, ale ich dátové typy sú rôzne.

V zdrojovej triede id je a String a bodov je a dvojitýkeďže v cieľovej triede id a bodov sú obaja celé číslos.

Pozrime sa teraz, ako Dozer správne spracováva konverziu:

@Test public void givenSourceAndDestWithDifferentFieldTypes_ whenMapsAndAutoConverts_thenCorrect () {Source2 source = new Source2 ("320", 15.2); Dest2 dest = mapper.map (zdroj, Dest2.class); assertEquals (dest.getId (), 320); assertEquals (dest.getPoints (), 15); }

Prešli sme “320” a 15.2, a String a a dvojitý do zdrojového objektu a výsledok mal 320 a 15, oboje celé číslos v cieľovom objekte.

5. Základné vlastné mapovania pomocou XML

Vo všetkých predchádzajúcich príkladoch, ktoré sme videli, majú zdrojové aj cieľové dátové objekty rovnaké názvy polí, čo umožňuje jednoduché mapovanie na našej strane.

V aplikáciách v reálnom svete však bude nespočetne veľa prípadov, keď dva dátové objekty, ktoré mapujeme, nebudú mať polia, ktoré zdieľajú spoločný názov vlastnosti.

Aby sme to vyriešili, Dozer nám dáva možnosť vytvoriť a konfigurácia vlastného mapovania v XML.

V tomto súbore XML môžeme definovať položky mapovania tried, ktoré použije mapovací stroj Dozer na rozhodnutie, ktorý zdrojový atribút sa má namapovať na aký cieľový atribút.

Pozrime sa na príklad a skúsme rozdeliť dátové objekty z aplikácie zostavenej francúzskym programátorom do anglického štýlu pomenovania našich objektov.

Máme Osoba objekt s názov, prezývka a Vek polia:

public class Osoba {private String name; súkromná prezývka reťazca; súkromný int vek; public Person () {} public Person (String name, String nickname, int age) {super (); this.name = meno; this.nickname = nickname; this.age = vek; } // štandardní zakladatelia a zakladatelia}

Objekt, ktorý nemeníme, je pomenovaný Personne a má polia žiadne M, surnom a Vek:

public class Personne {private String nom; privátny reťazec surnom; súkromný int vek; public Personne () {} public Personne (String nom, String surnom, int age) {super (); this.nom = nom; this.surnom = surnom; this.age = vek; } // štandardní zakladatelia a zakladatelia}

Tieto objekty skutočne dosahujú rovnaký účel, ale máme jazykovú bariéru. Aby sme pomohli s touto bariérou, môžeme pomocou Dozera zmapovať francúzštinu Personne namietať proti nášmu Osoba objekt.

Musíme iba vytvoriť vlastný mapovací súbor, ktorý Dozeru pomôže, nazvime ho dozer_mapping.xml:

   com.baeldung.dozer.Personne com.baeldung.dozer.Person nom názov   surnom prezývka

Toto je najjednoduchší príklad vlastného mapovacieho súboru XML, aký môžeme mať.

Zatiaľ si stačí všimnúť, že máme ako náš koreňový prvok, ktorý má dieťa , môžeme mať toľko týchto detí vo vnútri pretože existuje výskyt párov tried, ktoré potrebujú vlastné mapovanie.

Všimnite si tiež, ako určujeme zdrojové a cieľové triedy vo vnútri značky. Nasleduje a pre každú dvojicu zdrojových a cieľových polí, ktoré potrebujú vlastné mapovanie.

Na záver si všimnite, že sme pole nezahrnuli Vek v našom vlastnom mapovacom súbore. Francúzske slovo pre vek je stále vek, čím sa dostávame k ďalšej dôležitej vlastnosti Dozeru.

Vlastnosti, ktoré majú rovnaký názov, nie je potrebné zadávať v mapovacom súbore XML. Dozer automaticky mapuje všetky polia s rovnakým názvom vlastnosti zo zdrojového objektu do cieľového objektu.

Potom umiestnime náš vlastný súbor XML na cestu ku triede priamo pod src priečinok. Avšak kdekoľvek ho umiestnime na cestu ku triede, Dozer prehľadá celú cestu k triede a hľadá zadaný súbor.

Vytvorme pomocnú metódu na pridanie súborov mapovania do našich mapovač:

public void configureMapper (String ... mappingFileUrls) {mapper.setMappingFiles (Arrays.asList (mappingFileUrls)); }

Poďme teraz otestovať kód:

@Test public void givenSrcAndDestWithDifferentFieldNamesWithCustomMapper_ whenMaps_thenCorrect () {configureMapper ("dozer_mapping.xml"); Personne frenchAppPerson = nový Personne ("Sylvester Stallone", "Rambo", 70 rokov); Osoba englishAppPerson = mapper.map (frenchAppPerson, Person.class); assertEquals (englishAppPerson.getName (), frenchAppPerson.getNom ()); assertEquals (englishAppPerson.getNickname (), frenchAppPerson.getSurnom ()); assertEquals (englishAppPerson.getAge (), frenchAppPerson.getAge ()); }

Ako ukazuje test, DozerBeanMapper prijíma zoznam vlastných súborov mapovania XML a rozhoduje sa, kedy ich použije za behu.

Za predpokladu, že teraz začneme nemenovať tieto dátové objekty medzi našou anglickou a francúzskou aplikáciou. Nemusíme vytvárať ďalšie mapovanie v súbore XML, Dozer je dostatočne inteligentný na to, aby mapoval objekty oboma spôsobmi iba s jednou konfiguráciou mapovania:

@Test public void givenSrcAndDestWithDifferentFieldNamesWithCustomMapper_ whenMapsBidirectionally_thenCorrect () {configureMapper ("dozer_mapping.xml"); Osoba englishAppPerson = nová osoba ("Dwayne Johnson", "The Rock", 44); Personne frenchAppPerson = mapper.map (englishAppPerson, Personne.class); assertEquals (frenchAppPerson.getNom (), englishAppPerson.getName ()); assertEquals (frenchAppPerson.getSurnom (), englishAppPerson.getNickname ()); assertEquals (frenchAppPerson.getAge (), englishAppPerson.getAge ()); }

A tak tento príkladový test využíva túto ďalšiu vlastnosť Dozeru - skutočnosť, že mapovací motor Dozeru je obojsmerný, takže ak chceme mapovať cieľový objekt na zdrojový objekt, nemusíme do súboru XML pridávať ďalšie mapovanie tried.

Môžeme tiež načítať vlastný mapovací súbor z vonkajšej strany triedy, ak je to potrebné, použite „spis:”Predpona v názve prostriedku.

V prostredí Windows (napríklad test uvedený nižšie) samozrejme použijeme syntax súborov špecifických pre Windows.

V škatuli s Linuxom môžeme súbor uložiť pod /Domov a potom:

configureMapper ("súbor: /home/dozer_mapping.xml");

A v systéme Mac OS:

configureMapper ("súbor: /Users/me/dozer_mapping.xml");

Ak spúšťate testy jednotky z projektu github (ktorý by ste mali), môžete skopírovať mapovací súbor na príslušné miesto a zmeniť vstup pre configureMapper metóda.

Mapovací súbor je k dispozícii v priečinku test / resources projektu GitHub:

@Test public void givenMappingFileOutsideClasspath_whenMaps_thenCorrect () {configureMapper ("súbor: E: \ dozer_mapping.xml"); Osoba englishAppPerson = nová Osoba ("Marshall Bruce Mathers III", "Eminem", 43); Personne frenchAppPerson = mapper.map (englishAppPerson, Personne.class); assertEquals (frenchAppPerson.getNom (), englishAppPerson.getName ()); assertEquals (frenchAppPerson.getSurnom (), englishAppPerson.getNickname ()); assertEquals (frenchAppPerson.getAge (), englishAppPerson.getAge ()); }

6. Zástupné znaky a ďalšie prispôsobenie XML

Vytvorme druhý vlastný mapovací súbor s názvom dozer_mapping2.xml:

   com.baeldung.dozer.Personne com.baeldung.dozer.Person nom názov   surnom prezývka

Všimnite si, že sme pridali atribút divoká karta do prvok, ktorý tam predtým nebol.

Predvolene, divoká karta je pravda. Hovorí Dozerovmu motoru, že chceme, aby sa všetky polia v zdrojovom objekte mapovali na ich príslušné cieľové polia.

Keď sme to nastavili nepravda, Dozerovi hovoríme, aby mapoval iba polia, ktoré sme výslovne špecifikovali v XML.

Vo vyššie uvedenej konfigurácii teda chceme zmapovať iba dve polia, ktoré vynecháme Vek:

@Test public void givenSrcAndDest_whenMapsOnlySpecifiedFields_thenCorrect () {configureMapper ("dozer_mapping2.xml"); Osoba englishAppPerson = nová osoba ("Shawn Corey Carter", "Jay Z", 46); Personne frenchAppPerson = mapper.map (englishAppPerson, Personne.class); assertEquals (frenchAppPerson.getNom (), englishAppPerson.getName ()); assertEquals (frenchAppPerson.getSurnom (), englishAppPerson.getNickname ()); assertEquals (frenchAppPerson.getAge (), 0); }

Ako vidíme v poslednom tvrdení, cieľ Vek pole zostalo 0.

7. Vlastné mapovanie pomocou anotácií

Pre jednoduché prípady mapovania a prípady, keď máme tiež prístup na zápis k dátovým objektom, ktoré by sme chceli mapovať, možno nebudeme musieť používať mapovanie XML.

Mapovanie rôzne pomenovaných polí pomocou anotácií je veľmi jednoduché a musíme napísať oveľa menej kódu ako v mapovaní XML, ale môže nám pomôcť iba v jednoduchých prípadoch.

Replikujme naše dátové objekty Person2.java a Personne2.java bez zmeny polí.

Aby sme to mohli implementovať, stačí pridať @mapovač („destinationFieldName“) anotácia k getter metódy v zdrojovom objekte. Ako:

@ Mapovanie ("meno") public String getNom () {return nom; } @Mapping ("prezývka") public String getSurnom () {return surnom; }

Tentokrát liečime Personne2 ako zdroj, ale na tom nezáleží kvôli obojsmerná povaha motora buldozéra.

Teraz, keď je odstránený všetok kód súvisiaci s XML, je náš testovací kód kratší:

@Test public void givenAnnotatedSrcFields_whenMapsToRightDestField_thenCorrect () {Person2 englishAppPerson = nová Person2 ("Jean-Claude Van Damme", "JCVD", 55); Personne2 frenchAppPerson = mapper.map (englishAppPerson, Personne2.class); assertEquals (frenchAppPerson.getNom (), englishAppPerson.getName ()); assertEquals (frenchAppPerson.getSurnom (), englishAppPerson.getNickname ()); assertEquals (frenchAppPerson.getAge (), englishAppPerson.getAge ()); }

Môžeme tiež otestovať obojsmernosť:

@Test public void givenAnnotatedSrcFields_whenMapsToRightDestFieldBidirectionally_ thenCorrect () {Personne2 frenchAppPerson = new Personne2 ("Jason Statham", "transportér", 49); Person2 englishAppPerson = mapper.map (frenchAppPerson, Person2.class); assertEquals (englishAppPerson.getName (), frenchAppPerson.getNom ()); assertEquals (englishAppPerson.getNickname (), frenchAppPerson.getSurnom ()); assertEquals (englishAppPerson.getAge (), frenchAppPerson.getAge ()); }

8. Vlastné mapovanie API

V našich predchádzajúcich príkladoch, keď oddeľovávame dátové objekty z francúzskej aplikácie, sme na prispôsobenie nášho mapovania použili XML a anotácie.

Ďalšou alternatívou dostupnou v aplikácii Dozer, podobnou mapovaniu anotácií, je mapovanie API. Sú podobné, pretože vylučujeme konfiguráciu XML a striktne používame kód Java.

V tomto prípade použijeme BeanMappingBuilder trieda, definovaná v našom najjednoduchšom prípade takto:

Builder BeanMappingBuilder = nový BeanMappingBuilder () {@Override protected void configure () {mapping (Person.class, Personne.class) .fields ("name", "nom") .fields ("nick", "surnom"); }};

Ako vidíme, máme abstraktnú metódu, Konfigurovať (), ktoré musíme prepísať, aby sme mohli definovať naše konfigurácie. Potom, rovnako ako náš značiek v XML definujeme toľko TypeMappingBuilders ako požadujeme.

Títo stavitelia Dozerovi povedia, ktoré polia zdroja k cieľu mapujeme. Potom míňame BeanMappingBuilder do DozerBeanMapper ako by sme to robili, mapovací súbor XML, iba s iným API:

@Test public void givenApiMapper_whenMaps_thenCorrect () {mapper.addMapping (staviteľ); Personne frenchAppPerson = nový Personne ("Sylvester Stallone", "Rambo", 70 rokov); Osoba englishAppPerson = mapper.map (frenchAppPerson, Person.class); assertEquals (englishAppPerson.getName (), frenchAppPerson.getNom ()); assertEquals (englishAppPerson.getNickname (), frenchAppPerson.getSurnom ()); assertEquals (englishAppPerson.getAge (), frenchAppPerson.getAge ()); }

Mapovacie API je tiež obojsmerné:

@Test public void givenApiMapper_whenMapsBidirectionally_thenCorrect () {mapper.addMapping (staviteľ); Osoba englishAppPerson = nová osoba ("Sylvester Stallone", "Rambo", 70); Personne frenchAppPerson = mapper.map (englishAppPerson, Personne.class); assertEquals (frenchAppPerson.getNom (), englishAppPerson.getName ()); assertEquals (frenchAppPerson.getSurnom (), englishAppPerson.getNickname ()); assertEquals (frenchAppPerson.getAge (), englishAppPerson.getAge ()); }

Alebo sa môžeme rozhodnúť mapovať iba explicitne určené polia s touto konfiguráciou staviteľa:

BeanMappingBuilder builderMinusAge = nový BeanMappingBuilder () {@Override protected void configure () {mapping (Person.class, Personne.class) .fields ("name", "nom") .fields ("nick", "surnom") .exclude ("Vek"); }};

a náš vek == 0 test je späť:

@Test public void givenApiMapper_whenMapsOnlySpecifiedFields_thenCorrect () {mapper.addMapping (builderMinusAge); Osoba englishAppPerson = nová osoba ("Sylvester Stallone", "Rambo", 70); Personne frenchAppPerson = mapper.map (englishAppPerson, Personne.class); assertEquals (frenchAppPerson.getNom (), englishAppPerson.getName ()); assertEquals (frenchAppPerson.getSurnom (), englishAppPerson.getNickname ()); assertEquals (frenchAppPerson.getAge (), 0); }

9. Vlastné prevádzače

Ďalším scenárom, s ktorým sa môžeme pri mapovaní stretnúť, je miesto, kde by sme chceli vykonávať vlastné mapovanie medzi dvoma objektmi.

Pozreli sme sa na scenáre, kde sú názvy zdrojových a cieľových polí odlišné ako vo francúzštine Personne objekt. Táto časť rieši iný problém.

Čo ak dátový objekt, ktorý práve nemeníme, predstavuje pole dátumu a času, napríklad a dlho alebo Unixový čas takto:

1182882159000

Ale náš vlastný ekvivalentný údajový objekt predstavuje rovnaké pole dátumu a času a hodnotu v tomto formáte ISO, napríklad a Reťazec:

2007-06-26T21: 22: 39Z

Predvolený prevodník jednoducho namapuje dlhú hodnotu na a String ako:

"1182882159000"

To by našu aplikáciu určite pokazilo. Ako to teda vyriešiť? Riešime to pridanie konfiguračného bloku v mapovacom súbore XML a s uvedením vlastného prevodníka.

Najskôr si replikujme vzdialené aplikácie Osoba DTO s a názov, potom dátum a čas narodenia, dtob lúka:

public class Personne3 {private String name; súkromný dlhý dtob; public Personne3 (názov reťazca, dlhý dtob) {super (); this.name = meno; this.dtob = dtob; } // štandardní zakladatelia a zakladatelia}

a tu je náš vlastný:

public class Person3 {private String name; súkromný String dtob; public Person3 (String name, String dtob) {super (); this.name = meno; this.dtob = dtob; } // štandardní zakladatelia a zakladatelia}

Všimnite si typový rozdiel dtob v zdrojovom a cieľovom DTO.

Vytvorme si tiež svoje vlastné CustomConverter odovzdať do Dozeru v mapovacom XML:

public class MyCustomConvertor implementuje CustomConverter {@Override public Object convert (Object dest, Object source, Class arg2, Class arg3) {if (source == null) return null; if (zdroj instanceof Personne3) {Personne3 person = (Personne3) zdroj; Dátum dátum = nový Dátum (person.getDtob ()); Formát DateFormat = nový SimpleDateFormat ("rrrr-MM-dd'T'HH: mm: ss'Z '" "); Reťazec isoDate = format.format (dátum); vrátiť novú Person3 (person.getName (), isoDate); } else if (zdroj instanceof Person3) {Person3 person = (Person3) source; Formát DateFormat = nový SimpleDateFormat ("rrrr-MM-dd'T'HH: mm: ss'Z '" "); Dátum dátum = format.parse (person.getDtob ()); long timestamp = date.getTime (); vrátiť nové Personne3 (person.getName (), časová známka); }}}

Musíme iba prekonať konvertovať () metóda potom vrátiť to, čo sa k nej chceme vrátiť. Máme k dispozícii zdrojové a cieľové objekty a ich typy tried.

Všimnite si, ako sme sa postarali o dvojsmernosť za predpokladu, že zdrojom môže byť ktorákoľvek z dvoch tried, ktoré mapujeme.

Pre prehľadnosť vytvoríme nový mapovací súbor, dozer_custom_convertor.xml:

     com.baeldung.dozer.Personne3 com.baeldung.dozer.Person3 

Toto je normálny mapovací súbor, ktorý sme videli v predchádzajúcich častiach, pridali sme iba a blok, v ktorom môžeme definovať toľko vlastných prevádzačov, koľko požadujeme, s ich príslušnými triedami zdrojových a cieľových údajov.

Vyskúšajme náš nový CustomConverter kód:

@Test public void givenSrcAndDestWithDifferentFieldTypes_whenAbleToCustomConvert_ thenCorrect () {configureMapper ("dozer_custom_convertor.xml"); Reťazec dateTime = "2007-06-26T21: 22: 39Z"; long timestamp = new Long ("1182882159000"); Person3 person = new Person3 ("Rich", dateTime); Personne3 person0 = mapper.map (osoba, Personne3.class); assertEquals (timestamp, person0.getDtob ()); }

Môžeme tiež otestovať, či je obojsmerný:

@Test public void givenSrcAndDestWithDifferentFieldTypes_ whenAbleToCustomConvertBidirectionally_thenCorrect () {configureMapper ("dozer_custom_convertor.xml"); Reťazec dateTime = "2007-06-26T21: 22: 39Z"; long timestamp = new Long ("1182882159000"); Personne3 person = new Personne3 ("Rich", timestamp); Person3 person0 = mapper.map (person, Person3.class); assertEquals (dateTime, person0.getDtob ()); }

10. Záver

V tejto príručke máme predstavil väčšinu základov knižnice Dozer Mapping a ako ich používať v našich aplikáciách.

Plnú implementáciu všetkých týchto príkladov a útržkov kódu nájdete v projekte Dozer github.


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