SpringBoot中事务的属性

上文中说了在SpringBoot中如何开启事务,接下来就说下事务的一些属性

隔离性

事务的隔离性:指在一个事务执行期间,对数据库的其他事务进行访问的方式。

在实际开发与应用中存在多个事务同时运行的情况,多个事务同时执行期间对数据的读取可能会出现异常,数据的不一致性。为了解决这些异常因此需要对事务的隔离性进行设置。

事务的隔离性可以保证在一个事务执行期间,其他事务不会看到事务的未提交的更改,也不会影响其他事务的执行。

异常的种类

多个事务运行可能导致以下几种异常:

  • 脏读(Dirty Read)
  • 不可重复读(Non-repeatable Read)
  • 幻读(Phantom Read)。

脏读

脏读是指在一个事务中读取了另一个事务的未提交的更改,然后在这个事务中对这些更改进行了修改,最后另一个事务提交了这些更改,导致数据的一致性被破坏。

不可重复读

不可重复读是指在一个事务中多次读取同一数据,但是读取到的数据不同,这是因为另一个事务在当前事务执行期间修改了这个数据。

假设有一个订单表,其中包含一个customer_id字段,用于存储订单的客户ID。在一个事务中,你读取了一个订单,并记录下了这个订单的customer_id。然后,在另一个事务中,你修改了这个订单的customer_id。最后,在第一个事务中,你再次读取这个订单,那么你可能会读取到修改后的customer_id,这就是不可重复读。

幻读

幻读是指在一个事务中读取了多个数据,但是读取到的数据的数目不同,这是因为另一个事务在当前事务执行期间插入或删除了数据。

例如,如果在一个事务中读取了一个订单表中的所有订单,然后在另一个事务中插入了一个新的订单,最后当前事务再次读取这个订单表中的所有订单,那么就可能会出现幻读。这就是因为在当前事务执行期间,另一个事务插入了一个新的订单,导致当前事务读取到的数据的数目不同。

隔离级别

MySql中一共有4种级别,隔离级别从低到高分别是:

  • 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到。
  • 读提交:一个事务提交之后,它做的变更才会被其他事务看到。
  • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。
  • 串行化:顾名思义是对于同一行记录,"写"会加"写锁","读"会加"读锁"。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

为什么要设计这么多种呢?因为你隔离得越严实,效率就会越低。因此很多时候,我们都要在二者之间寻找一个平衡点。所以才会有不同的隔离级别供我们选择。

其中读提交解决了脏读,可重复读解决了"不可重复读",串行化解决了"幻读"。当然串行化也是效率最低的。

SpringBoot中的隔离级别

在SpringBoot中,可以通过

  • TransactionDefinition.ISOLATION_DEFAULT
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED
  • TransactionDefinition.ISOLATION_READ_COMMITTED
  • TransactionDefinition.ISOLATION_REPEATABLE_READ
  • TransactionDefinition.ISOLATION_SERIALIZE

来设置事务的隔离性。

其中TransactionDefinition.ISOLATION_DEFAULT是事务的默认隔离级别,它会根据数据库的设置来决定事务的隔离级别。在大多数情况下,这个级别就是你想要的级别。

第二种到第五种分别对应数据库中的四种隔离级别:读未提交读已提交可重复读串行化

MySQL 默认的隔离级别 REPEATABLE READ ,可重复读

  • 设置事务隔离级别
java 复制代码
 @Transactional(isolation = TransactionDefinition.ISOLATION_DEFAULT)
 public class UserService {
   //.......
 }

以上就是通过注解来实现事务,并设置事务隔离级别------默认。若是编程式事务,则

java 复制代码
  public void updateName(){
         // 定义事务属性
         DefaultTransactionDefinition def = new DefaultTransactionDefinition();
         def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        // ...
  }

事务的传播性

在Spring中,事务的传播性是指当一个事务方法被另一个事务方法调用时,事务该以何种状态存在?是否需要在当前事务中嵌套另一个事务,还是会新开启自己的事务。

举个例子:

java 复制代码
 @Service
 public class DemoService{
   @Autowired
   private UserService userSerice;
   
   @Transactional
   public void test(){
     userSerice.updateName("test");
     int i = 1/0;
   }
   
 }
 // userSerice.updateName() 该方法也开启了事务

当发生异常时,userSerice.updateName()它是否也会回滚。这个就要看事务的传播性设置的何种状态,如果userSerice.updateName它的事务传播属性为是REQUIRED,则它会回滚。若是REQUIRES_NEW,则不会回滚。

如果是REQUIRED,事务的属性都是继承于大事务的

7种传播种类

Spring中一共定义了7种方式:

java 复制代码
 public enum Propagation {
  REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
  SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
  MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
  REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
  NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
  NEVER(TransactionDefinition.PROPAGATION_NEVER),
  NESTED(TransactionDefinition.PROPAGATION_NESTED);
  private final int value;
  Propagation(int value) { this.value = value; }
  public int value() { return this.value; }
 }

它们的意思如下:

名称 含义
REQUIRED 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
SUPPORTS 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
REQUIRES_NEW 创建一个新的事务,如果当前存在事务,则把当前事务挂起
NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则把当前事务挂起
NEVER 以非事务方式运行,如果当前存在事务,则抛出异常
NESTED 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 TransactionDefinition.PROPAGATION_REQUIRED

开启事务后它的传播行为的默认值为:Propagation.REQUIRED

传播属性设置

java 复制代码
 transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
java 复制代码
 @Transactional( propagation= Propagation.REQUIRES_NEW)
 public void testAddEx(){
   // .....
 }

回滚规则

Spring中事务的回滚规则默认只有在抛出RuntimeException或Error时,才会发生事务回滚,在遇到检查型(Checked Exception)异常时不会回滚。

而我们可以通过设置回滚规则来自定义抛出哪些异常时进行回滚事务,包括checked异常。

java 复制代码
 @Transactional(rollbackFor = IOException.class)
 public void handle() {
    // .....
 }

超时时间

超时时间是说一个事务允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。

java 复制代码
 @Transactional(timeout = 10)
 public void handle() {
    // .....
 }

其中时间单位为秒。

只读属性

事务的是否只读(read-only)是指事务执行时是否可以修改数据库中的数据。如果一个事务被设置为只读,则事务执行时不能修改数据库中的数据,如果试图修改数据库中的数据,则事务会回滚并抛出异常。

java 复制代码
 @Transactional(readOnly = true)
 public void handle() {
    // .....
 }
相关推荐
一只爱打拳的程序猿9 分钟前
【Spring】更加简单的将对象存入Spring中并使用
java·后端·spring
杨荧11 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
minDuck13 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
为将者,自当识天晓地。31 分钟前
c++多线程
java·开发语言
daqinzl39 分钟前
java获取机器ip、mac
java·mac·ip
激流丶1 小时前
【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
java·大数据·kafka·topic
Themberfue1 小时前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
让学习成为一种生活方式1 小时前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
晨曦_子画1 小时前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
南宫生2 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法