日常的后端开发中离不开事务的处理,在多数事务隔离级别不变的情况下,我们更多在意的是如何通过事务的传播级别进行业务的实现。
声明式事务
@Transactional(注解参数),参数介绍如下:
属性 | 类型 | 描述 |
---|---|---|
propagation | 枚举型:Propagation | 可选的传播性设置(默认值:REQUIRED) |
isolation | 枚举型:Isolation | 可选的隔离性级别(默认值:ISOLATION_DEFAULT) |
readOnly | 布尔型 | 读写型事务/只读型事务 |
timeout | int型(以秒为单位) | 事务超时 |
rollbackFor | 一组Class类的实例,必须是Throwable的子类 | 一组异常类,遇到时必须进行回滚。默认情况下checked exceptions不进行回滚,仅unchecked exceptions(即RuntimeException的子类)才进行事务回滚。 |
rollbackForClassname | 一组Class类的名字,必须是Throwable的子类 | 一组异常类名,遇到时必须 进行回滚 |
noRollbackFor | 一组Class类的实例,必须是Throwable的子类 | 一组异常类,遇到时 必须不回滚。 |
noRollbackForClassname | 一组Class类的名字,必须是Throwable的子类 | 一组异常类,遇到时 必须不回滚 |
事务的传播级别
Spring约定一共7个级别,排序按照个人理解顺序:
- PROPAGATION_REQUIRED
默认级别,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行,这个级别通常能满足大多数的业务场景。 - PROPAGATION_REQUIRES_NEW
每次都要一个新事务,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。 - PROPAGATION_SUPPORTS
如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。所以说,并非所有的包在transactionTemplate.execute中的代码都会有事务支持。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。 - PROPAGATION_NESTED
嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。 - PROPAGATION_NOT_SUPPORTED
不支持,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。 - PROPAGATION_NEVER
该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行! - PROPAGATION_MANDATORY
该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
特殊场景实例
示例1:如何让T1不影响T2,T2影响T1
method T{
self().mothod T1();
self().mothod T2();
}
方案:
@Transactional(rollbackFor = Exception.class)
method T{
@Transactional(rollbackFor = Exception.class)
self().mothod T1();
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
self().mothod T2();
}
原理:T2新开事务即可,新开事务T2提交失败会影响到主事务T回滚,主事物T会影响Require事务T1。
示例2:在T1有子事务T11不可更改的情况下,如果做到T1不影响T2,T2影响T1(基于示例1)
method T{
self().mothod T1(){
@Transactional(rollbackFor = Exception.class)
sefl().method T11();
};
self().mothod T2();
}
方案:
@Transactional(rollbackFor = Exception.class)
method T{
@Transactional(rollbackFor = Exception.class)
self().mothod T1(){
try {
@Transactional(rollbackFor = Exception.class)
sefl().method T11();
} catch(Exception e) {
// 标记当前事务要回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
};
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
self().mothod T2();
}
原理:重点在于T1的子事务T11在报错被try catch之后,父事务T1在提交时候会报错:
Transaction rolled back because it has been marked as rollback-only
T1无法提交要回滚的T11,所以我们在T11出错时候手动设置T1要回滚事务就能解决。
T2事务新开,正常提交不影响T(T1、T11)的回滚,回滚会让T(T1、T11)也跟着回滚。
示例3:基于示例2,实现批量场景
全部执行方案:每次都是一个新事务,所以彼此不受影响
for循环{
try {
self().methodT();
} catch(Exception e) {
// 业务记录
}
}
单个失败全量回滚方案:所有的事物都在主事务中,一个失败全部回滚。
@Transactional(rollbackFor = Exception.class)
method T{
for循环{
@Transactional(rollbackFor = Exception.class)
self().mothod T1(){
@Transactional(rollbackFor = Exception.class)
sefl().method T11();
};
@Transactional(rollbackFor = Exception.class)
self().mothod T2();
}
}
原理:考虑批量的业务场景,决定主事务的开启形式,通过内层循环与外层循环来实现。
作者:舞鹤Roc
链接:https://www.jianshu.com/u/9df21f320e68
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
网友评论