Вопрос или проблема
У меня есть следующие данные (другие поля убраны для краткости и ясности):
@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 вы можете использовать следующий подход:
-
Создание отдельного маппера для
CategoryEntity
:
Начнем с создания маппера, который будет преобразовыватьcategoryName
изProductDto
вCategoryEntity
, что позволит нам использовать правильную категорию без вмешательства в основной маппер дляProductEntity
. -
Настройка
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;
}
Объяснение подхода
-
Использование
CategoryMapper
:
Этот маппер автоматически трансформирует строкуcategoryName
в объектCategoryEntity
. Это реализует принцип единой ответственности, так как мапперы теперь выполняют только свои специфические задачи. -
Сокращение ручного кода:
Благодаря автоматизации, вы избавляетесь от необходимости вручную устанавливатьCategoryEntity
в вашем сервисе. Все преобразования теперь обрабатываются через мапперы. -
Игнорирование id:
Мы игнорируем полеid
вCategoryEntity
при его создании, так как оно не должно быть установлено в результате преобразования из имени категории.
Заключение
Использование MapStruct в вашем проекте позволяет значительно упростить процесс маппинга, повысить читабельность кода и уменьшить вероятность ошибок. Настройка отдельных мапперов для вложенных объектов оставляет ваш код аккуратным и легким для сопровождения.
Таким образом, следуя приведенному примеру, вы можете эффективно реализовать автоматизированный подход к преобразованию DTO в Entity в вашей Java-аппликации.