Was ist MapStruct?

MapStruct ist ein Annotation-basierter Codegenerator zur Abbildung (Mapping) von Java-Beans. Ziel ist es, wiederkehrende und fehleranfällige Konvertierungslogik – etwa zwischen Entitäten und DTOs – deklarativ zu beschreiben und den eigentlichen Mapping-Code zur Compile-Zeit generieren zu lassen.

Im Gegensatz zu Reflection-basierten Frameworks wie ModelMapper oder Dozer erzeugt MapStruct reinen Java-Code ohne Laufzeit-Overhead. Dadurch ist es:

  • Performant (keine Reflection)
  • Typsicher (Fehler fallen beim Kompilieren auf)
  • Debug-freundlich (generierter Code ist lesbar)

Typische Einsatzszenarien:

  • Entity ↔ DTO Mapping
  • API-Modelle ↔ Domänenmodelle
  • Persistenzschicht ↔ Business-Schicht

Grundprinzip von MapStruct

Ein Mapper wird als Interface definiert und mit @Mapper annotiert:

@Mapper
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    UserDto toDto(User user);

    User toEntity(UserDto dto);
}

MapStruct generiert zur Compile-Zeit eine Implementierung wie UserMapperImpl.

Felder mit gleichem Namen und kompatiblem Typ werden automatisch gemappt. Abweichungen können mit @Mapping konfiguriert werden:

@Mapping(source = "firstName", target = "givenName")
UserDto toDto(User user);
Code-Sprache: CSS (css)

Collections in MapStruct

In realen Anwendungen werden selten einzelne Objekte gemappt – häufig sind es Listen oder Sets.

Ein einfaches Beispiel:

List<UserDto> toDtoList(List<User> users);
Code-Sprache: HTML, XML (xml)

MapStruct erkennt automatisch:

  • Es existiert eine toDto(User user)-Methode
  • Also wird diese für jedes Element der Liste verwendet

Der generierte Code sieht sinngemäß so aus:

List<UserDto> list = new ArrayList<>(users.size());
for (User user : users) {
    list.add(toDto(user));
}
Code-Sprache: PHP (php)

Doch was passiert, wenn:

  • mehrere mögliche Mapping-Methoden existieren?
  • spezielle Qualifier verwendet werden?
  • Null-Werte behandelt werden sollen?
  • ein anderes Collection-Format erzeugt werden soll?

Hier kommt @IterableMapping ins Spiel.


@IterableMapping – Kontrolle über Collection-Mappings

@IterableMapping erlaubt es, das Verhalten beim Mapping von Iterables (List, Set, Collection etc.) gezielt zu konfigurieren.

Beispiel:

@IterableMapping(qualifiedByName = "lightweight")
List<UserDto> toDtoList(List<User> users);
Code-Sprache: CSS (css)

Diese Annotation wird verwendet, wenn:

  1. Mehrere Mapping-Methoden für denselben Typ existieren
  2. Eine bestimmte Mapping-Variante erzwungen werden soll
  3. Formatierungen (z. B. bei Datumswerten) notwendig sind
  4. Null-Handling gesteuert werden soll

Beispiel: Mehrere Mapping-Varianten

Angenommen, es gibt zwei Varianten:

@Named("detailed")
UserDto toDetailedDto(User user);

@Named("lightweight")
UserDto toLightweightDto(User user);
Code-Sprache: CSS (css)

Nun kann explizit gesteuert werden, welche Methode für eine Liste verwendet wird:

@IterableMapping(qualifiedByName = "lightweight")
List<UserDto> toLightweightDtoList(List<User> users);
Code-Sprache: CSS (css)

Ohne @IterableMapping würde MapStruct einen Ambiguitätsfehler erzeugen.


Null-Handling mit @IterableMapping

MapStruct bietet verschiedene Strategien für Null-Werte.

@IterableMapping(nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT)
List<UserDto> toDtoList(List<User> users);
Code-Sprache: HTML, XML (xml)

Optionen:

  • RETURN_NULL (Standard)
  • RETURN_DEFAULT → leere Liste statt null

Gerade in REST-APIs ist RETURN_DEFAULT häufig sinnvoll, um NullPointerExceptions zu vermeiden.


Datumsformatierung in Collections

Auch Formatierungen können über @IterableMapping konfiguriert werden:

@IterableMapping(dateFormat = "dd.MM.yyyy")
List<String> mapDates(List<Date> dates);
Code-Sprache: JavaScript (javascript)

Hier wird jedes Date-Element mit dem angegebenen Format in einen String konvertiert.


ElementTargetType festlegen

Wenn der Zieltyp nicht eindeutig ist, kann elementTargetType helfen:

@IterableMapping(elementTargetType = SpecialUserDto.class)
List<UserDto> mapUsers(List<User> users);
Code-Sprache: HTML, XML (xml)

Dies ist insbesondere bei Vererbungshierarchien nützlich.


Zusammenspiel mit @Mapping und @BeanMapping

Wichtig zu verstehen:

  • @Mapping → Konfiguration einzelner Felder
  • @BeanMapping → Konfiguration für Objekt-Mappings
  • @IterableMapping → Konfiguration für Collection-Mappings

Beispiel:

@BeanMapping(ignoreByDefault = true)
@Mapping(target = "id", source = "id")
@Mapping(target = "name", source = "name")
UserDto toSlimDto(User user);

@IterableMapping(qualifiedByName = "toSlimDto")
List<UserDto> toSlimDtoList(List<User> users);
Code-Sprache: CSS (css)

So lassen sich sehr gezielt unterschiedliche Projektionen definieren.


Performance-Aspekte

Da MapStruct reinen Java-Code generiert:

  • Keine Reflection
  • Keine Proxy-Objekte
  • Keine Laufzeit-Analyse

Das macht es besonders attraktiv in:

  • Microservices
  • Hochperformanten REST-APIs
  • Batch-Verarbeitung großer Datenmengen

Gerade bei großen Collections ist dies ein entscheidender Vorteil gegenüber dynamischen Mappern.


Best Practices für @IterableMapping

  1. Explizit qualifizieren, wenn mehrere Mapping-Methoden existieren
  2. NullValueMappingStrategy bewusst setzen
  3. DTO-Varianten sauber benennen
  4. Collection-Mapping-Methoden klar von Einzel-Mappings trennen
  5. Keine unnötigen Konfigurationen – MapStruct arbeitet konventionsbasiert

Fazit

MapStruct bietet eine elegante, performante und typsichere Lösung zur Objektabbildung in Java. Während einfache Collection-Mappings automatisch funktionieren, ermöglicht @IterableMapping eine präzise Steuerung bei komplexeren Anforderungen.

Besonders in größeren Projekten mit:

  • mehreren DTO-Varianten
  • differenzierten API-Projektionen
  • Vererbungsstrukturen
  • spezifischem Null-Handling

ist @IterableMapping ein zentrales Werkzeug, um Mapping-Logik sauber, deklarativ und wartbar zu halten.

Wer strukturierte, explizite und compile-time geprüfte Mappings bevorzugt, findet in MapStruct eine robuste Alternative zu Reflection-basierten Lösungen – mit klaren Vorteilen in Performance und Wartbarkeit.