spring学习笔记(九)事务学习(上)

前述

​ 这段时间在工作中碰到一个事务相关的问题。先说下这个问题的场景,我们是一个商城项目,正在开发优惠券模块,现在有一个需求是需要批量领取优惠券,而且在领券时,其中一张领取失败不能影响其他符合要求的券的领取。由于之前在开发时,在领券这一块一直做的是单张领取,所以在做批量的时候很简单的做了个循环,然后封装成一个批量领券的方法中。伪代码如下:

@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class CouponService {
    /**
     * 单独领取某张券
     * @param couponCode
     * @return
     */
    public Coupon get(String couponCode) {
        log.info("查询是否存在这个券");
        if ("001".equals(couponCode) || "002".equals(couponCode)) {
            return Coupon.builder().code(couponCode).name("优惠券A").build();
        }
        // 自定义的异常类,专门指定不符合规则的券被领取时抛出的状态
        throw new CustomeException("不存在这个优惠券");
    }

    /**
     * 批量领取
     * @param codes
     * @return
     */
    public List<Coupon> batchGet(List<String> codes) {
        List<Coupon> target = new ArrayList<>();
        codes.forEach(code -> {
            Coupon coupon = null;
            try {
                coupon = get(code);
            } catch (CustomeException e) {
                log.error(e.getMessage());
            }
            target.add(coupon);
        });
        return target;
    }
}

上面的方法在执行时会抛出异常

Transaction rolled back because it has been marked as rollback-only

我们来分析下这个异常:

首先我们要知道,spring中事务的默认传播机制是

PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启

​ 在这种传播机制下,batchGet方法跟循环中的get方法会共享一个事务,而在get方法抛出异常时,这个事务已经被标记为rollback-only了,在这种情况下,batchGet方法捕获了这个异常并没有继续向上抛出,所以会执行commit操作,而对一个被标记成rollback-only的方法执行commit操作,就会抛出以上错误。

​ 我的解决办法就是,在batcGet方法上将事务的隔离级别设置为PROPAGATION_SUPPORTS:如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行

好了,这个异常我们分析完了,现在我们进入我们这篇文章需要学的主题内容,主要是事务的传播机制及隔离级别。预期两篇文章,一篇理想学习,一篇源码分析,源码分析将以SpringBoot为主。希望能跟大家一起进步,学习,每天进步一点点就好~~~

Spring中事务的传播机制:

  1. ROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启

解释:当A.methodA()和B.methodB()都打上REQUIRED的事务标志,执行A.methodA()方法的时候,看到上下文没有事务,会新建一个事务,当执行到b.methodB()的时候,发现上下文已经有事务了,则不会新建事务,用A.methodA()新建的那个事务。如果b.methodB()执行成功,a.methodA()执行失败,那么b.methodB()和a.methodA()都会回滚(用的都是a.methodA()的事务)

  1. PROPAGATION_SUPPORTS:如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行

解释:当B.methodB()打上PROPAGATION_SUPPORTS的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,会检查上下文有没有事务,如果A.methodA()有事务,则b.methodB()沿用该事务,反之b.methodB()就以非事物的方式执行

3. PROPAGATION_MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常

解释:当B.methodB()打上PROPAGATION_MANDATORY的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,会检查上下文有没有事务,如果A.methodA()有事务,则b.methodB()沿用该事务,如果没有,则会抛出异常

4.PROPAGATION_REQUIRES_NEW:总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起

解释:当B.methodB()打上PROPAGATION_REQUIRES_NEW的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,会检查上下文有没有事务,如果A.methodA()有事务,则会挂起A.methodA()的事务,新建一个属于b.methodB(),当b.methodB()的事务执行结束的时候,则会唤醒b.methodB()的事务。和PROPAGATION_REQUIRED的差别在于回滚,当b.methodB()的事务提交后,A.methodA()执行失败,只会回滚A.methodA不会回滚b.methodB(),当b.methodB()执行失败,异常被A.methodA()方法catch到的话,A.methodA()事务不会回滚

  1. PROPAGATION_NOT_SUPPORTED:总是非事务地执行,并挂起任何存在的事务

解释:当B.methodB()打上PROPAGATION_NOT_SUPPORTED的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,会检查上下文有没有事务,如果A.methodA()有事务,则会挂起A.methodA()的事务,当执行完b.methodB()方法的时候,A.methodA()方法继续以事务的方式执行

  1. PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常

解释:当B.methodB()打上PROPAGATION_NEVER的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,会检查上下文有没有事务,如果有事务,则抛出异常,如果没有则以非事务执行

  1. PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, PROPAGATION_REQUIRED 属性执行

解释:当B.methodB()打上PROPAGATION_NOT_SUPPORTED的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,如果A.methodA()方法有事务,则会用当前事务,如果 b.methodB()执行失败,只会回滚 b.methodB(),不会回滚A.methodA(),只有当A.methodA()执行完成后才会提交b.methodB()的事务,如果A.methodA()方法没有事务,就会新建一个事务;

Spring中事物的隔离级别

事务隔离级别:

ISOLATION_DEFAULT,

ISOLATION_READ_UNCOMMITTED,

ISOLATION_READ_COMMITTED,

ISOLATION_REPEATABLE_READ,

ISOLATION_GENERALIZABLE

1.ISOLATION_DEFAULT:这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别

2. ISOLATION_READ_UNCOMMITTED :这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。

3. ISOLATION_READ_COMMITTED :保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。:

4. ISOLATION_REPEATABLE_READ :这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。

5. ISOLATION_SERIALIZABLE :这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。

原文地址:https://www.cnblogs.com/daimzh/p/12854458.html