Spring声明式事务让我们从复杂的事务处理中得到解脱。使得我们再也无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。再也无需要我们在与事务相关的方法中处理大量的try…catch…finally代码。
Spring事务机制主要包括声明式事务和编程式事务,在这篇博客中,我们主要说声明式事务的使用。
而我们在使用Spring声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成。我们在进行事务划分时,需要进行事务定义,也就是配置事务的属性。分清楚事务的属性,我们才能合理的使用事务。使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务。
事务属性的定义在TransactionDefinition中定义这些属性,供PlatformTransactionManager使用,这个PlatformTransactionManager是spring事务管理的核心接口。那么我们来看看transaction中事务属性的定义:
<span style="font-family:Times New Roman;">public interface TransactionDefinition {
<span style="white-space:pre"> </span>int getPropagationBehavior();//返回事务的传播行为。
<span style="white-space:pre"> </span>int getIsolationLevel();//返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。
<span style="white-space:pre"> </span>int getTimeout();//返回事务必须在多少秒内完成。
<span style="white-space:pre"> </span>boolean isReadOnly();//事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的。
}</span>
事务的传播行为
首先是事务的传播行为,
我们先宏观来看一下如下spring的事务传播行为:
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
接下来,我们一一来解释一下这些传播行为各自如何来工作的?
PROPAGATION_REQUIRED
支持当前事务,如果当前没有事务,就新建一个事务,这是最常见的选择。代码如下:
情形一:单独使用methodB,methodB加了事务传播行为:
<span style="white-space:pre"> </span>main{
<span style="white-space:pre"> </span>metodB();
<span style="white-space:pre"> </span>}
等价于:
<span style="font-family:KaiTi_GB2312;font-size:18px;">Main{
Connection con=null;
try{
con = getConnection();
con.setAutoCommit(false);
//方法调用
methodB();
//提交事务
con.commit();
}
Catch(RuntimeException ex){
//回滚事务
con.rollback();
}
finally{
//释放资源
closeCon();
}
} </span>
Spring保证在methodB方法中所有的调用都获得到一个相同的连接。在调用methodB时,没有一个存在的事务,所以获得一个新的连接,开启了一个新的事务。
情形二:嵌套调用:methodA 的事务传播行为和methodB的事务传播行为都是required,如下:
<span style="white-space:pre"> </span><span style="font-family:KaiTi_GB2312;">//事务属性 PROPAGATION_REQUIRED
<span style="white-space:pre"> </span>methodA{
<span style="white-space:pre"> </span>……
<span style="white-space:pre"> </span>methodB();
<span style="white-space:pre"> </span>……
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>//事务属性 PROPAGATION_REQUIRED
<span style="white-space:pre"> </span>methodB{
<span style="white-space:pre"> </span>……
<span style="white-space:pre"> </span>}</span>
等价于
<span style="font-family:KaiTi_GB2312;">main{
<span style="white-space:pre"> </span>Connection con = null;
<span style="white-space:pre"> </span>try{
<span style="white-space:pre"> </span>con = getConnection();
<span style="white-space:pre"> </span>methodA();
<span style="white-space:pre"> </span>con.commit();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>catch(RuntimeException ex){
<span style="white-space:pre"> </span>con.rollback();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>finally{
<span style="white-space:pre"> </span>closeCon();
<span style="white-space:pre"> </span>}
} </span>
如上:调用MethodA时,环境中没有事务,所以开启一个新的事务.当在MethodA中调用MethodB时,环境中已经有了一个事务,所以methodB就加入当前事务,mehtodB不会再去开启一个新的事物。
PROPAGATION_SUPPORTS
如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
<span style="font-family:KaiTi_GB2312;font-size:18px;">//事务属性 PROPAGATION_REQUIRED
methodA(){
methodB();
}
//事务属性 PROPAGATION_SUPPORTS
methodB(){
……
}</span>
如上:单纯的调用methodB时,methodB方法是非事务的执行的,以为外层,我们没有添加任何的事务,但是当调用methdA时,在methodA中调用methodB,因为methodA的事务传播行为是required,那么methodB则加入了methodA的事务中执行。
PROPAGATION_MANDATORY
如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
<span style="font-family:Times New Roman;font-size:18px;"> //事务属性 PROPAGATION_REQUIRED
methodA(){
<span style="white-space: pre;"> </span>methodB();
}</span>
<span style="font-family:Times New Roman;font-size:18px;"> //事务属性 PROPAGATION_MANDATORY
methodB(){
<span style="white-space: pre;"> </span>……
}</span>
如上:当单独调用methodB时,因为当前没有一个活动的事务,则会抛出异常throw new IllegalTransactionStateException("Transaction propagation 'mandatory' but no existing transaction found");当调用methodA时,methodB则加入到methodA的事务中,事务地执行。
下篇博客,我们继续看事务身下的四个传播行为。