Добавление поддержки базы данных для проекта Spring Boot
В этой части учебника вы добавите и настроите базу данных в свой проект, используя JDBC. В JVM-приложениях вы используете JDBC для взаимодействия с базами данных. Для удобства Spring Framework предоставляет класс JdbcTemplate, который упрощает использование JDBC и помогает избежать распространённых ошибок.
Добавление поддержки базы данных
В приложениях на основе Spring Framework общепринятой практикой является реализация логики доступа к базе данных в так называемом слое сервиса — здесь находится бизнес-логика. В Spring вы должны пометить классы аннотацией @Service, чтобы указать, что класс принадлежит к слою сервиса приложения. В этом приложении вы создадите класс MessageService для этой цели.
В файле DemoApplication.kt создайте класс MessageService следующим образом:
import org.springframework.stereotype.Service
import org.springframework.jdbc.core.JdbcTemplate
@Service
class MessageService(val db: JdbcTemplate) {
fun findMessages(): List<Message> = db.query("select * from messages") { response, _ ->
Message(response.getString("id"), response.getString("text"))
}
fun save(message: Message){
db.update("insert into messages values ( ?, ? )",
message.id, message.text)
}
}
- Аргумент конструктора и инъекция зависимостей – (val db: JdbcTemplate)
-
Класс в Kotlin может иметь первичный конструктор и один или несколько вторичных конструкторов. Первичный конструктор является частью заголовка класса, и он следует за именем класса и необязательными параметрами типа. В нашем случае конструктор —
(val db: JdbcTemplate).Аргумент конструктора —
val db: JdbcTemplate:@Service class MessageService(val db: JdbcTemplate)
- Закрывающая лямбда и преобразование SAM
-
Функция
findMessages()вызывает функциюquery()классаJdbcTemplate. Функцияquery()принимает два аргумента: SQL-запрос в виде объекта String и обратный вызов, который будет отображать по одному объекту на строку:db.query("...", RowMapper { ... } )Интерфейс
RowMapperобъявляет только один метод, поэтому его можно реализовать с помощью лямбда-выражения, опуская имя интерфейса. Компилятор Kotlin знает, к какому интерфейсу нужно преобразовать лямбда-выражение, потому что вы используете его в качестве параметра для вызова функции. Это известно как преобразование SAM в Kotlin:db.query("...", { ... } )После преобразования SAM функция запроса получает два аргумента: строку на первом месте и лямбда-выражение на последнем. Согласно соглашению Kotlin, если последний параметр функции — функция, то лямбда-выражение, передаваемое в качестве соответствующего аргумента, может быть помещено за пределами скобок. Такая синтаксическая конструкция также известна как закрывающая лямбда:
db.query("...") { ... } - Подчеркивание для неиспользуемого аргумента лямбды
-
Для лямбды с несколькими параметрами вы можете использовать символ подчеркивания
_для замены имён параметров, которые вы не используете.Таким образом, окончательный синтаксис для вызова функции запроса выглядит следующим образом:
db.query("select * from messages") { response, _ -> Message(response.getString("id"), response.getString("text")) }
Обновление класса MessageController
Обновите MessageController, чтобы использовать новый класс MessageService:
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.PostMapping
@RestController
class MessageController(val service: MessageService) {
@GetMapping("/")
fun index(): List<Message> = service.findMessages()
@PostMapping("/")
fun post(@RequestBody message: Message) {
service.save(message)
}
}
- @PostMapping аннотация
Метод, отвечающий за обработку запросов HTTP POST, должен быть помечен аннотацией
@PostMapping. Чтобы иметь возможность преобразовать JSON, отправленный в качестве содержимого тела HTTP, в объект, вам нужно использовать аннотацию@RequestBodyдля аргумента метода. Благодаря наличию библиотеки Jackson в пути к классам приложения, преобразование происходит автоматически.
Обновление класса MessageService
Поле id для класса Message было объявлено как необязательная строка:
data class Message(val id: String?, val text: String)
Однако хранение значения null в базе данных как значение id будет неверным: вам нужно корректно обработать эту ситуацию.
Обновите код, чтобы генерировать новое значение, когда id является null при сохранении сообщений в базе данных:
import java.util.UUID
@Service
class MessageService(val db: JdbcTemplate) {
fun findMessages(): List<Message> = db.query("select * from messages") { response, _ ->
Message(response.getString("id"), response.getString("text"))
}
fun save(message: Message){
val id = message.id ?: UUID.randomUUID().toString()
db.update("insert into messages values ( ?, ? )",
id, message.text)
}
}
- Оператор Elvis – ?:
Код
message.id ?: UUID.randomUUID().toString()использует оператор Elvis (сокращение для проверки на null)?:. Если выражение слева от?:не являетсяnull, оператор Elvis возвращает его; в противном случае он возвращает выражение справа. Обратите внимание, что выражение справа оценивается только в том случае, если выражение слева являетсяnull.
Код приложения готов к работе с базой данных. Теперь необходимо настроить источник данных.
Настройка базы данных
Настройте базу данных в приложении:
-
Создайте файл
schema.sqlв каталогеsrc/main/resources. Он будет хранить определения объектов базы данных: -
Обновите файл
src/main/resources/schema.sqlследующим кодом:CREATE TABLE IF NOT EXISTS messages ( id VARCHAR(60) PRIMARY KEY, text VARCHAR NOT NULL );
Он создаёт таблицу
messagesс двумя столбцами:idиtext. Структура таблицы соответствует структуре классаMessage. -
Откройте файл
application.properties, расположенный в папкеsrc/main/resources, и добавьте следующие свойства приложения:spring.datasource.driver-class-name=org.h2.Driver spring.datasource.url=jdbc:h2:file:./data/testdb spring.datasource.username=name spring.datasource.password=password spring.sql.init.schema-locations=classpath:schema.sql spring.sql.init.mode=always
Эти настройки позволяют базе данных работать с приложением Spring Boot.
Полный список общих свойств приложения см. в документации Spring.
Добавление сообщений в базу данных через HTTP-запрос
Для работы с ранее созданными конечными точками вы должны использовать HTTP-клиент. В IntelliJ IDEA используйте встроенный HTTP-клиент:
-
Запустите приложение. После запуска приложения вы можете выполнять запросы POST для хранения сообщений в базе данных. Создайте файл
requests.httpи добавьте следующие HTTP-запросы:### Post "Hello!" POST http://localhost:8080/ Content-Type: application/json { "text": "Hello!" } ### Post "Bonjour!" POST http://localhost:8080/ Content-Type: application/json { "text": "Bonjour!" } ### Post "Privet!" POST http://localhost:8080/ Content-Type: application/json { "text": "Privet!" } ### Get all the messages GET http://localhost:8080/ -
Выполните все запросы POST. Используйте зелёную иконку Запуск в области слева от объявления запроса. Эти запросы записывают текстовые сообщения в базу данных:

-
Выполните запрос GET и просмотрите результат в окне инструментов Запуск:

Альтернативный способ выполнения запросов
Вы также можете использовать любой другой HTTP-клиент или утилиту командной строки cURL. Например, выполните следующие команды в терминале, чтобы получить тот же результат:
curl -X POST --location "http://localhost:8080" -H "Content-Type: application/json" -d "{ \"text\": \"Hello!\" }"
curl -X POST --location "http://localhost:8080" -H "Content-Type: application/json" -d "{ \"text\": \"Bonjour!\" }"
curl -X POST --location "http://localhost:8080" -H "Content-Type: application/json" -d "{ \"text\": \"Privet!\" }"
curl -X GET --location "http://localhost:8080"
Получение сообщений по идентификатору
Расширьте функциональность приложения для получения отдельных сообщений по идентификатору.
-
В классе
MessageServiceдобавьте новую функциюfindMessageById(id: String)для получения отдельных сообщений по идентификатору:import org.springframework.jdbc.core.query @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> = db.query("select * from messages") { response, _ -> Message(response.getString("id"), response.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { response, _ -> Message(response.getString("id"), response.getString("text")) } fun save(message: Message) { val id = message.id ?: UUID.randomUUID().toString() db.update("insert into messages values ( ?, ? )", id, message.text) } } -
Добавьте новую функцию
index(...)с параметромidв классMessageController:@RestController class MessageController(val service: MessageService) { @GetMapping("/") fun index(): List<Message> = service.findMessages() @GetMapping("/{id}") fun index(@PathVariable id: String): List<Message> = service.findMessageById(id) @PostMapping("/") fun post(@RequestBody message: Message) { service.save(message) } }- Получение значения из контекстного пути
Сообщение
idизвлекается из контекстного пути с помощью Spring Framework, так как вы аннотировали новую функцию с помощью@GetMapping("/{id}"). Аннотируя аргумент функции с помощью@PathVariable, вы сообщаете фреймворку использовать полученное значение в качестве аргумента функции. Новая функция вызываетMessageServiceдля получения отдельного сообщения по его идентификатору.- Позиция аргумента vararg в списке параметров
-
Функция
query()принимает три аргумента:Строка SQL-запроса, требующая параметра для выполнения
`id`, который является параметром типа String
RowMapper-экземпляр реализован с помощью лямбда-выражения
Второй параметр функции
query()объявлен как аргумент переменной длины (vararg). В Kotlin позиция параметра аргументов переменной длины не должна быть обязательно последней в списке параметров.
Запуск приложения
Приложение Spring готово к запуску:
Запустите приложение снова.
-
Откройте файл
requests.httpи добавьте новый GET-запрос:### Get the message by its id GET http://localhost:8080/id
Выполните GET-запрос для получения всех сообщений из базы данных.
-
В окне инструментов Запуск скопируйте один из идентификаторов и добавьте его в запрос, как показано ниже:
### Get the message by its id GET http://localhost:8080/f16c1d2e-08dc-455c-abfe-68440229b84f
-
Выполните GET-запрос и посмотрите результат в окне инструментов Запуск:

Следующий шаг
На этом заключительном шаге вы узнаете, как использовать более распространённое подключение к базе данных с помощью Spring Data.
Получить карту Kotlin языка
Получите свою личную карту языка Kotlin, чтобы помочь вам в навигации по функциям языка и отслеживании прогресса в изучении. Мы также вышлем вам советы по использованию языка и полезные материалы по использованию Kotlin с Spring.
© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/jvm-spring-boot-add-db-support.html