En guide til kartlegging med dozer

1. Oversikt

Dozer er en Java Bean til Java Bean mapper som rekursivt kopierer data fra ett objekt til et annet, attributt for attributt.

Biblioteket støtter ikke bare kartlegging mellom attributtnavn på Java Beans, men også konverterer automatisk mellom typer - hvis de er forskjellige.

De fleste konverteringsscenarier støttes ut av boksen, men Dozer lar deg også spesifiser tilpassede konverteringer via XML.

2. Enkelt eksempel

La oss for det første eksemplet anta at kilde- og destinasjonsdataobjektene alle har de samme vanlige attributtnavnene.

Dette er den mest grunnleggende kartleggingen man kan gjøre med Dozer:

offentlig klasse Kilde {privat strengnavn; privat alder; public Source () {} public Source (String name, int age) {this.name = name; this.age = alder; } // standard getters and setters}

Så målfilen vår, Dest.java:

offentlig klasse Dest {private Strengnavn; privat alder; public Dest () {} public Dest (String name, int age) {this.name = name; this.age = alder; } // standard getters and setters}

Vi må sørge for å inkluderer standard- eller nullargumentkonstruktører, siden Dozer bruker refleksjon under panseret.

Og for ytelsesformål, la oss gjøre kartleggeren vår global og lage et enkelt objekt vi vil bruke gjennom testene våre:

Kartlegger av DozerBeanMapper; @Før offentlig ugyldig før () kaster Unntak {mapper = ny DozerBeanMapper (); }

La oss nå kjøre vår første test for å bekrefte at når vi oppretter en Kilde objekt, kan vi kartlegge den direkte på en Dest gjenstand:

@Test offentlig ugyldig givenSourceObjectAndDestClass_whenMapsSameNameFieldsCorrectly_ thenCorrect () {Source source = new Source ("Baeldung", 10); Dest dest = mapper.map (kilde, Dest.klasse); assertEquals (dest.getName (), "Baeldung"); assertEquals (dest.getAge (), 10); }

Som vi kan se, blir resultatet etter Dozer-kartleggingen en ny forekomst av Dest objekt som inneholder verdier for alle felt som har samme feltnavn som Kilde gjenstand.

Alternativt, i stedet for å passere kartlegger de Dest klasse, kunne vi bare ha opprettet Dest objekt og passert kartlegger dens referanse:

@Test offentlig ugyldig givenSourceObjectAndDestObject_whenMapsSameNameFieldsCorrectly_ thenCorrect () {Source source = new Source ("Baeldung", 10); Dest dest = ny Dest (); mapper.map (kilde, dest); assertEquals (dest.getName (), "Baeldung"); assertEquals (dest.getAge (), 10); }

3. Maven-oppsett

Nå som vi har en grunnleggende forståelse av hvordan Dozer fungerer, la oss legge til følgende avhengighet til pom.xml:

 net.sf.dozer dozer 5.5.1 

Den siste versjonen er tilgjengelig her.

4. Eksempel på datakonvertering

Som vi allerede vet, kan Dozer tilordne et eksisterende objekt til et annet så lenge det finner attributter med samme navn i begge klassene.

Det er imidlertid ikke alltid tilfelle; og så, hvis noen av de tilordnede attributtene er av forskjellige datatyper, vil Dozer-kartleggingsmotoren gjøre det automatisk utføre en datatypekonvertering.

La oss se dette nye konseptet i aksjon:

offentlig klasse Kilde2 {privat streng-id; private doble poeng; public Source2 () {} public Source2 (String id, double points) {this.id = id; this.points = poeng; } // standard getters and setters}

Og destinasjonsklassen:

offentlig klasse Dest2 {privat int id; private int poeng; public Dest2 () {} public Dest2 (int id, int poeng) {super (); this.id = id; this.points = poeng; } // standard getters and setters}

Legg merke til at attributtnavnene er de samme men datatypene deres er forskjellige.

I kildeklassen, id er en String og poeng er en dobbeltmens i målklassen, id og poeng er begge heltalls.

La oss nå se hvordan Dozer håndterer konverteringen riktig:

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

Vi passerte “320” og 15.2, a String og en dobbelt inn i kildeobjektet og resultatet hadde 320 og 15, både heltalls i destinasjonsobjektet.

5. Grunnleggende tilpassede kartlegginger via XML

I alle de forrige eksemplene vi har sett, har både kilde- og destinasjonsdataobjektene de samme feltnavnene, noe som muliggjør enkel kartlegging på vår side.

Imidlertid vil det i virkelighetsapplikasjoner være utallige ganger der de to dataobjektene vi kartlegger ikke har felt som har et felles eiendomsnavn.

For å løse dette gir Dozer oss en mulighet til å lage en tilpasset kartleggingskonfigurasjon i XML.

I denne XML-filen kan vi definere klassekartoppføringer som Dozer-kartmotoren vil bruke til å bestemme hvilket kildeattributt som skal tilordnes til hvilket destinasjonsattributt.

La oss se på et eksempel, og la oss prøve å oppheve dataobjekter fra en applikasjon bygget av en fransk programmerer, til en engelsk stil for å navngi objektene våre.

Vi har en Person objekt med Navn, kallenavn og alder Enger:

offentlig klasse Person {private Strengnavn; privat streng kallenavn; privat alder; offentlig person () {} offentlig person (strengnavn, streng kallenavn, int alder) {super (); this.name = navn; this.nickname = kallenavn; this.age = alder; } // standard getters and setters}

Objektet vi ikke tar vare på, heter Personne og har felt ingen m, surnom og alder:

offentlig klasse Personne {private String nom; private String surnom; privat alder; public Personne () {} public Personne (String nom, String surnom, int age) {super (); this.nom = nom; this.surnom = surnom; this.age = alder; } // standard getters and setters}

Disse objektene oppnår virkelig det samme formålet, men vi har en språkbarriere. For å hjelpe til med den barrieren kan vi bruke Dozer til å kartlegge franskmennene Personne innvende mot vårt Person gjenstand.

Vi trenger bare å lage en tilpasset kartfil for å hjelpe Dozer med å gjøre dette, vi vil kalle det dozer_mapping.xml:

   com.baeldung.dozer.Personne com.baeldung.dozer.Person nom Navn   surnom kallenavn

Dette er det enkleste eksemplet på en tilpasset XML-kartfil vi kan ha.

For nå er det nok å legge merke til at vi har som vårt grunnelement, som har et barn , vi kan ha så mange av disse barna inne ettersom det er forekomster av klassepar som trenger tilpasset kartlegging.

Legg også merke til hvordan vi spesifiserer kilde- og destinasjonsklassene inne i koder. Dette følges av en for hvert kilde- og målfeltpar som trenger tilpasset kartlegging.

Merk til slutt at vi ikke har tatt med feltet alder i vår tilpassede kartleggingsfil. Det franske ordet for alder er fortsatt alder, noe som bringer oss til et annet viktig trekk ved Dozer.

Egenskaper med samme navn trenger ikke å spesifiseres i XML-filen. Dozer kartlegger automatisk alle felt med samme eiendomsnavn fra kildeobjektet til målobjektet.

Vi vil deretter plassere vår tilpassede XML-fil på klassestien rett under src mappe. Uansett hvor vi plasserer den på klassestien, vil Dozer imidlertid søke i hele klassestien på jakt etter den angitte filen.

La oss lage en hjelpemetode for å legge til kartleggingsfiler i vår kartlegger:

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

La oss nå teste koden:

@Test offentlig ugyldig givenSrcAndDestWithDifferentFieldNamesWithCustomMapper_ whenMaps_thenCorrect () {configureMapper ("dozer_mapping.xml"); Personne frenchAppPerson = nye Personne ("Sylvester Stallone", "Rambo", 70); Person englishAppPerson = mapper.map (franskAppPerson, Person.class); assertEquals (englishAppPerson.getName (), frenchAppPerson.getNom ()); assertEquals (englishAppPerson.getNickname (), franskAppPerson.getSurnom ()); assertEquals (englishAppPerson.getAge (), franskAppPerson.getAge ()); }

Som vist i testen, DozerBeanMapper aksepterer en liste over egendefinerte XML-kartleggingsfiler og bestemmer når de skal brukes hver mens de kjører.

Forutsatt at vi nå begynner å unmarshalling av disse dataobjektene frem og tilbake mellom vår engelske app og den franske appen. Vi trenger ikke lage en ny kartlegging i XML-filen, Dozer er smart nok til å kartlegge objektene begge veier med bare en kartleggingskonfigurasjon:

@Test offentlig ugyldig givenSrcAndDestWithDifferentFieldNamesWithCustomMapper_ whenMapsBidirectionally_thenCorrect () {configureMapper ("dozer_mapping.xml"); Person englishAppPerson = ny person ("Dwayne Johnson", "The Rock", 44); Personne frenchAppPerson = mapper.map (engelskAppPerson, Personne.class); assertEquals (frenchAppPerson.getNom (), englishAppPerson.getName ()); assertEquals (frenchAppPerson.getSurnom (), engelskAppPerson.getNickname ()); assertEquals (frenchAppPerson.getAge (), engelskAppPerson.getAge ()); }

Og så bruker denne eksemplet testen denne andre funksjonen i Dozer - det faktum at Dozer-kartmotoren er toveis, så hvis vi ønsker å tilordne destinasjonsobjektet til kildeobjektet, trenger vi ikke legge til en annen klassekartlegging i XML-filen.

Vi kan også laste inn en egendefinert kartfil fra utenfor klassestien, hvis vi trenger det, brukfil:”Prefiks i ressursnavnet.

I et Windows-miljø (for eksempel testen nedenfor) bruker vi selvfølgelig den Windows-spesifikke filsyntaksen.

I en Linux-boks kan vi lagre filen under /hjem og så:

configureMapper ("file: /home/dozer_mapping.xml");

Og på Mac OS:

configureMapper ("file: /Users/me/dozer_mapping.xml");

Hvis du kjører enhetstestene fra github-prosjektet (som du burde), kan du kopiere kartfilen til riktig sted og endre inngangen for configureMapper metode.

Kartleggingsfilen er tilgjengelig under test / ressursmappen til GitHub-prosjektet:

@Test offentlig ugyldig givenMappingFileOutsideClasspath_whenMaps_thenCorrect () {configureMapper ("file: E: \ dozer_mapping.xml"); Person englishAppPerson = ny person ("Marshall Bruce Mathers III", "Eminem", 43); Personne frenchAppPerson = mapper.map (engelskAppPerson, Personne.class); assertEquals (frenchAppPerson.getNom (), englishAppPerson.getName ()); assertEquals (frenchAppPerson.getSurnom (), engelskAppPerson.getNickname ()); assertEquals (frenchAppPerson.getAge (), engelskAppPerson.getAge ()); }

6. Jokertegn og ytterligere XML-tilpasning

La oss lage en ny tilpasset kartfil som heter dozer_mapping2.xml:

   com.baeldung.dozer.Personne com.baeldung.dozer.Person nom Navn   surnom kallenavn

Legg merke til at vi har lagt til et attributt jokertegn til element som ikke var der før.

Som standard, jokertegn er ekte. Det forteller Dozer-motoren at vi vil at alle feltene i kildeobjektet skal kartlegges til de aktuelle destinasjonsfeltene.

Når vi setter det til falsk, vi forteller Dozer å bare kartlegge felt vi eksplisitt har spesifisert i XML.

Så i konfigurasjonen ovenfor vil vi bare ha to felt kartlagt, utenom alder:

@Test offentlig ugyldig givenSrcAndDest_whenMapsOnlySpecifiedFields_thenCorrect () {configureMapper ("dozer_mapping2.xml"); Person englishAppPerson = ny person ("Shawn Corey Carter", "Jay Z", 46); Personne frenchAppPerson = mapper.map (engelskAppPerson, Personne.class); assertEquals (frenchAppPerson.getNom (), englishAppPerson.getName ()); assertEquals (frenchAppPerson.getSurnom (), engelskAppPerson.getNickname ()); assertEquals (frenchAppPerson.getAge (), 0); }

Som vi kan se i den siste påstanden, destinasjonen alder feltet forble 0.

7. Tilpasset kartlegging via merknader

For enkle kartleggingssaker og tilfeller der vi også har skrivetilgang til dataobjektene vi vil kartlegge, trenger vi kanskje ikke å bruke XML-kartlegging.

Kartlegging av forskjellige navngitte felt via merknader er veldig enkelt, og vi må skrive mye mindre kode enn i XML-kartlegging, men kan bare hjelpe oss i enkle tilfeller.

La oss replikere dataobjektene våre inn i Person2.java og Personne2.java uten å endre felt i det hele tatt.

For å implementere dette trenger vi bare å legge til @kartlegger (“destinasjonsfeltnavn”) kommentar på getter metoder i kildeobjektet. Som så:

@Mapping ("navn") offentlig String getNom () {return nom; } @Mapping ("kallenavn") offentlig String getSurnom () {return surnom; }

Denne gangen behandler vi Personne2 som kilde, men det spiller ingen rolle på grunn av toveis natur av bulldozermotoren.

Nå med all XML-relatert kode fjernet, er testkoden vår kortere:

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

Vi kan også teste for toveis:

@Test offentlig ugyldighet givenAnnotatedSrcFields_whenMapsToRightDestFieldBidirectionally_ thenCorrect () {Personne2 frenchAppPerson = new Personne2 ("Jason Statham", "transporter", 49); Person2 englishAppPerson = mapper.map (franskAppPerson, Person2.class); assertEquals (englishAppPerson.getName (), frenchAppPerson.getNom ()); assertEquals (englishAppPerson.getNickname (), franskAppPerson.getSurnom ()); assertEquals (englishAppPerson.getAge (), franskAppPerson.getAge ()); }

8. Tilpasset API-kartlegging

I våre tidligere eksempler der vi ikke tar vare på dataobjekter fra et fransk program, brukte vi XML og merknader for å tilpasse kartleggingen vår.

Et annet alternativ tilgjengelig i Dozer, som ligner på merkekartlegging, er API-kartlegging. De er like fordi vi eliminerer XML-konfigurasjon og bruker Java-kode strengt.

I dette tilfellet bruker vi BeanMappingBuilder klasse, definert i vårt enkleste tilfelle slik:

BeanMappingBuilder builder = new BeanMappingBuilder () {@Override protected void configure () {mapping (Person.class, Personne.class) .fields ("name", "nom") .fields ("nickname", "surnom"); }};

Som vi kan se, har vi en abstrakt metode, konfigurer (), som vi må overstyre for å definere våre konfigurasjoner. Så, akkurat som vår koder i XML, definerer vi så mange TypeMappingBuilders som vi krever.

Disse byggherrene forteller Dozer hvilken kilde til destinasjonsfeltene vi kartlegger. Vi passerer deretter BeanMappingBuilder til DozerBeanMapper som vi ville, XML-kartfilen, bare med en annen API:

@Test offentlig ugyldig givenApiMapper_whenMaps_thenCorrect () {mapper.addMapping (builder); Personne frenchAppPerson = nye Personne ("Sylvester Stallone", "Rambo", 70); Person englishAppPerson = mapper.map (franskAppPerson, Person.class); assertEquals (englishAppPerson.getName (), frenchAppPerson.getNom ()); assertEquals (englishAppPerson.getNickname (), franskAppPerson.getSurnom ()); assertEquals (englishAppPerson.getAge (), franskAppPerson.getAge ()); }

Kartleggings-API-et er også toveis:

@Test offentlig ugyldighet givenApiMapper_whenMapsBidirectionally_thenCorrect () {mapper.addMapping (builder); Person englishAppPerson = ny person ("Sylvester Stallone", "Rambo", 70); Personne frenchAppPerson = mapper.map (engelskAppPerson, Personne.class); assertEquals (frenchAppPerson.getNom (), englishAppPerson.getName ()); assertEquals (frenchAppPerson.getSurnom (), engelskAppPerson.getNickname ()); assertEquals (frenchAppPerson.getAge (), engelskAppPerson.getAge ()); }

Eller vi kan velge å bare kartlegge eksplisitt angitte felt med denne byggherrekonfigurasjonen:

BeanMappingBuilder builderMinusAge = ny BeanMappingBuilder () {@Override beskyttet tom konfigurere () {mapping (Person.class, Personne.class) .fields ("name", "nom") .fields ("nickname", "surnom") .exclude ("alder"); }};

og vår alder == 0 testen er tilbake:

@Test offentlig ugyldighet gittApiMapper_whenMapsOnlySpecifiedFields_thenCorrect () {mapper.addMapping (builderMinusAge); Person englishAppPerson = ny person ("Sylvester Stallone", "Rambo", 70); Personne frenchAppPerson = mapper.map (engelskAppPerson, Personne.class); assertEquals (frenchAppPerson.getNom (), englishAppPerson.getName ()); assertEquals (frenchAppPerson.getSurnom (), engelskAppPerson.getNickname ()); assertEquals (frenchAppPerson.getAge (), 0); }

9. Egendefinerte omformere

Et annet scenario vi kan møte i kartleggingen er hvor vi vil utføre tilpasset kartlegging mellom to objekter.

Vi har sett på scenarier der kilde- og destinasjonsfeltnavn er forskjellige som på fransk Personne gjenstand. Denne delen løser et annet problem.

Hva om et dataobjekt vi ikke tar vare på, representerer et dato- og tidsfelt som f.eks lang eller Unix-tid slik:

1182882159000

Men vårt eget ekvivalente dataobjekt representerer samme dato og tidsfelt og verdi i dette ISO-formatet, for eksempel Streng:

2007-06-26T21: 22: 39Z

Standardomformeren vil ganske enkelt kartlegge den lange verdien til a String som så:

"1182882159000"

Dette vil definitivt bugge appen vår. Så hvordan løser vi dette? Vi løser det med legge til en konfigurasjonsblokk i kartleggings-XML-filen og spesifisere vår egen omformer.

La oss først replikere det eksterne programmet Person DTO med en Navn, deretter fødselsdato og tidspunkt, dtob felt:

offentlig klasse Personne3 {privat strengnavn; privat lang dtob; offentlig Personne3 (strengnavn, lang dtob) {super (); this.name = navn; this.dtob = dtob; } // standard getters and setters}

og her er vår egen:

offentlig klasse Person3 {privat strengnavn; private String dtob; offentlig person3 (strengnavn, streng dtob) {super (); this.name = navn; this.dtob = dtob; } // standard getters and setters}

Legg merke til typeforskjellen på dtob i kilde- og destinasjons-DTO-ene.

La oss også lage våre egne CustomConverter å overføre til Dozer i kartleggings-XML:

offentlig klasse MyCustomConvertor implementerer CustomConverter {@Override public Object convert (Object dest, Object source, Class arg2, Class arg3) {if (source == null) return null; hvis (kildeinstans av Personne3) {Personne3 person = (Personne3) kilde; Dato dato = ny dato (person.getDtob ()); DateFormat format = nytt SimpleDateFormat ("åååå-MM-dd'T'HH: mm: ss'Z '"); Streng isoDate = format.format (dato); returner ny Person3 (person.getName (), isoDate); } annet hvis (kildeinstans av Person3) {Person3 person = (Person3) kilde; DateFormat format = nytt SimpleDateFormat ("åååå-MM-dd'T'HH: mm: ss'Z '"); Dato dato = format.parse (person.getDtob ()); lang tidsstempel = date.getTime (); returnere nye Personne3 (person.getName (), tidsstempel); }}}

Vi må bare overstyre konvertere() metode, og returner det vi vil returnere til det. Vi blir benyttet av kilde- og destinasjonsobjektene og deres klassetyper.

Legg merke til hvordan vi har tatt vare på toveis ved å anta at kilden kan være en av de to klassene vi kartlegger.

Vi vil lage en ny kartleggingsfil for klarhet, dozer_custom_convertor.xml:

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

Dette er den normale kartleggingsfilen vi har sett i foregående seksjoner, vi har bare lagt til en blokk der vi kan definere så mange egendefinerte omformere som vi trenger med deres respektive kilde- og destinasjonsdataklasser.

La oss teste det nye CustomConverter kode:

@Test offentlig ugyldig givenSrcAndDestWithDifferentFieldTypes_whenAbleToCustomConvert_ thenCorrect () {configureMapper ("dozer_custom_convertor.xml"); String dateTime = "2007-06-26T21: 22: 39Z"; lang tidsstempel = ny Lang ("1182882159000"); Person3 person = ny Person3 ("Rich", dateTime); Personne3 person0 = mapper.map (person, Personne3.class); assertEquals (tidsstempel, person0.getDtob ()); }

Vi kan også teste for å sikre at den er toveis:

@Test offentlig ugyldig givenSrcAndDestWithDifferentFieldTypes_ whenAbleToCustomConvertBidirectionally_thenCorrect () {configureMapper ("dozer_custom_convertor.xml"); String dateTime = "2007-06-26T21: 22: 39Z"; lang tidsstempel = ny Lang ("1182882159000"); Personne3 person = ny Personne3 ("Rich", tidsstempel); Person3 person0 = mapper.map (person, Person3.class); assertEquals (dateTime, person0.getDtob ()); }

10. Konklusjon

I denne opplæringen har vi det introduserte det meste av det grunnleggende i Dozer Mapping-biblioteket og hvordan du bruker den i applikasjonene våre.

Fullstendig implementering av alle disse eksemplene og kodebitene finner du i Dozer github-prosjektet.


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