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

Java 事务失效场景全解析

在 Java 开发中,事务管理是保证数据一致性的核心机制,尤其是在 Spring 框架中,@Transactional注解的使用极大简化了事务配置。然而,在实际开发中,事务常常会因为一些细节问题而失效,导致数据异常。本文将详细解析 Java 事务失效的八大场景,每个场景都提供代码示例与对应的修复方案。

一、事务方法非 public 修饰

失效原理

Spring 的@Transactional注解默认只对public方法生效。这是因为 Spring AOP 在实现事务管理时,无论是 JDK 动态代理还是 CGLIB 代理,都无法对非 public 方法(private、protected、默认访问权限)进行有效的事务增强。

失效代码

java运行

@Service
public class UserService {// 非public方法,事务注解失效@Transactionalvoid updateUser(Long id) { userMapper.updateStatus(id, 1);}
}

修复方案

将事务方法修改为public访问权限。

修复后代码

java运行

@Service
public class UserService {// 修改为public方法,事务生效@Transactionalpublic void updateUser(Long id) { userMapper.updateStatus(id, 1);}
}

二、异常被捕获且未重新抛出

失效原理

Spring 事务默认仅在遇到未捕获的RuntimeExceptionError时触发回滚。如果方法内部使用try-catch捕获了异常且未重新抛出,事务管理器会认为没有异常发生,从而不会执行回滚操作。

失效代码

java运行

@Service
public class OrderService {@Transactionalpublic void createOrder(Order order) {try {orderMapper.insert(order);// 模拟异常int i = 1 / 0;} catch (Exception e) {// 捕获异常但未抛出,事务不会回滚log.error("创建订单失败", e);}}
}

修复方案

方案一:捕获异常后重新抛出
方案二:使用TransactionAspectSupport手动触发回滚

修复后代码(方案一)

java运行

@Service
public class OrderService {@Transactionalpublic void createOrder(Order order) {try {orderMapper.insert(order);// 模拟异常int i = 1 / 0;} catch (Exception e) {log.error("创建订单失败", e);// 重新抛出异常,触发事务回滚throw new RuntimeException("创建订单失败", e);}}
}

修复后代码(方案二)

java运行

@Service
public class OrderService {@Transactionalpublic void createOrder(Order order) {try {orderMapper.insert(order);// 模拟异常int i = 1 / 0;} catch (Exception e) {log.error("创建订单失败", e);// 手动触发事务回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}}
}

三、错误配置 rollbackFor 属性

失效原理

@TransactionalrollbackFor属性用于指定需要回滚的异常类型,默认值为{RuntimeException.class, Error.class}。如果业务中抛出的是受检查异常(如IOExceptionSQLException),且未在rollbackFor中声明,事务不会回滚。

失效代码

java运行

@Service
public class FileService {// 未指定rollbackFor,受检查异常不会触发回滚@Transactionalpublic void importData(String filePath) throws IOException {// 读取文件(可能抛出IOException)FileReader reader = new FileReader(filePath);// 数据入库操作dataMapper.batchInsert(parseData(reader));}
}

修复方案

显式指定rollbackFor属性,包含需要回滚的异常类型。

修复后代码

java运行

@Service
public class FileService {// 显式指定rollbackFor包含IOException@Transactional(rollbackFor = {IOException.class, RuntimeException.class})public void importData(String filePath) throws IOException {FileReader reader = new FileReader(filePath);dataMapper.batchInsert(parseData(reader));}
}// 更通用的方式:捕获所有Exception
@Service
public class FileService {@Transactional(rollbackFor = Exception.class)public void importData(String filePath) throws IOException {// 业务逻辑不变}
}

四、事务传播机制配置不当

失效原理

Spring 事务的传播机制决定了事务方法之间的嵌套行为。若传播机制配置不合理(如使用NOT_SUPPORTEDSUPPORTS等),可能导致操作不在事务中执行。

失效代码

java运行

@Service
public class OrderService {@Autowiredprivate PaymentService paymentService;@Transactionalpublic void createOrder(Order order) {orderMapper.insert(order);// 调用支付服务(非事务方式执行)paymentService.processPayment(order.getId(), order.getAmount());}
}@Service
public class PaymentService {// 配置为非事务方式执行@Transactional(propagation = Propagation.NOT_SUPPORTED)public void processPayment(Long orderId, BigDecimal amount) {paymentMapper.insert(new Payment(orderId, amount));// 若此处发生异常,不会回滚}
}

修复方案

根据业务需求选择合适的传播机制,常用的是默认的REQUIRED(如果当前有事务则加入,否则创建新事务)。

修复后代码

java运行

@Service
public class PaymentService {// 使用默认传播机制REQUIRED@Transactionalpublic void processPayment(Long orderId, BigDecimal amount) {paymentMapper.insert(new Payment(orderId, amount));}
}

五、同类方法内部调用

失效原理

Spring 事务基于 AOP 代理实现,事务增强逻辑在代理对象中执行。若同一个类中的方法 A 调用方法 B(B 有@Transactional注解),由于调用未经过代理对象,方法 B 的事务注解会失效。

失效代码

java运行

@Service
public class UserService {// 方法A(无事务)调用方法B(有事务)public void updateUserInfo(Long id, String name, Integer age) {updateUserName(id, name);  // 内部调用,事务失效updateUserAge(id, age);    // 内部调用,事务失效}@Transactionalpublic void updateUserName(Long id, String name) {userMapper.updateName(id, name);}@Transactionalpublic void updateUserAge(Long id, Integer age) {userMapper.updateAge(id, age);}
}

修复方案

方案一:将方法拆分到不同的类中
方案二:通过AopContext获取代理对象调用

修复后代码(方案二)

java运行

// 1. 首先在启动类开启暴露代理
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)  // 关键配置
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}// 2. 在Service中通过代理对象调用
@Service
public class UserService {public void updateUserInfo(Long id, String name, Integer age) {// 通过AopContext获取代理对象UserService proxy = (UserService) AopContext.currentProxy();proxy.updateUserName(id, name);  // 代理对象调用,事务生效proxy.updateUserAge(id, age);    // 代理对象调用,事务生效}@Transactionalpublic void updateUserName(Long id, String name) {userMapper.updateName(id, name);}@Transactionalpublic void updateUserAge(Long id, Integer age) {userMapper.updateAge(id, age);}
}

六、数据库不支持事务

失效原理

事务最终依赖数据库支持。若使用的数据库引擎不支持事务(如 MySQL 的MyISAM引擎),即使代码中配置了事务,也无法生效。

失效场景

sql

-- 使用MyISAM引擎创建表,不支持事务
CREATE TABLE `user` (`id` bigint NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

此时即使 Service 层配置了@Transactional,数据库操作也不会有事务保障。

修复方案

使用支持事务的数据库引擎(如 MySQL 的InnoDB)。

修复后代码

sql

-- 使用InnoDB引擎创建表,支持事务
CREATE TABLE `user` (`id` bigint NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

七、未被 Spring 容器管理

失效原理

若事务所在的类未被 Spring 容器扫描并实例化(如未加@Service@Component等注解),@Transactional注解会因没有代理对象而失效。

失效代码

java运行

// 未加@Service注解,未被Spring管理
public class ProductService {@Autowiredprivate ProductMapper productMapper;@Transactionalpublic void updateStock(Long productId, Integer quantity) {productMapper.decreaseStock(productId, quantity);}
}

修复方案

为类添加 Spring 注解(如@Service),确保其被 Spring 容器管理。

修复后代码

java运行

// 添加@Service注解,被Spring容器管理
@Service
public class ProductService {@Autowiredprivate ProductMapper productMapper;@Transactionalpublic void updateStock(Long productId, Integer quantity) {productMapper.decreaseStock(productId, quantity);}
}

八、多线程场景下的事务隔离

失效原理

在事务方法中启动新线程执行数据库操作时,新线程的操作不会纳入当前事务管理(线程间事务上下文独立)。即使主线程事务回滚,新线程的操作也可能已提交。

失效代码

java运行

@Service
public class BatchService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate LogMapper logMapper;@Transactionalpublic void batchProcess(List<Long> userIds) {// 主线程操作userMapper.batchUpdateStatus(userIds, 1);// 新线程执行日志记录(不在当前事务中)new Thread(() -> {logMapper.insert(new Log("批量处理用户: " + userIds));}).start();}
}

修复方案

避免在事务方法中使用多线程执行数据库操作,或使用分布式事务协调机制。

修复后代码

java运行

@Service
public class BatchService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate LogMapper logMapper;@Transactionalpublic void batchProcess(List<Long> userIds) {// 主线程操作userMapper.batchUpdateStatus(userIds, 1);// 同一事务中执行日志记录logMapper.insert(new Log("批量处理用户: " + userIds));}
}

总结

事务失效的核心原因通常与以下几点相关:

  • 代理机制限制(非 public 方法、同类内部调用)
  • 异常处理不当(捕获未抛出、未配置 rollbackFor)
  • 事务属性配置错误(传播机制不合理)
  • 基础环境问题(数据库不支持、未被 Spring 管理)
  • 并发场景下的事务隔离问题

在实际开发中,需结合业务场景合理配置事务属性,同时注意编码规范,避免上述陷阱,才能确保事务机制有效运行,保障数据一致性。


文章转载自:

http://Np33FjzW.Lfmwt.cn
http://KkYD71Me.Lfmwt.cn
http://UIL9fwIb.Lfmwt.cn
http://C5xY2nVW.Lfmwt.cn
http://5tfkk7HX.Lfmwt.cn
http://mFB7tf5r.Lfmwt.cn
http://tmcO6V0F.Lfmwt.cn
http://2d47tfWZ.Lfmwt.cn
http://haAgWSDQ.Lfmwt.cn
http://m1N0rbrO.Lfmwt.cn
http://WFZVlrSj.Lfmwt.cn
http://1mJjyNJG.Lfmwt.cn
http://SV6ejkk4.Lfmwt.cn
http://NmR55E9I.Lfmwt.cn
http://5hfAGVOQ.Lfmwt.cn
http://2EFOnwPN.Lfmwt.cn
http://54IMY1F2.Lfmwt.cn
http://5w6W3d2I.Lfmwt.cn
http://uYP37LaB.Lfmwt.cn
http://drUMxHqb.Lfmwt.cn
http://sm5pjb3u.Lfmwt.cn
http://IHzFmFdn.Lfmwt.cn
http://IY4Pnc8B.Lfmwt.cn
http://tM8F78zm.Lfmwt.cn
http://HKhOUAEp.Lfmwt.cn
http://8DODUsOJ.Lfmwt.cn
http://Fxujckds.Lfmwt.cn
http://ZPwgqarZ.Lfmwt.cn
http://XY6b0TPY.Lfmwt.cn
http://4i5Pa3Mo.Lfmwt.cn
http://www.dtcms.com/a/384034.html

相关文章:

  • 简陋的进度条程序
  • SpringAOP中的通知类型
  • Python之文件读写 day9
  • 深度学习和神经网络之间有什么区别?
  • Linux驱动学习(SPI驱动)
  • 【MySQL|第七篇】DDL语句——数据库定义语言
  • 计算机毕设选题推荐:基于Java+SpringBoot物品租赁管理系统【源码+文档+调试】
  • Redis集群部署模式全解析:原理、优缺点与场景适配
  • ESP32的烧录和执行流程
  • ABP vNext + OpenXML / QuestPDF:复杂票据/发票模板与服务器端渲染
  • Java 注解入门:从认识 @Override 到写出第一个自定义注解
  • 网络层 -- IP协议
  • 社招面试BSP:BootROM知识一文通
  • Knockout.js DOM 操作模块详解
  • 面试题知识-NodeJS系列
  • 【层面一】C#语言基础和核心语法-02(反射/委托/事件)
  • Jmeter性能测试实战
  • CSP-S 2021 提高级 第一轮(初赛) 阅读程序(3)
  • TTC定时器中断——MPSOC实战3
  • [数据结构——lesson10.2堆排序以及TopK问题]
  • Maven 本地仓库的 settings.xml 文件
  • 绑定数据管理
  • RTU 全面科普:从入门到 AI 时代的智能化演进
  • lxml对于xml文件的操作
  • 第23课:行业解决方案设计
  • 深入理解 Java 内存模型与 volatile 关键字
  • Alibaba Lens:阿里巴巴推出的 AI 图像搜索浏览器扩展,助力B2B采购
  • I.MX6UL:主频和时钟配置实验
  • 【前端知识】package-lock.json 全面解析:作用、原理与最佳实践
  • 计算机视觉(opencv)实战二十——SIFT提取图像特征