#hibernate #spring-data-jpa #dto #mapstruct
Вопрос:
Учитывая исходный класс, как определено ниже:
class Person{
private String name;
private int age;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "peroson")
List<Phone> phones
// getters and setters
}
и класс телефона, как определено ниже:
class Phone{
private Long id;
private String phoneNumber;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "person")
private Person person;
// getters and setters
}
и объекты DTO:
class PersonDTO{
private String name;
private int age;
List<PhoneDTO> phones
// getters and setters
}
class PhoneDTO{
private Long id;
private String phoneNumber;
private PersonDTO person;
// getters and setters
}
У меня есть интерфейс :
@Mapper(uses={PersonMapper.class})
interface PhoneMapper{
PhoneDTO toDto(Phone source);
Phone toEntity(PhoneDTO source);
}
@Mapper(uses={PhoneMapper.class})
interface PersonMapper{
PersonDTO toDto(Person source);
Person toEntity(PersonDTO source);
}
когда я использую PeronMapper
или PhoneMapper
есть рекурсивный вызов, потому PersonMapper
что использую PhoneMapper
и PersonMapper
использую PhoneMapper
.
как это решить?
Ответ №1:
Существует множество способов избежать рекурсии.
Первый вариант:
Избегайте явного игнорирования в одном из ваших картографов
Второй вариант:
Используйте @Context
и напишите свои собственные воспоминания. В репозитории примеров mapstruct есть пример mapstruct-сопоставление с циклами.
Идея состоит в том, чтобы иметь @Context
public class CycleAvoidingMappingContext {
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 );
}
}
А затем передайте экземпляр этого контекста своим картографам:
@Mapper(uses={PersonMapper.class})
interface PhoneMapper{
PhoneDTO toDto(Phone source, @Context CycleAvoidingContext context);
Phone toEntity(PhoneDTO source, @Context CycleAvoidingContext context);
}
@Mapper(uses={PhoneMapper.class})
interface PersonMapper{
PersonDTO toDto(Person source, @Context CycleAvoidingContext context);
Person toEntity(PersonDTO source, @Context CycleAvoidingContext context);
}
Конечно CycleAvoidingMappingContext
, у них могут быть более конкретные источники, а не просто Object
. У вас может быть один для Person
и PersonDTO
только для примера. От вас зависит, как вы хотите это реализовать.
Редактировать:
Когда у вас есть циклическая зависимость между сопоставителями, единственным решением является использование структуры внедрения зависимостей. Mappers
Фабрика MapStruct по умолчанию не поддерживает циклы.
Комментарии:
1. Это решение для другой проблемы A -> B ->> A. Здесь проблема заключается в экземпляре картографа. Amapper вызывает Bmapper, но Bmapper также использует Amapper => рекурсивное использование. У меня та же проблема, но я не знаю, как ее решить
2. Вы правы @Sum_Pie. Я добавил правку в вопрос, чтобы уточнить, что
3. Хорошо, на самом деле сегодня я решил проблему таким образом:
@Mapper(uses=B_Mapper.class) A_Mapper { } interface B_Mapper{ A_Mapper mapA = Mappers.get(A_Mapper.class) }