事务的传播机制

1.什么是事务:

  事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么成功,要么失败)。

  数据库向用户提供保存当前程序状态的方法,叫事务提交(commit;当事务执行过程中,使数据库忽略当前的状态并回到前面保存的状态的方法叫事务回滚(rollback

2.事务的传播机制

  以spring的事务传播机制为例子:

  Spring事务机制主要包括声明式事务编程式事务,此处侧重讲解声明式事务,编程式事务在实际开发中得不到广泛使用,仅供学习参考。

  Spring声明式事务让我们从复杂的事务处理中得到解脱。使得我们再也无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。再也无需要我们在与事务相关的方法中处理大量的try…catch…finally代码。我们在使用Spring声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成。我们在进行事务划分时,需要进行事务定义,也就是配置事务的属性。


   spring在TransactionDefinition接口中定义了七个事务传播行为:

    • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
    • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
    • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
    • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
    • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
    • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

  (1)PROPAGATION_REQUIRED  如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。

  Java代码:

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

  使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务。

  单独调用methodB方法:

Java代码

main{
  metodB(); 
}

相当于

Java代码

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.

  执行效果相当于:

Java代码

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与不使用事务有少许不同。

Java代码:

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

  单纯的调用methodB时,methodB方法是非事务的执行的。当调用methdA时,methodB则加入了methodA的事务中,事务地执行。

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

Java代码:

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

  当单独调用methodB时,因为当前没有一个活动的事务,则会抛出异常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);当调用methodA时,methodB则加入到methodA的事务中,事务地执行。

(4)PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。

Java代码:

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

Java代码:

main(){ 
  methodA(); 
}

相当于

Java代码:

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方法在调用methodB方法后的doSomeThingB方法失败了,而methodB方法所做的结果依然被提交。而除了methodB之外的其它代码导致的结果却被回滚了。使用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作为事务管理器。需要JDBC 驱动的java.sql.Savepoint类。有一些JTA的事务管理器实现可能也提供了同样的功能。使用PROPAGATION_NESTED,还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true;而nestedTransactionAllowed属性值默认为false;

Java代码:

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

如果单独调用methodB方法,则按REQUIRED属性执行。如果调用methodA方法,相当于下面的效果:

Java代码:

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方法,保存当前的状态到savepoint。如果methodB方法调用失败,则恢复到之前保存的状态。但是需要注意的是,这时的事务并没有进行提交,如果后续的代码(doSomeThingB()方法)调用失败,则回滚包括methodB方法的所有操作。

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

  Spring 默认的事务传播行为PROPAGATION_REQUIRED,它适合于绝大多数的情况。假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。

 

posted @ 2019-05-01 18:49  myseries  阅读(41514)  评论(4编辑  收藏  举报