Object mapping magic with MapStruct

Posted on 2016-05-31 by

In our projects we often have several Java objects that needs to be mapped to other objects, depending on it’s purpose. One of the most common cases is a domain item that needs a representation on the frontend. Another case is that we want to store data in elasticsearch, but don’t want to use the domain item there as well.

Mapping one object to another is a boring task and could easily lead to mistakes. MapStruct can generate bean mappings at compile-time based on annotations. The generated mapping code uses plain method invocations and thus is fast, type-safe and easy to understand.

How does it work?

Defining a mapping is really simple. First you need to create an interface with the @Mapper annotation. Then add a method that expects the source as parameter and the target as return type.

@Mapper
public interface RelationMapper {

    RelationListDTO relationToListDto(Relation relation);

}

If your object contains another object you will need to define a mapping for that object as well. So if for example our Relation object contains an Address object and the RelationListDto contains a AddressListDto, the mapper has no idea how to map the properties of Address to our AddressListDto.

public class Relation {

    private String name;
    private String email;
    private Address address;

    // getters and setters
}

public class RelationListDTO {
    private String name;
    private String email;
    private AddressListDto address;

    // getters and setters
}

Therefor you need also need to create a mapper for Address and use that mapper in the RelationMapper class.

@Mapper(uses = {AddressMapper.class})
public interface RelationMapper {...}

Instead of defining a separate mapper for Address you can also specify how to map specific properties. Let us change the RelationListDto a little bit so that we only have a few properties instead of an Address object.

public class RelationListDTO {
    private String name;
    private String email;

    private String street;
    private String city;

    // getters and setters
}

Now we can specify how to map these properties with the @Mapping annotation:

@Mapper
public interface RelationMapper {

    @Mapping(source = "address.street", target = "street")
    @Mapping(source = "address.city", target = "city")
    RelationListDTO relationToListDto(Relation relation);
}

Dependency injection

If you use a dependency injection framework you can access the mapper objects via dependency injection as well. Currently they are supporting CDI and Spring Framework.  All you have to do is specify which framework you are using in the @Mapper annotation:

@Mapper(componentModel = "spring")
public interface RelationMapper {} 

Warnings

When you have a mismatch between properties in your objects, MapStruct will generate a warning that will tell you that you have unmapped properties. This can be a completely valid scenario as you maybe not want to map all properties. If this is the case you can add a mapper annotation with the property name and the ignore attribute. This way MapStruct wil skip the property from being mapped.

@Mapping(target = "somePropertyName", ignore = true)

And more..

These examples are just the simple use cases with MapStruct, but there is so much more. Things like implicit type conversions or referencing another mapper in a mapper. You can also customise your mapper with the @BeforeMapping and @AfterMapping which allows you to manipulate the objects before and after the mapper starts.

So it’s not really magic, but I like MapStruct. Just give it a try and maybe it will also help you in your project.


6 Comments

  • Thanks for this nice post and spreading the word on MapStruct! If you have any feature requests, questions etc. just get in touch, we’ll be glad to help.

  • Ruby

    Hi,thanks for the good post. I am using a spring project with maven in eclipse,and trying to use mapstruct. Can you please tell how to generate the implementation for the mapping code? I searched in many places but did not get a proper answer.P leasee could you help? I am badly stuck here. If you could provide a detail step on how to do that,it would be greatly appreciated.
    Thanks,Ruby

    • Roberto van der Linden

      Hi Ruby,

      I have used MapStruct with Gradle. I assume you already tried the configuration as described in the Mapstruct documentation? (http://mapstruct.org/documentation/1.0/reference/html/index.html#setup)

      But you should just add the dependency and the maven processor plugin. If you use java 8, make sure to use the java8 dependency. Configure the maven processor plugin with the mapstruct processor. (example: https://github.com/mapstruct/mapstruct (scroll down))

      Then you can do a maven build, which will trigger the generate-sources step. This should generate the implementations of your mappers in the folder ${project.build.directory}/generated-sources. Maybe you need the tell Eclipse that the generated-sources folder is part of your project? I’m using IntelliJ so I don’t know if that is really necessary.

      Hope this helps!
      Roberto

  • SAME

    Hi,
    I’m using Google Guice as DI framework, I want to use MapStruct as mapping API in our project, but I would like to know before if MapStruct plans to integrate Google Guice as did with Spring, CDI, etc…
    Thank you in advance for information.
    SAME

  • zandolsi

    Hi,
    Thank you for this post.
    Could you show me how to map RelationListDTO to Relation?
    thank you in advance.

  • Cathy

    Thanks for your post. I wonder if you know how to initialize the mapper with customized bean id. Like my original mapper is a spring bean: @component(“a1”). Because I have several mappers with the same class name, I need to distinguish them using bean id.

Leave a Reply

Your email address will not be published. Required fields are marked *