这是我参与8月更文挑战的第9,活动详情查看:8月更文挑战
常见的是Spring管理事务,所以以Spring事务传播机制为例
参考资料:
1 事务传播概述
事务传播: 当一个事务方法被另一个事务方法调用时,必须指定事务怎么传播.具体选择传播行为如下表:
| 传播行为 | 说明 |
|---|---|
| propagation_requierd | 如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中(Spring事务传播默认) |
| propagation_supports | 支持当前事务,如果没有当前事务,就以非事务方法执行 |
| propagation_mandatory | 使用当前事务,如果没有当前事务,就抛出异常 |
| propagation_required_new | 新建事务,如果当前存在事务,把当前事务挂起 |
| propagation_not_supported | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
| propagation_never | 以非事务方式执行操作,如果当前事务存在则抛出异常 |
| propagation_nested | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作 |
2 事务详情
Spring事务机制包括,编程式和声明式, 在工作中常用的是声明式,即通过添加事务注解,使用Spring的AOP切面编程解析的方式.下面案列以注解形式说明.
1 PROPAGATION_REQUIRED
如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
//事务属性 PROPAGATION_REQUIRED
methodA{
……
methodB();
……
}
//事务属性 PROPAGATION_REQUIRED
methodB{
……
}
1 单独调用方法B
metodB();
编程式代码:
Connection con=null;
try{
// 建立连接
con = getConnection();
con.setAutoCommit(false);
// 方法调用
methodB();
// 提交事务
con.commit();
}
Catch(RuntimeException ex){
// 回滚事务
con.rollback();
}
finally{
// 释放资源
closeCon();
}
在调用methodB方法时,获得同一个相同的连接,没有存在的事务,就新建一个事务维护
2 方法A调用方法B
methodA{
...
methodB();
...
}
编程式代码:
Connection con=null;
try{
// 建立连接
con = getConnection();
con.setAutoCommit(false);
// 方法调用
methodA();
// 提交事务
con.commit();
}
Catch(RuntimeException ex){
// 回滚事务
con.rollback();
}
finally{
// 释放资源
closeCon();
}
在调用方法A时,没有事务,新建一个事务维护. 方法A中调用方法B时, 因为已存在事务,所以方法B加入到当前事务中,不新建事务,且事务一旦回滚,方法A和方法B中,状态都会回滚.
2 PROPAGATION_SUPPORTS
如果存在一个事务,支持当前事务,如果没有当前事务,就以非事务方法执行.
//事务属性 PROPAGATION_REQUIRED
methodA(){
methodB();
}
//事务属性 PROPAGATION_SUPPORTS
methodB(){
……
}
1 单独调用方法B
metodB();
单独调用方法B,因为当前没有事务,所以以非事务执行.
2 方法A调用方法B
methodA{
...
methodB();
...
}
编程式代码:
Connection con=null;
try{
// 建立连接
con = getConnection();
con.setAutoCommit(false);
// 方法调用
methodA();
// 提交事务
con.commit();
}
Catch(RuntimeException ex){
// 回滚事务
con.rollback();
}
finally{
// 释放资源
closeCon();
}
在调用方法A时,没有事务,新建一个事务维护. 方法A中调用方法B时, 因为已存在事务,所以方法B加入到当前事务中,且事务一旦回滚,方法A和方法B中,状态都会回滚.
如果方法A中没有事务,则方法B以非事务执行.
3 PROPAGATION_MANDATORY
//事务属性 PROPAGATION_REQUIRED
methodA(){
methodB();
}
//事务属性 PROPAGATION_MANDATORY
methodB(){
……
}
1单独调用方法B
单独调用方法B时,当前没有事务,会抛出异常throw new IllegalTransactionStateException
2方法A调用方法B
methodA{
...
methodB();
...
}
如果方法A有事务,在调用方法B时,会加入到当前事务中; 如果方法A没有事务,则会抛出上述异常.
4 PROPAGATION_REQUIRED_NEW
创建一个新的事务。如果有事务已经存在,则将这个存在的事务挂起.
//事务属性 PROPAGATION_REQUIRED
methodA(){
doSomeThingA();
methodB();
doSomeThingB();
}
//事务属性 PROPAGATION_REQUIRES_NEW
methodB(){
……
}
1单独调用方法B
当前没有事务,调用方法B会创建一个新的事务.
2方法A调用方法B
methodA();
编程式代码:
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{
// 释放资源
}
在双层嵌套的事务中, 外层和内层的事务, 互相独立,互不影响.使用PROPAGATION_REQUIRES_NEW,需要使用JtaTransactionManager作为事务管理器。
5 PROPAGATION_NOT_SUPPORTED
总是非事务地执行,并挂起任何存在的事务.(需要使用JtaTransactionManager作为事务管理器)
1 单独调用方法B
当前没有事务,调用方法B会以非事务执行;当前有事务,方法B同样以非事务执行.
2方法A调用方法B
methodA();
编程式代码:
TransactionManager tm = null;
try{
// 获得一个JTA事务管理器
tm = getTransactionManager();
// 开启一个新的事务
tm.begin();
Transaction ts1 = tm.getTransaction();
doSomeThing();
// 挂起当前事务
tm.suspend();
methodB();
// methodB执行完后,复恢第一个事务
tm.resume(ts1);
doSomeThingB();
// 提交第一个事务
ts1.commit();
}
catch(RunTimeException ex){
// 回滚第一个事务
ts1.rollback();
}
finally{
// 释放资源
}
6 PROPAGATION_NEVER
总以非事务地执行,如果存在一个活动事务,则抛出异常.
1 单独调用方法B
当前没有事务,调用方法B会以非事务执行;当前有事务,则抛出异常.
2 方法A调用方法B
方法A没有事务,调用方法B会以非事务执行;方法A有事务,则抛出异常.
7 PROPAGATION_NESTED
如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行.
(使用PROPAGATION_NESTED,还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true;而nestedTransactionAllowed属性值默认为false;)
//事务属性 PROPAGATION_REQUIRED
methodA(){
doSomeThingA();
methodB();
doSomeThingB();
}
//事务属性 PROPAGATION_NESTED
methodB(){
……
}
1 单独调用方法B
单独调用方法A,相当于REQUIRED属性,即Spring默认的属性.
2 方法A调用方法B
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{
//释放资源
}
当调用方法B时,设置回滚点, 调用方法B,如果调用失败,则回滚到之前的状态. 代码继续向下运行.
如果后续代码出现问题,则会进行全部的回滚,包括方法B.
即内层方法B调用错误,只会回滚到方法B之前的状态,不会影响方法B外面的内容.
外层方法调用错误. 会导致全部回滚,即方法A中所有和方法B.
8 总结
Spring的默认事务是PROPAGATION_REQUIRED.如果业务中的方法都添加了事务注解,说明方法都配Spring的事务管理,在整个调用的链路中,所有方法都是在同一个事务中.