Expected behavior
Field Optional<Type> field, when Type is annotated with Lombok @Builder, is instantiated using a builder obtained through static factory method.
Actual behavior
Causes compilation error when the target type is not in the same package as mapper, as the mapping implementation tries to instantiate the builder using a constructor which has limited visibility.
Steps to reproduce the problem
Here is a simple test logic:
import lombok.Builder;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
public class Test {
public static void main(String[] args) {
final var source = new EmployeeSource(new PersonSource("John Doe"));
Mappers.getMapper(LocalMapper.class).toTarget(source);
}
@Builder
public record EmployeeTarget (
Optional<PersonTarget> person
) {}
@Builder
public record PersonTarget (
String name
) {}
public record EmployeeSource (
PersonSource person
) {}
public record PersonSource (
String name
) {}
@Mapper
public interface LocalMapper {
EmployeeTarget toTarget(EmployeeSource source);
}
}
This code compiles, but only because the mapper and PersonTarget are in the same package.
If you move the target types to a different package the compilation fails.
This is because the generated implementation is:
protected Optional<PersonTarget> personSourceToPersonTargetOptional(PersonSource personSource) {
// ...
PersonTarget.PersonTargetBuilder personTarget = new PersonTarget.PersonTargetBuilder(); // <-- incorrect
// ...
}
But if you make the target field non-optional:
@Builder
public record EmployeeTarget (
PersonTarget person
) {}
the generated implementation uses the builder correctly:
protected PersonTarget personSourceToPersonTarget(PersonSource personSource) {
// ...
PersonTarget.PersonTargetBuilder personTarget = PersonTarget.builder(); // <-- correct
// ...
}
So this problem is specific to Optional mapping, so this super recent functionality added in the tested version.
Workaround
Disable builder for mapping logic:
@Mapper(builder = @org.mapstruct.Builder(disableBuilder = true))
You can still use builders in your logic, simply the generated implementation will omit it.
MapStruct Version
1.7.0.Beta1
Expected behavior
Field
Optional<Type> field, whenTypeis annotated with Lombok@Builder, is instantiated using a builder obtained through static factory method.Actual behavior
Causes compilation error when the target type is not in the same package as mapper, as the mapping implementation tries to instantiate the builder using a constructor which has limited visibility.
Steps to reproduce the problem
Here is a simple test logic:
This code compiles, but only because the mapper and
PersonTargetare in the same package.If you move the target types to a different package the compilation fails.
This is because the generated implementation is:
But if you make the target field non-optional:
the generated implementation uses the builder correctly:
So this problem is specific to
Optionalmapping, so this super recent functionality added in the tested version.Workaround
Disable builder for mapping logic:
You can still use builders in your logic, simply the generated implementation will omit it.
MapStruct Version
1.7.0.Beta1