Проект

Общее

Профиль

TransactionManager управление транзакциями

@Transactional(isolation=Isolation.DEFAULT)

transaction_manager.jpg

По умолчанию установлено Isolation.DEFAULT
В большинстве случаев, вы будете использовать настройки по-умолчанию до тех пор, пока у вас не появится особые требования.
Сообщает менеджеру транзакции (tx), что для текущего tx должен использоваться следующий уровень изоляции. Должен быть установлен в точке, откуда начинается tx, потому что мы не можем изменить уровень изоляции после запуска tx.

  • DEFAULT Использовать уровень изоляции установленный по умолчанию в базовой базе данных.
  • READ_COMMITTED (чтение фиксированных данных): Постоянная, указывающая, что “грязное” чтение предотвращено; могут возникать неповторяющееся чтение и фантомное чтение.
  • READ_UNCOMMITTED (чтение незафиксированных данных): Этот уровень изоляции указывает, что транзакция может считывать данные, которые еще не удалены другими транзакциями.
  • REPEATABLE_READ (повторяемость чтения): Постоянная, указывающая на то, что “грязное” чтение и неповторяемое чтение предотвращаются; может появляться фантомное чтение.
  • SERIALIZABLE (упорядочиваемость): Постоянная, указывающая, что “грязное” чтение, неповторяемое чтение и фантомное чтение предотвращены.

Что означают эти жаргонизмы: “грязное” чтение, фантомное чтение или повторяемое чтение?

“Грязное” чтение (Dirty Read): транзакция «A» производит запись. Между тем, транзакция «B» считывает ту же самую запись до завершения транзакции A. Позже транзакция A решает откатится, и теперь у нас есть изменения в транзакции B, которые несовместимы. Это грязное чтение. Транзакция B работала на уровне изоляции READ_UNCOMMITTED, поэтому она могла считывать изменения, внесенные транзакцией A до того, как транзакция завершилась.
Неповторяющееся чтение (Non-Repeatable Read): транзакция «A» считывает некоторые записи. Затем транзакция «B» записывает эту запись и фиксирует ее. Позже транзакция A снова считывает эту же запись и может получить разные значения, поскольку транзакция B вносила изменения в эту запись и фиксировала их. Это неповторяющееся чтение.
Фантомные чтение (Phantom Read): транзакция «A» читает ряд записей. Между тем, транзакция «B» вставляет новую запись в этот же ряд, что и транзакция A. Позднее транзакция A снова считывает тот же диапазон и также получит запись, которую только что вставила транзакция B. Это фантомное чтение: транзакция извлекала ряд записей несколько раз из базы данных и получала разные результирующие наборы (содержащие фантомные записи).

@Transactional(name=) Если в файле конфигурации есть несколько TransactionManager, вы можете использовать это свойство, чтобы указать, какой менеджер транзакций выбран.

@Transactional(timeout=60) По умолчанию используется таймаут, установленный по умолчанию для базовой транзакционной системы. Сообщает менеджеру tx о продолжительности времени, чтобы дождаться простоя tx, прежде чем принять решение об откате не отвечающих транзакций.

@Transactional(propagation=Propagation.REQUIRED) Если не указано, распространяющееся поведение по умолчанию — REQUIRED.

Другие варианты: REQUIRES_NEW , MANDATORY , SUPPORTS , NOT_SUPPORTED , NEVER и NESTED .

REQUIRED Указывает, что целевой метод не может работать без активного tx. Если tx уже запущен до вызова этого метода, то он будет продолжаться в том же tx, или новый tx начнется вскоре после вызова этого метода.

REQUIRES_NEW Указывает, что новый tx должен запускаться каждый раз при вызове целевого метода. Если уже идет tx, он будет приостановлен, прежде чем запускать новый.

MANDATORY Указывает, что для целевого метода требуется активный tx. Если tx не будет продолжаться, он не сработает, выбросив исключение.

SUPPORTS Указывает, что целевой метод может выполняться независимо от tx. Если tx работает, он будет участвовать в том же tx. Если выполняется без tx, он все равно будет выполняться, если ошибок не будет. Методы, которые извлекают данные, являются лучшими кандидатами для этой опции.

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

NEVER Указывает, что целевой метод вызовет исключение, если выполняется в транзакционном процессе. Этот вариант в большинстве случаев не используется в проектах.

@Transactional (rollbackFor=Exception.class) Значение по умолчанию: rollbackFor=RunTimeException.class В Spring все классы API бросают RuntimeException, это означает, что если какой-либо метод не выполняется, контейнер всегда откатывает текущую транзакцию. Проблема заключается только в проверенных исключениях. Таким образом, этот параметр можно использовать для декларативного отката транзакции, если происходит Checked Exception.

@Transactional (noRollbackFor=IllegalStateException.class) Указывает, что откат не должен происходить, если целевой метод вызывает это исключение.

Теперь последним, но самым важным шагом в управлении транзакциями является размещение аннотации @Transactional . В большинстве случаев возникает путаница, где должна размещаться аннотация: на сервисном уровне или на уровне DAO?

@Transactional: Сервисный или DAO уровень?

Сервис — лучшее место для размещения @Transactional, сервисный уровень должен содержать поведение варианта использования на уровне детализации для взаимодействия пользователя, которое логически переходит в транзакцию.

Существует много CRUD-приложений, у которых нет существенной бизнес-логики, имеющих сервисный уровень, который просто передает данные между контроллерами и объектами доступа к данным, что не является полезным. В этих случаях мы можем поместить аннотацию транзакции на уровень DAO.

Поэтому на практике вы можете поместить их в любом месте, это зависит от вас.

Кроме того, если вы поместите @Transactional в уровень DAO и если ваш уровень DAO будет повторно использоваться разными службами, тогда будет сложно разместить его на уровне DAO, так как разные службы могут иметь разные требования.

Если ваш сервисный уровень извлекает объекты с помощью Hibernate, и, допустим, у вас есть ленивые инициализации в определении объекта домена, тогда вам нужно открыть транзакцию на сервисном уровне, иначе вам придется столкнуться с LazyInitializationException, брошенным ORM.

Рассмотрим другой пример, когда ваш уровень обслуживания может вызывать два разных метода DAO для выполнения операций БД. Если ваша первая операция DAO завершилась неудачей, остальные две могут быть переданы, и вы закончите несогласованное состояние БД. Аннотирование на сервисном уровне может спасти вас от таких ситуаций.

Reference manual: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions