MapStruct Mapper
Last updated
Was this helpful?
Last updated
Was this helpful?
MapStruct is a code generator that simplifies the implementation of mappings between Java bean types based on a convention-over-configuration approach. It is a tool designed to help developers map data from one Java object to another. It is a popular choice for mapping objects, especially in large-scale enterprise applications, due to its performance and ease of use.
Refer to documentation for more details:
Include the required dependencies in pom.xml file.
It comprises the following artifacts:
org.mapstruct:mapstruct: contains the required annotations such as @Mapping
org.mapstruct:mapstruct-processor: contains the annotation processor which generates mapper implementations
MapStruct processor options -
mapstruct. suppressGeneratorTimestamp
If set to true
, the creation of a time stamp in the @Generated
annotation in the generated mapper classes is suppressed.
false
mapstruct.verbose
If set to true
, MapStruct in which MapStruct logs its major decisions. Note, at the moment of writing in Maven, also showWarnings
needs to be added due to a problem in the maven-compiler-plugin configuration.
false
mapstruct. suppressGeneratorVersionInfoComment
If set to true
, the creation of the comment
attribute in the @Generated
annotation in the generated mapper classes is suppressed. The comment contains information about the version of MapStruct and about the compiler used for the annotation processing.
false
mapstruct.defaultComponentModel
The name of the component model based on which mappers should be generated.
Supported values are:
default
: the mapper uses no component model, instances are typically retrieved via Mappers#getMapper(Class)
cdi
: the generated mapper is an application-scoped (from javax.enterprise.context or jakarta.enterprise.context, depending on which one is available with javax.inject having priority) CDI bean and can be retrieved via @Inject
spring
: the generated mapper is a singleton-scoped Spring bean and can be retrieved via @Autowired
or lombok annotation like @RequiredArgsConstructor
jsr330
: the generated mapper is annotated with {@code @Named} and can be retrieved via @Inject
(from javax.inject or jakarta.inject, depending which one is available with javax.inject having priority), e.g. using Spring
jakarta
: the generated mapper is annotated with {@code @Named} and can be retrieved via @Inject
(from jakarta.inject), e.g. using Spring
jakarta-cdi
: the generated mapper is an application-scoped (from jakarta.enterprise.context) CDI bean and can be retrieved via @Inject
If a component model is given for a specific mapper via @Mapper#componentModel()
, the value from the annotation takes precedence.
default
mapstruct.defaultInjectionStrategy
The type of the injection in mapper via parameter uses
. This is only used on annotated based component models such as CDI, Spring and JSR 330.
Supported values are:
field
: dependencies will be injected in fields
constructor
: will be generated constructor. Dependencies will be injected via constructor.
When CDI componentModel
a default constructor will also be generated. If a injection strategy is given for a specific mapper via @Mapper#injectionStrategy()
, the value from the annotation takes precedence over the option.
field
mapstruct.unmappedTargetPolicy
The default reporting policy to be applied in case an attribute of the target object of a mapping method is not populated with a source value.
Supported values are:
ERROR
: any unmapped target property will cause the mapping code generation to fail
WARN
: any unmapped target property will cause a warning at build time
IGNORE
: unmapped target properties are ignored
If a policy is given for a specific mapper via @Mapper#unmappedTargetPolicy()
, the value from the annotation takes precedence. If a policy is given for a specific bean mapping via @BeanMapping#unmappedTargetPolicy()
, it takes precedence over both @Mapper#unmappedTargetPolicy()
and the option.
WARN
mapstruct.unmappedSourcePolicy
The default reporting policy to be applied in case an attribute of the source object of a mapping method is not populated with a target value.
Supported values are:
ERROR
: any unmapped source property will cause the mapping code generation to fail
WARN
: any unmapped source property will cause a warning at build time
IGNORE
: unmapped source properties are ignored
If a policy is given for a specific mapper via @Mapper#unmappedSourcePolicy()
, the value from the annotation takes precedence. If a policy is given for a specific bean mapping via @BeanMapping#ignoreUnmappedSourceProperties()
, it takes precedence over both @Mapper#unmappedSourcePolicy()
and the option.
WARN
mapstruct. disableBuilders
If set to true
, then MapStruct will not use builder patterns when doing the mapping. This is equivalent to doing @Mapper( builder = @Builder( disableBuilder = true ) )
for all of our mappers.
false
MapStruct provides a set of core features that allow to map properties between different objects seamlessly. These include:
Basic Type Mapping: MapStruct automatically maps properties with the same name and compatible types.
Handling Null Values: By default, MapStruct maps null values, but we can customize this behavior.
Customizing Mappings with @Mapping
: This annotation allows to define how individual fields are mapped.
It is possible to have mapped attribute with the same type or different in the source and target objects. We need to understand how MapStruct deals with such data type conversions.
Mapstruct applies the following conversion automatically.
Between all Java primitive data types and their corresponding wrapper types, e.g. between int
and Integer
, boolean
and Boolean
etc. When converting a wrapper type into the corresponding primitive type a null
check will be performed.
Between all Java primitive number types and the wrapper types, e.g. between int
and long
or byte
and Integer
Between all Java primitive types (including their wrappers) and String
, e.g. between int
and String
or Boolean
and String
. A format string as understood by java.text.DecimalFormat
can be specified.
Between enum
types and String
.
Between big number types (java.math.BigInteger
, java.math.BigDecimal
) and Java primitive types (including their wrappers) as well as String. A format string java.text.DecimalFormat
can be specified.
Between JAXBElement<T>
and T
, List<JAXBElement<T>>
and List<T>
Between java.util.Calendar
/java.util.Date
and JAXB’s XMLGregorianCalendar
Between java.util.Date
/XMLGregorianCalendar
and String
. A format string as understood by java.text.SimpleDateFormat
can be specified via the dateFormat
option
Between Jodas org.joda.time.DateTime
, org.joda.time.LocalDateTime
, org.joda.time.LocalDate
, org.joda.time.LocalTime
and String
. A format string as understood by java.text.SimpleDateFormat
can be specified via the dateFormat
option (see above).
Between Jodas org.joda.time.DateTime
and javax.xml.datatype.XMLGregorianCalendar
, java.util.Calendar
.
Between Jodas org.joda.time.LocalDateTime
, org.joda.time.LocalDate
and javax.xml.datatype.XMLGregorianCalendar
, java.util.Date
.
Between java.time.LocalDate
, java.time.LocalDateTime
and javax.xml.datatype.XMLGregorianCalendar
.
Between java.time.ZonedDateTime
, java.time.LocalDateTime
, java.time.LocalDate
, java.time.LocalTime
from Java 8 Date-Time package and String
. A format string as understood by java.text.SimpleDateFormat
can be specified via the dateFormat
option (see above).
Between java.time.Instant
, java.time.Duration
, java.time.Period
from Java 8 Date-Time package and String
using the parse
method in each class to map from String
and using toString
to map into String
.
Between java.time.ZonedDateTime
from Java 8 Date-Time package and java.util.Date
where, when mapping a ZonedDateTime
from a given Date
, the system default timezone is used.
Between java.time.LocalDateTime
from Java 8 Date-Time package and java.util.Date
where timezone UTC is used as the timezone.
Between java.time.LocalDate
from Java 8 Date-Time package and java.util.Date
/ java.sql.Date
where timezone UTC is used as the timezone.
Between java.time.Instant
from Java 8 Date-Time package and java.util.Date
.
Between java.time.ZonedDateTime
from Java 8 Date-Time package and java.util.Calendar
.
Between java.sql.Date
and java.util.Date
Between java.sql.Time
and java.util.Date
Between java.sql.Timestamp
and java.util.Date
When converting from a String
, omitting Mapping#dateFormat
, it leads to usage of the default pattern and date format symbols for the default locale. An exception to this rule is XmlGregorianCalendar
which results in parsing the String
according to XML Schema.
Between java.util.Currency
and String
.
When converting from a String
, the value needs to be a valid ISO-4217 alphabetic code otherwise an IllegalArgumentException
is thrown.
Between java.util.UUID
and String
.
When converting from a String
, the value needs to be a valid UUID otherwise an IllegalArgumentException
is thrown.
Between String
and StringBuilder
Between java.net.URL
and String
.
When converting from a String
, the value needs to be a valid URL otherwise a MalformedURLException
is thrown.
Suppose, Event has reference to Address object.
MapStruct will generate a method based on the name of the source and target property. In many occasions these names do not match. The ‘.’ notation in an @Mapping
source or target type can be used to control how properties should be mapped when names do not match.
Sometimes, some fields require custom logic. For example, MapStruct will take the entire parameter source
and generate code to call the custom method mapVolume
in order to map the FishTank
object to the target property volume
.
MapStruct can also invoke mapping methods defined in other classes, be it mappers generated by MapStruct or hand-written mapping methods. For eg, when generating code for the implementation of the carToCarDto()
method, MapStruct will look for a method which maps a Date
object into a String, find it on the DateMapper
class and generate an invocation of asString()
for mapping the manufacturingDate
attribute.
Additional context or state information can be passed through generated mapping methods to custom methods with @Context
parameters. Such parameters are passed to other mapping methods, @ObjectFactory
methods or @BeforeMapping
/ @AfterMapping
methods when applicable and can thus be used in custom code.
When mapping a property from one type to another, MapStruct looks for the most specific method which maps the source type into the target type. The method may either be declared on the same mapper interface or on another mapper which is registered via @Mapper#uses()
Default value will be used if the returned value from the @Named method is null.
The @Mapper
annotation causes the MapStruct code generator to create an implementation of the UserMapper
interface during build-time. MapStruct will generate a method based on the name of the source and target property.
Default values and constants are specified as String values. If the source is null, default value will be used.
As per documentation, MapStruct will not validate the java expression at generation-time, but errors will show up in the generated classes during compilation. Fully qualified package name is specified because MapStruct does not take care of the import of the TimeAndFormat
class (unless it’s used otherwise explicitly in the SourceTargetMapper
). This can be resolved by defining imports
on the @Mapper
annotation.
Default expressions are a combination of default values and expressions. They will only be used when the source attribute is null
.
It is used when both input and result types have an inheritance relation. Suppose an Apple
and a Banana
, which are both specializations of Fruit
.
If we would just use a normal mapping both the AppleDto
and the BananaDto
would be made into a Fruit
object, instead of an Apple
and a Banana
object.
When result types have an inheritance relation, selecting either mapping method (@Mapping
) or a factory method (@BeanMapping
) can become ambiguous. Suppose an Apple and a Banana, which are both specializations of Fruit.
As per documentation, when the source argument of the mapping method equals null
then by default null
will be returned.
However, by specifying nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT
on @BeanMapping
, @IterableMapping
, @MapMapping
, or globally on @Mapper
or @MapperConfig
, the mapping result can be altered to return empty default values. This means for:
Bean mappings: an 'empty' target bean will be returned, with the exception of constants and expressions, they will be populated when present.
Iterables / Arrays: an empty iterable will be returned.
Maps: an empty map will be returned.
Some frameworks generate bean properties that have a source presence checker. Often this is in the form of a method hasXYZ
, XYZ
being a property on the source bean in a bean mapping method. MapStruct will call this hasXYZ
instead of performing a null
check when it finds such hasXYZ
method.
A custom condition method is a method that is annotated with org.mapstruct.Condition
and returns boolean
.
e.g. if you only want to map a String property when it is not `null, and it is not empty then you can do something like:
When using this in combination with an update mapping method it will replace the null-check
there, for example:
Calling applications may require handling of exceptions when calling a mapping method. These exceptions could be thrown by hand-written logic and by the generated built-in mapping methods or type-conversions of MapStruct.
MapStruct also supports mapping of immutable types via builders. When performing a mapping MapStruct checks if there is a builder for the type being mapped. This is done via the BuilderProvider
SPI. If a Builder exists for a certain type, then that builder will be used for the mappings.
MapStruct supports using constructors for mapping target types. When doing a mapping MapStruct checks if there is a builder for the type being mapped. If there is no builder, then MapStruct looks for a single accessible constructor.
If a constructor is annotated with an annotation named @Default
it will be used.
If a single public constructor exists then it will be used to construct the object, and the other non public constructors will be ignored.
If a parameterless constructor exists then it will be used to construct the object, and the other constructors will be ignored.
If there are multiple eligible constructors then there will be a compilation error due to ambiguous constructors.
Map
to BeanWe want to map Map<String, ???>
into a specific bean. When a raw map or a map that does not have a String as a key is used, then a warning will be generated.
But with this, we need to repeatedly instantiating new instances if we need to use it in several classes. To fix this, a mapper interface should define a member called INSTANCE
which holds a single instance of the mapper type.
When using Spring Framework, it is recommended to obtain mapper objects via dependency injection and not via the Mappers
class as described above.
When using dependency injection, we can choose between field and constructor injection. This can be done by providing injection strategy via @Mapper
or @MapperConfig
annotation. Constructor injection is recommended to simplify testing. As per documentation, for abstract classes or decorators setter injection should be used.
MapStruct supports the use of meta annotations like @Retention. This allows @Mapping
to be used on other (user defined) annotations for re-use purposes.
For example below. The @ToTransactionHeader
assumes both target beans TransactionEntity
and PaymentEntity
have properties: "id"
, "creationDate"
and "name"
As per documentation, the mapping of collection types (List
, Set
etc.) is done in the same way as mapping bean types, i.e. by defining mapping methods with the required source and target types in a mapper interface
As per documentation, when an iterable or map mapping method declares an interface type as return type, one of its implementation types will be instantiated in the generated code.
Iterable
ArrayList
Collection
ArrayList
List
ArrayList
Set
LinkedHashSet
SortedSet
TreeSet
NavigableSet
TreeSet
Map
LinkedHashMap
SortedMap
TreeMap
NavigableMap
TreeMap
ConcurrentMap
ConcurrentHashMap
ConcurrentNavigableMap
ConcurrentSkipListMap
As per documentation, mapping of java.util.Stream
is done in a similar way as the mapping of collection types, i.e. by defining mapping methods with the required source and target types in a mapper interface.
MapStruct supports enum to a String mapping on the similar lines.
As per documentation, when no @ValueMapping
(s) are defined then each constant from the source enum is mapped to a constant with the same name in the target enum type. However, there are cases where the source enum needs to be transformed before doing the mapping. E.g. a suffix needs to be applied to map from the source into the target enum.
MapStruct provides the following enum name transformation strategies:
suffix - Applies a suffix on the source enum
stripSuffix - Strips a suffix from the source enum
prefix - Applies a prefix on the source enum
stripPrefix - Strips a prefix from the source enum
case - Applies case transformation to the source enum. Supported case transformations are:
upper - Performs upper case transformation to the source enum
lower - Performs lower case transformation to the source enum
capital - Performs capitalisation of the first character of every word in the source enum and everything else to lowercase. A word is split by "_"
By default, the generated code for mapping one bean type into another or updating a bean will call the default constructor to instantiate the target type. Alternatively, Mapstruct supports custom object factories which will be invoked to obtain instances of the target type.
Method-level configuration annotations such as @Mapping
, @BeanMapping
, @IterableMapping
, etc., can be inheritedfrom one mapping method to a similar method using the annotation @InheritConfiguration
In case of bi-directional mappings, e.g. from entity to DTO and from DTO to entity, the mapping rules for the forward method and the reverse method are often similar and can simply be inversed by switching source
and target
MapStruct offers the possibility to define a shared configuration by pointing to a central interface annotated with @MapperConfig
. For a mapper to use the shared configuration, the configuration interface needs to be defined in the @Mapper#config
property. Attributes specified in @Mapper
take precedence over the attributes specified via the referenced configuration class
If we need to apply custom logic before or after certain mapping methods, MapStruct provides two ways for doing so: decorators which allow for a type-safe customization of specific mapping methods and the before-mapping and after-mapping lifecycle methods which allow for a generic customization of mapping methods with given source or target types.
Conditional Mapping is a type of . The difference is that it allows users to write custom condition methods that will be invoked to check if a property needs to be mapped or not.