Mapper для DTO

  • Рубрика записи:Spring back-end

Есть такая штука, как DTO – Data Transfer Object. Это такие объекты для транспортировки данных. Но сами собой они не рождаются. Нужно конвертировать объект сущности (entity) в объект DTO. И наоборот. Как?

Как раз-таки конвертацией и занимаются mapper-ы. В контексте Spring-приложения это конвертация entity в DTO и обратно. Например, у вас есть сущность Book, представляющая книгу в базе данных, и объект BookDto, используемый для передачи данных о книге между клиентом и сервером. Маппер позволит вам легко преобразовывать данные из Book в BookDto и обратно. Приступим!

DTO-классы сущностей User и Post

Раньше мы с вами создали таблицы в БД, users и posts. Затем мы создали классы сущностей (entity), User и Post. После чего занялись созданием DTO для них – UserDto и PostDto. И вот теперь мы напишем для них мапперы.

Но для начала вспомним, как выглядели DTO-шки для User и Post. Я их создавала с участием библиотеки Lombok. Она позволила не писать геттеры, сеттеры и класс-строитель Builder, а просто указать три аннотации: @Getter, @Setter и @Builder. Удобно!

Класс UserDto:

Класс PostDto:

Вот такие краткие и лаконичные классы получились. Приступим к написанию мапперов.

Ручное создание mapper-а

Конвертеры можно написать самостоятельно. В таком случае, нужно будет просто вручную создавать новые объекты и передавать туда параметры старых. Вот, как будет выглядеть “ручной” mapper для конвертации сущности User:

Как видите, ничего сверхъестественного. Просто переносим информацию из одних объектов в другие. Метод toDtos(), конвертирующий целый список сущностей User в DTO, просто использует метод toDto() для каждого из элементов. Как правило, конвертировать список DTO-шек в сущности не приходится.

Также класс UserMapper помечен как @Component, чтобы мы могли его использовать в других местах легко и непринуждённо. То есть позволяет Spring управлять его жизненным циклом и внедрять его в другие компоненты.

А вот и mapper для сущности Post:

PostMapper использует UserMapper для конвертации своего пользователя. Поэтому мы внедряем UserMapper внутрь PostMapper с помощью аннотации @Autowired.

Однако, как можно заметить, здесь есть повторяющийся, шаблонный код. И всё бы ничего, но что, если у сущностей не по 3-5, а по 10 полей? И самих сущностей за 20. Для каждой из них писать такие мапперы? Есть решение получше – библиотеки!

Создание mapper-а с помощью библиотеки Mapstruct

Библиотека MapStruct предлагает автоматическую генерацию мапперов на основе интерфейсов. Для использования MapStruct в проекте Spring с Maven, добавьте зависимость в файл pom.xml:

Теперь можно приступать к самому интересному. А самое интересное то, что писать мапперы с помощью Mapstruct легко и просто. Просто посмотрите, как изменится объём кода мапперов.

UserMapper:

PostMapper:

И да, теперь это не классы, а интерфейсы. Библиотека Mapstruct реализует их автоматически. Указывая аннотацию @Mapper(componentModel = “spring”), мы даём понять проекту, что нужно будет производить инъекцию этой зависимости. Проще говоря, легко и непринуждённо вставлять маппер в нужные нам классы.

Она может конвертировать entity в DTO и обратно потому, что поля называются одинаково и в сущностях, и в DTO-шках. Например, email, password, username и другие. Но если бы у вас была другая ситуация, вам бы понадобилось дополнительно пометить этот момент. Например, если бы в сущности User поле называлось username, а в UserDto – просто name:

Аннотация @Mapping указывает с помощью параметров source и target названия соответствующих друг другу полей в классах. Аннотация @IterableMapping говорит методу toDtos(), что для конвертации объектов в списке нужно для каждого из них применять метод toDto().

Проверка работы

Итак, мы закончили создание слоя mapper. Как и всех слоёв программы из структуры Spring-приложения! А это означает, что пора проверять, как это работает и наслаждаться результатом.

Только сначала нужно заменить отдаваемые и принимаемые controller-ом сущности на DTO-шки.

UserController с применением DTO и mapper-а:

PostController:

Отлично. Перед тем, как начать делать http-запросы к контроллерам, создадим содержимое в таблицах users и posts в БД:

Таблица posts

Содержимое таблицы posts

Таблица users

Содержимое таблицы users

Отлично. У нас есть два пользователя. У одного из них – 2 поста, у другого – 1. Пора делать запросы. Нужно запустить Spring-приложение, после чего ввести в строку запроса localhost:порт/путь. Обычно порт стоит 8080, но он у меня занят, и поэтому в файле application.properties я поменяла его на 8090. Путь – это строка, которая формируется из того, что мы указывали в controller-е.

Итак, результаты запросов:

JSON-объекты в ответ на http-запрос localhost:8090/users в Spring-приложении
JSON-объект в ответ на http-запрос localhost:8090/users/3 в Spring-приложении
JSON-объект в ответ на http-запрос localhost:8090/users/4 в Spring-приложении

JSON-объекты в ответ на http-запрос localhost:8090/posts в Spring-приложении

JSON-объект в ответ на http-запрос localhost:8090/posts/2 в Spring-приложении
JSON-объект в ответ на http-запрос localhost:8090/posts/3 в Spring-приложении

JSON-объекты в ответ на http-запрос localhost:8090/posts/user/3 в Spring-приложении

Всё работает именно так, как предполагается! Controller-ы отдают данные, и при этом никаких рекурсий. Да, конечно, controller-ы, которые мы написали, могут обрабатывать не только get-запросы, но и всякие post, put и delete. Но из обычного браузера это сделать не так легко. Обычно на клиентской части приложения уже создают возможность отправлять эти самые post, put и delete. Но даже без них видно – работа была проделана на ура!

Вот и подошло к концу создание простого Spring back-end приложения. Надеюсь, вы тоже поразились удобству, которое предоставляет Spring Framework. Поверьте, я ведь до знакомства со Spring писала всё это ручками, писала классы DAO, в которых прописывала реальные SQL-запросы, для некоторых сущностей они были огромнейшими ввиду множества связей с другими сущностями. Сервлеты, jsp… Всё это было. Spring действительно сильно упрощает шаблонные задачи, позволяя экономить время и силы, направляя их в более творческое русло. Например, обдумывание концепции приложения.

Всем удачной веб-разработки!

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