RuoYi学习-事务(Transactional)
本文最后更新于 2024-04-01,欢迎来到我的Blog! https://www.zpeng.site/
RuoYi学习-事务(Transactional)
1.介绍
事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功;
ACID,是指数据库管理系统(DBMS)在写入或更新资料的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性:原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability);
在数据库系统中,一个事务是指:由一系列数据库操作组成的一个完整的逻辑过程;例如银行转帐,从原账户扣除金额,以及向目标账户添加金额,这两个数据库操作的总和,构成一个完整的逻辑过程,不可拆分;这个过程被称为一个事务,具有ACID特性;
原子性(Atomicity): 事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
一致性(Consistency): 事务开始前和结束后,数据库的完整性约束没有被破坏。比如A向B转账,不可能A扣了钱,B却没收到。
隔离性(Isolation): 同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
持久性(Durability): 事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
2.事务
新建的Spring Boot
项目中,一般都会引用spring-boot-starter
或者spring-boot-starter-web
,而这两个起步依赖中都已经包含了对于spring-boot-starter-jdbc
或spring-boot-starter-data-jpa
的依赖。 当我们使用了这两个依赖的时候,框架会自动默认分别注入DataSourceTransactionManager
或JpaTransactionManager
。 所以我们不需要任何额外配置就可以用@Transactional
注解进行事务的使用。
@Transactional注解只能应用到public可见度的方法上,可以被应用于接口定义和接口方法,方法会覆盖类上面声明的事务。
例如用户新增需要插入用户表、用户与岗位关联表、用户与角色关联表,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作, 这样可以防止出现脏数据,就可以使用事务让它实现回退。
做法非常简单,我们只需要在方法或类添加@Transactional
注解即可。
@Transactional
public int insertUser(User user)
{
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
return rows;
}
常见坑点1:遇到检查异常时,事务开启,也无法回滚。 例如下面这段代码,用户依旧增加成功,并没有因为后面遇到检查异常而回滚!!
@Transactional
public int insertUser(User user) throws Exception
{
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
// 模拟抛出SQLException异常
boolean flag = true;
if (flag)
{
throw new SQLException("发生异常了..");
}
return rows;
}
原因分析:因为Spring
的默认的事务规则是遇到运行异常(RuntimeException)
和程序错误(Error)
才会回滚。如果想针对检查异常进行事务回滚,可以在@Transactional
注解里使用 rollbackFor
属性明确指定异常。
例如下面这样,就可以正常回滚:
@Transactional(rollbackFor = Exception.class)
public int insertUser(User user) throws Exception
{
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
// 模拟抛出SQLException异常
boolean flag = true;
if (flag)
{
throw new SQLException("发生异常了..");
}
return rows;
}
常见坑点2: 在业务层捕捉异常后,发现事务不生效。 这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,
Spring
自然不知道这里有错,更不会主动去回滚数据。
例如:下面这段代码直接导致用户新增的事务回滚没有生效。
@Transactional
public int insertUser(User user) throws Exception
{
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
// 模拟抛出SQLException异常
boolean flag = true;
if (flag)
{
try
{
// 谨慎:尽量不要在业务层捕捉异常并处理
throw new SQLException("发生异常了..");
}
catch (Exception e)
{
e.printStackTrace();
}
}
return rows;
}
推荐做法:在业务层统一抛出异常,然后在控制层统一处理。
@Transactional
public int insertUser(User user) throws Exception
{
// 新增用户信息
int rows = userMapper.insertUser(user);
// 新增用户岗位关联
insertUserPost(user);
// 新增用户与角色管理
insertUserRole(user);
// 模拟抛出SQLException异常
boolean flag = true;
if (flag)
{
throw new RuntimeException("发生异常了..");
}
return rows;
}
注意事项
同一个事务下是无法切换数据源
禁止父方法使用
@Transactional
创建事务,子方法使用@DataSource
切换数据源正确用法: 子方法单独创建事务或父方法使用
@Transactional(propagation = Propagation.REQUIRES_NEW)
为所有子方法创建新事务
3.属性
propagation属性
propagation 代表事务的传播行为,默认值为 Propagation.REQUIRED
,其他的属性信息如下:
Propagation.REQUIRED
:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。( 也就是说如果A方法和B方法都添加了注解,在默认传播模式下,A方法内部调用B方法,会把两个方法的事务合并为一个事务 )Propagation.SUPPORTS
:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。Propagation.MANDATORY
:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。Propagation.REQUIRES_NEW
:重新创建一个新的事务,如果当前存在事务,暂停当前的事务。( 当类A中的 a 方法用默认Propagation.REQUIRED模式,类B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中调用 b方法操作数据库,然而 a方法抛出异常后,b方法并没有进行回滚,因为Propagation.REQUIRES_NEW会暂停 a方法的事务 )Propagation.NOT_SUPPORTED
:以非事务的方式运行,如果当前存在事务,暂停当前的事务。Propagation.NEVER
:以非事务的方式运行,如果当前存在事务,则抛出异常。Propagation.NESTED
:和 Propagation.REQUIRED 效果一样。
isolation 属性
isolation :事务的隔离级别,默认值为Isolation.DEFAULT
Isolation.DEFAULT
:使用底层数据库默认的隔离级别。Isolation.READ_UNCOMMITTED
:读未提交Isolation.READ_COMMITTED
:读已提交Isolation.REPEATABLE_READ
:可重复读Isolation.SERIALIZABLE
:串行化
timeout 属性
timeout :事务的超时时间,默认值为 -1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
readOnly 属性
readOnly :指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollbackFor 属性
rollbackFor :用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
noRollbackFor属性
noRollbackFor:抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。
- 感谢你赐予我前进的力量