Spring Boot JPA 中transaction的使用

transaction是我们在做数据库操作的时候不能回避的一个话题,通过transaction,我们可以保证数据库操作的原子性,一致性,隔离性和持久性。

本文我们将会深入的探讨Spring Boot JPA中@Transactional注解的使用。

通过@Transactional注解,我们可以设置事物的传播级别和隔离级别,同时可以设置timeout, read-only, 和 rollback等特性。

@Transactional的实现

Spring通过创建代理或者操纵字节码来实现事物的创建,提交和回滚操作。如果是代理模式的话,Spring会忽略掉@Transactional的内部方法调用。

如果我们有个方法callMethod,并标记它为@Transactional,那么Spring Boot的实现可能是如下方式:

createTransactionIfNecessary();
try {
callMethod();
commitTransactionAfterReturning();
} catch (exception) {
completeTransactionAfterThrowing();
throw exception;
}

@Transactional的使用

@Transactional使用起来很简单,可以放在class上,可以放在interface上,也可以放在方法上面。

如果放在方法上面,那么该方法中的所有public方法都会应用该Transaction。

如果@Transactional放在private方法上面,则Spring Boot将会忽略它。

Transaction的传播级别

传播级别Propagation定义了Transaction的边界,我们可以很方便的在@Transactional注解中定义不同的传播级别。

下面我们来分别看一下Transaction的传播级别。

REQUIRED

REQUIRED是默认的传播级别,下面的两种写法是等价的:

    @Transactional
public void deleteBookWithDefaultTransaction(Long id) {
bookRepository.deleteBookById(id);
} @Transactional(propagation = Propagation.REQUIRED)
public void deleteBookWithRequired(Long id) {
}

Spring会检测现在是否有一个有效的transaction。如果没有则创建,如果有transaction,则Spring将会把该放方法的业务逻辑附加到已有的transaction中。

我们再看下REQUIRED的伪代码:

if (isExistingTransaction()) {
if (isValidateExistingTransaction()) {
validateExisitingAndThrowExceptionIfNotValid();
}
return existing;
}
return createNewTransaction();

SUPPORTS

在SUPPORTS的情况下,Spring首先会去检测是否有存在Transaction,如果存在则使用,否则不会使用transaction。

我们看下代码怎么使用:

    @Transactional(propagation = Propagation.SUPPORTS)
public void deleteBookWithSupports(Long id) {
}

SUPPORTS的实现伪代码如下:

if (isExistingTransaction()) {
if (isValidateExistingTransaction()) {
validateExisitingAndThrowExceptionIfNotValid();
}
return existing;
}
return emptyTransaction;

MANDATORY

在MANDATORY情况下,Spring先会去检测是否有一个Transaction存在,如果存在则使用,否则抛出异常。

我们看下代码怎么使用:

    @Transactional(propagation = Propagation.MANDATORY)
public void deleteBookWithMandatory(Long id) {
}

MANDATORY的实现逻辑如下:

if (isExistingTransaction()) {
if (isValidateExistingTransaction()) {
validateExisitingAndThrowExceptionIfNotValid();
}
return existing;
}
throw IllegalTransactionStateException;

NEVER

如果是NEVER的情况下,如果现在有一个Transaction存在,则Spring会抛出异常。

使用的代码如下:

    @Transactional(propagation = Propagation.NEVER)
public void deleteBookWithNever(Long id) {
}

实现逻辑代码如下:

if (isExistingTransaction()) {
throw IllegalTransactionStateException;
}
return emptyTransaction;

NOT_SUPPORTED

如果使用的是NOT_SUPPORTED,那么Spring将会首先暂停现有的transaction,然后在非transaction情况下执行业务逻辑。

我们这样使用:

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
public void deleteBookWithNotSupported(Long id) {
}

REQUIRES_NEW

当REQUIRES_NEW使用时,Spring暂停当前的Transaction,并创建一个新的。

我们看下代码怎么使用:

    @Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteBookWithRequiresNew(Long id){
}

相应的实现代码如下:

if (isExistingTransaction()) {
suspend(existing);
try {
return createNewTransaction();
} catch (exception) {
resumeAfterBeginException();
throw exception;
}
}
return createNewTransaction();

NESTED

NESTED顾名思义,是嵌套的Transaction,Spring首先检查transaction是否存在,如果存在则创建一个savepoint,如果我们的程序抛出异常的时候,transaction将会回滚到该savepoint。如果没有transaction,NESTED的表现和REQUIRED一样。

我们看下怎么使用:

    @Transactional(propagation = Propagation.NESTED)
public void deleteBookWithNested(Long id){
}

Transaction的隔离级别

隔离级别就是我们之前提到的原子性,一致性,隔离性和持久性。隔离级别描述了改动对其他并发者的可见程度。

隔离级别主要是为了防止下面3个并发过程中可能出现的问题:

  1. 脏读: 读取一个transaction还没有提交的change
  2. 不可重复读:在一个transaction修改数据库中的某行数据时,另外一个transaction多次读取同一行数据,获取到的不同的值。
  3. 幻读: 在一个transaction添加或者删除数据库的数据时,另外一个transaction做范围查询,获得了不同的数据行数。

READ_UNCOMMITTED

READ_UNCOMMITTED是隔离级别中最低的级别。这个级别下,并发的3个问题都可能出现。

我们这样使用:

    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void deleteBookWithReadUncommitted(Long id){
}

READ_COMMITTED

READ_COMMITTED可以防止脏读。

我们看下代码:

    @Transactional(isolation = Isolation.READ_COMMITTED)
public void deleteBookWithReadCommitted(Long id){
}

REPEATABLE_READ

REPEATABLE_READ可以防止脏读和不可重复读。

使用的代码如下:

    @Transactional(isolation = Isolation.REPEATABLE_READ)
public void deleteBookWithRepeatableRead(Long id){
}

SERIALIZABLE

SERIALIZABLE是最严格的基本,可以防止脏读,不可重复读和幻读。

我们看下怎么使用:

    @Transactional(isolation = Isolation.SERIALIZABLE)
public void deleteBookWithSerializable(Long id){
}

本文的例子可以参考https://github.com/ddean2009/learn-springboot2/tree/master/springboot-transaction

更多教程请参考 flydean的博客

Spring Boot JPA 中transaction的使用的更多相关文章

  1. Spring Boot JPA中java 8 的应用

    文章目录 Optional Stream API CompletableFuture Spring Boot JPA中java 8 的应用 上篇文章中我们讲到了如何在Spring Boot中使用JPA ...

  2. spring boot JPA中实体类常用注解

    spring boot jpa中的注解很多,参数也比较多.没必要全部记住,但是经常查看官方文档也比较麻烦,记录一下一些常用的注解.通过一些具体的例子来帮助记忆. @Entity @Table(name ...

  3. Spring Boot JPA中关联表的使用

    文章目录 添加依赖 构建Entity 构建Repository 构建初始数据 测试 Spring Boot JPA中关联表的使用 本文中,我们会将会通过一个Book和Category的关联关系,来讲解 ...

  4. Spring Boot JPA中使用@Entity和@Table

    文章目录 默认实现 使用@Table自定义表格名字 在JPQL Queries中重写表格名字 Spring Boot JPA中使用@Entity和@Table 本文中我们会讲解如何在Spring Bo ...

  5. spring boot jpa 使用update 报错解决办法

    在spring boot jpa 中自定义sql,执行update操作报错解决办法: 在@Query(...)上添加 @Modifying@Transactional注解

  6. Spring Boot JPA的查询语句

    文章目录 准备工作 Containing, Contains, IsContaining 和 Like StartsWith EndsWith 大小写不敏感 Not @Query Spring Boo ...

  7. Spring Boot JPA 连接数据库

    本文将介绍怎样在Spring Boot project中加入JPA作为持久化方式. 改动 pom.xml 依赖 与上一篇介绍的 jdbc 不同的是 spring-boot-starter-jdbc 改 ...

  8. Spring boot Jpa添加对象字段使用数据库默认值

    Spring boot Jpa添加对象字段使用数据库默认值 jpa做持久层框架,项目中数据库字段有默认值和非空约束,这样在保存对象是必须保存一个完整的对象,但在开发中我们往往只是先保存部分特殊的字段其 ...

  9. Spring Boot(五):Spring Boot Jpa 的使用

    在上篇文章Spring Boot(二):Web 综合开发中简单介绍了一下 Spring Boot Jpa 的基础性使用,这篇文章将更加全面的介绍 Spring Boot Jpa 常见用法以及注意事项. ...

随机推荐

  1. 28. 实现 strStr()

    d地址:https://leetcode-cn.com/problems/implement-strstr/ <?php /** 实现 strStr() 函数. 给定一个 haystack 字符 ...

  2. 使用ElasticSearch赋能HBase二级索引 | 实践一年后总结

    前言:还记得那是2018年的一个夏天,天气特别热,我一边擦汗一边听领导大刀阔斧的讲述自己未来的改革蓝图.会议开完了,核心思想就是:我们要搞一个数据大池子,要把公司能灌的数据都灌入这个大池子,然后让别人 ...

  3. [原创] 关于步科eview人机界面HMI的使用 - HMI做Slave - Modbus RS485通讯

    做测试设备,或者自动化设备常常用到HMI 触摸屏 我有个案子用到了 步科的eview 触摸屏 型号 ET070 我的是单片机主板 控制 HMI显示,通讯用485  MODBUS 单片机板充当 主控 , ...

  4. Ring 笔记 - 核心概念

    Ring 是一个在 Clojure 中的对于 HTTP 的抽象,是构建 Web 应用的底层接口和库,类似于 Java 中的 Servlet 核心概念 Handler Handler 是一个定义web应 ...

  5. VBScript - 弹出“文件选择对话框”方法大全!

    本文记录,VBScript 中,各种打开 "文件选择对话框" 的方法. 实现方法-1 (mshta.exe): 首先,我们要实现的就是,弹出上面的这个"文件选择对话框&q ...

  6. 自定义yum仓库

                                             自定义yum仓库 案例4:自定义yum软件仓库 4.1问题 本例要求在CentOS真机上利用RHEL7的光盘镜像文件准 ...

  7. PAT 链表倒序的算法优化

    之前的答案错误问题已经解决了,现在还有运行超时的问题,先贴上之前的代码 1 #include <iostream> 2 #include <string.h> 3 using ...

  8. String 对象-->判断是否相等

    1.定义和用法 == 值相等 === 绝对相等(值和类型都相等) 举例: var str = '8' var str1 = 8 console.log(str == str1) console.log ...

  9. SpringMVC中利用HandlerExceptionResolver完成异常处理

    在解决Controller层中的异常问题时,如果针对每个异常处理相对较为繁琐.在SpringMVC中提供了HandlerExceptionResolver用于处理捕获到的异常,从而重新定义返回给前端的 ...

  10. centos7用户管理及root忘记密码恢复

    查看用户相关命令:#id 用户和组的信息#whoami #查看当前有效用户名#who #显示目前登入系统的用户信息.#w # w 命令用于显示已经登陆系统的用户列表#users #用于显示当前登录系统 ...