前述

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

@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 :这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。

spring学习笔记(九)事务学习(上)的更多相关文章

  1. 学习笔记:CentOS7学习之十九:Linux网络管理技术

    目录 学习笔记:CentOS7学习之十九:Linux网络管理技术 本文用于记录学习体会.心得,兼做笔记使用,方便以后复习总结.内容基本完全参考学神教育教材,图片大多取材自学神教育资料,在此非常感谢MK ...

  2. 软件测试之loadrunner学习笔记-01事务

    loadrunner学习笔记-01事务<转载至网络> 事务又称为Transaction,事务是一个点为了衡量某个action的性能,需要在开始和结束位置插入一个范围,定义这样一个事务. 作 ...

  3. WCF学习笔记之事务编程

    WCF学习笔记之事务编程 一:WCF事务设置 事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元: WCF通过System.ServiceModel.TransactionFlowA ...

  4. 多线程学习笔记九之ThreadLocal

    目录 多线程学习笔记九之ThreadLocal 简介 类结构 源码分析 ThreadLocalMap set(T value) get() remove() 为什么ThreadLocalMap的键是W ...

  5. MDX导航结构层次:《Microsoft SQL Server 2008 MDX Step by Step》学习笔记九

    <Microsoft SQL Server 2008 MDX Step by Step>学习笔记九:导航结构层次   SQL Server 2008中SQL应用系列及BI笔记系列--目录索 ...

  6. go微服务框架kratos学习笔记九(kratos 全链路追踪 zipkin)

    目录 go微服务框架kratos学习笔记九(kratos 全链路追踪 zipkin) zipkin使用demo 数据持久化 go微服务框架kratos学习笔记九(kratos 全链路追踪 zipkin ...

  7. c++学习笔记之封装篇(上)

    title: c++学习笔记之封装篇(上) date: 2017-03-12 18:59:01 tags: [c++,c,封装,类] categories: [学习,程序员,c/c++] --- 一. ...

  8. python3.4学习笔记(九) Python GUI桌面应用开发工具选择

    python3.4学习笔记(九) Python GUI桌面应用开发工具选择 Python GUI开发工具选择 - WEB开发者http://www.admin10000.com/document/96 ...

  9. Go语言学习笔记九: 指针

    Go语言学习笔记九: 指针 指针的概念是当时学C语言时了解的.Go语言的指针感觉与C语言的没啥不同. 指针定义与使用 指针变量是保存内存地址的变量.其他变量保存的是数值,而指针变量保存的是内存地址.这 ...

  10. Spark学习笔记2——RDD(上)

    目录 Spark学习笔记2--RDD(上) RDD是什么? 例子 创建 RDD 并行化方式 读取外部数据集方式 RDD 操作 转化操作 行动操作 惰性求值 Spark学习笔记2--RDD(上) 笔记摘 ...

随机推荐

  1. 解决click与hover(mouseover)的冲突问题

    主要应用到一个知识点:$(selector).data('name','value'); <!DOCTYPE HTML> <html> <head> <met ...

  2. 用threejs实现三维全景图

    网络上看到了3D全景图,发现threejs里面有一个库竟然可以实现,一下我贴出代码: <!DOCTYPE html> <html> <head> <meta ...

  3. 惊呆了,Servlet Filter和Spring MVC Interceptor的实现居然这么简单

    前言 创建型:单例模式,工厂模式,建造者模式,原型模式 结构型:桥接模式,代理模式,装饰器模式,适配器模式,门面模式,组合模式,享元模式 行为型:观察者模式,模板模式,策略模式,责任链模式,状态模式, ...

  4. 百度api实现人脸对比

    第一步(注册账号): 点这里注册百度云账号 如图: 创建应用得到 APP_ID API_KEY SECRET_KEY   第二步(代码): import requests import base64 ...

  5. 架构师修炼之微服务部署 - Docker简介

    Docker简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器或Windows 机器上,也可以实现虚拟化,容器是 ...

  6. 永恒之蓝MS17010复现

    MS17010复现 靶机win7:192.168.41.150 攻击kali:   192.168.41.147 扫描 通过auxiliary/scanner/smb/smb_ms17_010模块扫描 ...

  7. 当td中文字过长时,显示为省略号

    当表格中的文字过长时,可选择已省略号显示.这里是用js实现的.首先获取td中的文字长度(innerText.length),如果长度超过了设定的长度,则截取内容,加上省略号显示.示例代码如下: $(f ...

  8. Pytorch实现的语义分割器

    使用Detectron预训练权重输出 *e2e_mask_rcnn-R-101-FPN_2x* 的示例 从Detectron输出的相关示例 使用Detectron预训练权重输出 *e2e_keypoi ...

  9. netcore 下的policy授权自定义返回结果

    目前一直在用policy做权限校验,但是好像组里需要将返回结果统一,之前用的都是直接继承AuthorizationHandler然后调用context.Fail(),但是这样会导致没办法自定义返回结果 ...

  10. Matplotlib 误差线的绘制和子图的创建方式

    一.绘制误差线 使用errorbar方法可以绘制误差线. x = np.linspace(0,10,50) dy=0.8 y = np.cos(x) + dy*np.random.randn(50) ...