Speaker: Gerrit Brehmer
Event: W-JAX 2015
07.11.2015
weitere Vorträge von inovex: https://www.inovex.de/de/content-pool/vortraege
Bean Mapping ist kein Thema, mit dem man sich lange aufhalten möchte. Jedoch wird es häufig zum Mappen von internen Klassenstrukturen zu DTOs und wieder zurück benötigt. Wird der Mapping-Code selbst geschrieben, sind Flüchtigkeitsfehler an der Tagesordnung. Alternativen wie Dozer oder BeanUtils nehmen einiges an Arbeit ab, sitzen aber mit Reflection und XML-Konfiguration wie ein Fremdkörper in der Applikation und können die Performance beeinträchtigen.
MapStruct verspricht dagegen Typsicherheit, Schnelligkeit und Flexibilität. Der Vortrag zeigt die Stärken der Java-Only-Lösung, die (fast) den kompletten ungeliebten Boilerplate-Mapping-Code anhand von annotierten Interfaces zur Compile-Zeit generiert. Die übrig gebliebenen Sonderfälle übernimmt man dann gerne selbst.
Infrastructure as (real) Code – Manage your K8s resources with Pulumi
MapStruct - der neue Stern am Bean-Mapping Himmel?!
1.
2. WER ERZÄHLT DIE NÄCHSTEN 55+ MINUTEN
ETWAS?
Gerrit Brehmer, Karlsruhe
Auf der Arbeit: Software-Entwickler/Architekt ( / Projektleiter) bei der
inovex GmbH
In der Freizeit: Smart Home
3. BEAN-MAPPING: EINSATZ
Wann müssen Beans gemappt werden?
Entities zu DTOs
interne zu generierten Klassen (JAXB etc.)
zur Einbindung externer Dienste
standardisierten Schnittstellen
eigenen REST/SOAP Schnittstellen
Allgemein Austauschformate zwischen Schichten und Applikationen
4. BEAN MAPPING: UMSETZUNG
Was benutzen wir dafür bisher?
von Hand geschriebener Mapping-Code
typsicher, schnell
fehleranfällig, aufwendig
Dozer
wenig Aufwand, Rekursives Auto-Mapping
Reflection & XML: langsam
Apache Commons BeanUtils
wenig Aufwand
Reflection, nur nicht komplexe Properties, langsam
7. WIE FUNKTIONIERT MAPSTRUCT
Annotation Processor
Verarbeitet Annotations am eigenen Quellcode
siehe andere bekannte Libs: JPA MetaModel, Lombok, QueryDSL
z.B. mit Hilfe maven-processor-plugin
Generiert Java Quellcode
Zusammenspiel verschiedener FreeMarker Templates
9. INTEGRATION IN ECLIPSE #1
MapStruct eclipse Plugin
noch work-in-progress
Hauptsächlich noch fehlende Features, keine Bugs
Code Completion: JAXB Listen, Nested Properties
Quick Fix: Collection Mappings
10. INTEGRATION IN ECLIPSE #2
m2e APT Plugin
Aktivierung nicht
vergessen!
Inkompatibilitäten mit
Eclipse Compiler pre Mars
(ab 1.0.0.CR2)
Manchmal 'Clean Project'
notwendig
Maven: build-helper-
maven-plugin
automatische Build-Path
Anpassung
11. GENERIERTE ABHÄNGIGKEITEN
Zu mappende Klassen sind ebenfalls generiert (z.B. JAXB per Schema)
Resultat: Compiler Fehler bei Generierung MapStruct Mapper
Lösung:
Weiteres Maven Modul/Artefakt für abhängige Klassen
Maven Plugins in unterschiedliche Phasen verlegen
12. AUTOMAPPING #1
Primitive & Wrapper-Typen
Datums-Typen
Joda / Java 8 DateTime API Klassen
Date
Calendar
dest.setLocalDateTime(java.time.LocalDateTime.ofInstant(
src.getDate().toInstant(),
java.time.ZoneId.systemDefault())
);
13. AUTOMAPPING #2
Collection- & Array-Typen
Mapping nicht nur per Setter
Adder (ohne Null Prüfung!)
Getter (ohne Null Prüfung!)
Setter
Mapping-Methoden zwischen Collection/Array Typen müssen nicht
definiert werden
Maps
JAXB
JAXBElement
XMLGregorianCalendar
14. VERSCHACHTELTE MAPPINGS
@Mapping(source = "source.nested.param" target = "param")
public Target toTarget(Source source);
inkl. Null-Prüfungen
Bisher nur für Quell-Parameter
Feature Request für Ziel-Parameter vorhanden
15. MAPPING VON ENUM-TYPEN
@Mapping(source = "FINAL", target = "LAST")
TargetEnum map(SourceEnum enum);
String-Match oder explizites Mapping
fehlende Mappings zu Zielwerten werden signalisiert
16. 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 Methoden
angewendet werden
17. QUALIFIER
Unterscheidung bei identischem Quell- und Zieltyp
Ohne Qualifier Zuordnung nicht eindeutig = Compiler Fehler
spezielle 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);
18. BEAN-FACTORIES
Als Ersatz für Standard-Konstruktor
Selbst geschriebene Factory-Methoden (leere Parameterliste,
Rückgabetyp = Zieltyp)
Generische Factories möglich
Verwendung 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) {
...
}
}
19. MAPPING PER EXPRESSION
@Mapping(
expression = "java(java.util.UUID.randomUUID().toString())",
target = "uniqueId")
TargetDto mapTo(SourceEntity source);
Aktuell nur Unterstützung für Java Code
echte Skriptsprachen für zukünftige Versionen geplant
Notwendige Imports können am @Mapper konfiguriert werden
20. 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
21. MAPPING ALS UPDATE
@Mapper
public interface Mapper {
Target update(Source src, @MappingTarget Target target);
}
mehrere Quellen möglich
Return Type: voidoder identisch mit Ziel-Typ
23. CUSTOM MAPPER #1
@Mapper(uses = CustomMapper.class)
public interface Mapper {
...
}
public class CustomMapper {
public TargetDto toTarget(Source source) {
....
}
}
Mapper Klassen
notwendig, wenn automatische Mapping-Methoden nicht
generiert werden können
Einzelnes Element gemappt auf Liste
Liste mit Inhalten aus verschiedenen Quellen
Liste komplexer Elemente gemappt auf Liste primitiver
Werte
24. 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 generiert
public Methoden enthalten den nicht generierbaren Mapper-Code
25. KOMPONENTENMODELLE
Konfiguration pro Mapper
Mehrere werden bereits unterstützt
Spring (@Component, @Autowired)
CDI (@ApplicationScoped, @Inject)
JSR 330 (@Named, @Inject)
Alle miteinander verbundenen Mapper müssen dasselbe
Komponentenmodell verwenden
Keine Abhängigkeit zu MapStruct zur Laufzeit
@Mapper(componentModel = "spring")
public interface Mapper {
...
}
@Component
public class MapperImpl {
...
}
30. FAZIT & AUSBLICK
MapStruct ist ready-to-use
umfangreiche Dokumentation
lebendige Community
keine 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 Mapping
Fehler
Das Feature-Set ist noch nicht komplett: Weitere nützliche Features
werden sicher folgen!