一、什么是事务
事务是一组不可分割的数据库操作序列,这些操作要么全部成功执行 ,要么全部不执行,是一个完整的逻辑工作单元。
事务经典例子:银行转账
sql
-- 从A账户转100元到B账户
UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';
UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';
如果没有事务管理,可能出现以下问题:
- A账户扣款成功,B账户收款失败 → A的钱消失了
- 系统在操作过程中崩溃 → 数据不一致
有了事务机制,就能确保:要么两个操作都成功,要么都失败。
二、事务的四大特性(ACID)
- 原子性(Atomicity):事务内的操作要么全部成功,要么全部失败
- 一致性(Consistency):事务执行前后数据保持一致状态
- 隔离性(Isolation):并发事务之间相互隔离
- 持久性(Durability):事务提交后数据永久保存
三、SQL中的事务控制
sql
-- 1. 开始事务
START TRANSACTION; -- 或 BEGIN
-- 2. 执行一系列操作
INSERT INTO orders (user_id, amount) VALUES (1, 100.00);
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
-- 3. 根据情况选择
COMMIT; -- 确认提交,永久保存
-- 或
ROLLBACK; -- 撤销所有操作
四、Spring事务的使用
1.基础配置
Java 配置方式:
java
@Configuration
@EnableTransactionManagement // 启用事务管理
@ComponentScan("com.example")
public class AppConfig {
@Bean
public DataSource dataSource() {
// 配置数据源
return new DriverManagerDataSource(...);
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
Spring Boot 自动配置:
Spring Boot 会自动配置事务管理器,只需要添加依赖:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2.使用 @Transactional 注解
基本使用
java
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional // 添加事务注解
public User createUser(User user) {
// 业务操作
userRepository.save(user);
// 其他数据库操作
userRepository.saveLog(user);
// 如果抛出异常,事务会自动回滚
return user;
}
}
说明:可以在 application.yml 配置文件中开启事务管理日志,这样就能在控制台看到和事务相关的日志信息了
yml
#spring事务管理日志
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug
3.@Transactional 参数详解
java
@Transactional(
propagation = Propagation.REQUIRED, // 传播行为
isolation = Isolation.DEFAULT, // 隔离级别
readOnly = false, // 是否只读
timeout = 30, // 超时时间(秒)
rollbackFor = Exception.class, // 触发回滚的异常
noRollbackFor = RuntimeException.class // 不触发回滚的异常
)
public void businessMethod() {
// 业务逻辑
}
3.1 事务传播行为(Propagation)
事务的传播行为是指,当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。
java
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED) // 默认值
public void placeOrder(Order order) {
// 如果当前没有事务,就新建一个事务
// 如果已经存在事务,就加入该事务
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateInventory() {
// 总是新建一个事务,如果当前有事务则挂起
}
@Transactional(propagation = Propagation.SUPPORTS)
public void getOrder() {
// 如果当前有事务,就加入该事务
// 如果没有事务,就以非事务方式执行
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sendNotification() {
// 以非事务方式执行,挂起当前事务
}
@Transactional(propagation = Propagation.NEVER)
public void validateData() {
// 以非事务方式执行,如果当前存在事务则抛出异常
}
@Transactional(propagation = Propagation.NESTED)
public void processSubTask() {
// 如果当前存在事务,则在嵌套事务内执行
// 嵌套事务可以独立提交或回滚
}
}
说明:大部分情况下都是使用 REQUIRED 传播行为即可,当我们不希望事务之间互相影响时,可以使用 REQUIRES_NEW 传播行为。比如:下订单前需要记录日志,不论订单保存成功与否,都要保证日志记录能够记录成功。
3.2 隔离级别(Isolation)
java
@Service
public class AccountService {
@Transactional(isolation = Isolation.READ_COMMITTED) // 常用
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
// 读取已提交的数据,避免脏读
}
@Transactional(isolation = Isolation.REPEATABLE_READ)
public BigDecimal getBalance(Long accountId) {
// 可重复读,MySQL默认级别
}
@Transactional(isolation = Isolation.SERIALIZABLE)
public void batchUpdate() {
// 串行化,性能最低但最安全
}
}
| 隔离级别 | 说明 |
|---|---|
| READ UNCOMMITTED | 允许读取其他事务未提交的数据 |
| READ COMMITTED | 只能读取其他事务已提交的数据 |
| REPEATABLE READ | 确保在同一事务中多次读取同一数据,结果一致 |
| SERIALIZABLE | 强制事务串行执行,完全隔离 |
说明:Isolation.DEFAULT 是 Spring 事务中一个特殊的隔离级别常量,表示使用底层数据库的默认隔离级别。
java
// Spring 事务隔离级别枚举
public enum Isolation {
DEFAULT(-1), // 使用数据库默认级别
READ_UNCOMMITTED(1), // 读未提交
READ_COMMITTED(2), // 读已提交
REPEATABLE_READ(4), // 可重复读
SERIALIZABLE(8); // 串行化
private final int value;
Isolation(int value) {
this.value = value;
}
}
3.3 触发回滚的异常(rollbackFor)
为什么需要 rollbackFor?
默认情况下,Spring 事务只在运行时异常 (RuntimeException)和错误 (Error)发生时回滚,而受检异常(Checked Exception)不会触发回滚。
java
@Service
public class BankService {
@Transactional
public void transferMoney1() {
// 运行时异常 → 自动回滚 ✓
throw new RuntimeException("系统错误");
}
@Transactional
public void transferMoney2() throws IOException {
// 受检异常 → 不会回滚!❌
throw new IOException("网络异常");
// 事务会提交!这很危险
}
}
基本用法
java
// 指定单个异常类型
@Transactional(rollbackFor = Exception.class)
public void createOrder(Order order) throws BusinessException {
try {
// 业务逻辑
validateOrder(order);
saveOrder(order);
reduceInventory(order);
} catch (ValidationException e) {
// 指定 BusinessException 触发回滚
throw new BusinessException("订单创建失败", e);
}
}
// 指定多个异常类型
@Transactional(rollbackFor = {
BusinessException.class,
PaymentException.class,
SQLException.class
})
public void processPayment() {
// 当这些异常发生时,事务会回滚
}