mapstruct / mapstruct-examples

Examples for using MapStruct
Other
1.28k stars 511 forks source link

CycleAvoidingMappingContext uses Lombok Builder instead of TargetType #119

Closed ffroliva closed 2 years ago

ffroliva commented 3 years ago

Hi,

I have a bidirectional mapping Father <--> Children and I decided to try to use this CycleAvoidingMappingContext from the example: https://github.com/mapstruct/mapstruct-examples/blob/master/mapstruct-mapping-with-cycles/src/main/java/org/mapstruct/example/mapper/CycleAvoidingMappingContext.java

I have a builder class in my Entity and DTO from Lombok but I believe the problem seems to happen also without it.

When the MapperStruct processor created the Mapper Implementation the context object is trying to use the Entity/DTO Builder as the actual targetType which caused compilation error.

I have created a sample project that reproduces the problem. Please have a look.

mapstruct-mapping-with-cycles.zip

I tried to introduce generics to the CycleAvoidingMappingContext to eliminate this problem but it didn't help? When I introduced generics and removed the @TargetType Class<T> targetType from getMappedInstance method the Mapper implementation no longer is added to the Mapper implementation.

public class CycleAvoidingMappingContext<S, T> {
    private final Map<S, T> knownInstances = new IdentityHashMap<S, T>();

    @BeforeMapping
    public T getMappedInstance(S source) {
        return knownInstances.get( source );
    }

    @BeforeMapping
    public void storeMappedInstance(S source, @MappingTarget T target) {
        knownInstances.put( source, target );
    }
}
public class EmployeeMapperImpl implements EmployeeMapper {

    @Override
    public Employee toEmployee(EmployeeDto employeeDto, CycleAvoidingMappingContext<EmployeeDto, Employee> context) {
        if ( employeeDto == null ) {
            return null;
        }

        EmployeeBuilder employee = Employee.builder();

        employee.name( employeeDto.getEmployeeName() );
        employee.reportsTo( toEmployee( employeeDto.getReportsTo(), context ) );
        employee.team( employeeDtoListToEmployeeList( employeeDto.getTeam(), context ) );

        return employee.build();
    }

    @Override
    public EmployeeDto fromEmployee(Employee employee, CycleAvoidingMappingContext<Employee, EmployeeDto> context) {
        if ( employee == null ) {
            return null;
        }

        EmployeeDtoBuilder employeeDto = EmployeeDto.builder();

        employeeDto.employeeName( employee.getName() );
        employeeDto.reportsTo( fromEmployee( employee.getReportsTo(), context ) );
        employeeDto.team( employeeListToEmployeeDtoList( employee.getTeam(), context ) );

        return employeeDto.build();
    }

    protected List<Employee> employeeDtoListToEmployeeList(List<EmployeeDto> list, CycleAvoidingMappingContext<EmployeeDto, Employee> context) {
        if ( list == null ) {
            return null;
        }

        List<Employee> list1 = new ArrayList<Employee>( list.size() );
        for ( EmployeeDto employeeDto : list ) {
            list1.add( toEmployee( employeeDto, context ) );
        }

        return list1;
    }

    protected List<EmployeeDto> employeeListToEmployeeDtoList(List<Employee> list, CycleAvoidingMappingContext<Employee, EmployeeDto> context) {
        if ( list == null ) {
            return null;
        }

        List<EmployeeDto> list1 = new ArrayList<EmployeeDto>( list.size() );
        for ( Employee employee : list ) {
            list1.add( fromEmployee( employee, context ) );
        }

        return list1;
    }
}

Any Ideas?

Regards,

Flávio Oliva

turo-90 commented 3 years ago

Did you fix the problem, I have the same bug in version 14.2.Final In version 1.5.0.Beta1, code compiles, but unit tests indicate StackOverflow

ffroliva commented 3 years ago

For the time being, I simply removed the builder from my class which was unfortunate! That's how I went forward!

This bug from what I could understand needs to be fixed within MapStruct Annotation Processor engine.

As I said, if I introduce generics to CycleAvoidingMappingContext the getMappedInstance method from the context instance no longer gets introduced in the beginning of the converter. That's why you are probably facing this Stackoverflow.

PunKHS commented 2 years ago

I have the same problem in version 1.4.2.Final with lombok 1.18.12

filiphr commented 2 years ago

When you are using builders the lifecycles are treated slightly differently have a look at https://github.com/mapstruct/mapstruct/issues/1454.

For the time being, I simply removed the builder from my class which was unfortunate! That's how I went forward!

You don't have to do that. You can always use something like;

I am going to close this issue since the relevant things around this are in https://github.com/mapstruct/mapstruct/issues/1454