1
votes

I have trying to implement MapStruct mapping library. I have made samples and for simple mapping it works fine but I stucked in 1 issue. I have 2 jpa entity classes which have two way relationships. One is in another and another is in one. It creates cyclic mapping issue so MapStruct throws StackOverflow error. I have created minimal code to reproduce the case on github. Sample code:

public class A {
    private Long id;
    private String name;
    private B bData;
    //getter-setter
}

public class B {
    private Long id;
    private String name;
    private Set<A> aData;
    //getter-setter
}

DataGenerator

public class DataGenerator {
    public static A generateData(){
        A a = new A();
        a.setId(1L);
        a.setName("foo");
        B b = new B();
        b.setId(2L);
        b.setName("bar");

        A a2 = new A();
        a2.setId(3L);
        a2.setName("john");
        a2.setbData(b);
        A a3 = new A();
        a3.setId(4L);
        a3.setName("doe");
        a3.setbData(b);

        Set<A> aData = new HashSet<A>();
        aData.add(a2);
        aData.add(a3);
        b.setaData(aData);

        a.setbData(b);
        return a;
    }
}

Mapper

@Mapper
public interface CustomMapper {
    CustomMapper INSTANCE = Mappers.getMapper(CustomMapper.class);
    ADto atoADto(A a);
}

App

public class AppMain {
    public static void main(String[] args) {
        A a = DataGenerator.generateData();
        ADto aDto = CustomMapper.INSTANCE.atoADto(a);
        System.out.println(aDto.getId());
    }
}

Dto/Destination classes are same as original source classes. The main is cyclic/recursive mapping issue which causes stackoverflow error.

Same thing working with spring BeanUtils.copyProperties but I want to implement MapStruct. Currently I am thinking to replace spring BeanUtils with MapStruct.

any suggestions?

2
I have tried the minimal code to reproduce and it works fine. (It prints 1) - i.bondarenko
@i.bondarenko code of master branch is working fine, I have created separate branch to reproduce the issue. - user3145373 ツ

2 Answers

1
votes

There's an example here in the MapStruct repo how to deal with cycles and recursion. Basically you need to keep track of state. The example makes use of a context object to do so.

1
votes

See this mapstruct github issue for the solution, which is to ignore the field causing the recursion. I quote:

"You can achieve it with the @Qualifier. You can use @Named and qualifiedByName, or you can use your own custom @CountryWithoutCities qualifier with qualifiedBy.

Class country{

     String id;
     String name;
     List<City> cities;
}

Class City{

     String id;
     String name;
     Country country;
}

@Mapper(uses = CityMapper.class)
interface CountryMapper {

    @Mapping( target = "cities", qualifiedByName = "noCountry")
    CountryDto toDto(Country country);

    @CountryWithoutCities
    @Mapping( target = "cities", ignore = true)
    CountryDto toDtoWithoutCities(Country country);
}

@Mapper(uses = CountryMapper.class)
interface CityMapper {

    @Named( "noCountry" )
    @Mapping( target = "country", ignore = true)
    CityDto toDtoWithoutCountry(City city);

    @Mapping( target = "country", qualifiedBy= CountryWithoutCities.class)
    CityDto toDto(City city);
}