spring事务在实际项目开发中的使用
一, 事务的一些基础知识简单回顾一下,讲的不是很深入,网上博客很多。
1,关于事务的四大特性:原子性、隔离性、一致性、持久性 本文不再赘述;
2,事务的隔离级别:读未提交,读已提交,可重复读,串行化(这里应该深入了解各个级别会出现什么问题,比如脏读,不可重复读,幻读)
3,事务的传播行为:事务传播行为指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。 例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。默认采用:PROPAGATION_REQUIRED
二,接下来我们简单回顾一下java的异常体系:
Throwable 是 Java 语言中所有错误或异常的超类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。
实例分为 Error 和 Exception 两种。
Error 类是指 java 运行时系统的内部错误和资源耗尽错误。应用程序不会抛出该类对象。如果
出现了这样的错误,除了告知用户,剩下的就是尽力使程序安全的终止。
Exception 又有两个分支 , 一个是运行时异常 RuntimeException , 一 个是检查异常 CheckedException。
RuntimeException 如 :NullPointerException 、 ClassCastException ;
CheckedException 如: I/O 错误导致的 IOException、SQLException。
RuntimeException 是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。 如果出现 RuntimeException,那么一
定是程序员代码书写导致的错误.
CheckedException:一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强
制程序去捕获此类异常,即会出现要求你把这段可能出现异常的程序进行 try catch
三,言归正传,项目中事务的使用
springboot自动配置默认为我们配置好了事务管理器,参考我们另一篇文章https://www.cnblogs.com/enchaolee/p/11364025.html
我们在项目的实际开发中,对于事务的处理,在编码中无外乎使用@Transactional这个注解,以及对于异常的处理,下面我们详细说一下。
1,方法上加入@Transactional注解后,这个方法就成为了事务方法,如果对于注解的一些属性不做特殊配置的话,方法中如果出现了RuntimeException(运行时异常),事务会进行回滚,如果出现了checkedException(编译时异常),则不会回滚。那么对于这类异常,我们想让他在抛出的时候也进行回滚怎么办呢,当我们点进去@Transactional注解后,看下图:
我们可以看到,图中有标红的两个注解中的属性,我们可以在这里进行手动配置,以实现方法抛出具体异常进行回滚的逻辑。例如我们可以这样配置:@Transactional(rollbackFor = Exception.class);
当然还有两个字段可以设置抛出某某异常不进行回滚:noRollbackFor,noRollbackForClassName;
2,对于异常捕获,事务如何处理
如果我们在事务方法中,手动捕获了异常,并没有让事务抛出去,也没有手动指定需要回滚,那么事务方法即使出现异常,也会提交事务。
比如我们这样去做,只是记录了日志,即使使用rollbackfor=Exception.class制定了需要回滚的逻辑,但是事务仍然会提交,除非加上下面这一行:
有这句代码就不需要再手动抛出运行时异常了,但是不建议这样做,因为这样做代码中会多出很多事务回滚的代码,不利于维护,还是交得spring去处理更妥当。3,的
3,我们该怎么样处理异常回滚呢?
如图所示,CommonException是我们定义的全局异常类,我们可以把catch到的异常统一按照一定的格式进行抛出。下面是CommonException相关代码
public class CommonException extends RuntimeException { protected String errMsg; // 错误提示信息,显示给用户
protected String detailMsg; // 错误的具体信息,可能包含一些参数ID等信息
protected CommonErrorCode error; // 错误码
protected Object data; // 返回内容 public CommonException(CommonErrorCode error) {
super(error.getDesc());
this.error = error;
this.errMsg = error.getDesc();
this.detailMsg = error.getDesc();
} public CommonException(CommonErrorCode error, String errMsg) {
super(errMsg);
this.error = error;
this.errMsg = errMsg;
this.detailMsg = errMsg;
} public CommonException(CommonErrorCode error, String errMsg, String detailMsg) {
super(StringUtils.isEmpty(detailMsg) ? errMsg : detailMsg);
this.error = error;
this.errMsg = errMsg;
this.detailMsg = detailMsg;
} public CommonException(CommonErrorCode error, String errMsg, Throwable cause) {
super(errMsg, cause);
this.error = error;
this.errMsg = errMsg;
this.detailMsg = errMsg;
} public CommonException(CommonErrorCode error, String errMsg, String detailMsg, Throwable cause) {
super(StringUtils.isEmpty(detailMsg) ? errMsg : detailMsg, cause);
this.error = error;
this.errMsg = errMsg;
this.detailMsg = detailMsg;
} public CommonException(CommonErrorCode error, Throwable cause) {
super(error.getDesc(), cause);
this.error = error;
this.errMsg = error.getDesc();
this.detailMsg = error.getDesc();
} public String getErrMsg() {
return errMsg;
} public CommonErrorCode getError() {
return error;
} public String getDetailMsg() {
return detailMsg;
} public void setDetailMsg(String detailMsg) {
this.detailMsg = detailMsg;
} public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
} public Object getData() {
return data;
} public CommonException setData(Object data) {
this.data = data;
return this;
}
}
4,事务方法调用事务方法,怎么去处理
首先我们先确认一个前提:事务的传播行为我们使用默认的即:required,并且我们假设有两个事务方法a,b;a调用b。
(1)根据上面我们讲过的required的特性,我们知道spring对于这种事务方法间的调用,会默认把它当做一个事务;我们假设如果b中抛出了NullpointException,并且a,b都没有做异常的处理,那么由a,b组成的整个事务肯定都会进行回滚,这是毋庸置疑的。
(2)如果b出现异常,a catch了异常,并且没有抛出去,就像我们上面例子讲的只是记录了日志,我们会发现这个异常,思考一下为什么呢?
我们知道spring 事务管理,启用事务的方法,调用另一个事务方法时,会进行事务传播。注解@Transactional默认的传播机制是PROPAGATION_REQUIRED。再来回顾一下required特性:表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
所以是补齐方法运行的时候,同步的事务合并到了补齐的事务里面。当同步发票发生异常后,被try catch 捕获,没有抛出来。但是事务还是会进行回滚,回滚执行到 DataSourceTransactionManager 类的 doSetRollbackOnly 方法时,设置了rollbackOnly = true;
由于异常被catch, 不阻断整个事务执行。整个事务执行完后,执行commit 提交,我们打开这个抽象类AbstractPlatformTransactionManager看一下commit的逻辑
这里我标红了这个方法,我们继续往下看,我们打开DefaultTransactionStatus这个类,可以看到标红方法的具体实现。
在执行commit 提交逻辑时,执行到 DefaultTransactionStatus 类的 isGlobalRollbackOnly方法时,判断rollbackOnly 为true, 则执行回滚,并且打出那句报错的日志”Transaction rolled back because it has been marked as rollback-only”。
5,需要注意的一些点
(1)事务方法需要标记为public的
(2)@Transactional注解只能写在service中,不能再controller中,否则会报404错误
(3)如果在使用事务的情况下,所有操作都是读操作,那建议把事务设置成只读事务,当事务被标识为只读事务时,Spring可以对某些可以针对只读事务进行优化的资源就可以执行相应的优化措施,需要手动设置成true。但是方法再执行增删改回抛异常。
spring事务在实际项目开发中的使用的更多相关文章
- 团队项目开发中,常见的版本控制有svn,git
团队项目开发中,常见的版本控制有svn,git
- 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获
项目开发中的一些注意事项以及技巧总结 1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...
- Angular 项目开发中父子组件传参
在项目开发中经常会遇到 组件之间传参的问题.今天总结下在使用angular的项目中父子组件传参的问题: 1.父组件向子组件传参: 然后在父组件中 然后在父组件的html中 然后就可以在子组件中使用了 ...
- 《Maven在Java项目开发中的应用》论文笔记(十七)
标题:Maven在Java项目开发中的应用 一.基本信息 时间:2019 来源:山西农业大学 关键词:Maven:Java Web:仓库:开发人员:极限编程; 二.研究内容 1.Maven 基本原理概 ...
- 炼金术(1): 识别项目开发中的ProtoType、Demo、MVP
软件开发是很分裂的,只有不断使用原则和规律,才能带来质量. 只要不是玩具性质的项目,项目应该可以大概划分为0-1,1-10,10-100,100-1000四个种重要阶段.其中,0-1是原型验证性的:1 ...
- 项目开发中的git简单使用
原文地址: https://www.zhuyilong.fun/tech/the-blog-git.html 示例远程仓库地址: https://github.com/zhu-longge/gitWo ...
- MyBatis 项目开发中是基于 XML 还是注解?
只要你对 MyBatis 有所认识和了解,想必知道 MyBatis 有两种 SQL 语句映射模式,一种是基于注解,一种是基于XML. 基于 XML <mapper namespace=" ...
- Java项目开发中实现分页的三种方式一篇包会
前言 Java项目开发中经常要用到分页功能,现在普遍使用SpringBoot进行快速开发,而数据层主要整合SpringDataJPA和MyBatis两种框架,这两种框架都提供了相应的分页工具,使用 ...
- 项目开发中Maven的单向依赖-2022新项目
一.业务场景 工作多年,在真实的项目开发中经常会遇到将一个项目拆分成多个工程的情况,比如将一个真实的项目拆分成controller层,service层, dao层,common公共服务层等等.这样拆分 ...
随机推荐
- 深入理解 JavaScript 面向对象
我们在学习编程时,避免不了会接触一个概念,叫:面向对象编程(Object-oriented programming,缩写:oop) (不是搞对象那个对象哈),其实我们的编程方式,不止有面向对象,还有 ...
- 网页判断浏览器是否安装flash
<script> //检验flash版本 以及falsh是否安装 function detectFlash() { try { if(navigator.mimeTypes.length& ...
- 通过windug判断某个模块导致程序不能退出。
1.windug附加进程. 2.~* kb 3.看堆栈
- 【CYH-02】NOIp考砸后虐题赛:转换式:题解
这道题真的不难吧. 如@AKEE@AKEE@AKEE 大佬所说,此题的确可以将n推广到一般情况. 但题面还是良心的只到了N<=4N<=4N<=4 以目前的题目来看,简单模拟即可. 分 ...
- Hadoop值Partition分区
分区操作 为什么要分区? 要求将统计结果按照条件输出到不同文件中(分区).比如:将统计结果按 照手机归属地不同省份输出到不同文件中(分区) 默认 partition 分区 /** 源码中:numRed ...
- android 发送邮件--实现 send email for android
Android 发送邮件消息 用途:发送验证码,通过邮箱找回密码 不需要调用客户端直接使用代码进行发送 本项目使用到的jar包–本文结尾会附带下载链接 activation.jar additionn ...
- C#4.0新增功能01 动态绑定 (dynamic 类型)
连载目录 [已更新最新开发文章,点击查看详细] C# 4 引入了一个新类型 dynamic. 该类型是一种静态类型,但类型为 dynamic 的对象会跳过静态类型检查. 大多数情况下,该对象就像 ...
- C#3.0新增功能10 表达式树 05 解释表达式
连载目录 [已更新最新开发文章,点击查看详细] 表达式树中的每个节点将是派生自 Expression 的类的对象. 该设计使得访问表达式树中的所有节点成为相对直接的递归操作. 常规策略是从根节点 ...
- Docker 工作原理及容器化简易指南
Docker 非常棒! 它使软件开发者无需担心配置和依赖性,在任何地方打包,发送和运行他们的应用程序.而在与 Kubernetes 相结合后,它使应用集群部署和管理变得更方便.这使得 Docker 深 ...
- 如何实现Kali linux系统下的U盘启动(小白指导)
一.准备工作: 声明:这个“操作”并不会影响你原装的系统,真正的即插即用的哦. (1)4GB的U盘<读写速度比较快的> (2)Kali linux镜像文件 (3)软件Universal-U ...