文章目录
-
- 1. Spring事务基础
-
- 1.1 什么是Spring事务
- 1.2 Spring事务的实现原理
- 1.3 `@Transactional`注解的主要属性
- 1.4 使用Spring事务的简单示例
- 2. Spring事务失效的常见场景及解决方案
-
- 2.1 方法不是public的
-
- 问题描述
- 问题示例
- 解决方案
- 技术原理解释
- 2.2 自调用问题(同一个类中的方法调用)
-
- 问题描述
- 问题示例
- 解决方案
- 技术原理解释
- 2.3 异常被捕获而未被抛出
-
- 问题描述
- 问题示例
- 解决方案
- 技术原理解释
- 2.4 抛出的异常类型不正确
-
- 问题描述
- 问题示例
- 解决方案
- 技术原理解释
- 2.5 事务传播行为设置不当
-
- 问题描述
- 问题示例
- 解决方案
- 技术原理解释
- 2.6 未被Spring管理的类
-
- 问题描述
- 问题示例
- 解决方案
- 技术原理解释
- 2.7 数据库引擎不支持事务
-
- 问题描述
- 问题示例
- 解决方案
- 技术原理解释
- 2.8 使用了错误的事务管理器
-
- 问题描述
- 问题示例
- 解决方案
- 技术原理解释
- 2.9 多线程环境下的事务问题
-
- 问题描述
- 问题示例
- 解决方案
- 技术原理解释
- 2.10 代理的限制(CGLIB代理和final方法)
-
- 问题描述
- 问题示例
- 解决方案
- 技术原理解释
- 3. 常见问题与解答
-
- Q1: 为什么在同一个类中调用`@Transactional`方法会导致事务失效?
- Q2: Spring事务默认对哪些异常回滚?
- Q3: 为什么有时候抛出异常后事务没有回滚?
- Q4: `@Transactional`注解应该放在接口上还是实现类上?
- Q5: 有没有办法在一个事务中部分回滚?
- Q6: 如何在事务提交前或回滚后执行某些操作?
- Q7: 如何在测试中验证事务是否正常工作?
- 4. 最佳实践
-
- 4.1 设计和开发阶段
- 4.2 实现阶段
- 4.3 测试阶段
- 5. 总结
1. Spring事务基础
1.1 什么是Spring事务
事务是指对数据库执行的一系列操作,这些操作要么全部成功执行,要么全部不执行,以保证数据的一致性和完整性。Spring事务管理是Spring框架中一个强大的功能,它提供了一套完整的事务管理机制,使开发人员能够以声明式或编程式的方式管理事务。
Spring事务管理的核心是抽象出了一套事务管理的API,无论使用JDBC、Hibernate还是JPA,Spring都能以一致的方式提供事务支持。
1.2 Spring事务的实现原理
Spring事务的实现原理主要基于AOP(面向切面编程)和代理模式。当我们在一个方法上添加@Transactional注解时,Spring会使用AOP创建一个代理对象,这个代理对象会在目标方法执行前开启事务,在方法执行后提交事务,如果发生异常则回滚事务。
具体流程如下:
- Spring容器初始化时,扫描带有
@Transactional注解的方法 - 通过BeanPostProcessor创建代理对象(默认使用JDK动态代理,如果类没有实现接口则使用CGLIB代理)
- 当调用带有
@Transactional注解的方法时,会首先执行代理对象的逻辑 - 代理对象负责开启事务、执行原方法、提交或回滚事务
1.3 @Transactional注解的主要属性
@Transactional(
propagation = Propagation.REQUIRED, // 事务传播行为
isolation = Isolation.DEFAULT, // 事务隔离级别
timeout = -1, // 事务超时时间
readOnly = false, // 是否只读事务
rollbackFor = Exception.class, // 遇到哪些异常回滚
noRollbackFor = FileNotFoundException.class // 遇到哪些异常不回滚
)
1.4 使用Spring事务的简单示例
下面是一个基本的Spring事务使用示例:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUser(User user) {
userRepository.save(user);
// 如果后续操作抛出异常,该操作会回滚
}
}
2. Spring事务失效的常见场景及解决方案
2.1 方法不是public的
问题描述
Spring官方文档明确指出,@Transactional注解只能应用于public方法上。如果将其应用于非public方法,该注解不会生效。
问题示例
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// 事务无效,因为方法不是public的
@Transactional
protected void createUser(User user) {
userRepository.save(user);
throw new RuntimeException("故意抛出异常");
}
}
解决方案
确保所有使用@Transactional注解的方法都是public的。
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// 正确使用,方法是public的
@Transactional
public void createUser(User user) {
userRepository.save(user);
// 如果抛出异常,事务会回滚
}
}
技术原理解释
Spring的事务管理是通过AOP实现的,而Spring AOP的默认行为是只拦截public方法。这是因为在代理对象中,非public方法无法被外部调用,所以Spring AOP也就无法为其创建代理。
2.2 自调用问题(同一个类中的方法调用)
问题描述
当一个类中的方法调用同一个类中的另一个事务方法时,事务不会生效。这是因为在这种情况下,调用的是目标对象的方法,而不是代理对象的方法。
问题示例
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// 事务生效的方法
@Transactional
public void createUserWithRoles(User user, List<Role> roles) {
// 直接调用同类中的另一个事务方法
this.createUser(user); // 这里的调用不会使用事务代理
// 添加角色逻辑
for (Role role : roles) {
userRepository.saveUserRole(user.getId(), role.getId());
}
// 如果这里抛出异常,createUser方法不会回滚
throw new RuntimeException("故意抛出异常");
}
@Transactional
public void createUser(User user) {
userRepository.save(user);
}
}
在上面的例子中,如果createUserWithRoles方法抛出异常,通过调用this.createUser(user)创建的用户不会回滚,因为这是一个自调用,没有通过Spring的事务代理。
解决方案
有以下几种解决方案:
- 将方法移到其他类中:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleService roleService;
@Transactional
public void createUserWithRoles(User user, List<Role> roles) {
// 调用其他类的方法,事务会正常工作
roleService.createUser(user);
for (Role role : roles) {
userRepository.saveUserRole(user.getId(), role.getId());
}
throw new RuntimeException("故意抛出异常");
}
}
@Service
public class RoleService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUser(User user) {
userRepository.save(user);
}
}
- 使用自我注入:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// 自我注入,获取代理对象
@Autowired
private UserService self;
@Transactional
public void createUserWithRoles(User user, List<Role> roles) {
// 通过代理对象调用方法,事务会正常工作
self.createUser(user);
for (Role role : roles) {
userRepository.saveUserRole(user.getId(), role.getId());
}
throw new RuntimeException("故意抛出异常");
}
@Transactional
public void createUser(User user) {
userRepository.save(user);
}
}
- 使用AopContext获取代理对象(需要配置
exposeProxy = true):
@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUserWithRoles(User user, List<Role> roles) {
// 获取当前代理对象,调用其方法
((UserService) AopContext.currentProxy()).createUser(user);
for (Role role : roles) {
userRepository.saveUserRole(user.getId(), role.getId());
}
throw new RuntimeException("故意抛出异常");
}



















