1

Пытаюсь замапить BookDTO.java в Book.java используя MapStruct, но сталкиваюсь с ошибкой.

Вот тут все мои DTO:

BookDTO.java:

@Data
@Document
public class BookDTO {
 @Id
 private UUID id;
 private BookSpecificationsDTO specifications;
} 

BookSpecificationsDTO interface:

public interface BookSpecificationsDTO {
 BookSpecificationsTypeDTO type = null;
 default BookSpecificationsTypeDTO getType() {
 return type;
 }
}

BookSpecificationsTypeDTO:

public enum BookSpecificationsTypeDTO{
 CLASSIC,
 PHANTASY
}

ClassicBookSpecificationDTO:

@Data
public class ClassicBookSpecificationDTO implements BookSpecificationsDTO {
 private BookSpecificationsTypeDTO type = BookSpecificationsTypeDTO.CLASSIC;
 private String genre;
 private int length;
}

PhantasyBookSpecificationDTO:

@Data
 public class PhantasyBookSpecificationDTO implements BookSpecificationsDTO {
 private BookSpecificationsTypeDTO type = BookSpecificationsTypeDTO.PHANTASY;
 private String genre;
 }

Тут все мои модели, которые в моем случае являются идентичными DTO:

Book.java:

@Data
@Document
public class Book {
 @Id
 private UUID id;
 private BookSpecifications specifications;
}

BookSpecifications interface:

public interface BookSpecifications {
 BookSpecificationsType type = null;
 default BookSpecificationsType getType() {
 return type;
 }
}

BookSpecificationsType:

public enum BookSpecificationsType{
 CLASSIC,
 PHANTASY
}

ClassicBookSpecification:

@Data
public class ClassicBookSpecification implements BookSpecifications {
 private BookSpecificationsType type = BookSpecificationsType.CLASSIC;
 private String genre;
 private int length;
}

PhantasyBookSpecification:

@Data
 public class PhantasyBookSpecification implements BookSpecifications {
 private BookSpecificationsType type = BookSpecificationsType.PHANTASY;
 private String genre;
 }

Я хотел замапить свой BookDTO.java в Book.java model. Вот тут mapper class:

@Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface BookMapper {
 
 @Mapping(target = "id", expression = "java(java.util.UUID.randomUUID())")
 Book toBook(BookDTO bookDTO);
}

Вот тут ошибки, кторые я получаю:

java: The return type BookSpecificationsDTO is an abstract class or interface. Provide a non abstract / non interface result type or a factory method.

и

java: Can't map property "BookSpecificationsDTO specifications" to "BookSpecifications specifications". Consider to declare/implement a mapping method: "BookSpecifications map(BookSpecificationsDTO value)".

Насколько я понял, проблема заключается в том, что в классе Book и BookDTO находится поле BookSpecifications, которое является интерфейсом, имеет тип ClassicBookSpecification или PhantasyBookSpecification в зависимости от введенного параметра type, и MapStruct не может замапить поле, которое является итерфейсом. Возможно ли как-то данный маппинг реализовать?

aleksandr barakin
68.8k243 золотых знака82 серебряных знака231 бронзовый знак
задан 5 авг. 2021 в 9:00

1 ответ 1

0

Вообще, так как тут необходима информация времени выполнения, не существует простого и одновременно "чистого" способа решить эту задачу.

"Грязный" способ заключается в использовании рефлексии. Вы можете сделать следующее:

  1. для каждой пары классов-наследников определить свой собственный метод трансформации;
  2. определить общий метод трансформации интерфейсов, в котором в зависимости от типа переданного параметра дергать тот или иной метод из группы (1)

Выглядеть это может примерно так:

@Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface BookMapper {
 
 @Mapping(target = "id", expression = "java(java.util.UUID.randomUUID())")
 Book toBook(BookDTO bookDTO);
 /**
 * ...
 */
 ClassicBookSpecification toBookSpecification(ClassicBookSpecificationDTO specDTO);
 /**
 * ...
 */
 PhantasyBookSpecification toBookSpecification(PhantasyBookSpecification specDTO);
 /**
 * ...
 */
 default BookSpecifications toBookSpecificationGeneric(BookSpecificationsDTO specDTO) {
 if(specDTO == null) {
 return null;
 }
 switch(specDTO.getType()) {
 case CLASSIC:
 return toBookSpecification((ClassicBookSpecification)specDTO);
 case PHANTASY:
 return toBookSpecification((PhantasyBookSpecification)specDTO);
 }
 throw new InvalidArgumentException(specDTO.getType());
 }
}

Второй способ - способ без рефлексии, - это написание маппера-визитера.

public interface BookSpecifications {
 BookSpecificationsType type = null;
 default BookSpecificationsType getType() {
 return type;
 }
 BookSpecification toBookSpecification(BookMapper mapper);
}
// ...
@Data
public class ClassicBookSpecificationDTO implements BookSpecificationsDTO {
 
 // ...
 public BookSpecifications toBookSpecification(BookMapper mapper) {
 return mapper.toBookSpecification(this);
 }
}
// ...
@Data
public class PhantasyBookSpecificationDTO implements BookSpecificationsDTO {
 
 // ...
 public BookSpecifications toBookSpecification(BookMapper mapper) {
 return mapper.toBookSpecification(this);
 }
}
// ...
@Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface BookMapper {
 
 @Mapping(target = "id", expression = "java(java.util.UUID.randomUUID())")
 Book toBook(BookDTO bookDTO);
 /**
 * ...
 */
 ClassicBookSpecification toBookSpecification(ClassicBookSpecificationDTO specDTO);
 /**
 * ...
 */
 PhantasyBookSpecification toBookSpecification(PhantasyBookSpecification specDTO);
 /**
 * ...
 */
 default BookSpecifications toBookSpecificationGeneric(BookSpecificationsDTO specDTO) {
 return specDTO.toBookSpecification(this);
 }
}

Плюсы и минусы у каждого подхода - свои, а применимость должна выбираться исходя из конкретной задачи

ответ дан 9 авг. 2021 в 14:58

Ваш ответ

Черновик сохранён
Черновик удалён

Зарегистрируйтесь или войдите

Регистрация через Google
Регистрация через почту

Отправить без регистрации

Необходима, но никому не показывается

Отправить без регистрации

Необходима, но никому не показывается

Нажимая «Отправить ответ», вы соглашаетесь с условиями пользования и подтверждаете, что прочитали политику конфиденциальности.

Начните задавать вопросы и получать на них ответы

Найдите ответ на свой вопрос, задав его.

Задать вопрос

Изучите связанные вопросы

Посмотрите похожие вопросы с этими метками.