I'm not sure what I'm missing here. My custom logic applies to all String attributes, instead of just one, that I specified for target.
@ApiModel(value="ShopProduct")
@Data
public class ShopProductDTO {
private String description;
private String fullImagePath;
}
@Entity
@Table(name = "shop_product")
public class ShopProduct implements Serializable {
@Column(name = "desc")
private String description;
@Column(name = "thumb_path")
private String thumbPath;
//getters,setters
ImageMapper:
@Component
public class ImagePathMapper {
AppConfig appConfig;
public ImagePathMapper(AppConfig appConfig) {
this.appConfig = appConfig;
}
public String toFullImagePath(String thumbPath){
return appConfig.getImagePath() + thumbPath;
}
}
ShopProduct Mapper:
@Mapper(componentModel = "spring", uses = {ImagePathMapper.class})
@Component
public interface ShopProductMapper {
@Mapping(target = "fullImagePath", source = "thumbPath")
ShopProductDTO shopProductToShopProductDTO(ShopProduct shopProduct);
}
The generated mapstruct class:
ShopProductDTO shopProductDTO = new ShopProductDTO();
shopProductDTO.setDescription( imagePathMapper.toFullImagePath( shopProduct.getName() ) );
shopProductDTO.setFullImagePath( imagePathMapper.toFullImagePath( shopProduct.getThumbPath() ) );
}
Why is the description field also being used with toFullImagePath
?
Shouldn't this @Mapping(target = "fullImagePath", source = "thumbPath")
specify that I want fullImagePath
changed only?
This is not how it works.
When you define a mapping, Mapstruct will try to map every property in the source object to the target object based mainly on property name conventions.
When you define a @Mapping
annotation you are indicating exceptions to this basic mapping.
In your example, when you define this:
@Mapping(target = "fullImagePath", source = "thumbPath")
You are indicating that the property thumbPath
in the source object should be mapped to the fullImagePath
in the destination object: it is necessary because they have different names and otherwise the map will not succeed.
On the other hand, when you define the uses=ImagePathMapper.class
attribute you are instructing Mapstruct to convert every property defined as a certain Class
type, String
in this case, found in the source object: as long as all the fields defined in ShopProduct
are String
s this mapper will be applied for every property.
If you want to apply the mapper just for one field you can invoke a custom mapping method, or use decorators, or the before and after mapping annotations, like in the following example:
@Mapper(componentModel = "spring", uses=AppConfig.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public abstract class ShopProductMapper {
abstract ShopProductDTO shopProductToShopProductDTO(ShopProduct shopProduct);
@Autowired
public void setAppConfig(AppConfig appConfig) {
this.appConfig = appConfig;
}
@AfterMapping
public void toFullImagePath(@MappingTarget ShopProductDTO shopProductDTO, ShopProduct shopProduct) {
String thumbPath = shopProduct.getThumbPath();
if (thumbPath != null) {
shopProductDTO.setFullImagePath(appConfig.getImagePath() + thumbPath);
}
}
}
Please note in the example that you need to do several changes to your mapper in order to work properly with Spring: see this and this.