@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事务源码深度解析 感谢🙇‍ 

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
嵌套使用@Transactional注解的正确方式是: 1. 在外层方法上添加@Transactional注解,并设置propagation属性为REQUIRES_NEW,表示外层方法的事务将会被挂起,内层方法会开启新的事务。 2. 在内层方法上添加@Transactional注解,并设置propagation属性为REQUIRED,表示内层方法的事务将会使用外层方法的事务。 举个例子来说明,在Service层中有两个方法,一个是添加用户的方法addUser,另一个是添加用户地址的方法addUserAddress。这两个方法都需要开启事务,但是添加用户地址的方法应该在添加用户的事务中执行。因此,可以使用嵌套的方式来管理事务,具体代码如下: ``` @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Autowired private UserAddressDao userAddressDao; @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public void addUser(User user) { userDao.addUser(user); // 调用添加用户地址的方法 addUserAddress(user.getId(), "北京市朝阳区"); } @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED) public void addUserAddress(Long userId, String address) { userAddressDao.addUserAddress(userId, address); } } ``` 在上面的代码中,添加用户的方法addUser使用了嵌套的方式来管理事务。在外层方法中,使用了@Transactional注解,并设置了propagation属性为REQUIRES_NEW,表示添加用户的方法将会开启新的事务。在内层方法中,使用了@Transactional注解,并设置了propagation属性为REQUIRED,表示添加用户地址的方法将会使用外层方法的事务。 使用嵌套的方式来管理事务时,需要注意以下几点: 1. 内层方法的事务将会受外层事务的影响,如果外层事务回滚,内层事务也会被回滚。 2. 内层方法的异常将会被传播到外层方法中,如果内层方法抛出异常,外层方法也会抛出异常。 3. 在使用嵌套的方式来管理事务时,需要特别注意事务的一致性问题,避免产生数据不一致的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值