Spring的@Transactional 声明式事务失效场景

声明式事务(@Transactional)

基于AOP面向切面的,它将具体业务与事务处理部分解耦,代码侵入性很低,所以在实际开发中声明式事务用的比较多。但是声明式事务用不对在某些场景下容易失效。

代码 demo

@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {

    @Override
    @Transactional
    public String saveLog(LogAggregateRoot log){
        MsLog msLog = new MsLog();
        msLog.setUserId(log.getUserId());
        return super.save(msLog).getLogId().toString();
    }
}

目标类没有被spring管理

demo

//@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {

    @Override
    @Transactional
    public String saveLog(LogAggregateRoot log){
        MsLog msLog = new MsLog();
        msLog.setUserId(log.getUserId());
        return super.save(msLog).getLogId().toString();
    }
}

因为@Transactional 声明式事务基于spring的aop实现的,LogDAOImpl没有被spring管理,就无法开始事务。

同一个类内方法调用

demo

@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {

     @Override
    public String save(){
        LogAggregateRoot log = new LogAggregateRoot();
        return this.saveLog(log);
    }

    @Override
    @Transactional
    public String saveLog(LogAggregateRoot log){
        MsLog msLog = new MsLog();
        msLog.setUserId(log.getUserId());
        return super.save(msLog).getLogId().toString();
    }
}

因为@Transactional 声明式事务基于spring的aop实现的,同一个类内部方法调用没有经过代理,外部调用方法save() -> ProxyLogDAOImpl.save(),方法save()在类内部调用方法saveLog(LogAggregateRoot log) -> LogDAOImpl.saveLog(LogAggregateRoot log),造成事务失效。

事务方法不是 public

demo

@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {

    @Override
    @Transactional
    private String saveLog(LogAggregateRoot log){
        MsLog msLog = new MsLog();
        msLog.setUserId(log.getUserId());
        return super.save(msLog).getLogId().toString();
    }
}

spring进行事务代理时候获取方法的属性,但是非public的方法无法被获取到属性,不会开始事务,故事务失效。

异常被业务代码 catch

demo

@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {

    @Override
    @Transactional
    private String saveLog(LogAggregateRoot log){
        try {
          MsLog msLog = new MsLog();
          msLog.setUserId(log.getUserId());
          return super.save(msLog).getLogId().toString();
        }
        catch (Exception e) {
          logger.error("LogDAOImpl exception", e);
        }
       return null;
    }
}

类的业务代码中直接把异常catch住了,Spring自然就 catch不到异常,因此事务回滚的逻辑就不会执行,事务就失效了。

rollbackFor属性配置错误

demo

@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {

    @Override
    @Transactional
    private String saveLog(LogAggregateRoot log){
        MsLog msLog = new MsLog();
        msLog.setUserId(log.getUserId());
        throw new Exception("保存日志错误");
        return super.save(msLog).getLogId().toString();
    }
}

因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下
@Transactional(rollbackFor = Exception.class)

事务传播属性使用错误

demo

@Service
public class LogDAOImpl extends AbstracRepositoryImpl implements ILogDAO {

    @Lazy
    @Autowired
    private LogDAOImpl logDAOImpl;

    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public String save(){
        LogAggregateRoot log = new LogAggregateRoot();
        return logDAOImpl.saveLog(log);
    }

    @Override
    public String saveLog(LogAggregateRoot log){
        MsLog msLog = new MsLog();
        msLog.setUserId(log.getUserId());
        return super.save(msLog).getLogId().toString();
    }
}

我们先对 Propagation类中 Spring事务传播机制进行总结:
PROPAGATION_REQUIRED:要求使用事务,如果当前没有事务,则创建一个新的事务,如果当前存在事务,就加入该事务,该设置是默认也是最常用的设置。
PROPAGATION_SUPPORTS:支持使用事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
PROPAGATION_MANDATORY:强制使用事务,如果当前存在事务,就加入该事务,如果当前不存在事务,则抛出异常。
PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
PROPAGATION_NOT_SUPPORTED:不支持事务,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER:不允许使用事务,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED:内嵌事务,如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
上述 save()方法的事务传播机制是 Propagation.SUPPORTS,也就是当前方法有事务就假如事务,不存在事务则以非事务执行,因为 saveLog(LogAggregateRoot log)方法不存在事务,所以该方法就以非事务执行,因此事务失效。
此案例是典型的 Spring事务传播机制使用错误,我们只需要将 @Transactional(propagation = Propagation.SUPPORTS) 修改成 @Transactional(propagation = Propagation.REQUIRED),事务就可以生效了。

数据库不支持事务

re:mysql
MySQL 数据库的MyISAM 引擎不支持事务,而 InnoDB 引擎支持事务。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

后端马农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值