回想起过往的面试,经常被问到有关事务的问题,如果没有做过微服务的那么就会问简单事务基础,事务四大属性,隔离级别,传播行为,@Transcational原理等等.... 日常的crud业务中,增删改都是涉及到事务,简单的事务处理就是在方法上面加@Transcational注解,至于有没有用那就不是初级或者刚入行的考虑啦!其实@Transcational是很重要的增删改的注解,基于看了这么多的博文和demo,我测试总结了相关的@Transcational失效场景,分享给大家。 其实也就是所有博主都提到的6中场景:
- 1、数据库是否支持事务 有些朋友会问,数据库还有不支持事务的?我原来也是有这个疑问,数据库没有事务,那还搞个锤子。哈哈,幼稚的想法让我受了不少罪,接触到大数据后就知道有很多数据库是不需要事务的,当然主流的数据库的事务都是可以自己配置的,你想不要,就不配置喽,直接关了也就没了呗。当面试官问你事务注解什么时候回失效,其实是一个很常见的开发问题.首先我们应该想一想,数据库是否开启了事务,数据库的事务相关配置是否支持当前的编程操作,例如mysql的myIsam引擎就是不支持事务的,还有一些大数据的库,大多都是不支持事务的,例如impala,你想想,千万级别数据的模型建立,然后入库分析,根本不需要事务啊,因为跟业务增删改根本没关系的。
- 2、注解所在类是否被spring容器管理,也就是是否是bean @Transcational注解其实也是代理模式管理事务的,你想想,代理的老大是谁,当然是spring了,不是爸爸的儿子,管不到啊!例如我们常用的@Service、@Configuration、@Bean等等注解都是IOC的具体实现,只有都是爸爸的好大儿,爸爸才会管你的
- 3、自身调用,单传播行为互斥 自己搞自己 也是没有结果的,爸爸也是不管的,自己玩自己的,爸爸也不知道你需要管啊。 这里复制一串代码,举个栗子: @Service public class OrderServiceImpl implements OrderService { @Transactional public void update(Order order) { updateOrder(order); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateOrder(Order order) { // update order } } 可以看到,这里的update方法上面加了注解,但是事务都是默认配置,方法里面调用当前类的updateOrder方法,这就是知己搞自己,在看updateOrder方法,传播行为是REQUIRES_NEW,而update方法默认的传播行为是REQUIRED,事务管理代理这个时候就不知道该听谁的了啊,一会你要这样,一会你又要那样,你到底想怎样!
- 4、修饰符非public spring的事务是AOP动态代理的,只作用于public修饰的方法、接口、类。翻看源码可以看到AbstractFallbackTransactionAttributeSource这个类中的computeTransactionAttribute方法
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
可以看到这个里面对修饰符做了判断,非public修饰的都无法代理。
- 5、异常被捕获了 指定异常回滚,但是异常发生的时候被捕获了,这个时候异常没有抛出,AOP代理也是获取不到异常的,就不会触发异常回滚事件,下面的例子中指定了回滚异常,但是代码块中发生的异常都被捕获了,这时候事务回滚就不生效了
@Transactional(rollbackFor = Exception.class)
public void method(){
try{
insert();
update();
}catch(Exception ex){
return;
}
}
另外补充一些@Transcational注解的相关基础属性,其实也就是事务相关的属性,只是在spring里面定义了一些常量枚举规范。以下补充资料为其他地方复制来的,也贴在这里分享一下
isolation
- 事务的隔离级别,默认是
Isolation.DEFAULT。 - 几种值的含义如下:
-
Isolation.DEFAULT:事务默认的隔离级别,使用数据库默认的隔离级别。Isolation.READ_UNCOMMITTED:这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。Isolation.READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻读。Isolation.REPEATABLE_READ:这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻读。Isolation.SERIALIZABLE:这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。
propagation
- 代表事务的传播行为,默认值为
Propagation.REQUIRED。 Propagation.REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。比如A方法内部调用了B方法,此时B方法将会使用A方法的事务。Propagation.MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。Propagation.NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。Propagation.NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。Propagation.REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。比如A方法使用默认的事务传播属性,B方法使用REQUIRES_NEW,此时A方法在内部调用B方法,一旦A方法出现异常,A方法中的事务回滚了,但是B方法并没有回滚,因为A和B方法使用的不是同一个事务,B方法新建了一个事务。Propagation.NESTED:支持当前事务,新增Savepoint点,也就是在进入子事务之前,父事务建立一个回滚点,与当前事务同步提交或回滚。 子事务是父事务的一部分,在父事务还未提交时,子事务一定没有提交。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
timeout
- 事务的超时时间,单位为秒。
readOnly
- 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。如果一个事务只涉及到只读,可以设置为true。
rollbackFor 属性
- 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
- 默认是在
RuntimeException和Error上回滚。
noRollbackFor
- 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。