MapStruct 1.0.0.Beta3 is out with nested properties, qualifiers and more

I’m delighted to announce the release of MapStruct 1.0.0.Beta3.

This version brings the long awaited support for mapping nested source properties, built-in mappings for Java 8 date/time types, fine-grained selection of mapping methods via qualifiers and much more. You can find the complete list of a whopping 46 closed issues here.

Note that existing applications using MapStruct need to adapt to some changes we had to do in order to iron out some glitches from the previous beta releases. These changes are described in more detail at the end of this post.

Before diving into the details, let me say a huge thank you to Sjaak Derksen, Andreas Gudian, Timo Eckhardt and Christian Schuster! Again you guys went far beyond what anyone could have hoped for; this release would not have been possible without you.

Mapping nested properties

One of the most wished-for features in MapStruct was to map attributes from nested elements of a source object (tree) into target objects. That’s finally possible now, using simple “dot paths”, just as you’d expect it:

@Mapper
public interface CustomerMapper {

    @Mapping(source = "address.firstName", target = "firstName")
    @Mapping(source = "address.lastName", target = "lastName")
    CustomerDto customerToDto(Customer customer);
}

This mapping method will map the firstName and lastName attributes from the Address object referenced by the mapped Customer object to the firstName and lastName properties of the target object.

Of course you also can refer to properties nested deeper in the hierarchy, e.g. address.city.name. That’s a great way to flatten and select parts of complex hierarchies e.g. for view objects returned to a client.

Improved built-in mappings

There is now built-in support for the Java 8 time and date types (JSR 310). That means properties of types such as java.time.ZonedDateTime or java.time.LocalDateTime will automatically be mapped to Strings as well as the legacy date types java.util.Calendar and java.util.Date. Also java.util.Calendar will automatically mapped to String and java.util.Date.

Refer to the documentation for the list of all built-in mappings.

Another improvement relates to the mapping of collection-typed properties. Let’s assume the Order class from the previous example had a List<OrderLine> which should be mapped to a list of DTOs in the target object. So far you would have been required to declare a method such as the following on the mapper interface:

List<OrderLineDto> orderLinesToOrderLineDtos(Iterable<OrderLine> orderLines);

That’s not necessary any longer, it will now be added automatically as a private method to the generated mapper class if required.

Qualifiers

Qualifiers provide a way to resolve ambiguities in case several mapping methods are suitable to map a given bean property. E.g. let’s assume you’d have two methods for mapping Dates into Strings in a manually implemented mapper class:

public class DateMapper {

    // returns e.g. 2014-30-11
    String dateToString(Date date) { ... }

    // returns e.g. 2014-30-11 18:16
    String dateToStringWithTime(Date date) { ... }
}

That mapper is used by MapStruct-generated mapper:

@Mapper(uses=DateMapper.class)
public class OrderMapper {

    // Order#date of type Date, OrderDto#date of type String
    OrderDto orderToOrderDto(Order order);
}

In previous releases you’d have gotten an error during generation, as both methods from DateMapper are suitable to map the date property. You can now use qualifiers to resolve that ambiguity and specify which method should be used. To do so, define a simple qualifier annotation type:

@Qualifier
@Target(ElementType.METHOD)
public @interface ShortDate {}

Tag mapping methods using such qualifiers like so:

public class DateMapper {

    @ShortDate
    String dateToString(Date date) { ... }

    @LongDate
    String dateToStringWithTime(Date date) { ... }
}

And specify a qualifier via @Mapping for the concerned property:

@Mapper(uses=DateMapper.class)
public class OrderMapper {

    @Mapping(target="date", qualifiedBy = ShortDate.class)
    OrderDto orderToOrderDto(Order order);
}

This will make sure that the dateToString() method will be invoked to map the order date property. If required, you also could specify several qualifiers via qualifiedBy().

Migration notes

For the sake of increased consistency and better usability, we had to do some changes which may require existing applications which already use MapStruct to be adapted. Most prominently, reverse mapping methods must now be explicitly marked as such using the new @InheritInverseConfiguration annotation:

@Mapper(uses=DateMapper.class)
public class OrderMapper {

    OrderDto orderToOrderDto(Order order);

    @InheritInverseConfiguration
    Order orderDtoToOrder(OrderDto order);
}

That annotation makes it explicit which one is the reverse mapping method and thus should inherit the configuration from its counterpart. You still can add further mappings to the reverse method in order to amend or override the inherited mappings. @InheritInverseConfiguration can also be used to specify the name of the method to inherit from in case several methods qualify as per their source and target types.

Another change affects the existing processor options. In order to avoid conflicts with other annotation processors, these options must now be given using the “mapstruct.” prefix, e.g. “mapstruct.suppressGeneratorTimestamp”.

We have created a wiki page where we’ll collect all incompatible changes for future releases. Of course we’ll try hard to avoid this sort of changes whenever possible.

How do I get it?

You can fetch distribution bundles (ZIP, TAR.GZ) from SourceForge. Alternatively, you can obtain the dependencies from Maven Central. The GAV coordinates are:

The Beta3 release is planned to be the last beta, next will be CR1 (candidate release).

Anything you’d like to propose for inclusion in the 1.0 Final release? Then let us know by commenting below or posting to the mapstruct-users group. Bugs and feature requests can be reported in the issue tracker. And if you’d like to hack on MapStruct yourself, check out the development guide.

comments powered by Disqus