Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MapStruct : enrich mapping annotation to define custom mapper

Tags:

java

mapstruct

Here is my context: I am using byteBuddy to dynamically generate a class that transform an object into another one based on a external configuration. I encountered some issues and I wanted to find an alternative that's how I discovered MapStruct.

So I tried to build simple mapper and I wanted to know if there is the possibility to customize the annotation to add transformation functions. For instance I would like to have:

@Mapping(
    source = "mySourceField", 
    sourceType = "String",
    target = "myTargetField",
    targetType = "Integer",
    transformation = {"toInteger", "toSquare"}
),

And on the mapper implementation I would have something like :

 public TypeDest toSiteCatTag(TypeSrc obj) {

    if ( obj == null ) {

        return null;
    }

    TypeDest objDest = new TypeDest();

    objDest.myTargetField = Formatter.toSquare(
        Formatter.toInteger(obj.mySourceField));

    return objDest;
}

If someone can help me to achieve that I would be grateful and it would save me a lot of time.

Thanks in advance.

like image 943
nbchn Avatar asked Jun 02 '17 17:06

nbchn


People also ask

What is @mapper annotation in spring boot?

A MapStruct mapper is an interface or an abstract class annotated with @Mapper . This special annotation is used by the MapStruct code generator to automatically generate a working implementation of this Java file at build-time.

What is @mapping annotation Java?

@Mapping annotation is used when the field names in the source and destination class differ. For example, The below code would map the fullName field in the source class object to the name field in the destination class object. This is particularly useful when you are mapping objects of two different classes.

How do you map a list of objects using MapStruct?

Using Mapstruct we can map list in similar fashion as we map primitives. To get a list of objects, we should provide a mapper method which can map an object.


1 Answers

If your 2 types TypeDest and TypeSrc are not generated on runtime, i.e. they are your compiled classes, then you can achieve what you want. MapStruct does not work on runtime as it is an Annotation Processor and generates java code. If there are some issues, like you are trying to map non-existing fields or there are ambiguous mapping methods then you will get compile time errors.

It will look something like:

@Mapper
public interface MyMapper {

    @Mapping(source = "mySourceField", target = "myTargetField", qualifiedByName = "myTransformation")// or you can use a custom @Qualifier annotation with qualifiedBy
    TypeDest toSiteCatTag(TypeSrc obj);

    @Named("myTransformation")// or your custom @Qualifier annotation
    default Integer myCustomTransformation(String obj) {
        return Formatter.toSquare(Formatter.toInteger(obj));
    }
}

There is a way to do it without the custom method in the mapper, but you'll need to have a method somewhere that applies the toInteger and then toSquare transformation. If you have a method with the signature Integer squaredString(String obj) in your Formatter.

e.g.

@Qualifier
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface SquaredString {}

public class Formatter {

    @SquaredString// you can also use @Named, this is just as an example
    public static Integer squaredString(String obj) {
        return toSquare(toInteger(obj));
    }
    //your other methods are here as well
}

Then you can do this in your mapper:

@Mapper(uses = { Formatter.class })
public interface MyMapper {

    @Mapping(source = "mySourceField", target = "myTargetField", qualifiedBy = SquaredString.class)
    TypeDest toSiteCatTag(TypeSrc obj);
}

The examples above will only be applied to the specific mapping since qualifedByName / qualified is used. If you want to have a different way of converting a String to Integer, then you can define a method either in your Mapper or in some of the classes in Mapper#uses with the signature Integer convertString(String obj). MapStruct will then delegate the conversion from String to Integer to this method.

You can find more about mapping with qualifiers here in the reference documentation, and here for more information regarding the mapping method resolution.

like image 167
Filip Avatar answered Oct 09 '22 00:10

Filip