TGViewer
Грамота от Кузьмича Грамота от Кузьмича @java_ot_kuzmicha · 208 subscribers
Post #23 226
Хелоу, на ночь глядя - о мапперах, библиотеке MapStruct и чайных делах.

Что делать если я тестирую API, и для одного и того же по смыслу предмета от одного эндпоинта приходит одна модель данных, а от другого другая?
На примере - представим APIшку магазина китайского чая🫖

У APIшки есть два эндпоинта:

1. ➕эндпоинт для добавления нового сорта чая в ассортимент магазина
POST /tea
в теле запроса отправляется json с данными о добавляемом сорте чая, типа такого:
{
"newTeaName": "Жоу Гуй",
"type": "улун"
}


2. ☕️эндпоинт для получения рекомендуемого чая сезона
GET /teaOfTheDay
а в теле ответа возвращается объект:
{
"id":"2"
"teaName":"Жоу Гуй"
"type":"улун"
}

Чертовски похожие объекты, что там чай, что там чай, ну окей. Смоделировать json-объект отправляемого чая (1) и возвращаемого чая (2) можно такими нехитрыми ДТОшками с применением аннотации @Data (lombok):
@Data
public class Tea {
private String newTeaName;
private String type;
}
@Data
public class ShopTea {
private int id;
private String teaName;
private String type;
}


Мы видим что речь идет о практически идентичных объектах. Но их сложно сравнивать друг с другом по следующим причинам:
1. Tea & ShopTea - объекты разного типа
2. имена/состав полей не соответствуют друг другу или соответствуют не полностью (к примеру поля teaName & newTeaName хранят по сути одинаковую информацию но называются по разному)

А еще возможно что у вас уже написано немало методов-утилит для работы с классом Tea, но вот объект ShopTea туда уже не передать.
Задача перед нами - превратить объект типа ShopTea в объект типа Shop. Для таких превращений выделяют специальные классы-мапперы (mapper):

public class OldTeaMapper {
Tea shopTeaToTea(ShopTea shopTea) {
Tea tea = new Tea();
tea.setNewTeaName(shopTea.getTeaName());
tea.setType(shopTea.getType());
return tea;
}
}


Вроде и неплохо, но логику преобразования пришлось написать ручками. А самое главное - в жизни в json от сервисов - намного больше полей (десятки), и рукописного труда оказалось бы дофига. Тут и врывается MapStruct - для генерации таких классов-мапперов. Код для маппера с применением MapStruct будет выглядеть вот так:
@Mapper
public interface MapStructTeaMapper {
@Mapping(source = "teaName", target = "newTeaName")
Tea shopTeaToTea(ShopTea shopTea);
}

Причем логику преобразования совпадающих по имени-типу полей можно вообще не описывать - через @Mapping нам пришлось описать только особый случай, когда поля не совпадали по имени. Ну а если поле у первого объекта совпадает с полем второго и по типу и по имени - никаких аннотаций писать не надо.

Для работы этой библиотеки надо:
1. указать ее как зависимость
2. настроить ее работу с плагином компилятора
3. применить в коде

И все это, с пылу-с жару подготовленное для вас - находится здесь:
https://gitlab.com/RevRay/mapstruct-demo

Пункты 1 и 2 выполняются в pom.xml для Maven-проекта, а пункт 3 - код для разобранного выше примера - есть в классе MapStructTest.

Изучайте пример, читайте документацию (https://mapstruct.org/), пейте вкусный чай и пишите мапперы как преисполнившиеся в своем познании люди🤓
GitLab Aleksandr Kuzmichev / mapstruct-demo · GitLab
  • 🔥 8
  • 🤔 1
More from @java_ot_kuzmicha
  1. Jul 28, 2026Добавляю краски в музыку🟥🟨🟦🟧🟩 Пришла мне тут шальная мысль - что если каждый альбом к…
  2. Dec 10, 2025Более жизовые итоги чем эти ваши я.музыка или спотифаи. Кстати, че бы еще вы добавили в та…
  3. Nov 24, 2025уже пора?🤔 тренды все-таки
  4. Nov 19, 20253 из 25 - это не про лото и не про биатлон. Для тех кто пропустил релиз Java 25 - смотрим…
  5. Nov 1, 2025в рабочую субботу законодательно запрещены посты на серьезные темы. поэтому вот вам веселы…
  6. Oct 9, 2025Достал такое из почтового ящика. Скажите, кто вызывал таких мастеров - а с деплоем в прод…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →