@Transactional

Тема дорожной карты · Spring Boot

Транзакции в Spring обеспечивают единую абстракцию поверх разнообразных API управления транзакциями JDBC, JPA/Hibernate, JTA и других менеджеров ресурсов, обеспечивая декларативное разграничение транзакций в Spring Boot через аннотацию @Transactional. Когда @Transactional применяется к методу @Service, AOP-прокси Spring оборачивает вызов метода: он начинает транзакцию до вызова и фиксирует при успешном возврате, или откатывает при RuntimeException (и ошибках) по умолчанию — поведение отката настраивается атрибутами rollbackFor и noRollbackFor. Транзакции в Spring поддерживают стандартные уровни изоляции ACID (READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE) и поведения распространения (REQUIRED, REQUIRES_NEW, NESTED, SUPPORTS), настраиваемые непосредственно в аннотации @Transactional, обеспечивая тонкий контроль над тем, как транзакции разделяются между несколькими вызовами @Service. Критическая ловушка транзакций в Spring — самовызов: вызов @Transactional-метода из того же бина обходит AOP-прокси, поэтому граница транзакции не применяется — решение состоит во внедрении собственного прокси бина или реструктуризации кода. Spring Boot авто-конфигурирует JpaTransactionManager при наличии spring-boot-starter-data-jpa, а @DataJpaTest использует транзакции Spring для отката каждого тест-кейса, сохраняя базу данных чистой без ручного teardown.

Как это работает

@Transactional: Spring Data JPA оборачивает Hibernate (JPA-реализация). @Entity маппит Java-класс → DB-таблица; JpaRepository<T, ID> даёт CRUD + paging + sorting бесплатно. Method names типа findByEmailAndStatus парсятся в queries; сложные queries — @Query (JPQL или native SQL). Relations: @OneToMany, @ManyToOne, @ManyToMany — дефолтный fetch LAZY (one-to-many) или EAGER (many-to-one); знаменитая N+1 проблема прячется здесь. @Transactional оборачивает service-методы в DB-транзакцию.

Когда применять

Все relations LAZY по дефолту + JOIN FETCH или entity graphs для eager-загрузки только когда надо. Projections (DTO/interface) для read-only endpoints — пропускают Hibernate entity overhead. Мониторьте N+1 (Hibernate stats, p6spy или Datasource Proxy в тестах). Для heavy-read — raw JdbcTemplate или jOOQ — JPA отличен для CRUD, хуже для сложной аналитики.

Типичные ошибки

Ловушки @Transactional: N+1 queries (lazy loading в цикле); EAGER на каждом relation (огромные cascading joins); @Transactional на private/self-invocation (proxy не intercept-ит — аннотация ничего не делает); auto-DDL (spring.jpa.hibernate.ddl-auto=update) в prod (тихий + опасный schema drift).

Связанные понятия

Полезные ресурсы