31

MapStruct - der neue Stern am Bean-Mapping Himmel?!

Embed Size (px)

Citation preview

WER ERZÄHLT DIE NÄCHSTEN 55+ MINUTENETWAS?

Gerrit Brehmer, Karlsruhe

Auf der Arbeit: Software-Entwickler/Architekt ( / Projektleiter) bei derinovex GmbH

In der Freizeit: Smart Home

BEAN-MAPPING: EINSATZWann müssen Beans gemappt werden?

Entities zu DTOsinterne zu generierten Klassen (JAXB etc.)

zur Einbindung externer Dienstestandardisierten Schnittstelleneigenen REST/SOAP Schnittstellen

Allgemein Austauschformate zwischen Schichten und Applikationen

BEAN MAPPING: UMSETZUNGWas benutzen wir dafür bisher?

von Hand geschriebener Mapping-Codetypsicher, schnellfehleranfällig, aufwendig

Dozerwenig Aufwand, Rekursives Auto-MappingReflection & XML: langsam

Apache Commons BeanUtilswenig AufwandReflection, nur nicht komplexe Properties, langsam

GEHT ES BESSER?Wunschliste

schnelltypsicherwenig Aufwand

automatisches MappingNull-PrüfungenKonvertierungen von "ähnlichen" Typen

Fehlendes Mapping schnell erkennennachvollziehbar

MAPSTRUCTmapstruct.org / github.com/mapstructOpenSource (Lizenz: Apache 2.0)Initator & Maintainer: Gunnar Morling (RedHat,Hibernate Team)Aktuelle Version: 1.0.0.CR2 (Final Release in Kürze)Weiterentwicklung/Support

mehrere Stamm-Entwicklerschnelles Feedbackkontinuierliche Weiterentwicklung

WIE FUNKTIONIERT MAPSTRUCTAnnotation Processor

Verarbeitet Annotations am eigenen Quellcodesiehe andere bekannte Libs: JPA MetaModel, Lombok, QueryDSLz.B. mit Hilfe maven-processor-plugin

Generiert Java QuellcodeZusammenspiel verschiedener FreeMarker Templates

DEMO

INTEGRATION IN ECLIPSE #1MapStruct eclipse Plugin

noch work-in-progressHauptsächlich noch fehlende Features, keine Bugs

Code Completion: JAXB Listen, Nested PropertiesQuick Fix: Collection Mappings

INTEGRATION IN ECLIPSE #2

m2e APT PluginAktivierung nichtvergessen!Inkompatibilitäten mitEclipse Compiler pre Mars(ab 1.0.0.CR2)Manchmal 'Clean Project'notwendig

Maven: build-helper-maven-plugin

automatische Build-PathAnpassung

GENERIERTE ABHÄNGIGKEITENZu mappende Klassen sind ebenfalls generiert (z.B. JAXB per Schema)

Resultat: Compiler Fehler bei Generierung MapStruct MapperLösung:

Weiteres Maven Modul/Artefakt für abhängige KlassenMaven Plugins in unterschiedliche Phasen verlegen

AUTOMAPPING #1Primitive & Wrapper-TypenDatums-Typen

Joda / Java 8 DateTime API KlassenDateCalendar

dest.setLocalDateTime(java.time.LocalDateTime.ofInstant( src.getDate().toInstant(), java.time.ZoneId.systemDefault()) );

AUTOMAPPING #2Collection- & Array-Typen

Mapping nicht nur per SetterAdder (ohne Null Prüfung!)Getter (ohne Null Prüfung!)Setter

Mapping-Methoden zwischen Collection/Array Typen müssen nichtdefiniert werden

MapsJAXB

JAXBElementXMLGregorianCalendar

VERSCHACHTELTE MAPPINGS@Mapping(source = "source.nested.param" target = "param") public Target toTarget(Source source);

inkl. Null-PrüfungenBisher nur für Quell-Parameter

Feature Request für Ziel-Parameter vorhanden

MAPPING VON ENUM-TYPEN@Mapping(source = "FINAL", target = "LAST") TargetEnum map(SourceEnum enum);

String-Match oder explizites Mappingfehlende Mappings zu Zielwerten werden signalisiert

MAPPING DEFAULTS UND KONSTANTEN@Mapping(source = "src", target = "dest", defaultValue = "-1")

Default-Werte als Ersatz bei null-Werten in Quell-Instanzen

@Mapping(constant = "CONST", target = "dest")

String-Konstanten, auf die bei Bedarf Standard-Converter oder andere Mapping Methodenangewendet werden

QUALIFIERUnterscheidung bei identischem Quell- und Zieltyp

Ohne Qualifier Zuordnung nicht eindeutig = Compiler Fehlerspezielle Mappings für ansonsten automatisch generierten Mapping-Code

@Qualifier @Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE) public @interface Marker { }

@Marker public String specialStringMapping(String source) { ... }

@Mapping(source = "src", target = "dest", qualifiedBy = Marker.class) public Target map(Source source);

BEAN-FACTORIESAls Ersatz für Standard-KonstruktorSelbst geschriebene Factory-Methoden (leere Parameterliste,Rückgabetyp = Zieltyp)Generische Factories möglichVerwendung bereits vorhandener (z.B. ObjectFactory von JAXB)@Mapper(uses = CustomFactory.class) public interface Mapper { ... }

public class CustomFactory { public TargetDto createTargetDto() { .... } public <T extends AbstractEntity> T createEntity(@TargetType Class<T> entityClass) { ... } }

MAPPING PER EXPRESSION@Mapping( expression = "java(java.util.UUID.randomUUID().toString())", target = "uniqueId") TargetDto mapTo(SourceEntity source);

Aktuell nur Unterstützung für Java Codeechte Skriptsprachen für zukünftige Versionen geplant

Notwendige Imports können am @Mapper konfiguriert werden

UNTERSCHIEDLICHE QUELLEN@Mappings({ @Mapping(source = "param1.src", target = "dest1"), @Mapping(source = "param2.src", target = "dest2") }) Target map(SourceOne param1, SourceTwo param2);

Aber: Sind nur im Custom-Code / Expressions wiedervendbar

MAPPING ALS UPDATE@Mapper public interface Mapper { Target update(Source src, @MappingTarget Target target); }

mehrere Quellen möglichReturn Type: void oder identisch mit Ziel-Typ

BIDIREKTIONALES MAPPING@Mapper public interface Mapper {

@Mapping(source = "special", target = "destParam") Dto map(Entity source); @InheritInverseConfiguration Entity map(Dto source); }

Ausnahmengeschachtelte Quell-PropertyKonstantenExpressions

CUSTOM MAPPER #1@Mapper(uses = CustomMapper.class) public interface Mapper { ... }

public class CustomMapper { public TargetDto toTarget(Source source) { .... } }

Mapper Klassennotwendig, wenn automatische Mapping-Methoden nichtgeneriert werden können

Einzelnes Element gemappt auf ListeListe mit Inhalten aus verschiedenen QuellenListe komplexer Elemente gemappt auf Liste primitiverWerte

CUSTOM MAPPER #2@Mapper(uses = CustomMapper.class) public interface Mapper { ... }

@Mapper public abstract class CustomMapper { public TargetDto toTarget(Source source) { .... } @Mappings(...) public abstract NestingTargetDto toNestingTarget(NestingSource source); }

Abstrakte KlassenÄhnlich wie Mapper per Interface

abstrakte Methoden werden generiertpublic Methoden enthalten den nicht generierbaren Mapper-Code

KOMPONENTENMODELLEKonfiguration pro MapperMehrere werden bereits unterstützt

Spring (@Component, @Autowired)CDI (@ApplicationScoped, @Inject)JSR 330 (@Named, @Inject)

Alle miteinander verbundenen Mapper müssen dasselbeKomponentenmodell verwendenKeine Abhängigkeit zu MapStruct zur Laufzeit

@Mapper(componentModel = "spring") public interface Mapper { ... }

@Component public class MapperImpl { ... }

KONFIGURATION@MapperConfig(componentModel = ..., uses = ..., ...) public interface DefaultMapperConfig { ... }

@Mapper(config = "DefaultMapperConfig") public interface Mapper { ... }

MAPPING ASPEKTE #1Decorator

angepasster Code für ausgewählte MethodenAber: greift nur, wenn Methode vom eigenen Code oder anderenMapper-Klassen aufgerufen wird

@Mapper @DecoratedWith(MapperDecorator.class) public interface Mapper { ... }

public abstract class MapperDecorator implements Mapper { private final Mapper delegate; public MapperDecorator(Mapper delegate) { this.delegate = delegate; } @Override public Target toTarget(Source source) { Target target = delegate.toTarget(source); // ... custom mapping ... }

MAPPING ASPEKTE #2@BeforeMapping und @AfterMapping

Matching auf Mapping-Methoden anhand Quell- und Zieltypen@Mapper public abstract class Mapper { @BeforeMapping void flushEntity(AbstractEntity entity) { // flush entity to init all fields } @AfterMapping void manuelUpdateMissing(Source src, @MappingTarget Target target) { // e.g. as alternative for expressions / custom mapper } }

EXCEPTIONHANDLINGChecked Exceptions

nur wenn an Mapping Methode deklariertansonsten gewrapped in RuntimeException

RuntimeExceptions ohne Anpassung

FAZIT & AUSBLICKMapStruct ist ready-to-use

umfangreiche Dokumentationlebendige Communitykeine Bugs, die den Einsatz verhndern

Große Funktionsauswahl: Viele Wege führen zum Ziel!So viel wie möglich automatisch mappen

Damit verbundene Checks durch MapStruct minimieren MappingFehler

Das Feature-Set ist noch nicht komplett: Weitere nützliche Featureswerden sicher folgen!

VIELEN DANK FÜR EURE AUFMERKSAMKEIT!