Spring 事务机制详解
Spring 事务机制主要包括 声明式事务
和编程式事务
,这篇文章侧重讲解声明式事务,编程式事务用的不多,仅供参考。
Spring 声明式事务让我们从复杂的事务处理中得到解脱。使得我们再也无需去处理获得连接,关闭连接,事务提交和回滚等这些操作。我们在使用 Spring 声明式事务时,有一个非常重要的概念就是事务属性。它由事务传播行为,隔离级别,事务的超时值和事务只读标志组成。
Spring 的 TransactionDefinition 接口中定义这些属性,以提供 PlatfromTransactionManager 使用,PlatformTransactionManager 是 Spring 事务管理的核心接口。
public interface TransactionDefinition {
int getPropagationBehavior(); //返回事务的传播行为。
int getIsolationLevel(); //返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。
int getTimeout(); //返回事务必须在多少秒内完成。
boolean isReadOnly(); //事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的。
}
-
TransactionDefinition 接口中定义的五个隔离级别:
ISOLATION_DEFAULT 这是一个 PlatfromTransactionManager 默认隔离级别,使用数据库的隔离级别,另外四个与 JDBC 的隔离级别相对应。
ISOLATION_READ_UNCOMMITTED 这是事务隔离的最低级别,读未提交。
ISOLATION_READ_COMMITTED 读已提交
ISOLATION_REPEATABLE_READ 可重复读
ISOLATION_SERIALIZABLE 序列化
-
TransactionDefinition 定义了 7 个事务传播行为
-
PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
单独调用 methodB 方法:
main { metodB(); } 相当于 Main { Connection con=null; try{ con = getConnection(); con.setAutoCommit(false); //方法调用 methodB(); //提交事务 con.commit(); } Catch(RuntimeException ex) { //回滚事务 con.rollback(); } finally { //释放资源 closeCon(); } }
Spring 保证在 methodB 方法中所有的调用都获得到一个相同的连接。在调用 methodB 时,没有一个存在的事务,所以获得了一个新的连接,开启了一个新的事务。
单独调用 methodA 时,在 methodA 内又会调用 methodB。执行效果相当于:
Main { Connection con = null; try { con = getConnection(); methodA(); con.commit(); } catch(RuntimeException ex) { con.rollback(); } finally { closeCon(); } }
调用 methodA 时,环境中没有事务,所以开启了一个新的事务。
当 methodA 中调用 methodB 时,环境中已经有了一个事务,所以 methodB 就加入当前事务。 -
PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
// 事务属性 PROPAGATION_REQUIRED methodA() { methodB(); } // 事务属性 PROPAGATION_SUPPORTS methodB() { …… }
单纯的调用 methodB 时,methodB 方法是非事务执行的。当调用 methodA 时,methodB 则加入 methodA 的事务中执行。
-
PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
// 事务属性 PROPAGATION_REQUIRED methodA() { methodB(); } //事务属性 PROPAGATION_MANDATORY methodB() { …… }
单独调用 methodB 时,因为当前没有一个活动的事务,则会抛出异常 throw new IllegalTransactionStateException,当调用 methodA 时,methodB 则会加入到 methodA 的事务中执行。
-
PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
//事务属性 PROPAGATION_REQUIRED methodA() { doSomeThingA(); methodB(); doSomeThingB(); } //事务属性 PROPAGATION_REQUIRES_NEW methodB() { …… }
调用 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 称为内层事务。从上面的代码可以看出,ts1 和 ts2 是两个独立的事务,互不相干。ts2 是否成功并不依赖于 ts1.
-
PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
-
PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常。
-
PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
// 事务属性 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 { //释放资源 }
这里是一个嵌套事务的概念,内层事务是依赖于外层事务的。外层事务失败时,会回滚那层事务所做的动作。而那层事务失败并不会引起外层事务的回滚。
-
PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW 的区别
它们都是一个嵌套事务。如果不存在一个活动的事务,都会开启一个新的事务。使用 PROPAGATION_REQUIRES_NEW 时,内层事务和外层事务就像两个独立的事务,互不干扰。
使用 PROPAGATION_NESTED 时,外层事务的回滚可以引起内层事务的回滚,而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。