一、Spring事务的概念及特性
1、事务是什么
事务是由一组操作(对数据库数据的处理,例如增、删、改等操作)组成的逻辑单元,要么全部执行成功,要么全部执行失败进行回滚。
2、事务的基本特性
(1)原子性:是一个不可分割的整体;
(2)一致性:事务执行完后,数据的一致性状态要么不变,要么转移到另外一个一致性状态;
(3)隔离性:并发事务之间是独立的;
(4)持久性:对数据的更改永久保存;
二、spring事务的管理、传播方式
1、管理方式
(1)编程式:调用beginTransaction()、commit()、rollback()等事务管理相关的方法
(2)声明式:通过注解或 XML 配置控制事务,推荐使用。
2、传播方式
(1)REQUIRED(默认):默认事务传播行为,存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
(2)REQUIRE_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
(3)NESTED:如果当前存在事务,就嵌套当前事务中去执行,如果当前没有事务,那么就新建一个事务,类似 REQUIRE_NEW这个样一个传播行为。
(4)SUPPORTS:表示支持当前当前的事务,如果当前不存在事务,就以非事务的方式去执行。
(5)NOT_SUPPORT: 总是非事务地执行,并挂起任何存在的事务。
(6)MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
(7)NEVER:就是以非事务的方式来执行,如果存在事务则抛出一个异常
三、Spring事务的回滚机制
1、事务回滚触发条件
Spring事务的回滚主要基于异常的抛出:
(1)默认情况:
- 捕获 unchecked Exception(如
RuntimeException
)或Error
会导致事务回滚。 - 捕获
Checked Exception
(如IOException
)不会回滚,除非明确配置。
(2)可指定何种异常回滚
- 使用
@Transactional(rollbackFor = Exception.class)
明确指定需要回滚的异常。
2、事务回滚实现流程
Spring 事务的回滚过程涉及以下关键步骤:
(1)Spring 扫描创建代理对象管理事务,即事务管理器。
(2)方法执行时,事务管理器开启事务。
(3)如果方法抛出指定异常,事务管理器调用数据库的回滚操作。
(4)如果方法成功执行,事务管理器提交事务。
四、Spring事务回滚示例
1、声明式事务回滚示例
(1)默认情况下回滚:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
/**
* 该方法使用@Transactional注解,表示该方法受事务管理。
* 当RuntimeException抛出时,Spring 会自动回滚事务,insert操作不会持久化到数据库
* @param user
*/
@Override
@Transactional
public void insertUser(User user) {
userMapper.insertUser(user);
throw new RuntimeException("模拟异常");
}
}
(2)指定回滚条件
默认情况下, checked exception 异常抛出不会引起事务回滚,但是可以通过 rollbackFor
属性显式指定需要回滚的异常类型 。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
/**
* 该方法使用@Transactional注解,表示该方法受事务管理。
* 使用rollbackFor属性设置触发事务回滚异常类型
* 当checked exception抛出时,Spring 会自动回滚事务,insert操作不会持久化到数据库
* @param user
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void insertUser(User user) {
userMapper.insertUser(user);
throw new Exception("模拟异常");
}
}
2、编程式事务回滚示例
Spring 提供了 TransactionTemplate
类,用于编程式管理事务。我们可以显式捕获异常并手动控制事务的提交或回滚。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
@Autowired
private TransactionTemplate transactionTemplate;
/**
* 通过transactionTemplate声明事务逻辑
* 当异常抛出时,通过transactionStatus.setRollbackOnly()手动触发事务回滚
* @param user
*/
@Override
public void insertUser(User user) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
try {
userMapper.insertUser(user);
throw new RuntimeException("模拟异常");
}catch (Exception e){
//事务回滚
transactionStatus.setRollbackOnly();
}
}
});
}
}
五、事务回滚注意事项
1、异常传播问题
当异常被捕获,但是异常未抛出时也不会触发事务回滚。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
@Autowired
private TransactionTemplate transactionTemplate;
@Override
@Transactional
public void insertUser(User user) {
try {
userMapper.insertUser(user);
throw new RuntimeException("模拟异常");
}catch (Exception e){
//只捕获了异常,没有抛出,不会触发事务回滚
log.error("事务回滚"+e.getMessage());
}
}
}
解决方法:捕获异常后重新抛出,或直接让异常传播。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
@Autowired
private TransactionTemplate transactionTemplate;
@Override
@Transactional
public void insertUser(User user) {
try {
userMapper.insertUser(user);
throw new RuntimeException("模拟异常");
}catch (Exception e){
log.error("事务回滚"+e.getMessage());
//触发事务回滚
throw e;
}
}
}
2、自调用导致事务失效
Spring 事务依赖 AOP 实现,如果同一个类中自调用事务方法,事务可能失效。
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private IUserService userService;
@Override
@Transactional
public void updateUser(User user) {
userMapper.updateUser(user);
//调用同一个类中的方法,会导致事务失效
user.setName("调用方法");
userService.insertUser(user);
}
@Override
@Transactional
public void insertUser(User user) {
userMapper.insertUser(user);
throw new RuntimeException("模拟异常");
}
}
解决方法:
- 将
insertUser
移到另一个服务类,由代理对象调用。 - 使用
@Transactional(propagation = Propagation.REQUIRES_NEW)
强制新建事务。
六、总结
使用Spring事务机制,可以使我们不再需要手动编写复杂的事务管理代码,而是通过简单的配置和注解来实现事务的管理,提高开发效率和代码的可维护性。同时,Spring的事务机制还支持多种数据访问框架,如JDBC、Hibernate、MyBatis等,对事务管理变得更加灵活和易用。