I want mapstruct to not use a no-args-constructor even though it exists in my DTOs and Entities (as far as I know jsonb, jpa, jaxb, ... usually require a no-args-constructor):
@RequiredArgsConstructor
@NoArgsConstructor(access = PROTECTED)
@Getter
@Accessors(fluent = true)
public class DepartmentDTO
{
@NonNull @Setter private String name;
public DepartmentDTO(@NonNull DepartmentEntity department, @NonNull MapStructContext context)
{
this(department.name());
log.debug("context {}", context);
}
}
The according DepartmentEntity class is implemented analoguously.
In the mapper interface I implemented methods which are annotated with @ObjectFactory
and call a custom constructor with parameters in the mapper interface itself, but mapstruct still uses the no-args-constructor:
@Mapper
public interface MapStructMapper
{
MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class);
DepartmentEntity map(DepartmentDTO department, MapStructContext context);
DepartmentDTO map(DepartmentEntity department, MapStructContext context);
@ObjectFactory
default DepartmentDTO create(
@NonNull DepartmentEntity department, @NonNull @Context MapStructContext context)
{
return new DepartmentDTO(department, context);
}
@ObjectFactory
default DepartmentEntity create(
@NonNull DepartmentDTO department, @NonNull @Context MapStructContext context)
{
return new DepartmentEntity(department, context);
}
@ToString
class MapStructContext
{
private Map<Object, Object> knownInstances = new IdentityHashMap<Object, Object>();
@BeforeMapping
public <T> T getMappedInstance(Object source, @TargetType Class<T> targetType) {
return (T) knownInstances.get( source );
}
@BeforeMapping
public void storeMappedInstance(Object source, @MappingTarget Object target) {
knownInstances.put( source, target );
}
}
}
The context parameter is used for proper handling of cyclic dependencies in my custom constructors. Here is a part of the generated code:
public DepartmentEntity map(DepartmentDTO department, MapStructMapper.MapStructContext context) {
if (department == null && context == null) {
return null;
} else {
DepartmentEntity departmentEntity = new DepartmentEntity();
return departmentEntity;
}
}
public DepartmentDTO map(DepartmentEntity department, MapStructMapper.MapStructContext context) {
if (department == null && context == null) {
return null;
} else {
DepartmentDTO departmentDTO = new DepartmentDTO();
return departmentDTO;
}
}
What am I missing?
By the way: There is a lombok-generated setter in the DTO and in the Entity (fluent-style). Why is it not called in the generated mapper?
Turned out that I mistakenly thought that @Default is only there to resolve multiple constructors ambiguities. In fact adding @Default to a parameterised constructor lets mapstruct prefer that constructor over an existing no-args-constructor.