近期遇到了一个spring事务导致的问题,所以写了几个小程序了解了一下事务的传播特性,以下分别举样例分别看看事务的传播特性。
事务的几种传播特性
假设没有事务则开启
Java代码 /** * TransactionTestService test1和test2配有事务(PROPAGATION_REQUIRED) */ public interface TransactionTestService { //事务属性 PROPAGATION_REQUIRED public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); test2(); } //事务属性 PROPAGATION_REQUIRED public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); throw new Exception(); } } /** * main */ public class TransactionTestMain { public static void main(String[] args) throws Exception{ TransactionTestService transactionTestService = (TransactionTestService)context.getBean("transactionTestService"); try { transactionTestService.test1(); } catch (Exception e) { } } }上述代码中test1()和test2()都配有PROPAGATION_REQUIRED事务属性,test1()内部调用test2(),这样test1()和test2()方法将都处于同一事务之中,当在test2()中抛出异常。会导致test1()和test2()方法中的事务都回滚。
可是,假设test1()方法对调用test2()时捕获异常,结果会是如何的呢? test1应该能正常写入没问题,那么test2呢?
Java代码 //test1()中捕获test2()抛出的异常 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); try { test2(); }catch (Exception e) { } }最后的结果test2()也将会正常的写入。
事实上,上面情况中假设仅仅是test1()配有PROPAGATION_REQUIRED事务属性。test2()不配置不论什么事务属性。发生的结果也是一致的。上面的情形相当于:
Java代码 public static void main(String[] args) throws Exception{ Connection con = null; try { con=getConnection(); con.setAutoCommit(false); transactionTestService.test1(); //test1()和test2()处于同一事务之中 con.commit(); } catch (Exception e) { con.rollback(); } finally { closeCon(); } }上述test1()和test2()是同一个类的两个方法,那么要是它们处于不同类呢?
Java代码 //main函数不变,test1()中调用test2()地方换成调用还有一个类中配有PROPAGATION_REQUIRED事务属性的方法 //不正确otherService.test2()捕获异常的情形就不是必需说了。必然都回滚。 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); try { otherService.test2(); //PROPAGATION_REQUIRED事务属性 } catch (Exception e) { } } //otherService.test2() public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); throw new Exception(); }上述相同捕获了异常,可是结果会如何呢? 结果有点出乎意料。与之前test1(),test2()处于同一类的情形不同,这个时候。两个方法都将回滚。而且在调用test1()的地方会抛出以下异常。
这是因为子事务在回滚的时候已经将主事务标记成了rollback-only,这样导致主事务在提交的时候就会抛出以下这个异常。
另外:假设otherService.test2()没有配置不论什么事务属性,那么test2()抛出异常的时候,将导致test1()和test2()都回滚。
Java代码 org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only上述情况网上查询到一种解决方案是在transactionManager中将globalRollbackOnParticipationFailure 设置为false(默认是true)。可是这样又带来还有一个问题,子事务也给一并提交了(这个时候子事务产生异常,不想提交),详细的解决方案还没找到。可是我认为不应该将这两个配置在同一事务中。
Xml代码 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref local="dataSource" /> </property> <property name="globalRollbackOnParticipationFailure" value="false" /> </bean>
TransactionTestService的test1()配有PROPAGATION_SUPPORTS事务属性,我们知道这样的情形下假设配的是PROPAGATION_REQUIRED事务属性。那么test1()将新建事务执行。可是PROPAGATION_SUPPORTS属性不会新建,这样的情形下,test1()方法将正常提交。那假设是外层事务配有事务呢?例如以下所看到的,此种情形test2()将处于事务之中了。
Java代码 //PROPAGATION_REQUIRED事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); test2(); } //PROPAGATION_SUPPORTS事务属性 public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); throw new Exception(); }
Java代码 //情形1:PROPAGATION_REQUIRED事务属性 情形2:不配不论什么事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); otherService.test2(); } //otherService的test2()配置PROPAGATION_MANDATORY事务属性 public void test2() throws Exception { avRequestTunnel.insertAvRequest(); throw new Exception(); } //test1()处于情形2时抛出异常,test2()不可以提交,可是test1()却是可以成功提交的 org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
上述情况,当test1()处于情形1时,会是的test1()和test2()都处于事务其中;test1()处于情形2时,就会抛异常,可是这个时候test2()不可以提交,可是test1()却是可以成功提交的。此外,另一点,注意上述test2()是处于另一个类中的,假设是处于同一个类。那么PROPAGATION_MANDATORY不会由于外层是否有事务而抛异常。
Java代码 //情形1:PROPAGATION_REQUIRED事务属性 情形2:不配不论什么事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); otherService.test2(); } //otherService的test2()配置PROPAGATION_REQUIRES_NEW事务属性 public void test2() throws Exception { avRequestTunnel.insertAvRequest(); throw new Exception(); }
上述情况,test1()处于情形2时test2()新建事务。这点没有问题。那假设test1()处于情形1呢?也就是说test1()已经有事务了。结果是test2()处于新的事务中,怎么确定是处于新的事务中呢?看以下代码:
Java代码 //test1配置PROPAGATION_REQUIRED事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); otherService.test2(); //PROPAGATION_REQUIRES_NEW事务属性 } //otherService.test2() public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); throw new Exception(); }假设是在同一事务中。那么情形将同于讲述PROPAGATION_REQUIRED属性时的情形。test1()和test2()都将回滚,而且抛出异常事务被打上rollback-only标记的异常。可是这里,结果就是test2()回滚,test1正常提交,所以otherService.test2()处于新的事务中。注意:上述情况test2()也是还有一个类的方法,假设属于同一类,也就是test1()和test2()处于同一个类。test1()中调用test2(),那么配置的PROPAGATION_REQUIRES_NEW将无效(跟test2()什么事务属性都没配置一样)。
可是假设是main函数中直接调用test2(),那么还是会起一个新的事务。
第二种证明test1()和test2()处于不同事务的方式是,在test2()不抛出异常,然后再test1()调用了test2()之后,抛出异常,最后结果是()回滚。test2()正常提交。
假设otherService.test2() 没有配置事务属性,那么test2()抛出异常时,test1()和test2()都回滚。可是如今test2()配置了PROPAGATION_NOT_SUPPORTED事务属性,那么test2()将以非事务执行,而test1()继续执行在事务中。也就是说,此时,test1()回滚,test2()正常提交。
注意:假设test1()和test2()在同一类中。那么test2()的PROPAGATION_NOT_SUPPORTED失效。
当然上述情况。假设是test1()和test2()在同一类中。那么PROPAGATION_NEVER也将失效。
最easy弄混淆的事实上是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED。
PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被全然 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务開始运行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续运行。还有一方面, PROPAGATION_NESTED 開始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 嵌套事务開始运行时, 它将取得一个 savepoint. 假设这个嵌套事务失败, 我们将回滚到此 savepoint.。嵌套事务是外部事务的一部分, 仅仅有外部事务结束后它才会被提交。
由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大差别在于, PROPAGATION_REQUIRES_NEW 全然是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 假设外部事务 commit, 潜套事务也会被 commit, 这个规则相同适用于 roll back.
Java代码 //test1配置PROPAGATION_REQUIRED事务属性 public void test1() throws Exception{ avInfoTaskTunnel.insertAvInfoTask(); otherService.test2(); //PROPAGATION_NESTED事务属性 } //otherService.test2() public void test2() throws Exception{ avRequestTunnel.insertAvRequest(); }上述情况。假设otherService.test2()配置PROPAGATION_REQUIRES_NEW事务属性。这样test1()回滚,可是test2()正常提交,由于这是两个事务。可是假设otherService.test2()配置PROPAGATION_NESTED事务属性。然后test1()和test2()将回滚。
版权声明:本文博客原创文章,博客,未经同意,不得转载。
转载于:https://www.cnblogs.com/bhlsheji/p/4668176.html
相关资源:数据结构—成绩单生成器