Frequently Asked Questions (FAQ)

How is MapStruct different from other bean mapping tools?

Unlike most other bean mapping tools, MapStruct doesn’t work at runtime but is a compile-time code generator.

Generating mapping code at build time has many advantages:

  • Early feedback about erroneous or incomplete mappings, in command line builds as well as within your IDE
  • Excellent performance, as no reflection or byte code generation at runtime is needed; the generated code contains plain method invocations, just as if the mapper was hand-written
  • No runtime dependencies, making MapStruct a great solution for Android applications
  • The generated code is easy to understand (and debug, if ever needed)
  • The optional Eclipse plug-in assists you when writing mappings, e.g. by auto-completion

Can I use MapStruct within Eclipse?

Yes.

Check out the set-up instructions for Eclipse. There is also a work-in-progress Eclipse plug-in which facilitates the definition of mapper interfaces with auto-completion and some quick fixes.

Can I use MapStruct together with Project Lombok?

Yes, as of MapStruct 1.2.0.Beta1 and Lombok 1.16.14.

Project Lombok is an annotation processor that (amongst other things) adds getters and setters to the AST (abstract syntax tree) of compiled bean classes. AST modifications are not foreseen by Java annotation processing API, so quite some trickery was required within Lombok as well MapStruct to make both of them work together. Essentially, MapStruct will wait until Lombok has done all its amendments before generating mapper classes for Lombok-enhanced beans.

An example for using the two projects together can be found here.

If you are on an older version of MapStruct or Lombok, the solution is to put the JavaBeans to be amended by Lombok and the mapper interfaces to be processed by MapStruct into two separate modules of your project. Then Lombok will run in the compilation of the first module, causing the bean classes to be complete when MapStruct runs during the compilation of the second module.

Why does @Named not work?

Check out that you are actually using org.mapstruct.Named and not javax.inject.Named.

Why do I get this error: Could not retrieve @Mapper annotation during compilation?

This can happen if you are using mapstruct-jdk8 and some other dependency is using an older version of mapstruct. To solve the problem find the dependency that is using mapstruct and exclude it.

A known dependency that uses mapstruct and has this problem is springfox-swagger2.

For Maven you need to exclude it like:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>${swagger2.version}</version>
    <exclusions>
        <exclusion>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
        </exclusion>
    </exclusions>
</dependency>

For Gradle you need to exclude it like:

compile('io.springfox:springfox-swagger2:${swagger2.version}') {
    exclude group: 'org.mapstruct', module: 'mapstruct'
}

Why is it not possible for MapStruct to generate implementations for Iterable, Stream and Map Types from update (`@MappingTarget`) methods?

Consider this (when thinking what MapStruct should do for updating collections in general):

  • What if there’s no match: should the non-matching elements be removed?
  • Should the non matching source elements be added?
  • What exactly constitutes to a match: equals? hashcode? comparator==0?
  • Can there be more than one match (Lists, but also depending on what is considered a match.)
  • How should the resulting collection be sorted?
  • Should a newly created object be added to a persistence context?
  • What about JPA child-parent relations?

About the latter one, many IDEs also generates remove methods. So should MapStruct call these in the light of the above?

At this moment it works like this: whenever the user wants a collection update method, MapStruct generates a regular call to element mappings (in stead of an update call), because it is the only sensible thing to do. All the remainder is highly dependent on the use-case.

How do I handle null properties in the source bean?

The strategies were developed over time and hence the naming / behavior deserves attention in future versions of MapStruct to getter better allignment. This would introduce backward incompatibillties, so we cannot not do this in the 1.x versions of MapStruct.

The following table expresses when the current strategies apply:

source property source bean direct mapping update mapping (@MappingTarget)
NullValueCheckStrategy x x x
NullValuePropertyMappingStrategy x x
@Mapping#defaultValue x x x
NullValueMappingStrategy x x x

We’ve noticed a common mistake that NullValuePropertyMappingStrategy is used in relation to direct mapping, which is understandble because of its naming. So lets look at the following example:

@Mapper
public interface MyMapper {

    Bar map( Foo source );    
}

public class Foo {

    private String string;
    // setters/getters
}

public class Bar {

    private String string;
    // setters/getters
}

generates:

public class MyMapperImpl implements MyMapper {

    @Override
    public Bar map(Foo source) {
        if ( source == null ) {
            return null;
        }

        Bar bar = new Bar();

        bar.setString( source.getString() );

        return bar;
    }
}

So, lets look what it would mean if NullValuePropertyMappingStrategy could be applied to direct mappings:

  • SET_TO_NULL. What does this mean when the source.string is null? It would set the source to null when null. But that is what it already does above without any strategy.

  • IGNORE. When source.string is null, it would ignore setting the target. But that can be achieved with NullValueCheckStrategy.ALWAYS. Look at the example below:

@Mapper( nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS )
public interface MyMapper {

    Bar map( Foo source );
}

generates:

public class MyMapperImpl implements MyMapper {

    @Override
    public Bar map(Foo source) {
        if ( source == null ) {
            return null;
        }

        Bar bar = new Bar();

        if ( source.getString() != null ) {
            bar.setString( source.getString() );
        }

        return bar;
    }
}
  • SET_TO_DEFAULT. SET_TO_DEFAULT is not covered by other cases in direct mapping, but can be achieved as well. Lets asume we would like to set an empty String as default value on null source. There are 2 possibilities:
@Mapper
public interface MyMapper {

    @Mapping( target = "string", defaultValue = "" )
    Bar map( Foo source );
}

generates:

public class MyMapperImpl implements MyMapper {

    @Override
    public Bar map(Foo source) {
        if ( source == null ) {
            return null;
        }

        Bar bar = new Bar();

        if ( source.getString() != null ) {
            bar.setString( source.getString() );
        }
        else {
            bar.setString( "" );
        }

        return bar;
    }
}

but has the drawback that this needs to be done for each property.

The other option would be to create the target object with a default property value, either inside the target (Bar) during construction -if you have control over the target beans-, or via an object factory method:

@Mapper( nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
public interface MyMapper {

    Bar map( Foo source );

    @ObjectFactory
    default Bar create() {
        Bar bar = new Bar();
        bar.setString( "" );
        return bar;
    }
}

Finally overview below shows on what level a strategy can be applied:

@MapperConfig @Mapper @BeanMapping @Mapping
NullValueCheckStrategy x x x x
NullValuePropertyMappingStrategy x x x x
@Mapping#defaultValue x
NullValueMappingStrategy x x x

More detailed information can be found in the reference guide.