предисловие
В этом руководстве мы изучим уровень изоляции транзакций и поведение распространения аннотации @Transactional.
Что такое аннотация @Transcational?
мы можем использовать@Transactional
Аннотация добавляет в метод поддержку транзакций.
Мы можем использовать его, чтобы указать поведение распространения, время ожидания и условия отката установленной транзакции. Кроме того, через него мы также можем указать менеджера транзакций.
Spring создает, фиксирует и откатывает транзакции, создавая прокси-объекты или манипулируя байт-кодами.
В случае управления транзакциями через режим прокси, для вызовов изнутри класса@Transactional
не удастся.
Проще говоря, если у нас естьcallMethod
метод и метод@Transactional
Аннотация отмечена.
Spring завершит этот метод некоторым кодом для управления транзакциями,@Transactional
Реализация аннотации может быть представлена с помощью следующего псевдокода:
// 如有必要则创建事务
createTransactionIfNecessary();
try {
// 调用callMethod方法
callMethod();
// 调用成功则提交事务
commitTransactionAfterReturning();
} catch (exception) {
// 调用失败则回滚事务
completeTransactionAfterThrowing();
throw exception;
}
Как использовать аннотацию @Transcational
Мы можем использовать эту аннотацию для интерфейсов, классов и методов, а также для интерфейсов, классов и методов.@Transactional
Аннотации будут перезаписываться в соответствии с разными приоритетами. Самый низкий приоритет будет перезаписан более высоким. Приоритет переопределения от низкого к высокому:
- интерфейс
- суперкласс
- Добрый
- метод интерфейса
- метод суперкласса
- метод класса
Весна отметит класс на@Transactional
Аннотация относится ко всему классуpublic
метод, поэтому нам не нужно маркировать метод отдельно@Transactional
.
Однако, если мыprivate
илиprotected
Если метод помечен аннотациями @Transcational, то Spring проигнорирует эти аннотации и не выдаст никаких сообщений об ошибках.
Начнем с аннотирования интерфейса аннотацией @Transcational:
@Transactional
public interface TransferService {
void transfer(String user1, String user2, double val);
}
Обычно не рекомендуется отмечать на интерфейсе@Transactional
Аннотацию, однако допустимо использовать в некоторых интерфейсах, таких как: Spring Data@Repository
интерфейс.
Мы можем аннотировать эту аннотацию в определении класса, которая может переопределить аннотацию в интерфейсе или суперклассе.@Transactional
аннотация:
@Service
@Transactional
public class TransferServiceImpl implements TransferService {
@Override
public void transfer(String user1, String user2, double val) {
// ...
}
}
Теперь давайте поместим эту аннотацию непосредственно в определение метода:
@Transactional
public void transfer(String user1, String user2, double val) {
// ...
}
Поведение распространения транзакции
Поведение распространения транзакции определяет границу транзакции нашей бизнес-логики, и Spring запускает или прерывает транзакцию в соответствии с поведением распространения транзакции, которое мы настраиваем.
Весенние звонкиTransactionManager::getTransaction
Затем метод получает или создает транзакцию на основе поведения распространения транзакции, которая поддерживает некоторые изTransactionManager
Поведение распространения транзакций определено в .TransactionManager
Реализовать для поддержки.
REQUIRED
REQUIRED
— поведение распространения транзакций по умолчанию. Для этого поведения распространения Spring проверяет, есть ли активная транзакция в контексте текущего потока.
Новая транзакция создается, если нет активной транзакции.
Если есть активная транзакция, текущая бизнес-логика включается в область действия этой активной транзакции:
@Transactional(propagation = Propagation.REQUIRED)
public void requiredExample(String user) {
// ...
}
из-заREQUIRED
является поведением распространения транзакций по умолчанию, поэтому приведенный выше код можно сократить как:
@Transactional
public void requiredExample(String user) {
// ...
}
Давайте рассмотрим созданиеREQUIRED
Псевдокод для транзакции поведения распространения:
// 检测是否已经位于事务中
if (isExistingTransaction()) {
if (isValidateExistingTransaction()) {
validateExisitingAndThrowExceptionIfNotValid();
}
// 返回已有的事务
return existing;
}
// 否则不使用事务
return createNewTransaction();
SUPPORTS
заSUPPORTS
Другими словами, Spring сначала проверит, есть ли активная транзакция в контексте потока, и использует ее, если активная транзакция есть. Если нет, выполните его без транзакцииsupportsExample()
метод::
@Transactional(propagation = Propagation.SUPPORTS)
public void supportsExample(String user) {
// ...
}
Давайте посмотрим на использованиеSUPPORTS
Псевдокод поведения распространения для создания транзакции:
// 检测是否已经位于事务中
if (isExistingTransaction()) {
if (isValidateExistingTransaction()) {
validateExisitingAndThrowExceptionIfNotValid();
}
// 返回已有的事务
return existing;
}
// 否则不使用事务
return emptyTransaction;
MANDATORY
Когда поведение распространения установлено наMANDATORY
, если Spring не найдет активной транзакции в контексте потока, он выдаст исключение:
@Transactional(propagation = Propagation.MANDATORY)
public void mandatoryExample(String user) {
// ...
}
Давайте посмотрим на псевдокодовое представление этой опции:
// 检测是否已经位于事务中
if (isExistingTransaction())
{
if (isValidateExistingTransaction()) {
validateExisitingAndThrowExceptionIfNotValid();
}
// 返回已有的事务
return existing;
}
// 否则抛出异常
throw IllegalTransactionStateException;
NEVER
Когда поведение распространения установлено наNEVER
, Spring выдает исключение, когда находит активную транзакцию в контексте потока:
@Transactional(propagation = Propagation.NEVER)
public void neverExample(String user) {
// ...
}
Давайте посмотрим на использованиеNEVER
Псевдокод поведения распространения для создания транзакции:
// 检测是否已经位于事务中
if (isExistingTransaction()) {
// 如果位于事务中就抛出异常
throw IllegalTransactionStateException;
}
// 否则不使用事务
return emptyTransaction;
NOT_SUPPORTED
Когда поведение распространения установлено наNOT_SUPPORTED
Когда Spring приостанавливает существующую транзакцию в контексте потока, а затем выполняет ее без использования транзакцииnotSupportedExample()
метод:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void notSupportedExample(String user) {
// ...
}
JTATransactionManager
Менеджер транзакций поддерживает настоящую приостановку транзакций по умолчанию. разное
Диспетчер транзакций имитирует приостановку транзакции, сохраняя ссылку на существующую транзакцию, а затем удаляя ее из контекста потока.
REQUIRES_NEW
Когда поведение распространения установлено наREQUIRES_NEW
Когда Spring обнаруживает, что в контексте потока есть активная транзакция, он сначала приостанавливает ее, а затем создает новую транзакцию для выполнения.requiresNewExample()
метод:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNewExample(String user) {
// ...
}
а такжеNOT_SUPPORTED
Варианты аналогичны, для истинной приостановки транзакции нужно использоватьJTATransactionManager
менеджер транзакций
Реализация псевдокода, соответствующая этой опции:
// 检测是否已经位于事务中
if (isExistingTransaction()) {
// 先挂起已存在的事务
suspend(existing);
try {
// 然后创建一个新事务
return createNewTransaction();
} catch (exception) {
// 如果新事务发生异常则恢复旧事务
resumeAfterBeginException();
throw exception;
}
}
// 没有找到旧事务,直接创建新事务
return createNewTransaction();
NESTED
заNESTED
Например, Spring определяет, есть ли активная транзакция в контексте потока.
Устанавливает точку сохранения в транзакции, если она уже существует, что означает, что если нашаnestedExample()
Если в методе возникнет исключение, транзакция будет отброшена к установленной нами точке сохранения.
Если существование активной транзакции не обнаружено, эта опция работает так же, какREQUIRED
Варианты те же.
DataSourceTransactionManager
Этот вариант поддерживается из коробки. также,JTATransactionManager
Некоторые реализации также поддерживают эту опцию.
JpaTransactionManager
Поддерживается только для соединений JDBCNESTED
опции. Однако, если мы будемnestedTransactionAllowed
установлен флагtrue
, а наш драйвер JDBC также поддерживает функцию точки сохранения, она также работает в транзакциях JPA.JDBC
код доступа.
Наконец, давайте установим поведение распространенияNESTED
:
@Transactional(propagation = Propagation.NESTED)
public void nestedExample(String user) {
// ...
}
Уровень изоляции транзакции
Изоляция является одним из свойств ACID:
- атомарность
- Последовательность
- Изоляция
- Долговечность
Изоляция относится к видимости данных между параллельными транзакциями.
Каждый уровень изоляции предотвращает появление ноля или более параллельных побочных эффектов в параллельных транзакциях:
- Грязные чтения: вы можете читать изменения, которые не были зафиксированы параллельными транзакциями.
- Неповторяющееся чтение: в параллельной транзакции, после того как транзакция изменяет данные строки, многократное чтение строки другими транзакциями может дать разные результаты.
- Фантомное чтение: в параллельных транзакциях, если одна транзакция добавляет или удаляет некоторые строки и фиксирует, другие транзакции могут получить другие результаты после повторного выполнения запроса диапазона.
мы можем пройти@Transactional::isolation
Устанавливает уровень изоляции транзакции. В Spring он может использовать следующие пять значений перечисления:
- DEFAULT
- READ_UNCOMMITTED
- READ_COMMITTED
- REPEATABLE_READ
- SERIALIZABLE
Управление уровнем изоляции в Spring
Уровень изоляции по умолчанию для транзакций в Spring:DEFAULT
. Таким образом, когда Spring создает новую транзакцию, уровень изоляции устанавливается в нашей базе данных. Поэтому, если мы вносим изменения в данные в базе данных, нам нужно обратить на это внимание.
Нам также нужно подумать о том, что происходит, когда каждый метод соответствует разному уровню изоляции транзакции в цепочке вызовов.
Обычно уровень изоляции транзакции также устанавливается при создании транзакции.Если мы не хотим, чтобы методы в нашей цепочке вызовов имели разные уровни изоляции транзакции, мы можем установитьTransactionManager::setValidateExistingTransaction
заtrue
, его функция выражается в псевдокоде как:
// 如果隔离级别不是DEFAULT
if (isolationLevel != ISOLATION_DEFAULT) {
// 当前事务设置的隔离级别与已存在事务的隔离级别不一致则抛出异常
if (currentTransactionIsolationLevel() != isolationLevel) {
throw IllegalTransactionStateException
}
}
Теперь давайте углубимся в различные уровни изоляции и то, что они делают.
READ_UNCOMMITTED
READ_UNCOMMITTED
Это самый низкий уровень изоляции и самая высокая производительность одновременного доступа.На этом уровне будут происходить грязные чтения, неповторяющиеся чтения и фантомные чтения.
Мы можем установить этот уровень изоляции для методов или классов:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void log(String message) {
// ...
}
Постгрес не поддерживаетREAD_UNCOMMITTED
уровень изоляции, который используетREAD_COMMITED
Уровень изоляции как альтернатива. Оракл не поддерживаетREAD_UNCOMMITTED
уровень.
READ_COMMITTED
Второй уровень изоляцииREAD_COMMITTED
. Это предотвращает грязное чтение.
На этом уровне изоляции по-прежнему существуют неповторяющиеся чтения и фантомные чтения, поэтому незафиксированные изменения в параллельных транзакциях на нас не влияют, однако после фиксации транзакции повторные запросы одних и тех же данных могут иметь разные результаты.
Следующий код устанавливает уровень изоляции транзакции наREAD_COMMITTED
:
@Transactional(isolation = Isolation.READ_COMMITTED)
public void log(String message){
// ...
}
REPEATABLE_READ
Третий уровень изоляцииREPEATABLE_READ
. Это предотвращает грязные чтения, неповторяющиеся чтения. Поэтому на нас не влияют незафиксированные изменения в параллельных транзакциях.
Кроме того, когда мы повторно запрашиваем строку данных, мы не получаем других результатов. Но при повторном выполнении запроса диапазона мы можем получить новые добавленные или удаленные строки.
Кроме того, это минимальный уровень, необходимый для предотвращения потери обновлений, когда две или более параллельных транзакций читают и обновляют одну и ту же строку.REPEATABLE_READ
Параллельным транзакциям не разрешен доступ к одной и той же строке данных в одно и то же время. Поэтому потери обновлений не будет.
Следующий код устанавливает уровень изоляции транзакции наREAD_COMMITTED
:
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void log(String message){
// ...
}
REPEATABLE_READ
уровень изоляции по умолчанию в Mysql. Оракл не поддерживаетREPEATABLE_READ
уровень изоляции.
SERIALIZABLE
SERIALIZABLE
Это высший уровень изоляции. Это предотвращает грязные чтения, неповторяющиеся чтения и фантомные чтения.
Поскольку он превращает параллельные транзакции в последовательные, это может привести к самой низкой производительности параллельного доступа.
Другими словами, результат параллельного выполнения набора транзакций такой же, как и при их последовательном выполнении.
Теперь давайте посмотрим, как установить уровень изоляции наSERIALIZABLE
:
@Transactional(isolation = Isolation.SERIALIZABLE)
public void log(String message){
// ...
}
Суммировать
В этом руководстве мы подробно изучили поведение распространения @Transaction. Затем мы узнали о побочных эффектах и уровнях изоляции параллельных транзакций.
Как всегда, вы можетеGitHubНайдите полный код на .