**一、事务传播属性(Propagation)**
传播属性定义事务方法之间的调用规则。Spring 支持 7 种传播行为,核心代码如下:
**1. REQUIRED
(默认)**
-
规则:如果当前存在事务,则加入该事务;否则新建一个事务。
-
场景:适用于大多数业务逻辑(如订单创建和库存扣减)。
-
代码示例 :
java@Service public class OrderService { @Autowired private UserService userService; @Transactional(propagation = Propagation.REQUIRED) public void createOrder() { // 1. 如果外层无事务,此处新建事务;否则加入外层事务 jdbcTemplate.update("INSERT INTO orders (amount) VALUES (100)"); // 2. 调用另一个事务方法(默认传播行为为 REQUIRED) userService.updateUser(); // 加入当前事务 } } @Service public class UserService { @Transactional(propagation = Propagation.REQUIRED) public void updateUser() { // 3. 加入 OrderService 的事务 jdbcTemplate.update("UPDATE user SET order_count = order_count + 1"); } }
行为说明 :
- 如果
createOrder()
方法触发异常,整个事务(订单和用户操作)均会回滚。
- 如果
**2. REQUIRES_NEW
**
-
规则:无论当前是否存在事务,均新建独立事务。
-
场景:用于日志记录、审计等需独立提交的操作。
-
代码示例 :
java@Transactional(propagation = Propagation.REQUIRED) public void processOrder() { jdbcTemplate.update("INSERT INTO orders (amount) VALUES (200)"); // 外层事务 // 调用 REQUIRES_NEW 方法 auditService.logOperation("Order created"); // 新建独立事务 } @Service public class AuditService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void logOperation(String message) { // 独立事务:即使外层事务回滚,此操作仍提交 jdbcTemplate.update("INSERT INTO audit_log (message) VALUES (?)", message); } }
行为说明 :
- 若
processOrder()
方法回滚,logOperation()
的事务仍会提交。
- 若
**3. NESTED
**
-
规则:在当前事务中嵌套子事务(通过数据库 Savepoint 实现)。
-
场景:部分操作需要独立回滚(如批量处理中的单条失败)。
-
代码示例 :
java@Service public class BatchService { @Transactional(propagation = Propagation.REQUIRED) public void batchProcess(List<Item> items) { for (Item item : items) { try { processItem(item); // 嵌套事务 } catch (Exception e) { // 仅回滚当前 item 的操作 } } } @Transactional(propagation = Propagation.NESTED) public void processItem(Item item) { // 处理单个 item } }
行为说明 :
- 子事务回滚不影响外层事务,但外层事务回滚会导致子事务一同回滚。
4. 其他传播行为
- **
SUPPORTS
**:当前存在事务则加入,否则以非事务方式执行。 - **
NOT_SUPPORTED
**:以非事务方式执行,若当前存在事务则挂起。 - **
MANDATORY
**:强制要求当前存在事务,否则抛出异常。 - **
NEVER
**:强制要求当前无事务,否则抛出异常。
**二、事务隔离级别(Isolation)**
隔离级别控制事务间的数据可见性,解决并发问题。Spring 支持 5 种隔离级别:
**1. READ_UNCOMMITTED
(读未提交)**
-
问题:允许脏读(读取未提交的数据)。
-
代码示例 :
java@Transactional(isolation = Isolation.READ_UNCOMMITTED) public void transfer() { // 事务A:读取未提交数据 Integer balance = jdbcTemplate.queryForObject( "SELECT balance FROM account WHERE id = 1", Integer.class ); // 若事务B正在修改 balance 但未提交,此处可能读到中间状态 }
测试用例 :
java@Test public void testDirtyRead() throws InterruptedException { // 线程1:更新数据但不提交 new Thread(() -> { transactionTemplate.execute(status -> { jdbcTemplate.update("UPDATE account SET balance = 200 WHERE id = 1"); return null; // 不提交,模拟长时间事务 }); }).start(); Thread.sleep(500); // 等待线程1执行更新 // 线程2:读取未提交数据 Integer balance = transactionTemplate.execute(status -> jdbcTemplate.queryForObject("SELECT balance FROM account WHERE id = 1", Integer.class) ); assert balance == 200; // 脏读 }
**2. READ_COMMITTED
(读已提交,默认)**
-
解决脏读,但允许不可重复读。
-
代码示例 :
java@Transactional(isolation = Isolation.READ_COMMITTED) public void checkBalance() { // 第一次读取(值为100) Integer balance1 = jdbcTemplate.queryForObject( "SELECT balance FROM account WHERE id = 1", Integer.class ); // 事务B在此处提交更新(balance=200) // 第二次读取(值变为200,不可重复读) Integer balance2 = jdbcTemplate.queryForObject( "SELECT balance FROM account WHERE id = 1", Integer.class ); }
**3. REPEATABLE_READ
(可重复读)**
-
解决不可重复读,通过快照保证多次读取结果一致。
-
代码示例 :
java@Transactional(isolation = Isolation.REPEATABLE_READ) public void validateTransaction() { // 第一次读取(值为100) Integer balance1 = jdbcTemplate.queryForObject( "SELECT balance FROM account WHERE id = 1", Integer.class ); // 事务B在此处提交更新(balance=200) // 第二次读取(仍为100) Integer balance2 = jdbcTemplate.queryForObject( "SELECT balance FROM account WHERE id = 1", Integer.class ); }
**4. SERIALIZABLE
(串行化)**
-
解决幻读,通过锁机制禁止其他事务插入新数据。
-
代码示例 :
java@Transactional(isolation = Isolation.SERIALIZABLE) public void batchInsert() { // 查询符合条件的记录(当前无数据) List<Account> accounts = jdbcTemplate.query( "SELECT * FROM account WHERE balance > 1000", new AccountRowMapper() ); // 事务B尝试插入符合条件的记录会被阻塞 jdbcTemplate.update("INSERT INTO account (balance) VALUES (2000)"); }
三、配置与调试技巧
1. 事务配置模板
java
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager manager) {
return new TransactionTemplate(manager);
}
}
2. 调试事务传播
-
启用事务日志 :
bash# application.properties logging.level.org.springframework.transaction.interceptor=TRACE logging.level.org.springframework.jdbc.datasource.DataSourceTransactionManager=DEBUG
3. 避免自调用问题
java
@Service
public class PaymentService {
@Autowired
private PaymentService selfProxy; // 注入代理对象
@Transactional
public void methodA() {
selfProxy.methodB(); // 通过代理对象调用,事务生效
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() { ... }
}
四、总结
-
传播属性:
REQUIRED
:默认行为,适用于大多数场景。REQUIRES_NEW
:独立事务,用于关键日志或审计。NESTED
:嵌套事务,支持部分回滚(需数据库支持)。
-
隔离级别:
READ_UNCOMMITTED
:性能高,容忍脏读。READ_COMMITTED
:平衡性能与一致性(默认)。REPEATABLE_READ
:保证多次读取一致性(MySQL 默认)。SERIALIZABLE
:严格一致,性能最低。
-
最佳实践:
- 根据业务需求选择传播属性(如财务操作使用
REQUIRES_NEW
)。 - 高并发场景优先使用
READ_COMMITTED
,关键数据使用REPEATABLE_READ
。 - 通过日志和测试验证事务行为。
- 根据业务需求选择传播属性(如财务操作使用