SpringBoot MapStruct Сопоставление объекта с DTO с объектами отношений

Я использую библиотеку MapStruct для сопоставления объектов, и мне нужно перевести из сущности в dto, но есть проблема, например, в том, что сущность хранит типы животных в виде списка объектов, а dto хранит массив с идентификаторы этих типов. как это можно сделать правильно? Я сделал это, но только вручную, используя циклы. MapStruct для меня что-то новое, поэтому много чего непонятно, надеюсь на вашу помощь, спасибо!

У меня есть этот класс:

@FieldDefaults(level = AccessLevel.PRIVATE)
@Table(name = "animal")
public class Animal {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;

    @NotEmpty
    @MinCollectionSize
    @ElementOfCollectionNotNull
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "animal_types",
    joinColumns = {@JoinColumn(name = "animal_id",referencedColumnName = "id")},
    inverseJoinColumns = {@JoinColumn(name = "type_id",referencedColumnName = "id")})
    List<AnimalType> animalTypes;

    @NotNull
    @Min(1)
    Float weight;

    @NotNull
    @Min(1)
    Float length;

    @NotNull
    @Min(1)
    Float height;

    @NotNull
    @NotGender
    AnimalGender gender;

    AnimalLifeStatus lifeStatus;

    TimestampWithTimeZoneJdbcType chippingDateTime;

    @NotNull
    @ManyToOne
    @JoinColumn(name = "chipper_id")
    Account chipperId;

    @NotNull
    @ManyToOne
    @JoinColumn(name = "chipping_location_id")
    Location chippingLocationId;

    @OneToMany(mappedBy = "animal")
    List<AnimalLocation> visitedLocations;

    TimestampWithTimeZoneJdbcType deathDateTime;

}

И мне нужно это сопоставление Entity с этим DTO:

@FieldDefaults(level = AccessLevel.PRIVATE)
public class AnimalResponse {
    Long id;
    Long[] animalTypes;
    Float weight;
    Float length;
    Float height;
    AnimalGender gender;
    AnimalLifeStatus lifeStatus;
    TimestampWithTimeZoneJdbcType chippingDateTime;
    Integer chippedId;
    Long chippingLocationId;
    Long[] visitedLocations;
    TimestampWithTimeZoneJdbcType deathDateTime;
}

Это мои интерфейсы Mapper:

public interface BaseMapper<ENTITY, DTO> {

    DTO toDto(ENTITY entity);
    ENTITY toEntity(DTO dto);
    List<DTO> toDtoList(List<ENTITY> entityList);
    List<ENTITY> toEntityList(List<DTO> dtoList);

}
@Mapper
public interface AnimalMapper extends BaseMapper<Animal, AnimalResponse> {
    AnimalMapper INSTANCE = Mappers.getMapper(AnimalMapper.class);
}

Отвечает ли это на ваш вопрос? Преобразование списка объектов в список длинных идентификаторов с помощью Mapstruct

Paul Marcelin Bejan 15.02.2023 09:25

Не List<Long>, а массив длинных слов типа long[]

Hankie-2 15.02.2023 15:36
Как сделать движок для футбольного матча? (простой вариант)
Как сделать движок для футбольного матча? (простой вариант)
Футбол. Для многих людей, живущих на земле, эта игра - больше, чем просто спорт. И эти люди всегда мечтают стать футболистом или менеджером. Но, к...
Знайте свои исключения!
Знайте свои исключения!
В Java исключение - это событие, возникающее во время выполнения программы, которое нарушает нормальный ход выполнения инструкций программы. Когда...
Лучшая компания по разработке спортивных приложений
Лучшая компания по разработке спортивных приложений
Ищете лучшую компанию по разработке спортивных приложений? Этот список, несомненно, облегчит вашу работу!
Blibli Automation Journey - Как захватить сетевой трафик с помощью утилиты HAR в Selenium 4
Blibli Automation Journey - Как захватить сетевой трафик с помощью утилиты HAR в Selenium 4
Если вы являетесь веб-разработчиком или тестировщиком, вы можете быть знакомы с Selenium, популярным инструментом для автоматизации работы...
Фото ️🔁 Radek Jedynak 🔃 on ️🔁 Unsplash 🔃
Фото ️🔁 Radek Jedynak 🔃 on ️🔁 Unsplash 🔃
Что такое Java 8 Streams API? Java 8 Stream API
Деревья поиска (Алгоритм4 Заметки к учебнику)
Деревья поиска (Алгоритм4 Заметки к учебнику)
(1) Двоичные деревья поиска: среднее lgN, наихудшее N для вставки и поиска.
0
2
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
@Mapper
public interface AnimalMapper extends BaseMapper<Animal, AnimalResponse> {
    
    AnimalMapper INSTANCE = Mappers.getMapper(AnimalMapper.class);
    
    @Override
    @Named("toDto")
    @Mapping(source = "animalTypes", target = "animalTypes", qualifiedByName = "toArray")
    AnimalResponse toDto(Animal entity);
    
    @Override
    @IterableMapping(qualifiedByName = "toDto")
    List<AnimalResponse> toDtoList(List<Animal> entityList);
    
    @Override
    @Named("toEntity")
    @Mapping(source = "animalTypes", target = "animalTypes", qualifiedByName = "toArrayList")
    Animal toEntity(AnimalResponse dto);
    
    @IterableMapping(qualifiedByName = "toEntity")
    List<Animal> toEntityList(List<AnimalResponse> dtoList);
    
    @Named("toArray") 
    default Long[] toArray(List<AnimalType> animalTypes) { 
        return animalTypes.stream()
                          .map(AnimalType::getId)
                          .toArray(size -> new Long[size]);
    }
    
    @Named("toArrayList") 
    default List<AnimalType> toArrayList(Long[] animalTypes) { 
        return Arrays.stream(animalTypes)
                     .map(AnimalType::new) // new object just for example purposes, you can instead call the repository findById
                     .collect(Collectors.toList());
    }
    
}

Со следующим животным:

Animal(id=1, animalTypes=[AnimalType(id=1), AnimalType(id=2), AnimalType(id=3)])

Сгенерированный ответ, вызвав toDto

AnimalResponse response = AnimalMapper.INSTANCE.toDto(animal);

Будет:

AnimalResponse(id=1, animalTypes=[1, 2, 3])

И еще вопрос, если у меня есть другой массив длинных, таких как long[] VisitLocation, мне нужно использовать @Mapping(source = "visitedLocations", target = "visitedLocations",qualifiedByName = "toArray")?

Hankie-2 19.02.2023 17:35

Массивы берут длинное значение из другого объекта, поэтому нет, вы не можете использовать один и тот же метод. Вам нужно объявить новый с другим именем и использовать его для атрибута «qualifiedByName».

Paul Marcelin Bejan 20.02.2023 08:02

Если вы не хотите использовать Java Reflection

Paul Marcelin Bejan 20.02.2023 08:09

Посещенные локации берутся из одного и того же объекта

Hankie-2 20.02.2023 15:40

Я имею в виду длинные значения, для AnimalTypes длинное значение — это идентификатор AnimalType, а для VisitLocations длинное значение — это идентификатор AnimalLocation. Таким образом, единственный способ повторно использовать метод «toArray» — это реализовать его с помощью Java Reflection или изменить сущности, чтобы расширить класс или реализовать интерфейс с общим геттером, чтобы можно было использовать Java Generics.

Paul Marcelin Bejan 21.02.2023 01:10

Другие вопросы по теме