Подробное объяснение механизма транзакций Spring

Java задняя часть база данных Spring

Механизм транзакций Spring в основном включает声明式事务和编程式事务, здесь основное внимание уделяется декларативным транзакциям.Программные транзакции не используются широко в фактической разработке и предназначены только для справки по обучению.

Декларативные транзакции Spring освобождают нас от сложной обработки транзакций. сделай нас再也无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作. Нам больше не нужно иметь дело с большим количеством кода try...catch...finally в методах, связанных с транзакциями. Когда мы используем декларативные транзакции Spring, очень важной концепцией являются атрибуты транзакций.事务属性通常由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成. Когда мы разделяем транзакцию, нам нужно определить транзакцию, то есть настроить свойства транзакции.

Ниже приводится подробное объяснение четырех атрибутов транзакций, которое предназначено только для справки:

Spring определяет эти свойства в интерфейсе TransactionDefinition для PlatfromTransactionManager,PlatfromTransactionManager是Spring事务管理的核心接口.

public interface TransactionDefinition {
  int getPropagationBehavior(); //返回事务的传播行为。
  int getIsolationLevel(); //返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。
  int getTimeout(); //返回事务必须在多少秒内完成。
  boolean isReadOnly(); //事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的。
}
  1. В интерфейсе TransactionDefinition определены пять уровней изоляции:

ISOLATION_DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别, используя уровень изоляции транзакций базы данных по умолчанию.Остальные четыре соответствуют уровню изоляции JDBC;

ISOLATION_READ_UNCOMMITTED 这是事务最低的隔离级别, что позволяет другой транзакции видеть незафиксированные данные этой транзакции. Этот уровень изоляции производит грязные чтения, неповторяемые чтения и фантомные чтения.

ISOLATION_READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取. Другая транзакция не может прочитать незафиксированные данные транзакции. Этот уровень изоляции транзакций позволяет избежать грязных чтений, но может иметь место неповторяемое чтение и фантомное чтение.

ISOLATION_REPEATABLE_READ 这种事务隔离级别可以防止脏读,不可重复读. Но фантом можно прочитать. В дополнение к обеспечению того, чтобы транзакцию не удалось прочитать данные, которые не представлены другим бизнесом, он также гарантирует, что следующие условия избегают (не повторяются).

ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别. Транзакции обрабатываются для последовательного выполнения.除了防止脏读,不可重复读外,还避免了幻像读。

  1. В интерфейсе TransactionDefinition определены семь вариантов поведения распространения транзакций:

(1)PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务. Если транзакции нет, запустите новую транзакцию.

// 事务属性 PROPAGATION_REQUIRED
methodA {
   ……
   methodB();
   …… 
}
// 事务属性 PROPAGATION_REQUIRED
methodB {
   ……
}

Используя декларативные транзакции Spring, Spring использует АОП для поддержки декларативных транзакций.Он автоматически решает, открывать ли транзакцию до вызова метода в соответствии с атрибутами транзакции, и решает, следует ли зафиксировать или откатить транзакцию после выполнения метода.

Вызовите только метод methodB:

main {
   metodB();
}
相当于
Main {
   Connection con=null;
   try{
     con = getConnection();
     con.setAutoCommit(false);
     //方法调用
     methodB();
     //提交事务
     con.commit();
   } Catch(RuntimeException ex) {
     //回滚事务
     con.rollback();
   } finally {
     //释放资源
     closeCon();
   }
}

Весна гарантирует, что все вызовы в методе метода получают то же соединение. При вызове MEDICB,没有一个存在的事务,所以获得一个新的连接,开启了一个新的事务.

Когда MethodA вызывается отдельно, MethodB вызывается внутри MethodA. Эффект выполнения эквивалентен:

Main {
   Connection con = null;
   try {
     con = getConnection();
     methodA();
     con.commit();
   } catch(RuntimeException ex) {
     con.rollback();
   } finally {
     closeCon();
   }
}

При вызове MethodA в среде нет транзакции, поэтому запускается новая транзакция.当在MethodA中调用MethodB时,环境中已经有了一个事务,所以methodB就加入当前事务.

(2)PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行. Но для диспетчеров транзакций, синхронизированных с транзакциями, PROPAGATION_SUPPORTS немного отличается от неиспользования транзакций.

// 事务属性 PROPAGATION_REQUIRED
methodA() { 
   methodB();
}
// 事务属性 PROPAGATION_SUPPORTS
methodB() {
   ……
}

При простом вызове methodB метод methodB выполняется без транзакций.当调用methdA时,methodB则加入了methodA的事务中执行.

(3)PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常.

// 事务属性 PROPAGATION_REQUIRED
methodA() {
   methodB();
}
//事务属性 PROPAGATION_MANDATORY 
methodB() { 
   ……
}

Когда Methodb используется отдельно,因为当前没有一个活动的事务,则会抛出异常throw new IllegalTransactionStateException("Transaction propagation 'mandatory' but no existing transaction found");При вызове Methoda MessageB затем добавляется к выполнению методики транзакции.

(4)PROPAGATION_REQUIRES_NEW 总是开启一个新的事务. Если транзакция уже существует, приостановите существующую транзакцию.

//事务属性 PROPAGATION_REQUIRED
methodA() { 
   doSomeThingA();
   methodB();
   doSomeThingB();
}
//事务属性 PROPAGATION_REQUIRES_NEW
methodB() {
   ……
}
main() {
   methodA();
}

эквивалентно:

main() {
   TransactionManager tm = null;
   try { 
     // 获得一个JTA事务管理器 
     tm = getTransactionManager();
     tm.begin(); 
     // 开启一个新的事务
     Transaction ts1 = tm.getTransaction();
     doSomeThing(); 
     tm.suspend(); // 挂起当前事务 
     try {
       tm.begin();// 重新开启第二个事务 
       Transaction ts2 = tm.getTransaction();
       methodB();
       ts2.commit();// 提交第二个事务
     } catch(RunTimeException ex) {
        ts2.rollback(); // 回滚第二个事务
     } finally { 
        // 释放资源
     }
     // methodB执行完后,复恢第一个事务
     tm.resume(ts1);
     doSomeThingB(); 
     ts1.commit();// 提交第一个事务
   } catch(RunTimeException ex) {
     ts1.rollback();// 回滚第一个事务
   } finally {
     //释放资源
   }
}

Здесь я положилts1称为外层事务,ts2称为内层事务. Как видно из приведенного выше кода,ts2与ts1是两个独立的事务,互不相干。Ts2是否成功并不依赖于ts1. Если метод methodA терпит неудачу после вызова метода doSomeThingB метода methodB, результат метода methodB все еще отправляется. Результаты, вызванные другим кодом, кроме метода B, откатываются.使用PROPAGATION_REQUIRES_NEW,需要使用JtaTransactionManager作为事务管理器.

(5)PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务. С PROPAGATION_NOT_SUPPORTED вам также необходимо использовать JtaTransactionManager в качестве менеджера транзакций.

(6)PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常;

(7)PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行. Это вложенная транзакция, при использовании драйвера JDBC 3.0 в качестве диспетчера транзакций поддерживается только DataSourceTransactionManager. Требуется класс java.sql.Savepoint для драйвера JDBC. Существуют некоторые реализации менеджера транзакций JTA, которые могут обеспечивать ту же функциональность. Чтобы использовать PROPAGATION_NESTED, необходимо также установить для свойства nestedTransactionAllowed компонента PlatformTransactionManager значение true, а значение по умолчанию для свойства nestedTransactionAllowed — false;

// 事务属性 PROPAGATION_REQUIRED
methodA() {
   doSomeThingA();
   methodB();
   doSomeThingB();
}
//事务属性 PROPAGATION_NESTED
methodB() {
   ……
}

如果单独调用methodB方法,则按REQUIRED属性执行. Если вызывается метод methodA, это эквивалентно следующему эффекту:

main() {
    Connection con = null;
    Savepoint savepoint = null;
    try{
      con = getConnection();
      con.setAutoCommit(false);
      doSomeThingA();
      savepoint = con2.setSavepoint();
      try{
        methodB();
      } catch(RuntimeException ex) {
        con.rollback(savepoint);
      } finally {
        //释放资源 
      }
      doSomeThingB();
      con.commit();
    } catch(RuntimeException ex) {
      con.rollback();
    } finally {
      //释放资源
     }
}

Перед вызовом метода methodB вызовите метод setSavepoint, чтобы сохранить текущее состояние в точку сохранения. Если вызов метода methodB завершился неудачно, восстановите ранее сохраненное состояние. Однако следует отметить, что в этот момент транзакция еще не зафиксирована.Если последующий код (метод doSomeThingB()) не будет вызван, все операции, включая метод methodB, будут отброшены.

嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚.

Разница между PROPAGATION_NESTED и PROPAGATION_REQUIRES_NEW:

Они очень похожи, как и вложенная транзакция, если нет активной транзакции, начнется новая транзакция.使用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样После совершения внутренней транзакции внешняя транзакция не может откатить ее. Две транзакции не влияют друг на друга. Две транзакции не являются настоящей вложенной транзакцией. В то же время требуется поддержка менеджера транзакции JTA.

При использовании PROPAGATION_NESTED,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务. Когда DataSourceTransactionManager использует точку сохранения для поддержки PROPAGATION_NESTED, требуются драйвер JDBC версии 3.0 или выше и JDK версии 1.4 или выше. Другие реализации JTA TrasactionManager могут иметь другую поддержку.

PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. Эта транзакция будет полностью зафиксирована или откатана без зависимостей от внешней транзакции, у нее есть своя область изоляции, свои блокировки и т. д. Когда внутренняя транзакция начнет выполняться, внешняя транзакция будет приостановлена, а когда внутренняя транзакция завершится, внешняя транзакция Транзакция будет продолжать выполняться.

С другой стороны, PROPAGATION_NESTED запускает «вложенную» транзакцию, которая является настоящей подтранзакцией уже существующей транзакции.潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepointПогруженная транзакция является частью внешней транзакции и будет зафиксирована только после завершения внешней транзакции.

Видно, что самая большая разница между PROPAGATION_REQUIRES_NEW и PROPAGATION_NESTED заключается в том, чтоPROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back. PROPAGATION_REQUIRED должно быть нашим первым поведением при распространении транзакций. Он может удовлетворить потребности большинства наших дел.