一文带你彻底搞懂Spring事务传播机制(面试系列)

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

日日行,不怕千万里

什么是Spring的事务传播机制?项目中遇到长事务怎么处理?相信这是大多数人面试时会被问到的高频问题,那今天就来聊聊这个问题。

事务

什么是事务?

事务这个词大家都不会陌生,甚至ACID背的滚瓜烂熟,被问到这个,心里一喜,心想这不是手到擒来吗?直接撞咱枪口上了。

接下来你了你的表演:

  • 事务的特性

    • 原子性:一个事务中的所有操作,要么全部成功,要么全部失败,没有中间状态。

    • 一致性:事务执行结束后,数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态,这是事务追求的最终状态,可以说其他的三个特性都是为了保证事务的一致性。

    • 隔离性:事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰,严格的隔离性对应了串行化执行,但是实际操作中很少会用到串行化的隔离级别,一般来说可以分两个方面来保证隔离性。

      • A事务写对B事务写的影响:通过锁来保证
      • A事务写对B事务读的影响:通过MVCC来保证
    • 持久性:事务一旦提交,对数据库的影响应该是永久性的。

那你平时在项目中是如何使用到事务的?

哎呀,那这不又是正中下怀吗?

首先,Spring对事务的支持建立在你所使用的数据库,如果你使用的是mysql数据库,恰好又是使用的innodb引擎,那么恭喜你,是支持事务的。那Spring对事物的支持一般有两种方式:

  • 编程式事务管理:通过 TransactionTemplate或者TransactionManager手动管理事务,实际应用中很少使用,这不是本文的重点,就不在这里赘述。

    public class Test1 {
        @Autowired
        private TransactionTemplate transactionTemplate;
        public void testProgrammingTransaction() {
    ​
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                    try {
                        // ....  do something
                    } catch (Exception e){
                        //rollback
                        transactionStatus.setRollbackOnly();
                    }
    ​
                }
            });
        }
    }
    复制代码
  • 声明式事务管理:使用场景最多,也是最推荐使用的方式,直接加上@Transactional注解即可。

那你项目中遇到长事务是如何处理的呢,如果有一百个操作,怎么处理这种事务呢?

事务嵌套是如何处理的呢?再比如A事务中嵌套了B事务,B事务成功开启了吗?

完了完了,你开始支支吾吾,面试官看你回答不上来,问了几个无关紧要的问题,然后心不在焉的问你有什么要问他的,你知道,凉了,要回去等通知了...

Spring事务传播机制

你回去仔细研究了一下发现,事务传播机制一共有七种:

PROPAGATION_REQUIRED:Spring的默认传播级别,如果上下文中存在事务则加入当前事务,如果不存在事务则新建事务执行。

PROPAGATION_SUPPORTS:如果上下文中存在事务则加入当前事务,如果没有事务则以非事务方式执行。

PROPAGATION_MANDATORY:该传播级别要求上下文中必须存在事务,否则抛出异常。

PROPAGATION_REQUIRES_NEW:每次执行都会创建新事务,并同时将上下文中的事务挂起,执行完当前线程后再恢复上下文中事务。

PROPAGATION_NOT_SUPPORTED:当上下文中有事务则挂起当前事务,执行完当前逻辑后再恢复上下文事务。

PROPAGATION_NEVER:该传播级别要求上下文中不能存在事务,否则抛出异常。

PROPAGATION_NESTED:嵌套事务,如果上下文中存在事务则嵌套执行,如果不存在则新建事务

哦豁,已看完这个,已经更晕了,于是你下定决心一定要搞懂,不然下次面试又得嗝屁。你去翻了翻网上大神的文章,又问了问身边的同事大佬,大佬告诉你,这七个中,你只需要记住其中两个就能应付大多数的问题,这里的大多数是大于等于90%。

  • PROPAGATION_REQUIRED:Spring默认传播级别当然不用多说,那如何理解上下文中如果存在事务,就加入当前事务呢,如果不存在就新建事务执行呢?

    • 如果A事务中如果嵌套了B事务,B事务也不会是单独的,也会加入到A事务中。
    • 如果A事务失败,回滚。
    • 如果A事务中,B事务开始前的前置操作执行成功,事务不会提交,继续执行B事务。
    • 如果B事务失败,因为B事务是加入了当前事务(上下文中的事务,A事务),连同A事务一起回滚

    在下面代码中,a不可以正常插入数据库,会造成回滚。原因在于使用的Spring默认的事务传播机制,Test1的事务会加入到Test的事务中。

    public class Test {
        @Autowired
        private static Test1 test1;
        @Autowired
        private static AMapper aMapper;
        @Transactional
        public void testTransaction(A a) throws Exception {
            aMapper.insert(a);
            try {
                test1.required();
            }catch (Exception e){
                System.out.println(e);
            }
        }
    }
    复制代码
    public class Test1 {
        @Transactional
        public void required() {
            throw new NullPointerException("throw a test exception");
        }
    }
    复制代码
  • PROPAGATION_REQUIRES_NEW:每次执行都会创建新事务,并同时将上下文中的事务挂起,执行完当前线程后再恢复上下文中事务,这句话应该比较好理解了,可以理解为事务是单独的,嵌套的事务不会影响上下文中的事务。

    • 如果A事务中嵌套了B事务,B事务会是单独执行的,此过程中A事务会暂时挂起。
    • 如果B事务执行成功,B事务提交,A事务恢复。
    • 如果B事务执行失败,B事务回滚,A事务恢复。
    • B对于A来说是单独的,B事务的失败不会造成A事务的失败,当然是在有try-catch的情况下。
    • 如果没有try-catch或者说A事务直接执行失败了,不用想,数据是肯定会插入失败的。

    就像下面的代码一样,有try-catch,a可以插入成功,如果没有try-catch,a一定会插入失败。

    public class Test {
        @Autowired
        private static Test1 test1;
        @Autowired
        private static AMapper aMapper;
        @Transactional
        public void testTransaction(A a) throws Exception {
            aMapper.insert(a);
            try {
                test1.required();
            }catch (Exception e){
                System.out.println(e);
            }
        }
    }
    复制代码
    public class Test1 {
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public void required() {
            throw new NullPointerException("throw a test exception");
        }
    }
    复制代码

其他情况

那么如果遇到其他情况,乖乖的来看你需要的是哪种Spring事务传播机制吧。

另:@Transactional默认情况下,只回滚RuntimeException哦。

后记

你不可能真想把这玩意全背下来吧,退一万步讲,就算你全背下来了,面试官会不知道你是背的?

面试官内心OS:这能全知道?肯定这小子是个卷王,八股文背的这么溜,要是招进来,还不得把我也卷出去?

这些文章我有空会给大家做一个整理,到时候给大家放在一个地方,如果有疑惑或者问题,欢迎指出或者私信我哦。

同时也希望大家可以关注我的公众号【际遇技术栈】,会定期与大家分享我的一些想法与学习感悟,谢谢各位!

猜你喜欢

转载自juejin.im/post/7090563623491207198