Используя Mapstruct в Java, как сопоставить toEntity с внутренним Entity?

Вопрос или проблема

У меня есть следующие данные (другие поля убраны для краткости и ясности):

    @Entity
    class CategoryEntity {
        private Long id;
        private String name;
    }
    
    @Entity
    class ProductEntity {
        private Long id;
        private String name;
        private CategoryEntity category;
    }

Входные данные:

record ProductDto(String name, String categoryName) { }

Код в сервисе выглядит так:

    public ProductEntity processProduct(ProductDto productDto) {
        // найти и проверить, существует ли
        CategoryEntity categoryEntity = categoryService.findCategoryByName(productDto.categoryName());

        ProductEntity productEntity = productMapper.toEntity(productDto);
        productEntity.setCategory(categoryEntity);

        // провести некоторые обработки
        // ... 
        return productEntity;
    }

Как я могу реализовать маппер для преобразования Product dto в сущность и использовать предоставленную сущность Category без ручной установки?

Я пытался передать экземпляр сущности Category в качестве параметра:

@Mapper
interface ProductMapper {
    @Mapping(target = "category", source = "categoryEntity")
    ProductEntity toEntity(ProductDto dto, CategoryEntity categoryEntity);
}

но в этом случае categoryEntity.id используется для установки productEntity.id, что совершенно неправильно.

Это проект Quarkus с MapStruct 1.5.5.

Ответ или решение

Как использовать MapStruct в Java для маппинга DTO в Entity с внутренним Entity

В вашем проекте на Quarkus вы столкнулись с задачей конвертации DTO (Data Transfer Object) в Entity, при этом необходимо обеспечить правильное связывание вложенных объектов, таких как CategoryEntity. В данном ответе мы рассмотрим, как настроить MapStruct для автоматизации этого процесса и обхода проблемы с неправильным использованием идентификаторов.

Описание структуры данных

Ваша структура данных состоит из следующих классов:

@Entity
class CategoryEntity {
    private Long id;
    private String name;
}

@Entity
class ProductEntity {
    private Long id;
    private String name;
    private CategoryEntity category;
}

record ProductDto(String name, String categoryName) { }

Задача

Необходимо создать маппер ProductMapper, который будет конвертировать ProductDto в ProductEntity, установив в него корректный объект category.

Решение

Для решения вашей задачи с использованием MapStruct вы можете использовать следующий подход:

  1. Создание отдельного маппера для CategoryEntity:
    Начнем с создания маппера, который будет преобразовывать categoryName из ProductDto в CategoryEntity, что позволит нам использовать правильную категорию без вмешательства в основной маппер для ProductEntity.

  2. Настройка ProductMapper для автоматического использования категории:
    Мы создадим два маппера: один для CategoryEntity, а другой для ProductEntity. Второй маппер будет использовать первый для извлечения объекта CategoryEntity.

Вот так будет выглядеть код:

@Mapper(componentModel = "cdi")
interface CategoryMapper {
    @Mapping(target = "id", ignore = true) // Игнорируем id, так как он будет установлен при создании CategoryEntity
    CategoryEntity toEntity(String categoryName);
}

@Mapper(componentModel = "cdi", uses = CategoryMapper.class)
interface ProductMapper {
    @Mapping(target = "category", source = "categoryName")
    ProductEntity toEntity(ProductDto dto);
}

Пример использования в сервисе

Используя созданные мапперы, ваш метод processProduct может выглядеть следующим образом:

public ProductEntity processProduct(ProductDto productDto) {
    // Получаем CategoryEntity с помощью маппера
    CategoryEntity categoryEntity = categoryMapper.toEntity(productDto.categoryName());

    // Создаем ProductEntity с помощью основного маппера
    ProductEntity productEntity = productMapper.toEntity(productDto);

    // Устанавливаем правильно полученную категорию
    productEntity.setCategory(categoryEntity);

    // Дополнительная обработка продукта
    // ...
    return productEntity;
}

Объяснение подхода

  1. Использование CategoryMapper:
    Этот маппер автоматически трансформирует строку categoryName в объект CategoryEntity. Это реализует принцип единой ответственности, так как мапперы теперь выполняют только свои специфические задачи.

  2. Сокращение ручного кода:
    Благодаря автоматизации, вы избавляетесь от необходимости вручную устанавливать CategoryEntity в вашем сервисе. Все преобразования теперь обрабатываются через мапперы.

  3. Игнорирование id:
    Мы игнорируем поле id в CategoryEntity при его создании, так как оно не должно быть установлено в результате преобразования из имени категории.

Заключение

Использование MapStruct в вашем проекте позволяет значительно упростить процесс маппинга, повысить читабельность кода и уменьшить вероятность ошибок. Настройка отдельных мапперов для вложенных объектов оставляет ваш код аккуратным и легким для сопровождения.

Таким образом, следуя приведенному примеру, вы можете эффективно реализовать автоматизированный подход к преобразованию DTO в Entity в вашей Java-аппликации.

Оцените материал
Добавить комментарий

Капча загружается...