面试题:Spring 事务在什么情况下会失效?

Spring 事务管理是 Spring 框架中的一个重要特性,但在某些情况下,事务可能会失效。以下是导致 Spring 事务失效的常见场景及其原因:


1. 方法非 public 修饰

Spring 事务默认只对 public 方法生效。如果事务方法被定义为 protectedprivate 或包可见(默认修饰符),事务将不会生效。

示例

@Service
public class UserService {
@Transactional
private void updateUser(User user) { // 事务失效
// 业务逻辑
}
}
@Service
public class UserService {

    @Transactional
    private void updateUser(User user) { // 事务失效
        // 业务逻辑
    }
}
@Service public class UserService { @Transactional private void updateUser(User user) { // 事务失效 // 业务逻辑 } }
  • 解决方案:确保事务方法是 public 的。

2. 异常未被正确抛出

Spring 事务默认只在抛出 未检查异常(unchecked exceptions) 时回滚,例如 RuntimeException 及其子类。如果抛出的是 已检查异常(checked exceptions),事务不会回滚。

示例

@Service
public class UserService {
@Transactional
public void updateUser(User user) throws IOException { // 抛出已检查异常
// 业务逻辑
if (someCondition) {
throw new IOException("File not found"); // 事务不会回滚
}
}
}
@Service
public class UserService {

    @Transactional
    public void updateUser(User user) throws IOException { // 抛出已检查异常
        // 业务逻辑
        if (someCondition) {
            throw new IOException("File not found"); // 事务不会回滚
        }
    }
}
@Service public class UserService { @Transactional public void updateUser(User user) throws IOException { // 抛出已检查异常 // 业务逻辑 if (someCondition) { throw new IOException("File not found"); // 事务不会回滚 } } }
  • 解决方案
    • 在 @Transactional 注解中指定 rollbackFor 属性,明确需要回滚的异常类型。

3. 异常被捕获并未重新抛出

如果在事务方法中捕获了异常但没有重新抛出,Spring 无法感知到异常,因此不会触发回滚。

示例

@Service
public class UserService {
@Transactional
public void updateUser(User user) {
try {
// 业务逻辑
} catch (Exception e) {
// 捕获异常但未重新抛出
logger.error("Error occurred", e);
}
}
}
@Service
public class UserService {

    @Transactional
    public void updateUser(User user) {
        try {
            // 业务逻辑
        } catch (Exception e) {
            // 捕获异常但未重新抛出
            logger.error("Error occurred", e);
        }
    }
}
@Service public class UserService { @Transactional public void updateUser(User user) { try { // 业务逻辑 } catch (Exception e) { // 捕获异常但未重新抛出 logger.error("Error occurred", e); } } }
  • 解决方案:在捕获异常后重新抛出。
@Service
public class UserService {
@Transactional
public void updateUser(User user) {
try {
// 业务逻辑
} catch (Exception e) {
logger.error("Error occurred", e);
throw new RuntimeException(e); // 重新抛出异常
}
}
}
@Service
public class UserService {

    @Transactional
    public void updateUser(User user) {
        try {
            // 业务逻辑
        } catch (Exception e) {
            logger.error("Error occurred", e);
            throw new RuntimeException(e); // 重新抛出异常
        }
    }
}
@Service public class UserService { @Transactional public void updateUser(User user) { try { // 业务逻辑 } catch (Exception e) { logger.error("Error occurred", e); throw new RuntimeException(e); // 重新抛出异常 } } }

4. 事务传播行为配置不当

Spring 事务的传播行为(Propagation)配置不当可能导致事务失效。例如,如果在一个没有事务的方法中调用一个有事务的方法,且传播行为为 REQUIRED,则事务不会生效。

示例

@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED)
public void updateUser(User user) {
// 业务逻辑
}
public void doSomething(User user) {
updateUser(user); // 事务失效,因为 doSomething 没有事务
}
}
@Service
public class UserService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void updateUser(User user) {
        // 业务逻辑
    }

    public void doSomething(User user) {
        updateUser(user); // 事务失效,因为 doSomething 没有事务
    }
}
@Service public class UserService { @Transactional(propagation = Propagation.REQUIRED) public void updateUser(User user) { // 业务逻辑 } public void doSomething(User user) { updateUser(user); // 事务失效,因为 doSomething 没有事务 } }
  • 解决方案:确保调用方和被调用方的事务传播行为一致。

5. 同一类中方法调用

在同一个类中,一个非事务方法调用另一个事务方法时,事务不会生效。这是因为 Spring 的事务管理是基于代理的,而代理无法拦截类内部的调用。

示例

@Service
public class UserService {
public void doSomething(User user) {
updateUser(user); // 事务失效
}
@Transactional
public void updateUser(User user) {
// 业务逻辑
}
}
@Service
public class UserService {

    public void doSomething(User user) {
        updateUser(user); // 事务失效
    }

    @Transactional
    public void updateUser(User user) {
        // 业务逻辑
    }
}
@Service public class UserService { public void doSomething(User user) { updateUser(user); // 事务失效 } @Transactional public void updateUser(User user) { // 业务逻辑 } }
  • 解决方案
    • 将事务方法移到另一个类中。使用 AopContext.currentProxy() 获取当前代理对象并调用事务方法。

6. 数据源未配置事务管理器

如果数据源没有配置事务管理器,Spring 事务将无法生效。

示例

@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource("jdbc:mysql://localhost:3306/mydb", "root", "password");
}
// 缺少事务管理器配置
}
@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource("jdbc:mysql://localhost:3306/mydb", "root", "password");
    }

    // 缺少事务管理器配置
}
@Configuration public class DataSourceConfig { @Bean public DataSource dataSource() { return new DriverManagerDataSource("jdbc:mysql://localhost:3306/mydb", "root", "password"); } // 缺少事务管理器配置 }
  • 解决方案:配置事务管理器。
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }

7. 使用了错误的代理模式

Spring 默认使用 JDK 动态代理,如果目标类没有实现接口,事务将不会生效。

示例

@Service
public class UserService { // 没有实现接口
@Transactional
public void updateUser(User user) {
// 业务逻辑
}
}
@Service
public class UserService { // 没有实现接口

    @Transactional
    public void updateUser(User user) {
        // 业务逻辑
    }
}
@Service public class UserService { // 没有实现接口 @Transactional public void updateUser(User user) { // 业务逻辑 } }
  • 解决方案
    • 确保目标类实现了接口。或者强制使用 CGLIB 代理

8. 多线程调用

如果在一个事务方法中启动了新线程,新线程中的操作不会受到事务管理。

示例

@Service
public class UserService {
@Transactional
public void updateUser(User user) {
new Thread(() -> {
// 新线程中的操作不受事务管理
}).start();
}
}
@Service
public class UserService {

    @Transactional
    public void updateUser(User user) {
        new Thread(() -> {
            // 新线程中的操作不受事务管理
        }).start();
    }
}
@Service public class UserService { @Transactional public void updateUser(User user) { new Thread(() -> { // 新线程中的操作不受事务管理 }).start(); } }
  • 解决方案:避免在事务方法中启动新线程。

总结

Spring 事务失效的常见场景包括:

  1. 方法非 public 修饰。
  2. 异常未被正确抛出或捕获后未重新抛出。
  3. 事务传播行为配置不当。
  4. 同一类中方法调用。
  5. 数据源未配置事务管理器。
  6. 使用了错误的代理模式。
  7. 多线程调用。

理解这些场景并采取相应的解决方案,可以避免 Spring 事务失效的问题,确保事务的正确性和一致性。

THE END
点赞9 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容