当前位置: 首页 > java >正文

Spring事务失效的常见原因

Spring 事务失效的常见原因

Spring 事务管理(@Transactional)是开发中常用的功能,但在某些情况下事务可能失效。以下是常见的事务失效原因、示例及对应的解决方案。


1. 方法访问权限问题(非 public 方法)

原因:Spring为方法创建代理、添加事务通知前提条件都是该方法是public的,Spring AOP 代理机制要求 @Transactional 只能应用于 public 方法,否则事务不会生效。

示例

@Service
public class UserService {@Transactionalprivate void createUser(User user) {  // 事务失效userDao.save(user);}
}

解决方案
确保 @Transactional 方法为 public

@Service
public class UserService {@Transactionalpublic void createUser(User user) {  // 事务生效userDao.save(user);}
}

2. 自调用问题(同一个类内部调用)

原因:Spring 事务基于 AOP 代理,自调用时不会经过代理类,导致事务失效。

示例

@Service
public class OrderService {public void placeOrder(Order order) {// 其他逻辑this.saveOrder(order);  // 自调用,事务失效}@Transactionalpublic void saveOrder(Order order) {orderDao.save(order);}
}

解决方案
方法1:拆分到不同类

@Service
public class OrderService {@Autowiredprivate OrderTransactionService orderTransactionService;public void placeOrder(Order order) {orderTransactionService.saveOrder(order);  // 代理生效}
}@Service
public class OrderTransactionService {@Transactionalpublic void saveOrder(Order order) {orderDao.save(order);}
}

方法2:使用 AopContext.currentProxy()(需开启 @EnableAspectJAutoProxy(exposeProxy = true)

@Service
public class OrderService {public void placeOrder(Order order) {((OrderService) AopContext.currentProxy()).saveOrder(order);  // 代理调用}@Transactionalpublic void saveOrder(Order order) {orderDao.save(order);}
}

3. 异常被捕获未抛出

原因:默认情况下,Spring 事务只在抛出 RuntimeExceptionError 时回滚。如果异常被 catch 但没有重新抛出,事务不会回滚。

示例

@Service
public class AccountService {@Transactionalpublic void transfer(Account from, Account to, BigDecimal amount) {try {accountDao.deduct(from, amount);accountDao.add(to, amount);} catch (Exception e) {log.error("转账失败", e);  // 异常被捕获,事务不回滚}}
}

解决方案
方法1:重新抛出异常

catch (Exception e) {log.error("转账失败", e);throw new RuntimeException(e);  // 触发回滚
}

方法2:指定 @Transactional(rollbackFor = Exception.class)

@Transactional(rollbackFor = Exception.class)  // 任何异常都回滚
public void transfer(Account from, Account to, BigDecimal amount) {// ...
}

4. 数据库引擎不支持事务(如 MySQL 的 MyISAM)

原因:某些数据库引擎(如 MyISAM)不支持事务,即使加了 @Transactional 也不会生效。

解决方案
确保使用支持事务的引擎(如 InnoDB)

ALTER TABLE your_table ENGINE=InnoDB;

5. 事务传播行为设置不当

原因:如果传播行为设置为 Propagation.NOT_SUPPORTEDPropagation.NEVER,事务不会生效。

示例

@Transactional(propagation = Propagation.NOT_SUPPORTED)  // 不支持事务
public void logOperation(Log log) {logDao.save(log);  // 无事务
}

解决方案
合理设置传播行为(默认 REQUIRED

@Transactional(propagation = Propagation.REQUIRED)  // 默认值,支持事务
public void logOperation(Log log) {logDao.save(log);
}

6. 类未被 Spring 管理

原因:如果类没有 @Service@Component 等注解,@Transactional 不会生效。

示例

public class ExternalService {  // 没有 @Service/@Component@Transactional  // 无效public void process() {// ...}
}

解决方案
确保类被 Spring 管理

@Service
public class ExternalService {@Transactional  // 生效public void process() {// ...}
}

7. 多数据源未正确配置事务管理器

原因:多数据源环境下,如果没有为每个数据源配置事务管理器,事务可能失效。

解决方案
配置多数据源事务管理器

@Configuration
@EnableTransactionManagement
public class DataSourceConfig {@Bean@Primarypublic PlatformTransactionManager primaryTransactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Beanpublic PlatformTransactionManager secondaryTransactionManager(DataSource secondaryDataSource) {return new DataSourceTransactionManager(secondaryDataSource);}
}

使用时指定事务管理器:

@Transactional("primaryTransactionManager")  // 指定事务管理器
public void primaryDbOperation() {// ...
}

8. 异常类型不匹配

原因:默认情况下,Spring事务只对RuntimeException和Error回滚,对受检异常(checked exception)不回滚。那什么是受检异常?受检异常(Checked Exceptions)是那些在编译时就必须被处理的异常,它们继承自Exception类但不继承RuntimeException类。以下是Java中常见的受检异常:

  1. IO相关异常
    IOException:输入输出操作失败的通用异常
    FileNotFoundException:试图打开不存在的文件
    EOFException:在输入过程中意外到达文件或流的末尾
    SocketException:底层协议有错误,如TCP错误
    MalformedURLException:URL格式不正确
  2. SQL相关异常
    SQLException:数据库访问错误或其他错误
    SQLTimeoutException:数据库操作超时
    SQLSyntaxErrorException:SQL语法错误
  3. 反射相关异常
    ClassNotFoundException:无法加载请求的类
    NoSuchMethodException:请求的方法不存在
    IllegalAccessException:对类、方法或字段的访问被拒绝
  4. 网络相关异常
    UnknownHostException:无法确定主机的IP地址
    ConnectException:连接服务器被拒绝
  5. 其他常见受检异常
    ParseException:解析字符串失败(如日期解析)
    CloneNotSupportedException:对象不支持克隆操作
    InterruptedException:线程被中断
    TimeoutException:阻塞操作超时

解决方法也很简单,添加rullbackFor: @Transactional(rollbackFor=Exception.class)
示例

@Service
public class FileService {@Transactionalpublic void processFile() throws IOException {  // 受检异常// 文件处理逻辑throw new IOException("文件处理失败");  // 默认情况下不会导致事务回滚}
}

受检异常 vs 非受检异常
继承关系: 受检异常继承Exception但不继承RuntimeException,非受检异常继承RuntimeException或Error
处理要求: 受检异常必须捕获或声明抛出, 非受检异常不强制要求处理
示例: 受检异常IOException, SQLException,非受检异常NullPointerException, ArrayIndexOutOfBoundsException
使用场景: 受检异常可恢复的条件,调用者应该处理的情况,非受检异常程序错误,通常不应捕获


总结

失效原因解决方案
方法非 public改为 public 方法
自调用问题拆分到不同类或使用 AopContext.currentProxy()
异常被捕获未抛出重新抛出异常或配置 rollbackFor
数据库引擎不支持事务使用 InnoDB 等支持事务的引擎
传播行为设置不当使用 REQUIRED(默认)
类未被 Spring 管理添加 @Service/@Component
多数据源未配置事务管理器为每个数据源配置 PlatformTransactionManager
异常类型不匹配添加 rollbackFor=Exception.class
http://www.xdnf.cn/news/17554.html

相关文章:

  • ROS2 QT 多线程功能包设计
  • 基于PyTorch一文讲清楚损失函数与激活函数并配上详细的图文讲解
  • redis集群-docker环境
  • 咪咕MGV3200-KLH_GK6323V100C_板号E503744_安卓9_短接强刷包-可救砖
  • 大数据架构演变之路
  • java的三种组件漏洞靶场攻略
  • LeetCode 组合总数
  • 人工智能系列(8)如何实现无监督学习聚类(使用竞争学习)?
  • 1. 电阻选型
  • 计算机网络:如何理解目的网络不再是一个完整的分类网络
  • mpv core_thread pipeline
  • jmeter常规压测【读取csv文件】
  • 北京JAVA基础面试30天打卡06
  • Vulhub靶场组件漏洞(XStream,fastjson,Jackson)
  • 北京天津廊坊唐山打捞失物日记
  • 双非二本如何找工作?
  • jxWebUI--按钮
  • 黑马SpringBoot+Elasticsearch作业2实战:商品搜索与竞价排名功能实现
  • 【RocketMQ 生产者和消费者】- ConsumeMessageConcurrentlyService 并发消费消息
  • socket编程中系统调用send()详细讲解
  • MySQL自增ID与UUID的区别及其在索引分裂中的表现与优化
  • 七、CV_模型微调
  • 通过sealos工具在ubuntu 24.02上安装k8s集群
  • DevOps:从GitLab .gitlab-ci.yml 配置文件到CI/CD
  • 第十五讲:set和map
  • WebAssembly技术详解:从浏览器到云原生的高性能革命
  • 本地WSL部署接入 whisper + ollama qwen3:14b 总结字幕增加利用 Whisper 分段信息,全新 Prompt功能
  • 国内外主流大模型深度体验与横向评测:技术、场景与未来展望
  • 生产工具革命:定制开发开源AI智能名片S2B2C商城小程序重构商业生态的范式研究
  • 密码学的数学基础2-Paillier为什么产生密钥对比RSA慢