В папке DOC sql-скрипты и др. полезные файлы.
Док. (ссылки) для изучения:
- Spring Boot Reference Documentation ;
- Spring Framework 6.1.5 Documentation ;
- Spring Framework 3.2.x Reference Documentation ;
- Getting Started Guides ;
- Developing with Spring Boot ;
Для начала проведем предварительную подготовку (первые 3-и шага из предыдущих частей):
Шаг 1. - в файле build.gradle добавим необходимые plugin-ы:
/*
Плагин Spring Boot добавляет необходимые задачи в Gradle
и имеет обширную взаимосвязь с другими plugin-ами.
*/
id 'org.springframework.boot' version '3.1.3'
/*
Менеджер зависимостей позволяет решать проблемы несовместимости
различных версий и модулей Spring-а
*/
id "io.spring.dependency-management" version '1.0.11.RELEASE'
/* Подключим Lombok */
id "io.freefair.lombok" version "8.3"
Шаг 2. - подключаем Spring Boot starter:
/*
Подключим Spring Boot Starter он включает поддержку
авто-конфигурации, логирование и YAML
*/
implementation 'org.springframework.boot:spring-boot-starter'
Шаг 3. - подключаем блок тестирования (Spring Boot Starter Test) (он будет активен на этапе тестирования):
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Шаг 4. - автоматически Gradle создал тестовую зависимость на Junit5 (мы можем использовать как Junit4, так и TestNG):
test {
useJUnitPlatform()
}
Шаг 5. - подключим блок для работы с БД (Spring Boot Starter Data Jpa)
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
Ставим БД PostgreSQL или локальную на машину, или разворачиваем Docker образ с сайта Docker.
Подключаем JPA-Starter, фактически само подключение описано выше на шаге 5., что легко можно наблюдать в build.gradle.
Из меню Gradle мы можем увидеть все зависимости загруженные вместе со spring-boot-starter-data-jpa см. DOC/SpringDataJPAStarterTransitiveDependencies.jpg
Настроим подключение к БД (пока на локальную машину, а не Docker контейнер) в application.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/SpringLessons
username: postgres
password: testadmin
driver-class-name: org.postgresql.Driver
Мы так же можем настроить и JPA см. DOC/SpringBootAndDataBases/DataSourceSetting.txt и application.yml. Чтобы посмотреть результат настроек можно запустить SpringAppRunner.java и изучить содержимое консоли. Упрощенно это выглядит так:
/* Приложение запускается и прогружает все нужные модули, видно как загружается HikariPool */
Bootstrapping Spring Data JPA repositories in DEFAULT mode.
Finished Spring Data repository scanning in 13 ms. Found 0 JPA repository interfaces.
Processing PersistenceUnitInfo [name: default]
HHH000412: Hibernate ORM core version 6.2.7.Final
HHH000406: Using bytecode reflection optimizer
HHH000021: Bytecode provider name : bytebuddy
No LoadTimeWeaver setup: ignoring JPA class transformer
HikariPool-1 - Starting...
HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@2fb25f4c
HikariPool-1 - Start completed.
HHH000021: Bytecode provider name : bytebuddy
HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
Initialized JPA EntityManagerFactory for persistence unit 'default'
Jpa configuration is enabled
/* Тут создаются наши bean-ы */
Closing JPA EntityManagerFactory for persistence unit 'default'
HikariPool-1 - Shutdown initiated...
HikariPool-1 - Shutdown completed.
/* Приложение закончило работу пулы Hikari освобождаются */
Создаем сущности для общения с Hibernate. Чтобы более подробно разобраться (повторить) данный вопрос см. уроки по Hibernate.
- База данных подключена, заполним ее тестовыми данными из: DOC/SqlScripts;
- Создадим сущности для работы с БД (см. пример из уроков по Hibernate);
Немного изменим структуру проекта и приведем в соответствие таблицы БД и Entity классы: database/entity
На данном этапе, мы:
- используя application.yml настроили связь с БД и настроили свойства Hibernate;
- создали Entity классы, см. комментарии внутри классов;
- настроили взаимосвязь (mapping) между сущностями и БД;
Запустив SpringAppRunner.java мы видим, что приложение отработало, как в предыдущем уроке - Hibernate настроен и работает.
См. док.:
Lesson 43 - @Transactional.
Spring Data Jpa позволяет быстро настроить работу с DAO и транзакциями см. TransactionManager.jpg, как уже стало понятно, многие настройки в SpringBoot проходят условно автоматически, в данном случае общением с БД занимается JpaBaseConfiguration. Через менеджера транзакций этой конфигурации (или так, менеджер транзакций конфигурируется в JpaBaseConfiguration) мы можем управлять транзакциями используя аннотации (декларативно) или вручную (TransactionTemplate)
- DOC/AnnotationTransactional - подробное описание аннотационного интерфейса и применения @Transactional;
- DOC/TransactionInSpring - подробное описание такого понятия как транзакция (особенность при работе в Spring);
- DOC/SpringTransactionManagement.txt - статья об управлении транзакциями в Spring;
- DOC/JPA - еще раз о JPA;
- DOC/SpringBootAndDataBases - особенности настройки и работы Spring Data JPA;
Пример работы с аннотациями @Transactional, @Rollback и @Commit приведен в CompanyRepositoryTest.java - особенности применения их читать в комментариях к коду тестового класса.
В случае работы с тестами аннотацию @Transactional обрабатывает TransactionalTestExecutionListener из TestContext см. DOC/TestContextFramework из Spring_part_8.
В нашем примере:
- данный слушатель фиксирует наличие аннотации @Transactional над классом или методом;
- автоматически открывает транзакцию;
- далее выполняется тестовый метод;
- затем происходит откат изменений внесенных нашими тестами;
Такое поведение работает по-умолчанию.
См. док. (пакеты):
- Пакет org.springframework.test.annotation ;
- Пакет org.springframework.transaction.annotation ;
- Пакет org.springframework.boot.autoconfigure.orm.jpa ;
- Spring Data Access ;
С приходом Spring Boot необходимость в аннотации @EnableTransactionManager в Spring приложении отпала. Теперь это перешло в ответственность авто-конфигурации Spring Boot-а. Происходит следующее - когда мы добавляем в проект зависимость spring-boot-starter-..., то подтягивается транзитивная зависимость - spring-boot-autoconfigure, которая содержит в файле spring.factories список авто-конфигураций.
Это файл: org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, содержит конфигурацию, которая будет загружена при подъеме контекста см. DOC/AnnotationTransactional/TransactionalAnnotation.txt
Когда мы ставим @Transactional над методом, Spring создает прокси-класс, который содержит CompanyService, и внедряет вместо нашего объекта этот прокси объект. Этот прокси-класс содержит такие же методы (прокси класс оборачивает вызов метода в транзакцию), что и наш класс, поэтому он подходит для внедрения в bean-ы, которые ожидают CompanyService, см. DOC/CompanyServiceCglibProxy.jpg и DOC/CompanyServiceSpringCglib.jpg
- CompanyService.java - класс в котором аннотирован @Transactional только один метод *.findById();
- CompanyServiceIT.java - интеграционный тест в котором мы проверяем работу метода *.findById();
Запуск тестового метода CompanyServiceIT происходит в режиме отладки DEBUG, дойдя до точки останова мы можем наблюдать следующую картину см. DOC/CompanyServiceSpringCglib.jpg, где явно видно, что вместо оригинального CompanyService создана его CGLIB прокси копия, см. схему реализации DOC/CompanyServiceCglibProxy.jpg.
Подробная документация по @Transactional см. DOC/AnnotationTransactional, а так же другие материалы в разделе DOC.
И так, повторимся, в обычном случае необходимо сделать две вещи:
- убедиться, что наша Configuration Spring сопровождена аннотацией @EnableTransactionManagement (!!! в Spring Boot это будет сделано автоматически !!!);
- убедиться, что мы указали менеджер транзакций в нашей Configuration Spring (это необходимо сделать в любом случае, однако см. отличие Spring Boot ниже).
!!! Единственное отличие Spring Boot заключается в том, что он автоматически устанавливает аннотацию @EnableTransactionManagement и создает ее PlatformTransactionManager для нас — с помощью авто-конфигурации JDBC !!!
- DOC/ProxyByProxyTransaction.jpg - схема описывает взаимодействие прокси-объектов созданных при работе (классов) методов помеченных аннотацией @Transactional. Так же, эта схема хорошо описывает т.н. 'подводный камень' при работе с прокси объектами, см. DOC/AnnotationTransactional/ProxyHowToWorksIs.txt (говорят, что это вопрос часто встречается на собеседованиях), т.е. ситуацию в которой при уже открытой транзакции, другая не открывается (все будет зависеть от настроек @Transactional см. ниже).
Работа аннотации описана:
- (RUS) DOC/AnnotationTransactional;
- (RUS) DOC/SpringTransactionManagement.txt;
- (ENG) Annotation Interface Transactional;
Сами настройки можно посмотреть в структуре интерфейса, некоторые из них описаны ниже:
Propagation - способ "распространения, деления" транзакций, выделяются следующие способы:
- MANDATORY - если есть текущая активная транзакция - выполняется в ней, иначе выбрасывается исключение;
- NESTED - выполняется внутри вложенной транзакции, если есть активная, если нет активной - то аналогично REQUIRED;
- NEVER - выполняется вне транзакции, если есть активная - выбрасывается исключение;
- NOT_SUPPORTED - выполняется вне транзакции - если есть активная, она приостанавливается;
- REQUIRED - (значение по умолчанию) - если есть активная, то выполняется в ней, если нет, то создается новая транзакция;
- REQUIRES_NEW - всегда создается новая транзакция, если есть активная - то она приостанавливается;
- SUPPORTS - если есть активная - то выполняется в ней, если нет - то выполняется не транзакционно;
Уровни или режимы деления, можно настроить в @Transactional следующим образом:
@Transactional(propagation = Propagation.REQUIRED);
... some class ...
@Transactional(propagation = Propagation.REQUIRES_NEW);
... some method...
Правила управления откатом:
- noRollbackFor и noRollbackForClassName - определяет исключения, при которых транзакция НЕ будет откачена;
- rollbackFor и rollbackForClassName - определяет исключения, при которых транзакция БУДЕТ откачена;
Уровни изоляции базы данных, является сложной темой, следует потратить некоторое время, чтобы полностью разобраться в ней:
- См. (RUS): https://github.com/JcoderPaul/Hibernate_Lessons/tree/master/Hibernate_part_8 ;
- См. (ENG): Transaction Isolation ;
Пример применения:
@Transactional(propagation=TransactionDefinition.NESTED,
isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED
)
... some class or method ...
!!! Внимание !!! Когда речь идет о переключении уровней изоляции во время транзакции, мы должны точно знать, какой драйвере JDBC/БД используем, чтобы понять, какие сценарии тем поддерживаются, а какие нет.
Вложенные транзакции в Spring - это точки сохранения JDBC / базы данных (savepoints), для понимания особенностей этого момента см. руководство (ENG): The Java™ Tutorials - Using Transactions
!!! Внимание !!! Поддержка точек сохранения зависит от выбранного драйвера JDBC/базы данных.
- CompanyRepositoryTemplateTest.java - тестовый класс, где применяется TransactionTemplate (см. комментарии в тесте);
- DOC/AnnotationTransactional/TransactionTemplate.txt - краткое описание TransactionTemplate;
См. официальные Guides:
- Getting Started Guides - Эти руководства, рассчитанные на 15–30 минут, содержат быстрые практические инструкции по созданию «Hello World» для любой задачи разработки с помощью Spring. В большинстве случаев единственными необходимыми требованиями являются JDK и текстовый редактор.
- Topical Guides - Тематические руководства предназначенные для прочтения и понимания за час или меньше, содержит более широкий или субъективный контент, чем руководство по началу работы.
- Tutorials - Эти учебники, рассчитанные на 2–3 часа, обеспечивают более глубокое контекстное изучение тем разработки корпоративных приложений, что позволяет вам подготовиться к внедрению реальных решений.