The problem is that, methods annotated with @AfterMapping
are not called at all. From the testToEntityMapping
it goes to toEntity
method but it doesn't call any of the toEntityAfterMapping()
methods. Why ? Is it possible ? how can I achieve that using MapStruct ?
(Here I have prepared a scenario that is meaningless but it fully captures the essence of my question)
Entity:
public class Ford {
private String color;
private String market;
private int totalWidth;
//getters and setters omitted for brevity
}
Dtos:
public abstract class FordDto {
public String market;
public String color;
//getters and setters omitted for brevity
}
public class EuropeanFordDto extends FordDto{
private int totalWidth;
public int getTotalWidth() {
return totalWidth + 2;//"+2" for EU market
}
//setter omitted for brevity
}
public class AmericanFordDto extends FordDto{
private int totalWidth;
public int getTotalWidth() {
return totalWidth + 1;//"+1" for US market
}
//setter omitted for brevity
}
Mappers:
public abstract class FordMapper<D extends FordDto> {
public Ford toEntity(D dto) {
/* fill in fields common to both ford versions */
final Ford ford = new Ford();
ford.setColor(dto.getColor());
ford.setMarket(dto.getMarket());
return ford;
}
}
@Mapper(componentModel = "spring")
public abstract class EuropeanFordMapper extends FordMapper<EuropeanFordDto> {
@AfterMapping
public void toEntityAfterMapping(final EuropeanFordDto dto, @MappingTarget final Ford entity) {
/* Fill in fields related to european version of the ford */
entity.setTotalWidth(dto.getTotalWidth());
}
}
@Mapper(componentModel = "spring")
public abstract class AmericanFordMapper extends FordMapper<AmericanFordDto> {
@AfterMapping
public void toEntityAfterMapping(final AmericanFordDto dto, @MappingTarget final Ford entity) {
/* Fill in fields related to american version of the ford */
entity.setTotalWidth(dto.getTotalWidth());
}
}
Service:
@Service
public class CarService {
@Autowired
private AmericanFordMapper americanFordMapper;
@Autowired
private EuropeanFordMapper europeanFordMapper;
public void testToEntityMapping(final FordDto dto) {
if (dto instanceof AmericanFordDto) {
americanFordMapper.toEntity((AmericanFordDto) dto);
} else {
europeanFordMapper.toEntity((EuropeanFordDto) dto);
}
}
}
MapStruct is an open-source Java-based code generator which creates code for mapping implementations. It uses annotation-processing to generate mapper class implementations during compilation and greatly reduces the amount of boilerplate code which would regularly be written by hand.
To do this, we use the MapStruct unmappedTargetPolicy to provide our desired behavior when there is no source field for the mapping: ERROR: any unmapped target property will fail the build – this can help us avoid accidentally unmapped fields. WARN: (default) warning messages during the build. IGNORE: no output or ...
MapStruct is a code generator tool that greatly simplifies the implementation of mappings between Java bean types based on a convention over configuration approach. The generated mapping code uses plain method invocations and thus is fast, type-safe, and easy to understand.
By marking it @Context you essentially tell MapStruct to ignore the parameter for mapping, but pass it along in underlying method calls. Also, although MapStruct can handle multiple source arguments, it cannot call multiple source parameter methods to handle nested mappings.
My solution to the same problem was that I forgot to add 'default' keyword to my @AfterMapping method (I used interface). After that method appeared in generated code.
And don't forget to run mvn/gradle clean and compile after making changes.
Ok, it was simpler than I thought it is.
public interface FordMapper<D extends FordDto> {
@Mapping(target = "totalWidth", ignore=true)
public abstract Ford toEntity(D dto);
}
you can even peek in the implementation that is in toEntity()
method, there is a call to toEntityAfterMapping()
, thus everything is correct and in accordance with our desired result.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With