Перейти к основному содержимому

Hibernate и JPA — практический старт

Разработчику Архитектору

Hibernate и JPA — практический старт

JPA (Jakarta Persistence API) — стандарт ORM: работа с БД через объекты. Hibernate — основная реализация JPA в Java.

В Spring Boot обычно используют Spring Data JPA, а не EntityManager вручную.

База Spring: Первая программа на Spring · обзор ORM: Экосистема Java-приложений · Spring Framework.


Зависимости (Maven)

В start.spring.io выберите: Spring Web, Spring Data JPA, H2 Database.

Или в pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

Разбор:

  • этот XML-блок описывает конфигурацию сборки, которую инструмент считывает до выполнения прикладного Java-кода.
  • элементы с артефактами spring-boot-starter-data-jpa, h2 включают нужные библиотеки и определяют доступные API в кодовой базе.
  • секция <scope>test</scope> ограничивает зависимость тестовым контуром и не тащит ее в production-артефакт.
  • любое изменение здесь влияет на classpath и поведение сборки, поэтому такие правки проверяют полным прогоном тестов.
  • после редактирования этого фрагмента проверяют, что проект собирается и тестовый раннер видит нужные классы без конфликтов версий.

application.properties:

spring.datasource.url=jdbc:h2:mem:notes
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

Разбор:

  • блок задает runtime-конфигурацию приложения, и эти параметры читаются фреймворком при старте контекста.
  • ключи spring.datasource.url, spring.h2.console.enabled, spring.jpa.hibernate.ddl-auto, spring.jpa.show-sql управляют подключением к БД, режимом генерации схемы и диагностическим выводом SQL.
  • изменение значений здесь мгновенно меняет поведение приложения без правок Java-классов.
  • такие параметры важно разделять по окружениям (dev/test/prod), чтобы безопасные настройки разработки не ушли в production.
  • корректность проверяется запуском приложения и фактическим поведением: подключение к БД, миграции и ожидаемые логи.

Сущность

Код ITЗагрузка примера кода…

Разбор:

  • фрагмент показывает прикладной сценарий по теме раздела: здесь есть входные данные, операции над ними и проверяемый итог.
  • аннотации Entity, Table, Id, GeneratedValue, Column включают поведение фреймворка (тестовый раннер, web-маршрутизация, ORM или транзакционные правила).
  • ключевые вызовы Table, GeneratedValue, Column, Note, getId, getText, setText задают основную механику этого примера: обработку, проверку и формирование результата.
  • чтение этого блока по шагам дает предсказуемую картину — сначала подготовка данных, затем действие API, затем получение конечного значения.
  • после запуска стоит проверить именно эффект этого примера — вывод, значение переменной, статус ответа, состояние БД или результат теста.
  • при переносе в production обычно добавляют валидацию входа, явную обработку ошибок и тест на граничные случаи вокруг этого сценария.

Репозиторий

package com.example.notes;

import org.springframework.data.jpa.repository.JpaRepository;

public interface NoteRepository extends JpaRepository<Note, Long> {
}

Разбор:

  • фрагмент показывает прикладной сценарий по теме раздела: здесь есть входные данные, операции над ними и проверяемый итог.
  • чтение этого блока по шагам дает предсказуемую картину — сначала подготовка данных, затем действие API, затем получение конечного значения.
  • после запуска стоит проверить именно эффект этого примера — вывод, значение переменной, статус ответа, состояние БД или результат теста.
  • при переносе в production обычно добавляют валидацию входа, явную обработку ошибок и тест на граничные случаи вокруг этого сценария.

Spring Data сгенерирует save, findAll, findById, deleteById без реализации.


REST-контроллер

Код ITЗагрузка примера кода…

Разбор:

  • фрагмент показывает прикладной сценарий по теме раздела: здесь есть входные данные, операции над ними и проверяемый итог.
  • аннотации RestController, RequestMapping, GetMapping, PostMapping, ResponseStatus, RequestBody включают поведение фреймворка (тестовый раннер, web-маршрутизация, ORM или транзакционные правила).
  • ключевые вызовы RequestMapping, NoteController, list, findAll, ResponseStatus, create, save задают основную механику этого примера: обработку, проверку и формирование результата.
  • чтение этого блока по шагам дает предсказуемую картину — сначала подготовка данных, затем действие API, затем получение конечного значения.
  • после запуска стоит проверить именно эффект этого примера — вывод, значение переменной, статус ответа, состояние БД или результат теста.
  • при переносе в production обычно добавляют валидацию входа, явную обработку ошибок и тест на граничные случаи вокруг этого сценария.
./mvnw spring-boot:run

Разбор:

  • команды в этом фрагменте выполняются по шагам и составляют практический workflow — запуск, тесты, сборка или проверка окружения.
  • последовательность ./mvnw spring-boot:run покрывает ключевые этапы жизненного цикла проекта в CLI.
  • если одна команда завершается ошибкой, последующие шаги не подтверждают корректность сценария, пока не устранена первопричина.
  • результат смотрят по exit code и по логам команды: успешный статус, ожидаемые артефакты и отсутствие stack trace.
  • такой же набор команд обычно фиксируют в CI, чтобы локальная проверка и pipeline давали одинаковый результат.

Проверка

curl http://localhost:8080/api/notes
curl -X POST http://localhost:8080/api/notes \
-H "Content-Type: application/json" \
-d '{"text":"JPA работает"}'

Разбор:

  • команды в этом фрагменте выполняются по шагам и составляют практический workflow — запуск, тесты, сборка или проверка окружения.
  • последовательность curl http://localhost:8080/api/notes, curl -X POST http://localhost:8080/api/notes \, -H "Content-Type: application/json" \, -d '{"text":"JPA работает"}' покрывает ключевые этапы жизненного цикла проекта в CLI.
  • если одна команда завершается ошибкой, последующие шаги не подтверждают корректность сценария, пока не устранена первопричина.
  • результат смотрят по exit code и по логам команды: успешный статус, ожидаемые артефакты и отсутствие stack trace.
  • такой же набор команд обычно фиксируют в CI, чтобы локальная проверка и pipeline давали одинаковый результат.

Консоль H2: http://localhost:8080/h2-console (JDBC URL из application.properties).


Ключевые понятия

ТерминСмысл
Entityтаблица в виде класса
PersistenceContextкэш сессии Hibernate
ddl-auto=updateсхема подстраивается (только dev!)
Lazy loadingсвязи грузятся по требованию

Production: миграции через Flyway или Liquibase, не ddl-auto=update.


Жизненный цикл сущности простыми словами

Чтобы перестать "магически" воспринимать JPA, полезно держать в голове четыре состояния объекта:

СостояниеЧто это значит
New (transient)объект создан через new, в БД еще нет
Managedобъект прикреплен к persistence context, Hibernate отслеживает изменения
Detachedобъект был managed, но контекст закрыт
Removedобъект помечен на удаление и будет удален при flush/commit

Практическая выгода — вы понимаете, почему save() иногда делает INSERT, а иногда UPDATE.


Сервисный слой и транзакции

В учебном примере контроллер работает напрямую с репозиторием, это нормально для старта. В боевом сервисе обычно добавляют слой сервиса и явную транзакцию:

Код ITЗагрузка примера кода…

Разбор:

  • фрагмент показывает прикладной сценарий по теме раздела: здесь есть входные данные, операции над ними и проверяемый итог.
  • аннотации Service, Transactional включают поведение фреймворка (тестовый раннер, web-маршрутизация, ORM или транзакционные правила).
  • ключевые вызовы NoteService, rename, findById, orElseThrow, ResponseStatusException, setText задают основную механику этого примера: обработку, проверку и формирование результата.
  • чтение этого блока по шагам дает предсказуемую картину — сначала подготовка данных, затем действие API, затем получение конечного значения.
  • после запуска стоит проверить именно эффект этого примера — вывод, значение переменной, статус ответа, состояние БД или результат теста.
  • при переносе в production обычно добавляют валидацию входа, явную обработку ошибок и тест на граничные случаи вокруг этого сценария.

Почему так удобнее:

  • бизнес-логика не размазывается по контроллерам;
  • правила транзакций концентрируются в одном месте;
  • код проще покрывать тестами.

Связи сущностей на минимальном примере

Частый шаг после первой таблицы — добавить автора заметки:

Код ITЗагрузка примера кода…

Разбор:

  • фрагмент показывает прикладной сценарий по теме раздела: здесь есть входные данные, операции над ними и проверяемый итог.
  • аннотации Entity, Id, GeneratedValue, ManyToOne, JoinColumn включают поведение фреймворка (тестовый раннер, web-маршрутизация, ORM или транзакционные правила).
  • ключевые вызовы GeneratedValue, ManyToOne, JoinColumn задают основную механику этого примера: обработку, проверку и формирование результата.
  • чтение этого блока по шагам дает предсказуемую картину — сначала подготовка данных, затем действие API, затем получение конечного значения.
  • после запуска стоит проверить именно эффект этого примера — вывод, значение переменной, статус ответа, состояние БД или результат теста.
  • при переносе в production обычно добавляют валидацию входа, явную обработку ошибок и тест на граничные случаи вокруг этого сценария.

На этом этапе важно понимать разницу EAGER и LAZY, иначе появляются лишние SQL-запросы и просадки производительности. Полезное продолжение: ввод-вывод и файлы и асинхронность, если API работает под нагрузкой.


Частые ошибки на старте JPA

  1. @Entity без пустого конструктора — Hibernate не сможет создать объект.
  2. Использование ddl-auto=update в production — миграции становятся непредсказуемыми.
  3. Возврат сущностей напрямую во внешний API без DTO — утечки полей и проблемы с lazy loading.
  4. Отсутствие индексов в БД на часто фильтруемые поля.

Дальше

  • связи @OneToMany, @ManyToOne;
  • @Transactional на сервисном слое;
  • JUnit 5 с @DataJpaTest.