上一篇:事务的两种形式
@Transactional介绍
@Transactional注解底层使用的是动态代理来进行实现的
Transactional注解可以作用在接口、类、类方法
- 作用接口: 不推荐,因为这只有在使用基于接口的代理时它才会生效
- 作用于类:表示该类的所有public方法都配置相同的事务属性信息
- 作用于类方法:只能用于public方法上。注意:如果类于类方法都配置了@Transactional,类方法事务会覆盖类事务
propagation属性
propagation代表事务的传播行为,默认值为Propagation.REQUIRED
- Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则新创建事务(也就是说如果A方法和B方法都添加了注解,默认传播模式下,A方法调用B方法,会将两个方法事务合并为一个)
- Propagation.SUPPORTS:如果当前存在事务,则加入,如果不存在,则以非事务形式运行
- Propagation.MANDATORY:如果当前存在事务,则加入事务,如果不存在事务,则抛异常
- Propagation.REQUIRES_NEW:重新创建一个事务,如果当前存在事务,暂停当前事务(如果A方法默认为Propagation.REQUIRED模式,B方法为Propagation.REQUIRES_NEW,在A方法中调用B方法,A方法抛出异常后,B方法不会回滚,因为Propagation.REQUIRES_NEW会暂停A方法的事务)
- Propagation.NOT_SUPPORTED:以非事务方法运行,如果当前存在事务,暂停当前事务
- 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,它的存在是为了让那些不需要事务的方法被排除掉,例如:读取数据,可以设置为readOnly为true
rollbackFor属性
用于指定能够触发事务回滚的异常类型,可以指定多个异常类型
noRollbackFor属性
指定的异常类型,不回滚事务,可以指定多个异常类型
@Transactional失效场景
-
@Transactional注解应用在非public方法上
之所以会失效式因为在Spring AOP代理时,如上图所示 TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,获取Transactional 注解的事务配置信息。
propagation 配置错误
其中这三种会存在以非事务形式运行
TransactionDefinition.PROPAGATION_SUPPORTS、TransactionDefinition.PROPAGATION_NOT_SUPPORTED、
TransactionDefinition.PROPAGATION_NEVER;
Propagation.REQUIRES_NEW也会存在事务失效的情况,见上述Propagation.REQUIRES_NEW的举例描述-
rollbackFor配置错误
Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor属性。
希望自定义的异常可以进行回滚@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class
若在目标方法中抛出的异常是 rollbackFor 指定的异常的子类,事务同样会回滚。
- 同一个类中方法间的调用
A调用本类中的B方法(不论B时public或者private),A没有声明事务,而B方法有。外部调用A方法后,方法B的事务不会起作用。
这是由于使用Spring AOP代理造成的,因为只有当前事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
//@Transactional
@GetMapping("/test")
private Integer A() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
/** * B 插入字段为 3的数据 */
this.insertB();
/** * A 插入字段为 2的数据 */
int insert = cityInfoDictMapper.insert(cityInfoDict);
return insert;
}
@Transactional()
public Integer insertB() throws Exception {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("3");
cityInfoDict.setParentCityId(3);
return cityInfoDictMapper.insert(cityInfoDict);
}
- 异常被你的catch“吃了”
@Transactional
private Integer A() throws Exception {
int insert = 0;
try {
CityInfoDict cityInfoDict = new CityInfoDict();
cityInfoDict.setCityName("2");
cityInfoDict.setParentCityId(2);
/** * A 插入字段为 2的数据 */
insert = cityInfoDictMapper.insert(cityInfoDict);
/** * B 插入字段为 3的数据 */
b.insertB();
} catch (Exception e) {
e.printStackTrace();
}
}
如果B方法发生了异常,而A方法catch住了B方法的异常,那么这个事务不能正常回滚
会抛出:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
因为当B方法中抛出了一个异常后,B标识当前事务需要rollback。但是A中由于收到铺货了这个异常并进行了处理,A任务当前事务应该可以正常commit。此时前后不一致,抛出UnexpectedRollbackException
Spring事务时在调用业务方法之前开始的,业务方法执行完毕之后才执行commit or rollback,事务是否执行取决于是否抛出RuntimeException,如果抛出了并且未catch到的话,事务就会回滚。
- 数据库不支持事务
mysql数据库默认使用支持事务的innodb引擎,一旦切换为不支持事务的myisam,那么事务就会失效
原文链接:https://baijiahao.baidu.com/s?id=1661650900351466294&wfr=spider&for=pc