可以作用的地方
类:表示该类的所有public方法都会配置相同的事务属性信息
方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务属性信息
接口:不推荐使用,一旦注解在interface上并且配置了Spring AOP使用CGLib动态代理,将会导致@Transactional失效
属性
propagation
代表事务的传播级别,默认Propagation.REQUIRED
- Propagation.REQUIRED:
如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务 - Propagation.SUPPORTS
如果当前存在事务,则加入事务,没有则一飞事务方式运行 - Propagation.MANDATORY
当前存在事务,则加入事务,不存在事务则抛出异常 - Propagation.REQUIRED_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
嵌套事务
isolation
事务隔离级别,默认Isolation.DEFAULT(底层数据库默认隔离级别)
- Isolation.READ_UNCOMMITTED
- Isolation.READ_COMMITTED
- Isolation.REPEATABLE_READ
- Isolation.SERIALIZABLE
timeout
事务超时时间,默认-1.如果超过该时间事务依然没有完成,回滚
readonly
指定事务为只读事务,默认false,为了忽略那些不需要事务的方法,比如读取数据,可以设置read-only为true
rollbackFor
触发事务回滚的异常,可以指定多个
norollbackFor
抛出指定的异常,不回滚事务,可以指定多个
失效的场景
- @Transaction应用在非public修饰的方法上
AOP代理的时候,在上图TransactionInterceptor(事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CGLIBAOPProxy的内部类)的intercept方法或者JdkDynamicAopProxy的invoke方法会间接调用AbstractFallbackTransactionAttributeSource的computeTransactionAttribute方法获取Transaction注解的事务配置信息
protected TransactionAttribute computeTransactionAttribute(Method method,
Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
此方法会检查目标方法的修饰符是否为public ,不是public的不好获取Transaction注解,虽然事务无效,但不会有任何报错
- 注解属性propagation设置错误
以下三种情况,事务将不会进行回滚
TransactionDefinition.PROPAGATION_SUPPORTS:
TransactionDefinition.PROPAGATION_NOT_SUPPORTED
TransactionDefinition.PROPAGATION_NEVER
- 注解属性rollbackFor设置错误
rollbackFor可以指定能够触发事务回滚的异常类型,Spring默认抛出未检出unchecked的异常(继承RunTimeException的异常)或者Error才会回滚事务,其他异常不会触发事务,如果事务中抛出其他异常类型,需要指定rollbackFor属性
// 希望自定义的异常可以进行回滚
@ransactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class
如果抛出的异常事务rollbackFor指定异常的子类,事务同样会进行回滚
private int getDepth(Class<?> exceptionClass, int depth) {
if (exceptionClass.getName().contains(this.exceptionName)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (exceptionClass == Throwable.class) {
return -1;
}
return getDepth(exceptionClass.getSuperclass(), depth + 1);
}
同一个类中的方法调动
开发中避免不了同一个类中的方法调用,类test中的A方法调用B方法,A没有声明注解事务,B方法有,外部调用方法A之后,B的事务是不会起效果的,原因是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理异常被catch,导致@Transaction失效
@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方法try catchB方法的异常,那么这是事务能正常回滚吗?
答案是不能
会抛出异常
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
当ServiceB中抛出异常,ServiceB标识当前事务需要rollback。但是ServiceA中由于你手动捕获了一个异常并处理,ServiceA会认为当前事务应该正常commit,这个时候就会出现前后不一致,这样就会出现上面的异常
Spring的事务在调用业务方法之前开始的,业务方法执行完毕之后才会执行commit 或者rollback,事务是否执行取决于是否爬出runtime异常,如果抛出runtime 异常,并在你的业务代码中并有catch到,事务就会回滚
在业务方法中一般不需要catch异常,非要catch就一定要throw new RunTimeExecption() 或者抛出注解中指定会回滚的异常,否则将会导致事务失效,数据commit造成数据不一致
- 数据库存储引擎不支持
比如MyISAM