Создадим простое веб-приложение на Spring Boot 3 с доступом к базе через Hibernate и подключим его к СУБД PostgreSQL.
Инициализация проекта
Перейдите на сайт Spring Initializr. В левой части экрана в графе Project выберите Gradle (если не знаете, Groovy или Kotlin, то оставьте Groovy). В Project Metadata в графе Artifact введите название проекта. В данном примере будет использовать название spring-hibernate-postgresql. Это повлияет на некоторые автоматически сгенерированные классы.
Затем в правой части экрана нажмите кнопку ADD DEPENDENCIES…
В появившемся модальном окне выберите следующие зависимости (удерживайте ctrl при клике на пункт меню, чтобы не приходилось каждый раз снова открывать модальное окно):
- Spring Web
- Spring Data Jpa
- PostgreSQL Driver
Другие зависимости подключаем по собственному усмотрению. Возможно, что вы заходите сразу подключить Lombok. В рамках данного туториала мы ограничимся необходимым минимумом, чтобы не перегружать изложение.
Когда все зависимости выбраны, нажимаем внизу экрана кнопку GENERATE. Распаковываем скачанный архив и открываем папку spring-hibernate-postgresql (в данном примере) в среде разработки, например, в IntelliJ Idea.
Первый запуск приложения, проверка хибернейта и подключения к БД PostgreSQL.
В папке src/main/resources
инициализатор создаст пустой файл application.properties. Заполним его следующим образом:
1 2 3 4 5 6 7 8 |
spring.datasource.url=jdbc:postgresql://localhost/testdb spring.datasource.username=postgres spring.datasource.password=qwerty spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect spring.jpa.hibernate.ddl-auto=update server.port=8080 |
Первые три строки особых пояснений не требуют. Через эти свойства мы передаём спрингу данные о подключении к СУБД: урл базы данных, имя пользователя и пароль. Если у вас в системе ещё не развёрнута СУБД PostgreSQL и не создана ни одна БД, то можете обратиться к этому туториалу.
Параметр spring.jpa.properties.hibernate.dialect сообщает спрингу, что хибернейт будет использовать для создания запросов соответствующий sql-диалект.
А параметр spring.jpa.hibernate.ddl-auto, установленный в значение update, заставит хибернейт при каждом запуске создавать/изменять таблицы в БД в соответствии с изменениями в классах предметной области. Добавим мы в класс новое поле, в таблице появится новая колонка и т.д.
Последний параметр server.port определяет, какой порт будет слушать встроенный сервер tomcat, в котором в итоге будет крутиться наше приложение.
Создадим класс предметной области:
1 2 3 4 5 6 7 8 9 10 |
@Entity public class Person { @Id @GeneratedValue private Long id; private String name; //Конструкторы, геттеры и сеттеры, toString() и т.д. } |
Самого добавления класса предметной области, помеченного аннотацией @jakarta.persistence.Entity, достаточно, чтобы хибернейт обнаружил его и создал ему соответствующую таблицу в БД (при условии, что мы выставили в настройках spring.jpa.hibernate.ddl-auto=update).
Чтобы в этом убедиться, давайте запустим наше приложение. Для этого можно поступить одним из двух способов:
Первый — открыть терминал в папке проекта и там выполнить команду
1 |
./gradlew bootRun |
Второй — не заморачиваться и запустить проект на выполнение средствами IDE (здесь пример с IntelliJ Idea).
Теперь если мы подключимся с нашей БД через psql и проверим таблицу командой
1 |
\d person |
где person — это имя таблицы, то обнаружим, что такая таблица, во-первых, есть. А во-вторых, её структура соответствует тому, что мы написали в классе Person.
Если у вас psql выводит кракозябры вместо кириллицы, то это можно исправить.
Более детальное описание таблицы, можно получить сделав такой запрос:
1 |
SELECT * FROM information_schema.columns WHERE table_name = 'person'; |
Его, конечно, удобней делать в графическом режиме, например, в pgAdmin, который поставляется вместе с СУБД postgreSQL.
Создание основных бинов веб-приложения
Создадим бин репозитория для сущности Person:
1 2 3 |
@Repository public interface PersonRepository extends JpaRepository<Person, Long> { } |
Поскольку мы используем Spring data JPA, то репозиторий мы создаём в виде интерфейса, имплементацию Спринг создаст автоматически.
Затем создадим сервис:
1 2 3 4 5 6 7 8 9 10 |
@Service public class PersonService { @Autowired PersonRepository personRepository; public Person add(Person person) { return personRepository.save(person); } } |
И наконец контроллер:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@RestController @RequestMapping(path = "/person") public class PersonController { @Autowired PersonService personService; @PostMapping("/add") public ResponseEntity<Long> add(@RequestBody Person person) { personService.add(person); return ResponseEntity.ok(person.getId()); } } |
Теперь мы можем снова запустить приложение и проверить работу контроллера, отправив POST запрос по адресу http://localhost:8080/person/add.
Сделать это можно следующей командой:
1 |
curl -d '{"name":"Vladimir"}' -H "Content-type: application/json" -X POST http://localhost:8080/person/add |
Команда curl, доступная через консоль PowerShell, работает совсем не так, как curl в *nix системах (в Windows это просто альяс для утилиты Invoke-WebRequest), поэтому выполнять эту команду нужно из Git Bash или чего-то подобного. Тогда всё должно отработать нормально.
Открыв консоль psql, можно убедиться, что запрос отработал корректно. Данные попали в контроллер, оттуда по цепочке в репозиторий, а затем Hibernate сохранил их в соответствующую таблицу в базе.
Написание тестового метода
Всё-таки проверять отдельные части приложения удобней, не запуская его, поэтому давайте напишем заготовку интеграционного теста. Инициализатор автоматически создал первый тестовый класс для приложения и разместил его в соответствующем пакете. В нашем случае класс называется SpringHibernatePostgresqlApplicationTests. Наполним его следующим содержанием:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
@SpringBootTest class SpringHibernatePostgresqlApplicationTests { @Autowired PersonRepository personRepository; @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @BeforeEach void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @Test void addNewPersonApi() throws Exception { Person irina = new Person("Irina"); String responseContent = mockMvc.perform(MockMvcRequestBuilders .post("/person/add") .contentType(MediaType.APPLICATION_JSON) .content(new ObjectMapper().writeValueAsString(irina))) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(); Long irinaId = Long.valueOf(responseContent); Long dbEntryId = personRepository.findById(irinaId).map(Person::getId).orElseThrow(); assertEquals(irinaId, dbEntryId); personRepository.deleteById(irinaId); } } |
Тест поднимет контекст Спринг-приложения, отправит POST запрос на наш контроллер и получит ответ с id сохранённой строки. Затем уже напрямую через бин репозитория извлечёт из БД строку по полученному id. И если в БД действительно окажется строка с таким же id, значит добавление прошло успешно. В конце мы удаляем тестовую строку из БД.
Создание исполняемого jar файла для дальнейшего развёртывания
Чтобы создать jar файл с нашим приложением, который можно было бы запустить командой java -jar appname.jar
, нужно выполнить задачу bootJar в Gradle. Это можно сделать либо через терминал в папке проекта, набрав
1 |
./gradlew bootJar |
Либо то же самое можно сделать, не выходя из IDE:
После выполнения этой задачи Gradle создаст в подпапке \build\libs
проекта исполняемый джарник, который и можно будет запустить с помощью команды java -jar appname.jar
.
Обратите внимание, что помимо bootJar в Gradle есть ещё задача jar. И она тоже создаёт джарник. Но его нельзя запустить. Он будет жаловаться на отсутствие манифеста. Может возникнуть желание погуглить проблему, создать так называемый толстый джарник (fat jar) и так далее и тому подобное.
Ничего этого делать не нужно. Для Spring Boot приложения в Gradle есть специальная задача (таска) по созданию джарника и это задача bootJar и, запустив её, мы получим нормальный исполняемый джарник с нашим приложением.