Christian Guetter
Christian Guetter

Reputation: 111

MapStruct: How to do an @AfterMapping with optional parameters?

I have created an abstract mapper class which has the following to methods:

public abstract Dest mapSourceToDest(Source source);
public abstract Dest mapSourceToDestWithParams(Source source, @Context Param1 param1; @Context Param2 param2);

There are two members in Source which are very expensive to convert when doing the conversion to Dest (in terms of performance).
The first above-mentioned method is supposed to be used when the conversion of all members of source should be done (including the two expensive conversions).
The second method is supposed to be used when the result of the conversion of the two "expensive" members is already known before the conversion from source to dest ist done. In this case, the conversion results for these two members are passed as parameters param1 and param2. These parameters are supposed to be used to avoid the expensive conversion.
The conversion of the two members is done in a method annotated with @AfterMapping. What I basically want to do in the AfterMapping method is the following:

It should be basically like this:

@AfterMapping
protected void setExpensiveParams(Source source, @MappingTarget Dest dest, 
                                   @Context Param1 param1, @Context Param2 param2) {
    if (param1 == null || param2 == null) {
        performExpensiveConversionForParam1AndParam2();
    } else {
       dest.setParam1(param1);
       dest.setParam2(param2); 
    } 
}

This does not work in both cases though as this AfterMapping method only gets called when the two parameters annotated with @Context were provided. This is as expected as the MapStruct documentation states clearly that context parameters like these may not be null.

I could not figure out a way to do perform these two alternative kinds of AfterMappings which depend on the existence of the optional parameters within one mapper class. My workaround for now is to use two mapper classes. I would really like to handle this with one mapper class though.

Upvotes: 1

Views: 8063

Answers (1)

Sjaak
Sjaak

Reputation: 4140

I see 2 options.

Option 1: * You could create your own context class with properties Param1 and Param2 which you either construct with Param1 and Param2 (optimization) or without. This context is passed to only one method. A little known feature of MapStruct is that you can put life cycle methods ( @AfterMapping ) in methods in the context. Depending whether or not the parameters are set you call the expensive mappings from the context in your mapper or implement them in the context

Option 2: Use @MappingTarget and create the target object upfront with the two target parameters already set of you have them already. Nullify the corresponding sources. Use nullvaluepropertymappingstrategy .ignore so that MapStruct skips these when the corresponding source properties are null. You could even implement this in a default method with the signature of your second method.

Upvotes: 3

Related Questions