Spring事务失效场景的详细整理

目录
  • 前言
  • 数据库引擎不支持事物
  • 方法不是 public 的
  • 自身调用问题
  • 不支持事物
  • 异常被吃掉
  • 异常类型错误
  • 总结

前言

项目中用Spring的 @Transactional 注解控制事务,使用中时常出现事物不生效的场景,本文仅限于日常项目开发中的点滴整理总结,总结以下几点,以备后续参考排查;可能不全,列举出来希望可以帮助有需要的同学,避免踩坑。

数据库引擎不支持事物

这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。

根据 MySQL 的官方文档:

https://dev.mysql.com/doc/refman/5.5/en/storage-engine-setting.html

从 MySQL 5.5.5 开始的默认存储引擎是:InnoDB,之前默认的都是:MyISAM,所以这点要值得注意,底层引擎不支持事务是硬伤。

没有被 Spring 管理

// @Service (此注解不能去掉)
public class AccountServiceImpl implements AccountService {
@Transactional
public void inser(Account account) {
// insert account
}
}

如果此时把 @Service 注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。

方法不是 public 的

以下来自于Spring 官方文档:

When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.

意思就是 @Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以考虑开启 AspectJ 代理模式。

自身调用问题

看下面代码

@Service
public class AccountServiceImpl implements AccountService {
public void insert(Account account) {
insertAccount(account);
}
@Transactional
public void insertAccount(Account account) {
// insert account
}
}

insert方法上面没有加 @Transactional 注解,调用有 @Transactional 注解的 insertAccount 方法,insertAccount 方法上的事务其实是不管用的。

再看下面的代码

@Service
public class AccountServiceImpl implements AccountService {
@Transactional
public void insert(Account account) {
insertAccount(account);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertAccount(Account account) {
// insert account
}
}

这次在 insert 方法上加了 @Transactional,insertAccount 加了 REQUIRES_NEW 新开启一个事务,那么新开的事务管用么?

这两个例子的答案是:不管用!

因为它们发生了自身调用,就调该类自己的方法,而没有经过 Spring 的代理类,默认只有在外部调用事务才会生效,这也是老生常谈的经典问题了。

数据源没有配置事物管理器

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource)
{
return new DataSourceTransactionManager(dataSource);
}

如上面所示,当前数据源若没有配置事务管理器,照样会失效!

不支持事物

@Service
public class AccountServiceImpl implements AccountService {
@Transactional
public void insert(Account account) {
insertAccount(account);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void insertAccount(Account account) {
// insert account
}
}

Propagation.NOT_SUPPORTED: 表示不以事务运行,当前若存在事务则挂起,详细的可以参考InnoDB的事务隔离级别和传播机制。

都主动不支持以事务方式运行了,那事务生效也是白搭!

异常被吃掉

这个是比较常见的场景

@Service (此注解不能去掉)
public class AccountServiceImpl implements AccountService {
@Transactional
public void inser(Account account) {
try {
// insert account
} catch {
}
}
}

把异常吃了,然后又不抛出来,事务就无法回滚!

异常类型错误

@Service (此注解不能去掉)
public class AccountServiceImpl implements AccountService {
@Transactional
public void inser(Account account) {
try {
// insert account
} catch {
throw new Exception("新增错误");
}
}
}

这样事务也是不生效的,因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下,如:

@Transactional(rollbackFor = Exception.class)

这个配置仅限于 Throwable 异常类及其子类。

查阅资料,其他失效的场景需注意:1) 像文件导入数据库,用多线程控制;可参考查询spring 多线程事务的问题 2)SpringBoot+Shiro引起事务失效

总结

本文总结了几种事务失效的场景,其实发生最多就是自身调用、异常被吃、异常抛出类型不对这三个了;像文章开头说的那样,本文不一定总结得全,只是根据经验总结常见的事务失效的场景。

到此这篇关于Spring事务失效场景的文章就介绍到这了,更多相关Spring事务失效场景内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 从Spring源码解析事务失效的原因

    目录 一.前言 二.方法不是 public 的 三.内部方法间调用导致事务失效 四.异常类型是否配置正确 五.异常被catch住 一.前言 1.Bean是否是代理对象 2.入口函数是否是public的 3.数据库是否支持事务(Mysql的Mvlsam不支持事务),行锁才支持事务 4.切点是否配置正确 5.内部方法间调用导致事务失效 因为this不是代理对象,可以配置 expose-proxy="true",就可以通过AopContext.currentProxy()获取到当前类的代理对

  • Spring事务失效场景原理及解决方案

    1.事务失效-自身调用(通过REQUIRES.REQUIRES_NEW传播属性):自身调用即调该类自己的方法. 同类OrderServiceImpl 中 doSomeThing()方法 不存在事务,该方法去调用本类中的存在事务注解的 insertAndUpdateOrderInfo() 方法.但是insertAndUpdateOrderInfo() 其实是无法保证预想的事务性. 示列验证: OrderServiceImpl.insertAndUpdateOrderInfo方法中upateData

  • Spring事务失效的几种原因

    数据库引擎不支持事务 在MySQL数据库中有几种引擎(InnoDB,MyISAM,Memory等等),仅仅InnoDB支持事务,如果数据库底层都不支持事务的话,那么再怎么折腾都是白搭. @transactional加在private方法上 @Transactional只能加在public方法上,如果需要在private方法中加入事务,可以使用Aspect配transactionManager使用. 本类方法调本类另一个方法 例如: @Service public class UserServic

  • Spring事务失效问题分析及解决方案

    这篇文章主要介绍了Spring事务失效问题分析及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 隔离级别 在 TransactionDefinition.java 接口中,定义了"四种"的隔离级别枚举: /** * [Spring 独有]使用后端数据库默认的隔离级别 * * MySQL 默认采用的 REPEATABLE_READ隔离级别 * Oracle 默认采用的 READ_COMMITTED隔离级别 */ int ISOL

  • Spring事务失效场景的详细整理

    目录 前言 数据库引擎不支持事物 方法不是 public 的 自身调用问题 不支持事物 异常被吃掉 异常类型错误 总结 前言 项目中用Spring的 @Transactional 注解控制事务,使用中时常出现事物不生效的场景,本文仅限于日常项目开发中的点滴整理总结,总结以下几点,以备后续参考排查;可能不全,列举出来希望可以帮助有需要的同学,避免踩坑. 数据库引擎不支持事物 这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用

  • 8个Spring事务失效场景详解

    目录 前言 Spring事务原理 Spring事务失效场景 1. 抛出检查异常 2. 业务方法本身捕获了异常 3. 同一类中的方法调用 4. 方法使用 final 或 static关键字 5. 方法不是public 6. 错误使用传播机制 7. 没有被Spring管理 8. 多线程 总结 前言 作为Java开发工程师,相信大家对Spring种事务的使用并不陌生.但是你可能只是停留在基础的使用层面上,在遇到一些比较特殊的场景,事务可能没有生效,直接在生产上暴露了,这可能就会导致比较严重的生产事故.

  • Spring事务失效场景实例详解

    1.Spring事务最终依赖的数据库的事务,如果用的是mysql的话,执行引擎要是innodb;因为只有innoDB 支持事务. 2.Spring的事务是原理是aop,所以加事务所在bean是要Spring容器管理的:自己new出来的对象肯定是不行的. 3.Spring事务标签@Transactional必须注解在public方法上.private.protected.default以及finally修饰的方法或者类,以及静态方法,事务都会失效的. 4.同一个类中内部方法调用,事务会失效的.调用

  • Spring事务失效的各种场景(13种)

    目录 一.访问权限 二.方法用final修饰 三.方法内部调用 四.没有被spring管理 五.多线程调用 六.设计的表不支持事务 七.没有开启事务 八.错误的事务传播 九.自己捕获了异常 十.手动抛出别的异常 十一.自定义回滚异常 十二.嵌套事务回滚过头 十三.编程式事务 一.访问权限 Java的访问权限主要是:private.default.protected.public,它们的权限则是依次变大.如果我们在开发的时候定义错误的访问权限,就会导致事务出现问题 @Service public

  • 一篇文章带你了解spring事务失效的多种场景

    目录 前言 一 事务不生效 1.访问权限问题 2. 方法用final修饰 3.方法内部调用 4.未被spring管理 5.多线程调用 6.表不支持事务 7.未开启事务 二 事务不回滚 1.错误的传播特性 2.自己吞了异常 3.手动抛了别的异常 4.自定义了回滚异常 5.嵌套事务回滚多了 三 其他 1 大事务问题 2.编程式事务 总结 前言 对于从事java开发工作的同学来说,spring的事务肯定再熟悉不过了. 在某些业务场景下,如果一个请求中,需要同时写入多张表的数据.为了保证操作的原子性(要

  • 解决try-catch捕获异常信息后Spring事务失效的问题

    一.首先在Spring Boot项目中,手动添加异常方法进行测试 @Transactional(rollbackFor=Exception.class) //表示此方法有异常时触发Spring事务 @Override public CommonResult<User> saveUser(User user) { int insert = baseMapper.insert(user); try { // 添加异常,并进行捕获 int a = 10/0; }catch (Exception e)

  • Spring事务失效的一种原因关于this调用的问题

    PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务.如果没有事务则开启事务: PROPAGATION_REQUIRES_NEW:总是开启一个新的事务.如果一个事务已经存在,则将这个存在的事务挂起: 问题: Spring中一个没有事务的方法A调用一个默认事务(PROPAGATION_REQUIRED)的方法B时,如果使用this调用方法B,方法B抛出RuntimeException,此时方法B事务未生效,不会回滚. @Service public class Employ

随机推荐