0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Типовая задача в веб-разработке - это создание API для CRUD сущности.</p>
1
<p>Типовая задача в веб-разработке - это создание API для CRUD сущности.</p>
2
<p><strong>CRUD</strong>(<em>Create/Read/Update/Delete</em>) - это набор типовых операций, который обычно выполняется над сущностью. В этом уроке мы соберем все изученное и создадим эталонный CRUD на примере поста в блог.</p>
2
<p><strong>CRUD</strong>(<em>Create/Read/Update/Delete</em>) - это набор типовых операций, который обычно выполняется над сущностью. В этом уроке мы соберем все изученное и создадим эталонный CRUD на примере поста в блог.</p>
3
<p>Общий план создания CRUD выглядит так:</p>
3
<p>Общий план создания CRUD выглядит так:</p>
4
<ol><li>Создаем сущность</li>
4
<ol><li>Создаем сущность</li>
5
<li>Создаем репозиторий</li>
5
<li>Создаем репозиторий</li>
6
<li>Создаем DTO</li>
6
<li>Создаем DTO</li>
7
<li>Создаем маппер</li>
7
<li>Создаем маппер</li>
8
<li>Пишем тест</li>
8
<li>Пишем тест</li>
9
<li>Реализуем контроллер</li>
9
<li>Реализуем контроллер</li>
10
</ol><h2>Создание сущности</h2>
10
</ol><h2>Создание сущности</h2>
11
<p>У поста в блоге есть четыре основных свойства:</p>
11
<p>У поста в блоге есть четыре основных свойства:</p>
12
<ul><li>Название</li>
12
<ul><li>Название</li>
13
<li>Слаг</li>
13
<li>Слаг</li>
14
<li>Текст</li>
14
<li>Текст</li>
15
<li>Автор</li>
15
<li>Автор</li>
16
</ul><p>Кроме того, еще можно добавить дату создания и последнего обновления:</p>
16
</ul><p>Кроме того, еще можно добавить дату создания и последнего обновления:</p>
17
<h2>Создание репозитория</h2>
17
<h2>Создание репозитория</h2>
18
<p>Использование слага в URL-адресе подразумевает, что мы сможем делать выборку сущности по слагу:</p>
18
<p>Использование слага в URL-адресе подразумевает, что мы сможем делать выборку сущности по слагу:</p>
19
<p>Сразу добавим этот метод в репозиторий:</p>
19
<p>Сразу добавим этот метод в репозиторий:</p>
20
<h2>Создание DTO</h2>
20
<h2>Создание DTO</h2>
21
<p>В целом нам понадобятся три разных DTO - для создания, обновления и просмотра поста:</p>
21
<p>В целом нам понадобятся три разных DTO - для создания, обновления и просмотра поста:</p>
22
<h2>Создание маппера</h2>
22
<h2>Создание маппера</h2>
23
<p>Для CRUD нам нужны три операции:</p>
23
<p>Для CRUD нам нужны три операции:</p>
24
<ul><li>Конвертация DTO для создания в пост</li>
24
<ul><li>Конвертация DTO для создания в пост</li>
25
<li>Конвертация поста в DTO для просмотра</li>
25
<li>Конвертация поста в DTO для просмотра</li>
26
<li>Обновление поста на основе DTO для обновления</li>
26
<li>Обновление поста на основе DTO для обновления</li>
27
</ul><p>Самое интересное в этом коде - это конвертация authorId из DTO в свойство author внутри поста. Чтобы выполнить эту операцию, нужно сделать запрос в базу данных и извлечь объект автора. По умолчанию MapStruct такого не умеет, но для него можно создать маппер, который решает эту задачу. Здесь мы сразу приведем его код:</p>
27
</ul><p>Самое интересное в этом коде - это конвертация authorId из DTO в свойство author внутри поста. Чтобы выполнить эту операцию, нужно сделать запрос в базу данных и извлечь объект автора. По умолчанию MapStruct такого не умеет, но для него можно создать маппер, который решает эту задачу. Здесь мы сразу приведем его код:</p>
28
<p>Чтобы этот код заработал, необходимо внедрить общий базовый интерфейс для всех моделей, на который маппер мог бы ориентироваться и понимать, применять метод toEntity(). Мы назовем его BaseEntity:</p>
28
<p>Чтобы этот код заработал, необходимо внедрить общий базовый интерфейс для всех моделей, на который маппер мог бы ориентироваться и понимать, применять метод toEntity(). Мы назовем его BaseEntity:</p>
29
<p>Определение класса Post в таком случае выглядит так:</p>
29
<p>Определение класса Post в таком случае выглядит так:</p>
30
<h2>Создание теста</h2>
30
<h2>Создание теста</h2>
31
<p>Код интеграционных тестов не завязан на устройство контроллера. Поэтому сам тест можно написать до реализации контроллера - так мы упростим создание контроллера и проверку его работоспособности. Такой подход называется<strong>TDD</strong>(<em>Test Driven Development</em>):</p>
31
<p>Код интеграционных тестов не завязан на устройство контроллера. Поэтому сам тест можно написать до реализации контроллера - так мы упростим создание контроллера и проверку его работоспособности. Такой подход называется<strong>TDD</strong>(<em>Test Driven Development</em>):</p>
32
<h2>Реализация контроллера</h2>
32
<h2>Реализация контроллера</h2>
33
<p>Перейдем к контроллеру. Здесь мы не добавляем ничего нового. Весь его код мы видели частями, а теперь собираем все вместе:</p>
33
<p>Перейдем к контроллеру. Здесь мы не добавляем ничего нового. Весь его код мы видели частями, а теперь собираем все вместе:</p>
34
<p>В целом, CRUD не ограничивается только перечисленными методами. По ситуации методов может быть больше:</p>
34
<p>В целом, CRUD не ограничивается только перечисленными методами. По ситуации методов может быть больше:</p>
35
<ul><li>Если у нас есть какое-то особое обновление и вывод списка</li>
35
<ul><li>Если у нас есть какое-то особое обновление и вывод списка</li>
36
<li>Если у нас есть несколько контроллеров для одной и той же сущности (например, для управления постами для пользователей и для администраторов нужно два разных контроллера)</li>
36
<li>Если у нас есть несколько контроллеров для одной и той же сущности (например, для управления постами для пользователей и для администраторов нужно два разных контроллера)</li>
37
</ul>
37
</ul>