知识模块
☕ Java 知识模块
八、Spring 全家桶
Spring 事务

Spring 事务

事务基础

@Transactional 注解

@Service
public class UserService {
    
    @Transactional
    public void createUser(User user) {
        userDao.insert(user);
    }
}

注解属性

属性说明默认值
value / transactionManager事务管理器名称默认事务管理器
propagation传播行为REQUIRED
isolation隔离级别数据库默认
timeout超时时间(秒)-1(不超时)
readOnly是否只读false
rollbackFor回滚异常类型RuntimeException
noRollbackFor不回滚异常类型

事务传播行为

七种传播行为

传播行为说明使用场景
REQUIRED(默认)有事务就加入,没有就新建大多数场景
REQUIRES_NEW总是新建事务,挂起当前事务日志记录、独立操作
SUPPORTS有事务就加入,没有就以非事务执行查询操作
NOT_SUPPORTED以非事务执行,挂起当前事务不需要事务的操作
MANDATORY必须在事务中调用,否则抛异常强制要求事务
NEVER不能在事务中调用,否则抛异常禁止事务
NESTED嵌套事务(保存点)部分回滚场景

图解传播行为

REQUIRED(默认)
┌─────────────────────────────────┐
│ 事务A                           │
│   ├── methodA()                 │
│   │   └── methodB() ← 加入事务A │
└─────────────────────────────────┘

REQUIRES_NEW
┌─────────────────────────────────┐
│ 事务A                           │
│   ├── methodA()                 │
│   │   └── 【挂起事务A】         │
│   │       ┌─────────────────┐   │
│   │       │ 事务B(新建)    │   │
│   │       │ └── methodB()   │   │
│   │       └─────────────────┘   │
│   │   ┌── 【恢复事务A】         │
└─────────────────────────────────┘

NESTED
┌─────────────────────────────────┐
│ 事务A                           │
│   ├── methodA()                 │
│   │   └── Savepoint sp1         │
│   │       ┌─────────────────┐   │
│   │       │ 嵌套事务         │   │
│   │       │ └── methodB()   │   │
│   │       └─────────────────┘   │
│   │   ← 可以回滚到 sp1         │
└─────────────────────────────────┘

代码示例

@Service
public class UserService {
    
    @Autowired
    private LogService logService;
    
    // REQUIRED:默认,加入现有事务
    @Transactional(propagation = Propagation.REQUIRED)
    public void createUser(User user) {
        userDao.insert(user);
        logService.log("创建用户"); // 加入当前事务
    }
    
    // REQUIRES_NEW:总是新建事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void log(String message) {
        // 独立事务,即使主事务回滚,日志也会保存
        logDao.insert(message);
    }
    
    // NESTED:嵌套事务
    @Transactional(propagation = Propagation.NESTED)
    public void batchCreate(List<User> users) {
        for (User user : users) {
            try {
                userDao.insert(user);
            } catch (Exception e) {
                // 回滚到保存点,继续处理其他用户
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
        }
    }
}

事务隔离级别

@Transactional(isolation = Isolation.READ_COMMITTED)
public User getUser(Long id) {
    return userDao.findById(id);
}
隔离级别脏读不可重复读幻读
DEFAULT使用数据库默认--
READ_UNCOMMITTED
READ_COMMITTED×
REPEATABLE_READ××√*
SERIALIZABLE×××

事务实现原理

AOP 代理实现

@Transactional 方法调用:
1. 代理对象拦截调用
2. 事务切面开启事务
3. 执行目标方法
4. 成功 → 提交事务
   失败 → 回滚事务

核心类

作用
TransactionInterceptor事务拦截器
TransactionAspectSupport事务切面支持
AbstractPlatformTransactionManager事务管理器抽象类
DataSourceTransactionManagerJDBC 事务管理器
JpaTransactionManagerJPA 事务管理器

事务执行流程

// TransactionInterceptor 核心逻辑
public Object invoke(MethodInvocation invocation) throws Throwable {
    // 1. 获取事务属性
    TransactionAttribute txAttr = getTransactionAttribute();
    
    // 2. 获取事务管理器
    PlatformTransactionManager tm = getTransactionManager();
    
    // 3. 开启事务
    TransactionInfo txInfo = tm.getTransaction(txAttr);
    
    try {
        // 4. 执行目标方法
        Object result = invocation.proceed();
        
        // 5. 提交事务
        tm.commit(txInfo);
        return result;
    } catch (Exception ex) {
        // 6. 回滚事务
        tm.rollback(txInfo);
        throw ex;
    }
}

事务失效场景

1. 方法非 public

// 失效:private 方法
@Transactional
private void createUser() { }
 
// 原因:Spring AOP 基于代理,private 方法无法被代理

2. 同类方法调用

@Service
public class UserService {
    
    public void methodA() {
        methodB(); // 直接调用,不走代理,事务失效
    }
    
    @Transactional
    public void methodB() {
        // ...
    }
}
 
// 解决方案:
// 1. 注入自己
@Autowired
private UserService self;
 
public void methodA() {
    self.methodB(); // 走代理
}
 
// 2. 使用 AopContext
public void methodA() {
    ((UserService) AopContext.currentProxy()).methodB();
}

3. 异常被捕获

@Transactional
public void createUser(User user) {
    try {
        userDao.insert(user);
        throw new RuntimeException("异常");
    } catch (Exception e) {
        // 异常被捕获,事务不会回滚
        log.error("创建失败", e);
    }
}
 
// 解决:手动回滚
@Transactional
public void createUser(User user) {
    try {
        userDao.insert(user);
        throw new RuntimeException("异常");
    } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

4. 异常类型不匹配

// 默认只回滚 RuntimeException 和 Error
@Transactional
public void createUser() throws Exception {
    throw new Exception("受检异常"); // 不会回滚
}
 
// 解决:指定回滚异常类型
@Transactional(rollbackFor = Exception.class)
public void createUser() throws Exception {
    throw new Exception("受检异常"); // 会回滚
}

5. 数据库引擎不支持事务

-- MySQL MyISAM 不支持事务
CREATE TABLE user (...) ENGINE=MyISAM;
 
-- 使用 InnoDB
CREATE TABLE user (...) ENGINE=InnoDB;

6. 类没有被 Spring 管理

// 失效:没有 @Service/@Component 等注解
public class UserService {
    @Transactional
    public void createUser() { }
}

只读事务

@Transactional(readOnly = true)
public User getUser(Long id) {
    return userDao.findById(id);
}

优势:

  • 数据库可以进行优化(不获取写锁)
  • Hibernate 不进行脏检查
  • 提高查询性能

注意: 只读事务中进行写操作会抛异常。

事务超时

@Transactional(timeout = 5) // 5秒超时
public void longRunningOperation() {
    // 超过5秒自动回滚
}

多事务管理器

@Configuration
public class DataSourceConfig {
    
    @Bean
    @Primary
    public PlatformTransactionManager primaryTransactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    
    @Bean
    public PlatformTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
 
// 使用指定事务管理器
@Transactional("secondaryTransactionManager")
public void useSecondaryDataSource() {
    // ...
}

编程式事务

@Service
public class UserService {
    
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public void createUser(User user) {
        transactionTemplate.execute(status -> {
            userDao.insert(user);
            return null;
        });
    }
    
    // 或者直接使用事务管理器
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    public void createUser2(User user) {
        TransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);
        
        try {
            userDao.insert(user);
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}

面试高频问题

Q1: Spring 事务的实现原理?

回答要点:

  1. AOP 代理:通过动态代理拦截方法调用
  2. TransactionInterceptor:事务拦截器处理事务逻辑
  3. PlatformTransactionManager:事务管理器执行具体操作
  4. ThreadLocal:存储当前线程的事务信息(连接)

Q2: REQUIRED 和 REQUIRES_NEW 的区别?

对比REQUIREDREQUIRES_NEW
事务数量可能共用一个总是新建
回滚影响整个事务回滚独立回滚
性能更好有额外开销

Q3: 事务什么时候会失效?

  1. 方法非 public
  2. 同类方法调用(不走代理)
  3. 异常被捕获未抛出
  4. 异常类型不匹配(默认只回滚 RuntimeException)
  5. 数据库不支持事务(MyISAM)
  6. 类未被 Spring 管理

Q4: @Transactional 如何保证线程安全?

回答:通过 ThreadLocal

每个线程有自己的数据库连接,存储在 ThreadLocal 中,互不干扰。

// TransactionSynchronizationManager 源码
private static final ThreadLocal<Map<Object, Object>> resources =
    new NamedThreadLocal<>("Transactional resources");

Q5: 嵌套事务和 REQUIRES_NEW 的区别?

对比NESTEDREQUIRES_NEW
事务关系父子关系独立事务
回滚影响子回滚不影响父完全独立
实现方式保存点(Savepoint)挂起当前事务
数据库要求需要支持保存点无特殊要求

总结

Spring 事务核心要点:
1. 注解:@Transactional 及其属性
2. 传播行为:7种,REQUIRED 默认,REQUIRES_NEW 常用
3. 隔离级别:与数据库一致
4. 失效场景:非 public、同类调用、异常处理、引擎不支持
5. 实现原理:AOP + ThreadLocal + PlatformTransactionManager