r3nw0rp
r3nw0rp

Reputation: 71

Java Object mapping framework working with builder pattern

Is there any class mapping framework which works with builders? I would like to keep some of my classes immutable and avoid multiple constructors - the Builder Pattern comes to the rescue. However I can't any mapping framework which would use builder automatically instead of getters/setters.

Upvotes: 1

Views: 8894

Answers (3)

abdella
abdella

Reputation: 734

Uing Lombok and ModelMapper configure as:

ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration()
           .setFieldMatchingEnabled(true)
           .setFieldAccessLevel(AccessLevel.PRIVATE);

By default ModelMapper uses only public setter method to map. When the class annotated with Lombok builder annotation it made the setter method as private. So to allow the ModelMapper to use the private setter method we need to add the above configureation.

OR

    Configuration builderConfiguration = modelMapper.getConfiguration().copy()
    .setDestinationNameTransformer(NameTransformers.builder())
    .setDestinationNamingConvention(NamingConventions.builder());

    modelMapper.createTypeMap(MyEntity.class, MyDto.MyDtoBuilder.class, builderConfiguration);

where MyEnity class is:

   @Data
    private static class MyEntity {
        private Long id;
        private String name;
        private String value;
    }

and builder class is:

    @Data
    @Builder
    private static class MyDto {
        private final Long id;
        private final String name;
        private final String value;
    }

click here for detail

Upvotes: 1

Filip
Filip

Reputation: 21393

This can be easily done with MapStruct and using a custom naming strategy for builders.

Have a look here in the documentation how to use Custom Accessor naming strategy.

Your mappings then need to look like:

@Mapper
public interface MyMapper {

    default Immutable map(Source source) {
        return mapToBuilder(source).build();
    }

    Immutable.Builder mapToBuilder(Source source);
}

Within MapStruct we are already working on a feature that would support out of the box support for builders. You can follow this issue for more details.

Update

MapStruct now (since 1.3.0.Beta1) has out of the box support for Immutables. This means that the mapper before can be written like:

@Mapper
public interface MyMapper {

    Immutable map(Source source);
}

The assumption is that there is a public static method without parameters in Immutable that returns the builder

Upvotes: 2

ther
ther

Reputation: 888

I got the following working with Lombok and ModelMapper. See: http://modelmapper.org/getting-started/

public class MyService {
     private ModelMapper modelMapper;

     public MyService(){
         this.modelMapper = new ModelMapper();
         this.modelMapper.getConfiguration()
            .setMatchingStrategy(MatchingStrategies.STRICT)
            .setDestinationNamingConvention(LombokBuilderNamingConvention.INSTANCE)
            .setDestinationNameTransformer(LombokBuilderNameTransformer.INSTANCE);
     }

     public OutputDTO aMethod(final InputDTO input){
          return modelMapper.map(input, OutputDTO.OutputDTOBuilder.class).build(); 
     }
}

Where LombokBuilderNamingConvention is:

import org.modelmapper.spi.NamingConvention;
import org.modelmapper.spi.PropertyType;

public class LombokBuilderNamingConvention implements NamingConvention {

    public static LombokBuilderNamingConvention INSTANCE = new LombokBuilderNamingConvention();

    @Override
    public boolean applies(String propertyName, PropertyType propertyType) {
        return PropertyType.METHOD.equals(propertyType);
    }

    @Override
    public String toString() {
        return "Lombok @Builder Naming Convention";
    }

}

And LombokBuilderNameTransformer is:

import org.modelmapper.spi.NameTransformer;
import org.modelmapper.spi.NameableType;


public class LombokBuilderNameTransformer implements NameTransformer {

    public static final NameTransformer INSTANCE = new LombokBuilderNameTransformer();

    @Override
    public String transform(final String name, final NameableType nameableType) {
        return Strings.decapitalize(name);
    }

    @Override
    public String toString() {
        return "Lombok @Builder Mutator";
    }
}

And OutputDTO can look like:

@Builder // Has .builder() static method
@Value // Thus immutable
public class OutputDTO {
   private String foo;
   private int bar;
} 

Upvotes: 3

Related Questions