关于Spring事务的问题
2016-08-04 17:57
232 查看
场景:在业务开发中有一个方法,这个方法是无论异常还是失败都要让他执行成功,比如说数据库要有一个计数的功能,只要调用了这个方法就会去把数据库表字段加1;假如我们在其他service里面调用了这个方法,而且还是多线程高并发的情况下,这个方法就很容易出问题;这个方法的步骤为读取改值,并且修改加1。
1.在多线程的情况下,容易读取到相同的值,比如在读取该值的时候,还没有来得及加1,
2.在整个业务方法没有执行成功的时候,该值不会去加1,所以其他线程看到的全部都是第一次读取过来的值;
这个时候解决思路是,读取值和修改值的方法加上同步,让它变成原子操作;
这里第二个不好解决,就是关于spring的事务配置问题,先来看看我们项目的事务声明方式
这里事务会在manage里面加上WithTx方法生效,如果我把某个manage方法不加上WithTx然后这个时候在用ibatis的
事务是不是就可以让这个方法不受spring管理而单独生效了,结果发现并不能,这里虽然没有加WithTx后缀,但是他仍然受spring的AOP管理,这个时候他并没有释放出他的权利,所以你Ibatis的事务根本不起作用,那么怎么解决这个办法了,这里要用到
在Spring的事务管理中,我们可以使用@Transactional这一annotation来对事务进行声明式的设定。具体而言,就是在类或者方法前添加@Transactional并传入属性参数以获取所需要的Transaction特性。Spring中的@Transactional有5个属性:Propagation、Isolation、Rollback Rules、Timeout和Read-Only,其中Propagation属性定义了Transaction的边界 — 是否使用Transaction、在Transaction已存在的情况下如何表现等。
@Transactional中Propagation属性有7个选项可供选择:
Propagation.MANDATORY 。当前方法必须在已经定义的Transaction中运行,如果没有已定义的Transaction则抛出异常。
Propagation.NESTED 。如果没有已定义的Transaction,当前方法新开一个Transaction并在该Transaction中运行。如果存在已定义的Transaction,当前方法在嵌套事务(Nested Transaction)中运行 — 嵌套事务中可以定义储存点,因此可以独立于外部的Transaction而进行rollback。
Propagation.NEVER 。当前方法不应在Transaction中运行,如果存在已经定义的Transaction则抛出异常。
Propagation.NOT_SUPPORTED 。当前方法不应在Transaction中运行,如果存在已经定义的Transaction,则该Transaction暂停(挂起)直至该方法运行完毕。
Propagation.REQUIRED 。 默认值 。当前方法必须在Transaction中运行。如果存在已经定义的Transaction,则该方法在已定义的Transaction中运行;如果不存在已经定义的Transaction,则该方法新开一个Transaction并在其中运行。
Propagation.REQUIRES_NEW 。当前方法必须在新开的Transaction中运行。如果存在已经定义的Transaction,则该已定义的Transaction暂停直至新开的Transaction执行完毕。
Propagation.SUPPORTS 。当前方法不需要在Transaction中运行,但如果存在已经定义的Transaction,则该方法也可以在Transaction中正常执行。
观察以下两个定义了@Transactional的方法,innerMethod()模拟了Transaction已经存在的情况,outMethod()则模拟了不存在已定义Transaction的情况:
对于这两个方法,定义不同的Propagation属性值所产生的效果如下(Propagation.NESTED的情况较为复杂,在此忽略):
只要在该方法上加上@Transactional(propagation = Propagation.NOT_SUPPORTED),这样Spring就会把它对这个方法事务的管理权释放出去,然后我们就可以在里面使用Ibatis事务去进行管理了。
1.在多线程的情况下,容易读取到相同的值,比如在读取该值的时候,还没有来得及加1,
2.在整个业务方法没有执行成功的时候,该值不会去加1,所以其他线程看到的全部都是第一次读取过来的值;
这个时候解决思路是,读取值和修改值的方法加上同步,让它变成原子操作;
这里第二个不好解决,就是关于spring的事务配置问题,先来看看我们项目的事务声明方式
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:owms-business/sqlMap-config.xml"/> </bean> <!--事务配置--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!--方法后缀是WithTx加入事务--> <tx:method name="*WithTx" propagation="REQUIRED" rollback-for="java.lang.Throwable"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="myPointCut" expression="execution(* com.business.manage.*.*.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointCut"/> </aop:config>
这里事务会在manage里面加上WithTx方法生效,如果我把某个manage方法不加上WithTx然后这个时候在用ibatis的
<span style="white-space:pre"> </span>sqlMapClient.startTransaction(); //事务开始 <span style="white-space:pre"> </span>方法写这里 sqlMapClient.commitTransaction(); //提交事务,从开始到这里是一个事务 sqlMapClient.endTransaction(); //事务结束
事务是不是就可以让这个方法不受spring管理而单独生效了,结果发现并不能,这里虽然没有加WithTx后缀,但是他仍然受spring的AOP管理,这个时候他并没有释放出他的权利,所以你Ibatis的事务根本不起作用,那么怎么解决这个办法了,这里要用到
@Transactional中的propagation属性
在Spring的事务管理中,我们可以使用@Transactional这一annotation来对事务进行声明式的设定。具体而言,就是在类或者方法前添加@Transactional并传入属性参数以获取所需要的Transaction特性。Spring中的@Transactional有5个属性:Propagation、Isolation、Rollback Rules、Timeout和Read-Only,其中Propagation属性定义了Transaction的边界 — 是否使用Transaction、在Transaction已存在的情况下如何表现等。
Propagation属性
@Transactional中Propagation属性有7个选项可供选择:Propagation.MANDATORY 。当前方法必须在已经定义的Transaction中运行,如果没有已定义的Transaction则抛出异常。
Propagation.NESTED 。如果没有已定义的Transaction,当前方法新开一个Transaction并在该Transaction中运行。如果存在已定义的Transaction,当前方法在嵌套事务(Nested Transaction)中运行 — 嵌套事务中可以定义储存点,因此可以独立于外部的Transaction而进行rollback。
Propagation.NEVER 。当前方法不应在Transaction中运行,如果存在已经定义的Transaction则抛出异常。
Propagation.NOT_SUPPORTED 。当前方法不应在Transaction中运行,如果存在已经定义的Transaction,则该Transaction暂停(挂起)直至该方法运行完毕。
Propagation.REQUIRED 。 默认值 。当前方法必须在Transaction中运行。如果存在已经定义的Transaction,则该方法在已定义的Transaction中运行;如果不存在已经定义的Transaction,则该方法新开一个Transaction并在其中运行。
Propagation.REQUIRES_NEW 。当前方法必须在新开的Transaction中运行。如果存在已经定义的Transaction,则该已定义的Transaction暂停直至新开的Transaction执行完毕。
Propagation.SUPPORTS 。当前方法不需要在Transaction中运行,但如果存在已经定义的Transaction,则该方法也可以在Transaction中正常执行。
Propagation属性实例
观察以下两个定义了@Transactional的方法,innerMethod()模拟了Transaction已经存在的情况,outMethod()则模拟了不存在已定义Transaction的情况:@Transactional public void outMethod() { exampleDAO.doSomething(); innerMethod(); } @Transactional public void innerMethod() { exampleDAO.doElse(); }
对于这两个方法,定义不同的Propagation属性值所产生的效果如下(Propagation.NESTED的情况较为复杂,在此忽略):
Propagation属性 | outMethod | innerMethod |
---|---|---|
Propagation.MANDATORY | .抛出异常 | .在outMethod的Transaction中运行 |
Propagation.NEVER | .不在Transaction中运行 | .抛出异常 |
Propagation.NOT_SUPPORTED | .不在Transaction中运行 | .outMethod的Transaction暂停直至innerMethod执行完毕 |
Propagation.REQUIRED ( 默认值 ) | .新开一个Transaction并在其中运行 | .在outMethod的Transaction中运行 |
Propagation.REQUIRES_NEW | .新开一个Transaction并在其中运行 | .outMethod的Transaction暂停直至innerMethod中新开的Transaction执行完毕 |
Propagation.SUPPORTS | .不在Transaction中运行 | .在outMethod的Transaction中运行 |
相关文章推荐
- 关于Spring事务回滚的问题
- 关于Spring 声明式事务处理时,throws exception不回滚的问题
- 关于Spring事务的原理,以及在事务内开启线程,连接池耗尽问题.
- Spring整合hibernate关于控制事务的问题
- 关于Spring的@Transactional注解失效以及事务无法回滚问题
- 关于Spring事务代理类型转换问题($ProxyXX cannot be cast to 类型)(一)
- 关于Spring事务的一个问题
- 关于Spring事务回滚的问题
- 关于Spring事务回滚的问题
- 关于Spring事务回滚的问题
- 关于Spring4+Hibernate4架构升级-整合的细节处理-由nested transactions not supported错误解决引申到事务嵌套问题处理
- 关于spring+mybatis操作数据库多次异常 事务不被spring管理问题
- 关于Spring事务回滚的问题
- 关于Spring事务回滚的问题
- 关于spring3.0 后的 事务注解管理指定数据源问题
- 关于Spring整合Hibernate事务问题
- 关于Spring事务代理类型转换问题($ProxyXX cannot be cast to 类型)(二)
- 关于配置事务时的spring service注入问题
- 关于使用Spring声明式事务时,在类的内部方法互相调用时,Spring无法拦截内部方法调用,导致事务不起作用的问题研究