All my entities extends a base entity that has a Customer createdBy
and String createdById
field, and some more that work the same. I want to exclude the full Customer
object when data is transferred around.
Some others entities extends another entity that itself extends the base entity, hence the following mappers :
@MapperConfig(componentModel = "spring", mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG)
public interface AuditingEntityMapper<Dto, Entity> {
@Mapping(target = "createdBy", source = "createdById")
@Mapping(target = "lastModifiedBy", source = "lastModifiedById")
Dto toDto(Entity entity);
@Mapping(target = "createdBy", ignore = true)
@Mapping(target = "lastModifiedBy", ignore = true)
Entity toEntity(Dto dto);
}
@MapperConfig(componentModel = "spring", mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG)
public interface ManageableEntityMapper<ManageableDtoType extends ManageableDTO, ManageableType extends Manageable> {
ManageableDtoType toDto(ManageableType manageable);
@Mapping(target = "project", ignore = true)
ManageableType toEntity(ManageableDtoType dto);
}
Then I exploit those two mappers :
@Mapper(config = AuditingEntityMapper.class)
public interface ManageableStatusMapper {
@Mapping(target = "project", ignore = true)
@Mapping(target = "organization", ignore = true)
ManageableStatus toEntity(ManageableStatusDTO manageableStatusDTO);
default ManageableStatus fromId(String id) {
if (id == null) {
return null;
}
ManageableStatus manageableStatus = new ManageableStatus();
manageableStatus.setId(id);
return manageableStatus;
}
}
// Returns this error and therefore others about mapping problems :
// More than one configuration prototype method is applicable. Use @InheritConfiguration to select one of them explicitly: java.lang.Object toDto(java.lang.Object entity), java.lang.Object toEntity(java.lang.Object dto).
@Mapper(config = ManageableEntityMapper.class)
public interface TaskMapper {
@Mapping(target = "timeEntries", ignore = true)
@Mapping(target = "assignments", ignore = true)
Task toEntity(TaskDTO taskDTO);
default Task fromId(String id) {
if (id == null) {
return null;
}
Task task = new Task();
task.setId(id);
return task;
}
}
// Returns this error and one more of the same type :
// Can't map property "java.lang.String createdBy" to "com.acme.domain.Customer createdBy". Consider to declare/implement a mapping method: "com.acme.domain.Customer map(java.lang.String value)".
If my business mapper doesn't need extra mapping it works fine.
Also, MapStruct seems to try to generate an implementation of both @MapperConfig
annotated classes, which generates a lot of errors, but it's probably connected to all those classes connected to them.
How am I supposed to implement this behavior ? The doc about inheritance and/or shared configuration is very confusing to me.
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.
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 ...
During compilation, MapStruct will generate an implementation of this interface. This implementation uses plain Java method invocations for mapping between source and target objects, i.e. no reflection or similar.
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.
This only works when ManageableStatus
extends Entity
and ManageableStatusDTO
extends Dto
.
The error message More than one configuration prototype method is applicable. Use @InheritConfiguration
(where is it in on your example) means that you have to be explicit and possibly indicate the name of the method because more methods qualify.
so:
@Mapper(config = AuditingEntityMapper.class)
public interface ManageableStatusMapper {
@InheritConfiguration // use this one to inherit stuf from the config
@Mapping(target = "project", ignore = true)
@Mapping(target = "organization", ignore = true)
ManageableStatus toEntity(ManageableStatusDTO manageableStatusDTO);
default ManageableStatus fromId(String id) {
if (id == null) {
return null;
}
ManageableStatus manageableStatus = new ManageableStatus();
manageableStatus.setId(id);
return manageableStatus;
}
}
or.. if MapStruct has a conflict
@Mapper(config = AuditingEntityMapper.class)
public interface ManageableStatusMapper {
@InheritConfiguration( name= "toDto" ) // really point to AuditingEntityMapper#toDto
@Mapping(target = "project", ignore = true)
@Mapping(target = "organization", ignore = true)
ManageableStatus toEntity(ManageableStatusDTO manageableStatusDTO);
default ManageableStatus fromId(String id) {
if (id == null) {
return null;
}
ManageableStatus manageableStatus = new ManageableStatus();
manageableStatus.setId(id);
return manageableStatus;
}
}
or do it lazy
@Mapper(config = AuditingEntityMapper.class, mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_ALL_FROM_CONFIG )
public interface ManageableStatusMapper {
// no explicit @InheritConfiguration required, because of AUTO_INHERIT_ALL_FROM_CONFIG
@Mapping(target = "project", ignore = true)
@Mapping(target = "organization", ignore = true)
ManageableStatus toEntity(ManageableStatusDTO manageableStatusDTO);
default ManageableStatus fromId(String id) {
if (id == null) {
return null;
}
ManageableStatus manageableStatus = new ManageableStatus();
manageableStatus.setId(id);
return manageableStatus;
}
}
By the way: there's also @InheritInverseConfigiration
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