在Java企业级开发中,Spring事务是保障数据一致性的核心机制。但实际项目中,开发者常遇到@Transactional注解加了却不生效的问题,这本质是对Spring事务实现原理理解不透彻或忽略关键细节导致的。
引言
在Java企业级开发中,Spring事务是保障数据一致性的核心机制。但实际项目中,开发者常遇到@Transactional注解加了却不生效的问题,这本质是对Spring事务实现原理理解不透彻或忽略关键细节导致的。
Spring 事务基础
在分析失效场景前,必须先明确 Spring 事务的核心原理 ——基于 AOP 动态代理实现,这是理解所有失效场景的关键。
事务的核心特性(ACID)
- 原子性(Atomicity):事务是不可分割的最小单元,要么全成功,要么全回滚;
- 一致性(Consistency):事务执行前后,数据从一个合法状态转换到另一个合法状态;
- 隔离性(Isolation):多个事务并发执行时,相互不干扰(由隔离级别控制,如READ_COMMITTED);
- 持久性(Durability):事务提交后,数据修改永久保存在数据库中。
Spring 事务的实现原理
Spring事务通过动态代理为目标Bean生成代理对象,当调用被@Transactional标注的方法时,代理对象会先拦截方法执行:
- 开启事务(创建数据库连接,设置事务隔离级别、传播行为等);
- 调用目标方法(业务逻辑执行);
- 若方法正常返回,提交事务;
- 若方法抛出指定异常,回滚事务;
- 若抛出未指定异常,不回滚(默认仅回滚RuntimeException和Error)。
关键结论:只有通过 Spring 容器管理的代理对象调用事务方法,事务才会生效;若绕开代理直接调用(如自调用),事务机制无法触发。
Spring 事务失效的场景及说明
场景 1:非 public 修饰的方法加 @Transactional
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
// 错误:private方法,@Transactional无效
@Transactional
private void createOrder(Order order) {
orderMapper.insert(order);
// 模拟异常
if (order.getAmount() < 0) {
throw new RuntimeException("订单金额非法");
}
}
}
// 外部调用private方法
public void submitOrder(Order order) {
createOrder(order); // 直接调用,无代理拦截
}
Spring事务默认通过AOP动态代理实现,而Spring AOP(无论是JDK动态代理还是CGLIB代理)对方法权限有明确限制:仅拦截public修饰的方法。
场景 2:事务方法内部自调用
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private LogMapper logMapper;
// 事务方法A
@Transactional
public void createOrder(Order order) {
orderMapper.insert(order);
// 错误:内部直接调用事务方法B,无代理拦截
this.addOrderLog(order.getId());
}
// 事务方法B(期望单独事务,但实际失效)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addOrderLog(Long orderId) {
Log log = new Log();
log.setOrderId(orderId);
log.setContent("订单创建成功");
logMapper.insert(log);
// 模拟异常:此时addOrderLog的事务不回滚,log仍会插入
throw new RuntimeException("日志记录异常");
}
}
Spring事务的触发依赖代理对象调用,而当一个事务方法(如methodA)内部直接调用另一个事务方法(如methodB)时,调用过程是目标对象→目标对象,而非代理对象→目标对象,绕开了AOP代理的拦截逻辑,导致methodB的事务不生效。
场景 3:异常被捕获(try-catch)且未重新抛出
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Transactional
public void createOrder(Order order) {
try {
orderMapper.insert(order);
// 模拟异常
if (order.getAmount() > 10000) {
throw new RuntimeException("订单金额超过上限");
}
} catch (Exception e) {
// 错误:仅打印日志,未重新抛出异常
log.error("创建订单失败", e);
}
}
}
Spring事务默认仅在方法抛出未被捕获的RuntimeException或Error时才会触发回滚。若开发者在事务方法中用try-catch捕获了异常,且未在catch块中重新抛出异常,Spring会认为方法执行成功,直接提交事务,导致异常发生时无法回滚。
场景 4:错误的事务传播机制
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
// 错误:使用NOT_SUPPORTED,不支持事务
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void createOrder(Order order) {
orderMapper.insert(order);
throw new RuntimeException("模拟异常"); // 异常抛出,但事务未开启,无法回滚
}
}
Spring事务传播机制定义了多个事务方法嵌套调用时,事务如何传递,若选择了不支持事务或强制不使用事务的传播行为,会导致事务失效。常见错误传播行为:
- Propagation.NOT_SUPPORTED:以非事务方式执行,若当前存在事务则暂停;
- Propagation.NEVER:以非事务方式执行,若当前存在事务则抛出异常;
- Propagation.SUPPORTS:若当前存在事务则加入,否则以非事务方式执行(非主动开启事务)。
场景 5:数据源未配置事务管理器
// 错误:仅配置数据源,未配置事务管理器
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/order_db");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
Spring事务的执行依赖事务管理器(TransactionManager),不同的数据源对应不同的事务管理器(如JDBC对应DataSourceTransactionManager,MyBatis对应SqlSessionTransactionManager)。若未在Spring容器中配置事务管理器,@Transactional注解会被忽略,事务无法生效。
Spring Boot中引入spring-boot-starter-jdbc或spring-boot-starter-data-jpa,会自动配置DataSourceTransactionManager,无需手动配置;但自定义数据源时,需手动绑定事务管理器。
场景 6:多线程调用事务方法
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private LogMapper logMapper;
@Transactional
public void createOrder(Order order) {
// 主线程:插入订单
orderMapper.insert(order);
// 错误:子线程调用事务方法,与主线程事务独立
new Thread(() -> {
addOrderLog(order.getId()); // 子线程事务不生效(或与主线程独立)
}).start();
// 模拟主线程异常:主线程回滚(订单不插入),但子线程日志已插入
throw new RuntimeException("主线程异常");
}
@Transactional
public void addOrderLog(Long orderId) {
Log log = new Log();
log.setOrderId(orderId);
logMapper.insert(log);
}
}
AI大模型学习福利
作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
一、全套AGI大模型学习路线
AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取
二、640套AI大模型报告合集
这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获
三、AI大模型经典PDF籍
随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获
四、AI大模型商业化落地方案

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获
作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量
736

被折叠的 条评论
为什么被折叠?



