Spring Boot
Теория: Автоматическая конвертация сущностей в DTO и обратно
Преобразование сущностей в DTO и обратно — это довольно утомительная операция с большим объемом однообразного кода. Например, такого:
Кто-то в своих проектах выбирает такой подход, но есть и альтернатива. Существуют библиотеки, позволяющие автоматизировать конвертацию в обе стороны. Самая популярная из них — это MapStruct. В этом уроке мы подключим ее и научимся использовать.
Установка
Для начала установим MapStruct:
Кроме обычной зависимости, MapStruct требует еще и установки обработчика аннотации. Такие обработчики выполняются во время компиляции и используются для генерации кода. Ниже мы увидим, зачем это делается и как работает.
Использование
MapStruct работает следующим образом. С его помощью создаются специальные мапперы под каждую сущность. Внутри них определяются правила конвертирования в DTO или из DTO в зависимости от потребностей. Дальше эти мапперы используются в нужных местах, сводя преобразования к одной строчке.
Разберем работу библиотеки на примере сущности Post, взятой из предыдущего урока. Чтобы было понятнее, мы начнем с конца. Сначала посмотрим, как использовать мапперы, а затем научимся их писать.
Сущность
Определение сущности выглядит так:
DTO
Для этой сущности реализуем три DTO для разных целей:
- Для создания сущности
- Для обновления сущности
- Для отображения сущности
Реализация выглядит так:
Контроллер
В конце мы напишем маппер, а пока посмотрим, как изменится код контроллера с их использованием. Все преобразование сведется к вызову postMapper.map():
Код контроллера стал предельно простым. Вместо ручного копирования данных здесь используется маппер PostMapper, который содержит:
- Метод
update() - Перегруженный метод
map(), работающий сразу с тремя классами:PostCreateDTOPostDTOPost
Мапперы
Перейдем к мапперам:
Маппер — это абстрактный класс с абстрактными методами для конвертации одних объектов в другие. Класс должен быть помечен аннотацией @Mapper с минимально указанной опцией componentModel = MappingConstants.ComponentModel.SPRING. Расположение класса, название класса и методов не фиксированы — программисты сами определяют, как все это организовать. MapStruct не ограничивает нас в DTO, мы можем преобразовывать объекты любых классов.
В документации MapStruct показаны примеры с интерфейсом, а не абстрактным классом. Технически эта библиотека работает и с интерфейсами, и абстрактными классами. Использовать последние удобнее, потому что в абстрактные классы можно сделать инъекцию зависимостей, если это необходимо.
Во время компиляции происходит генерация конкретных мапперов. Посмотреть исходник этих классов можно в директории build/generated/sources/annotationProcessor/java/main/io/spring/mapper. Это очень упрощает отладку. Код маппера PostMapperImpl созданного на базе абстрактного класса PostMapper:
MapStruct самостоятельно написал тот код, который мы до этого писали руками. Но как он это сделал? MapStruct сравнивает методы обоих классов и автоматически распознает те, что совпадают. Кроме этого, MapStruct автоматически пытается преобразовать типы, если они не совпадают. В большинстве случаев это работает автоматически, но там где нет, всегда есть возможность дописать правила конвертации и преобразования типов. Для примера представим, что поле name переименовали в title. Если нам нужно сохранить внешнее API без изменений, то мы можем определить правила преобразования в маппере:
Аннотация @Mapping позволяет указать правила преобразования свойств. Самый частый случай — это когда имя свойства в исходном объекте не совпадает с целевым. В аннотации source указывает на объект, который передается как параметр, target — это объект, возвращаемый из метода.



