Unravel โบ Java concepts โบ Libraries and tooling
MapStruct and object mapping
Object mapping copies data between similar classes, typically entity to DTO.
Learn these first
At a glance
A form-filling clerk: given a "from" form and a "to" form they copy each field with the same name across, and you only describe the exceptions ("the surname goes into the family name box").
- 1@Mapper(componentModel = "spring") interface OrderMapper { OrderDto toDto(Order o); } is all you writeAt build time MapStruct generates OrderMapperImpl that calls getters and setters.
- 2@Mapping(source = "customer.name", target = "customerName") handles different names or nested paths; @Mapping(target = "id", ignore = true) skips a field; expression = "java(...)" and qualifiedByName call custom logic
- 3@MappingTarget updates an existing object: void update(Dto d, @MappingTarget Order o)@InheritInverseConfiguration creates the reverse mapping.
- 4With componentModel = "spring" the mapper is a Spring bean you inject; otherwise use Mappers.getMapper(OrderMapper.class)
- Because the code is generated and compiled, mapping errors (unmapped target field, type mismatch) are compile-time warnings or errors (unmappedTargetPolicy = ERROR), not run-time surprises
- Hand-written mapping in the service layer is more explicit, MapStruct is less code with more safety: use it where dozens of DTOs repeat the same copy code
- MapStruct is an annotation processor: during javac it reads the interface through the compiler API and writes a Java source file with the mapping methods
import org.mapstruct.*;public class Mapping { static class Order { String id; Customer customer; } static class Customer { String name; } static class OrderDto { String id; String customerName; } @Mapper(componentModel = "spring") interface OrderMapper { @Mapping(source = "customer.name", target = "customerName") OrderDto toDto(Order order); void update(OrderDto dto, @MappingTarget Order order); }}- line 2 uses Mapping, the core of MapStruct and object mapping
- line 6 uses Mapper, the core of MapStruct and object mapping
- line 7 uses Mapper, the core of MapStruct and object mapping
- line 8 uses Mapping, the core of MapStruct and object mapping
- line 10 uses Mapping, the core of MapStruct and object mapping
Read the full explanation (about 2 min)
Object mapping copies data between similar classes, typically entity to DTO. MapStruct generates the plain Java copying code at compile time from an interface; ModelMapper does it with reflection at run time.
A mental model
A form-filling clerk: given a "from" form and a "to" form they copy each field with the same name across, and you only describe the exceptions ("the surname goes into the family name box").
How it works
@Mapper(componentModel = "spring") interface OrderMapper { OrderDto toDto(Order o); } is all you write. At build time MapStruct generates OrderMapperImpl that calls getters and setters.
@Mapping(source = "customer.name", target = "customerName") handles different names or nested paths; @Mapping(target = "id", ignore = true) skips a field; expression = "java(...)" and qualifiedByName call custom logic.
@MappingTarget updates an existing object: void update(Dto d, @MappingTarget Order o). @InheritInverseConfiguration creates the reverse mapping.
With componentModel = "spring" the mapper is a Spring bean you inject; otherwise use Mappers.getMapper(OrderMapper.class).
Going deeper
Because the code is generated and compiled, mapping errors (unmapped target field, type mismatch) are compile-time warnings or errors (unmappedTargetPolicy = ERROR), not run-time surprises. Generated code is also as fast as hand-written.
Hand-written mapping in the service layer is more explicit, MapStruct is less code with more safety: use it where dozens of DTOs repeat the same copy code. Watch for mapping lazy JPA relations: the generated code will call the getter and trigger the loads.
Order matters with Lombok: the annotation processors must be listed in the right order (lombok-mapstruct-binding) or MapStruct sees no getters.
Under the hood
MapStruct is an annotation processor: during javac it reads the interface through the compiler API and writes a Java source file with the mapping methods. Nothing from MapStruct is needed at run time besides the annotations.
Common mistakes
- Not enabling the unmapped-target check.
- Mapping JPA entities with lazy collections into DTOs outside a transaction.
- Missing Lombok and MapStruct processor configuration.
How Unravel recognises it
When you paste code, Unravel looks for these signs of mapstruct and object mapping. Spotting them yourself is a good habit when you read unfamiliar code:
- Annotations:
@Mapper@Mapping@Mappings@MappingTarget@InheritConfiguration@BeanMapping@AfterMapping@BeforeMapping@Named@InheritInverseConfiguration - Types:
MappersModelMapper - Method calls:
Mappers.getMapper()
Part of a learning path
- JDBC and everyday libraries ยท previous: Apache Commons helpers ยท next: JavaBeans, POJOs and BeanUtils
Related concepts
Frequently asked questions
What is MapStruct and object mapping?
Object mapping copies data between similar classes, typically entity to DTO. MapStruct generates the plain Java copying code at compile time from an interface; ModelMapper does it with reflection at run time.
What are the most common mistakes with MapStruct and object mapping?
Not enabling the unmapped-target check. Mapping JPA entities with lazy collections into DTOs outside a transaction. Missing Lombok and MapStruct processor configuration.
How can I see MapStruct and object mapping in real code?
Paste your own Java into Unravel and it will point out where mapstruct and object mapping appears, explain how it works at the detail level you choose, and draw the flow of the code around it.