@Transactional 使用说明

一.说明

 @Tranasctional注解是Spring 框架提供的声明式注解事务解决方案,我们在开发中使用事务保证方法对数据库操作的原子性,要么全部成功,要么全部失败,在使用@Transactional注解时需要注意以下问题:

1. @Transactional  注解只能用在public 方法上,如果用在protected或者private的方法上,不会报错,但是该注解不会生效。外部调用才能生效,内部调用不生效。

2. @Transactional注解只能回滚非检查型异常,具体为RuntimeException及其子类和Error子类,可以从Spring源码的DefaultTransactionAttribute类里找到判断方法rollbackOn。

	@Override
	public boolean rollbackOn(Throwable ex) {
		return (ex instanceof RuntimeException || ex instanceof Error);
	}

3. @Transactional 使用rollbackFor 属性来定义回滚的异常类型,使用 propagation 属性定义事务的传播行为。如:   回滚Exception类的异常,事务的传播行为支持当前事务,当前如果没有事务,那么会创建一个事务,默认的propagation是REQUIRED

 @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)

4. @Transactional注解不能回滚被try{}catch() 捕获的异常。

5. @Transactional注解只能对在被Spring 容器扫描到的类下的方法生效。 


二.事务的传播行为

事务行为 说明
PROPAGATION_REQUIRED 如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
PROPAGATION_SUPPORTS 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
PROPAGATION_MANDATORY 必须在一个已有的事务中执行,否则抛出异常
PROPAGATION_REQUIRES_NEW 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
PROPAGATION_NOT_SUPPORTED 容器不为这个方法开启事务
PROPAGATION_NEVER 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED 类似的操作。

三.业务场景案例

需求:每一个checker的调用都需要开启事务(doChecking方法),但是EqFundEqInvestmentLimitChecker 的这个checker里面需要用到EqPortAverageDateServiceImpl .prepareEqPortPctData()方法的保存数据

解决:单独提交方法需要放在不用的服务类里,使用@Transactional的propagation属性来实现隔离事务。设置propagation等于Propagation.REQUIRES_NEW,表示开启单独事务,不受其他事务影响。这样就可以让prepareEqPortPctData()作为一个父事务里面单独的子事务,执行完会将数据保存到数据库,这样checker就可以拿到新保存的数据作处理。

@Service
public class EqPortAverageDateServiceImpl implements EqPortAverageDateService{

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void prepareEqPortPctData(LimitExceptionContext limitExceptionContext) {
            this.saveBatch();
            System.out.println("业务逻辑");
        }
    }
}

@Component
public class TradeOrderCheckingServiceImpl {

    @Transactional(rollbackFor = Exception.class)
    public void dochecking(LimitExceptionContext limitExceptionContext) {
        EqFundEqInvestmentLimitChecker.checking(limitExceptionContext);
    }
}

@Component
public class EqFundEqInvestmentLimitChecker {
    public void checking(LimitExceptionContext limitExceptionContext) {
        eqPortAverageDateService.prepareEqPortPctData(limitExceptionContext);
        prepareData();
        setException();
    }
}

Tips:

PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:

它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。 
使用 PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA事务管理器的支持。

使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED时,需要JDBC 3.0以上驱动及1.4以上的JDK版本支持。其它的JTATrasactionManager实现可能有不同的支持方式。

PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行。

另一方面, PROPAGATION_NESTED 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。

由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back.


注:本篇文章结合了其他我觉得写的比较好的文章总结而成,如需要去看源码解析的可以自行前往以下地址:@Transactional注解详细使用_Dream_it_possible!的博客-CSDN博客_@transactional注解

Spring 事务Transaction源码深度解析_Dream_it_possible!的博客-CSDN博客_spring事务源码深度解析 感谢‍ 

你可能感兴趣的:(Java,java,spring,开发语言)