事务抽象与编程式事务
引言
转账扣款必须“要么都成功、要么都回滚”。Spring 不重新发明事务,而是抽象出统一的事务管理接口,屏蔽 JDBC/JPA/Hibernate 的差异,业务代码只依赖抽象。本章介绍抽象核心 PlatformTransactionManager 与编程式用法(TransactionTemplate),声明式(@Transactional)下一章讲。
核心概念
- 事务四特性 ACID:原子性、一致性、隔离性、持久性(具体语义由数据库保证)。
- PlatformTransactionManager(org.springframework.transaction):事务抽象的核心接口,三个方法决定边界:
getTransaction(TransactionDefinition)开启/加入、commit(TransactionStatus)提交、rollback(TransactionStatus)回滚。 - 常见实现:
DataSourceTransactionManager(单数据源 JDBC/MyBatis 场景最常用)、JpaTransactionManager、HibernateTransactionManager;多数据源/分布式需 JTA 或分布式事务方案(以官方文档为准)。 - TransactionDefinition:携带传播行为、隔离级别、超时、只读标志(第 15 章详述)。
- 编程式两种写法:直接用
PlatformTransactionManager手工 begin/commit/rollback;或TransactionTemplate——模板方法,回调里抛运行时异常自动回滚,代码更简洁。 - 事务底层资源绑定:实现通过线程绑定(如
DataSourceTransactionManager绑定数据库连接到当前线程),保证同一线程内多次 DAO 调用处于同一事务。
代码示例
// 1) 装配:数据源 + 事务管理器(生产用连接池数据源)
@Configuration
public class TxConfig {
@Bean
public DataSource dataSource() {
return new org.springframework.jdbc.datasource.DriverManagerDataSource(
"jdbc:mysql://localhost:3306/shop", "root", "secret");
}
@Bean
public PlatformTransactionManager txManager(DataSource ds) {
return new DataSourceTransactionManager(ds);
}
}
// 2) TransactionTemplate 编程式事务
@Service
public class AccountService {
private final TransactionTemplate txTemplate; // 默认 REQUIRED 传播
public AccountService(PlatformTransactionManager txManager) {
this.txTemplate = new TransactionTemplate(txManager);
}
public void transfer(long fromId, long toId, long amount) {
txTemplate.executeWithoutResult(status -> { // 抛异常自动回滚
jdbc.update("UPDATE account SET balance = balance - ? WHERE id = ?",
amount, fromId);
jdbc.update("UPDATE account SET balance = balance + ? WHERE id = ?",
amount, toId);
// 若中间抛 RuntimeException,两行更新一起回滚
});
}
}
注意点
- 事务管理器必须与 DAO 使用同一个数据源,否则事务管不住你的 SQL(经典错配,见第 16 章)。
TransactionTemplate默认对RuntimeException/Error回滚,受检异常不自动回滚(可在回调内 catch 后调用status.setRollbackOnly()或抛出运行时异常)。- 编程式事务把边界逻辑写进业务代码,适合事务边界在方法内部的少数场景;绝大多数场景声明式更清晰。
getTransaction返回的TransactionStatus代表“当前参与的事务”,commit/rollback 前先判status.isNewTransaction()更严谨。- 连接池(如 HikariCP)由外部库提供;示例中的 DriverManagerDataSource 仅演示,生产勿用(每次新建连接)。
小结
Spring 事务 = 抽象接口(PlatformTransactionManager)+ 边界描述(TransactionDefinition)+ 两种用法(手工/模板)。TransactionTemplate 让编程式事务只剩“回调里写业务”这一步。掌握资源线程绑定思想后,下一章的 @Transactional 只是把边界声明挪到注解上。