Support for constructors, Gradle incremental compilation and much more: MapStruct 1.4.0.Beta1 is out
It’ my pleasure to announce the first Beta release of MapStruct 1.4.
The new release comes with a whole lot of new functionality, e.g.:
- Making use of constructor arguments when instantiating mapping targets
- Support Gradle incremental annotation processing feature
- Map nested bean properties to current target
- Support value mapping between
String
andEnum
- Support
@Mapping
in meta annotations - User control over mapping features (direct, method, conversion, 2step)
- Support mapping from / to Java 14 records (preview feature)
- New
EnumTransformationStrategy
andEnumNamingStrategy
SPIs
Altogether, not less than 76 issues were fixed for this release.
This would not have been possible without our fantastic community of contributors:
- @Zomzog,
- @xcorail,
- @juliojgd,
- @power721,
- @mattdrees,
- @jakraska,
- @dekelpilli,
- @ttzn,
- @fml2,
- @marceloverdijk,
- @vegemite4me,
- @timjb,
- @sullis,
- @pradzins,
- @seime, and of course seasoned MapStruct hackers Sjaak Derksen, Filip Hrisafov, Christian Bandowski and Andrei Arlou. Thanks to Gunnar Morling and Andreas Gudian for the council and advice on this release.
Thank you everyone for all your hard work!
We know that it took us some time to do this, and we want to thank everyone for the patience.
This release is also the first release which uses our new Gem Tools project instead of Hickory. You can read more about it in the Announcing Gem Tools blog. With this release we also want to introduce a new community driven effort for different MapStruct extensions. More about it below.
Enough of the pep talk, let’s take a closer look at some of the new features!
Making use of constructor arguments when instantiating mapping targets
Making use of constructor arguments when instantiating mapping targets has been one of the oldest and the most requested feature of MapStruct.
We are happy to announce that as of 1.4.0.Beta1 MapStruct has out of the box support for mapping your immutable objects via constructors. This works with:
For more details how the Constructor support works have a look at the Using constructors section of the reference guide.
Gradle incremental annotation processing
Starting From Gradle 4.7, Gradle has supported incremental annotation processing. We are happy to say that starting from 1.4.0.Beta1 MapStruct is an isolating processor. MapStruct will no longer be the reason for triggering an entire recompilation in your Gradle projects.
Map nested bean properties to current target
Using this you can map nested properties to the current target object.
@Mapper public interface CustomerMapper { @Mapping(source = "record", target = ".") Customer customerDtoToCustomer(CustomerDto customerDto); }
The generated code will map every property from CustomerDto.record
to Customer
directly, without need to manually name any of them.
For more details have a look at the Mapping nested bean properties to current target section of the reference guide.
Value mapping between String
and Enum
We’ve had support for mapping between different enums for a while now.
In this release we iterated over that and expanded to support mapping between String
and Enum
.
public enum CheeseType { BRIE, ROQUEFORT } @Mapper public interface CheeseMapper { String cheeseToString(CheeseType cheese); CheeseType stringToCheese(String cheese); }
This would generate an implementation that would use the enum value constants for mapping.
This can be customised by using @ValueMapping
.
For more details have a look at the Mapping Value section of the reference guide.
Support @Mapping
in meta annotations
Starting from 1.4.0.Beta1 there is an experimental support for @Mapping
compositions.
This allows reusing @Mapping
via other annotations.
e.g.
@Retention(RetentionPolicy.CLASS) @Mapping(target = "id", ignore = true) @Mapping(target = "creationDate", expression = "java(new java.util.Date())") @Mapping(target = "name", source = "groupName") public @interface ToEntity { } @Mapper public interface StorageMapper { @ToEntity @Mapping( target = "weightLimit", source = "maxWeight") ShelveEntity map(ShelveDto source); @ToEntity @Mapping( target = "label", source = "designation") BoxEntity map(BoxDto source); }
This is an alternative to using inheritance configuration.
The difference here is that the entities don’t need to have any common hierarchy.
The @Mapping
annotations from @ToEntity
are transformed as if they have been written on the method.
For more details have a look at the Mapping Composition section of the reference guide.
User control over mapping features
MapStruct has an extensive support for implicit type conversions, reusing other mappers, 2 step conversion, etc. However, users were not able to control this. For example it was not possible to easily tell MapStruct to create a Mapper for deep cloning.
In 1.4.0.Beta1 we added a new experimental annotation (@MappingControl
) that can be used to control how the mapping should be performed.
It can enable or disable certain types of mappings.
This can be set over the different mechanisms that we support (@BeanMapping
, @Mapper
, @MapperConfig
, etc.)
The following mapping possibilities exist:
BUILT_IN_CONVERSION
- Allows using the built in MapStruct type conversionsCOMPLEX_MAPPING
- Allows using 2 step mappings (type conversion passed into a mapping method, mapping method passed into a type conversion and mapping method passed into another mapping method)DIRECT
- Allows using direct mapping when the source and target type match. Types from thejava
package are always directly mappedMAPPING_METHOD
- Allows using custom referred or built in mapping methods
Apart from being able to create your own @MappingControl
there are some out of the box controls:
@DeepClone
- Clones a source type to a target type (assuming source and target are of the same type).@NoComplexMapping
- Disables complex mappings, mappings that require 2 mapping means (method, built-in conversion) to constitute a mapping from source to target. This can speed up your compilation if you have mappers with a lot of methods
e.g.
public class ShelveDTO { private CoolBeerDTO coolBeer; public CoolBeerDTO getCoolBeer() { return coolBeer; } public void setCoolBeer(CoolBeerDTO coolBeer) { this.coolBeer = coolBeer; } } public class CoolBeerDTO { private String beerCount; public String getBeerCount() { return beerCount; } public void setBeerCount(String beerCount) { this.beerCount = beerCount; } } @Mapper(mappingControl = DeepClone.class) public interface CloningMapper { FridgeDTO clone(FridgeDTO in); }
In the example above without using @Mapper(mappingControl = DeepClone.class)
MapStruct will create a shallow copy of coolBeer
in FridgeDto
.
New SPIs for Value (Enum) mappings
There have been a lot of requests for supporting different ways of auto mapping enums. For example using a prefix / suffix when mapping between different generated enums.
In this release we’ve reiterated over this and have added new ways of customizing this.
We’ve added a new annotation (@EnumMapping
) for customizing how an enum mapping should look like.
And we added 2 new SPIs:
For more details have a look at the Mapping Value section of the reference guide.
EnumTransformationStrategy SPI
This is an SPI which allows registering custom functions that can be applied when mapping between enums on a case by case basis. The following are the out of the box functions:
suffix
- Applies a suffix on the source enumstripSuffix
- Strips a suffix from the source enumprefix
- Applies a prefix on the source enumstripPrefix
- Strips a prefix from the source enum
e.g.
public enum CheeseType { BRIE, ROQUEFORT } public enum CheeseTypePrefixed { CUSTOM_BRIE, CUSTOM_ROQUEFORT } public enum CheeseTypeSuffixed { BRIE_CHEESE, ROQUEFORT_CHEESE } @Mapper public interface CheeseMapper { @EnumMapping(nameTransformationStrategy = "prefix", configuration = "CUSTOM_") CheeseTypePrefixed map(CheeseType cheese); @InheritInverseConfiguration CheeseType map(CheeseTypePrefixed cheese); @EnumMapping(nameTransformationStrategy = "suffix", configuration = "_CHEESE") CheeseTypeSuffixed map(CheeseType cheese); @InheritInverseConfiguration CheeseType map(CheeseTypeSuffixed cheese); }
EnumNamingStrategy SPI
This SPI is more advanced and is similar to the AccessorNamingStrategy
.
It opens the door for even more customization and providing a way to auto map enums which follow a certain standard.
One example is the Protobuf Enum Style Guide.
Enhancements
- Add “verbose” processor option to print out details if required - Can be activated by using the compiler argument
mapstruct.verbose=true
- Add imports to
@MapperConfig
- Annotation processor option for default injection strategy
- Allow mapping between enum and non enum in the same way as mapping between primitive and objects
- Support for conversion between
java.time.LocalDateTime
andjavax.xml.datatype.XMLGregorianCalendar
MapStruct Community Extensions
Apart from all the new features and enhancements with this release. We would like to introduce a new concept with which we are trying to improve the MapStruct experience.
MapStruct has a lot of functionality and there are a lot of different ways that it can be used. Recently Raimund Klein came up with an interesting idea for providing some extra helpful additions to MapStruct when using the Spring Framework. This idea lead to an initial implementation, which lead to creating the first community extension, the MapStruct Spring Extensions. I would like to thank Raimund for the initiative and taking the lead for this project. There is still no release from this extension, because it is waiting on some enhancements coming from this release.
The idea of these extensions is to have their own releases which are entirely independent of MapStruct core and are driven by the community. The way we envision this is to have different extensions for different technology stacks (e.g. Quarkus, Micronaut, Protobuf, etc.). In case someone has an idea and wants to propose a new extension feel free to reach to us.
Download
This concludes our tour through MapStruct 1.4 Beta1. If you’d like to try out the features described above, you can fetch the new release from Maven Central using the following GAV coordinates:
- Annotation JAR: org.mapstruct:mapstruct:1.4.0.Beta1
- Annotation processor JAR: org.mapstruct:mapstruct-processor:1.4.0.Beta1
Alternatively, you can get ZIP and TAR.GZ distribution bundles - containing all the JARs, documentation etc. - from GitHub.
If you run into any trouble or would like to report a bug, feature request or similar, use the following channels to get in touch:
- Get help in our Gitter room or at the mapstruct-users group
- Report bugs and feature requests via the issue tracker
- Follow @GetMapStruct on Twitter