【业务功能篇138】SpringBoot中的事务传播属性运用REQUIRED、REQUIRES_NEW、NESTED 只读属性运用readOnly

95 篇文章 12 订阅
3 篇文章 0 订阅
本文详细解释了SpringBoot中事务的传播属性(如REQUIRED,REQUIRES_NEW,NESTED等),它们的区别,以及如何通过@Transactional注解设置只读事务。强调了只读事务在提高并发性能和数据一致性管理中的应用与注意事项。
摘要由CSDN通过智能技术生成

3.事务的传播属性

@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

日常开发中基本只会使用到REQUIRED(1),REQUIRES_NEW(4),NESTED(7)三种。

NESTED和REQUIRES_NEW是有区别的。NESTED传播行为会沿用当前事务的隔离级别和锁等特性,而REQUIRES_NEW则可以拥有自己独立的隔离级别和锁等特性。

使用REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时,外部事务将继续执行。两个事务互不影响,两个事务不是一个真正的嵌套事务,同时它还需要JTA事务管理器的支持。

使用NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。嵌套事务开始执行时, 它将取得一个 savepoint,如果这个嵌套事务失败, 将回滚到此savepoint。潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。

NESTED的实现主要依赖于数据库的保存点(SAVEPOINT)技术,SAVEPOINT记录了一个保存点,可以通过ROLLBACK TO SAVEPOINT来回滚到某个保存点。如果数据库支持保存点技术时就启用保存点技术;如果不支持就会新建一个事务去执行代码,也就相当于REQUIRES_NEW。

@Transactional注解支持9个属性的设置,其中使用较多的三个属性:readOnly、propagation、isolation。其中propagation属性用来枚举事务的传播行为,isolation用来设置事务隔离级别,readOnly进行读写事务控制。

最容易弄混淆的其实是PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED

PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全
commited 或 rolled back 而不依赖于外部事务,它拥有自己的隔离范围, 自己的锁, 等等.
当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
PROPAGATION_REQUIRES_NEW常用于日志记录,或者交易失败仍需要留痕

另一方面, PROPAGATION_NESTED 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正
的子事务. 潜套事务开始执行时, 它将取得一个 savepoint.
如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分,
只有外部事务结束后它才会被提交.
由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于,
PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED
则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit,
这个规则同样适用于 roll back.

几个例子理解REQUIRED、REQUIRES_NEW、NESTED 的使用注意事项(TRY…CATCH配合使用)

1、REQUIRED的使用注意项

1.1 REQUIRED保证其处理过程同一个事务,如果调用的同一个类的配置的REQUIRED的方法,且此方法存在TRY CATCH 代码块, 如果此代码块出现异常,程序可以继续执行。

1.2 但如果调用的其他类的配置REQUIRED方法,且TRY CATCH住,则全部的提交全部回滚,且报出异常: Transaction rolled back because it has been marked as rollback-only
因为事务报出异常后要全部回滚,包括父类的调用。

1.3 如果service中包含多个dao的方法,其都属于同一个事务,其中报错全部回滚,try catch住不影响程序代码的继续执行.

class A{
  //PROPAGATION_REQUIRED
    void methodA() {
       try{
         methodB(); //可以继续执行,因为是同一个类
       }catch(Exception ex){
         ex.printStrace();
       }
       
       try{
         methodC(); //报错Transaction rolled back because it has been marked as rollback-only
                    //因为回滚整个事务,不能用try catch住.当然通过不会try catch一个事务的部分代码
       }catch(Exception ex){
         ex.printStrace();
       }
  }
  
  //PROPAGATION_REQUIRED
  void methodB() {
  
  }
}

class B{
  
  //PROPAGATION_REQUIRED
  void methodC() {
  
  }
}

2NESTED的具体用途如下:
在此方法出现异常时,通过TRY CATCH 代码块包含住, 继续往下执行或者执行CATCH中的处理.
此点REUQIRED做不到, REQUIRED_NEW能做到, 但它是单独的事务,不与父类一块提交的。

ServiceA { 
/**

* 事务属性配置为 PROPAGATION_REQUIRED

*/

void methodA() {

try {

//savepoint

ServiceB.methodB(); //PROPAGATION_NESTED 级别

} catch (SomeException) {

// 执行其他业务, 如 ServiceC.methodC();
}}}

Spring中的7个事务传播行为:

事务行为说明
PROPAGATION_REQUIRED支持当前事务,假设当前没有事务。就新建一个事务
PROPAGATION_SUPPORTS支持当前事务,假设当前没有事务,就以非事务方式运行
PROPAGATION_MANDATORY支持当前事务,假设当前没有事务,就抛出异常
PROPAGATION_REQUIRES_NEW新建事务,假设当前存在事务。把当前事务挂起
PROPAGATION_NOT_SUPPORTED以非事务方式运行操作。假设当前存在事务,就把当前事务挂起
PROPAGATION_NEVER以非事务方式运行,假设当前存在事务,则抛出异常
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

举例说明

ServiceA

ServiceA {   
     void methodA() {
         ServiceB.methodB();
     }
}

ServiceB

ServiceB { 
     void methodB() {
     }  
}
1.PROPAGATION_REQUIRED

  假如当前正要运行的事务不在另外一个事务里,那么就起一个新的事务 比方说,ServiceB.methodB的事务级别定义PROPAGATION_REQUIRED, 那么因为执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务。这时调用ServiceB.methodB,ServiceB.methodB看到自己已经执行在ServiceA.methodA的事务内部。就不再起新的事务。而假如ServiceA.methodA执行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在ServiceA.methodA或者在ServiceB.methodB内的不论什么地方出现异常。事务都会被回滚。即使ServiceB.methodB的事务已经被提交,可是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚
在这里插入图片描述

2.PROPAGATION_SUPPORTS

  假设当前在事务中。即以事务的形式执行。假设当前不在一个事务中,那么就以非事务的形式执行

3.PROPAGATION_MANDATORY

  必须在一个事务中执行。也就是说,他仅仅能被一个父事务调用。否则,他就要抛出异常

4.PROPAGATION_REQUIRES_NEW

  这个就比较绕口了。 比方我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW。那么当运行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起。ServiceB.methodB会起一个新的事务。等待ServiceB.methodB的事务完毕以后,他才继续运行。
他与PROPAGATION_REQUIRED 的事务差别在于事务的回滚程度了。由于ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。假设ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚。ServiceB.methodB是不会回滚的。假设ServiceB.methodB失败回滚,假设他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。
在这里插入图片描述

5.PROPAGATION_NOT_SUPPORTED

  当前不支持事务。比方ServiceA.methodA的事务级别是PROPAGATION_REQUIRED 。而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB时。ServiceA.methodA的事务挂起。而他以非事务的状态执行完,再继续ServiceA.methodA的事务。

6.PROPAGATION_NEVER

  不能在事务中执行。
如果ServiceA.methodA的事务级别是PROPAGATION_REQUIRED。 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB就要抛出异常了。

7.PROPAGATION_NESTED

  如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

事务只读属性

在 Spring Boot 中,事务只读属性是指一个事务是否只读,即只能读取数据而不能修改数据。在只读事务中,如果尝试修改数据,则会抛出异常。只读事务可以提高事务的并发性能,因为在只读事务中,数据库不需要进行锁定,从而提高了并发度。

在 Spring Boot 中,只读事务是通过 @Transactional 注解的 readOnly 属性来实现的。如果将 readOnly 属性设置为 true,则表示该事务是只读事务,否则为读写事务。下面是一个使用 @Transactional 注解的例子:

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional(readOnly = true)
    public User getUserById(Long userId) {
        return userRepository.findById(userId).orElse(null);
    }
    
    @Transactional
    public void saveUser(User user) {
        userRepository.save(user);
    }
    
}

在上面的例子中,getUserById() 方法是一个只读方法,因此将 readOnly 属性设置为 true。而 saveUser() 方法是一个写方法,因此不需要设置 readOnly 属性。
只读事务的原理

只读事务的实现原理与普通事务的实现原理类似,都是通过 AOP 机制来实现的。在 Spring Boot 中,只读事务的实现原理如下:

当一个方法被标记为只读事务时,Spring Boot 会创建一个新的只读事务,并将其绑定到当前线程上。
当方法执行完成后,Spring Boot 会提交或回滚事务,然后将事务与当前线程解绑,从而释放资源。

在只读事务中,因为不需要进行锁定操作,所以可以提高事务的并发性能。此外,只读事务还可以用于一些特殊场景,例如在数据库主从复制时,可以将只读操作发送到从数据库中执行,从而分担主数据库的压力。
如何使用只读事务

在 Spring Boot 中,只需要在方法上加上 @Transactional(readOnly = true) 注解即可将该方法设置为只读事务。下面是一个例子:

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional(readOnly = true)
    public User getUserById(Long userId) {
        return userRepository.findById(userId).orElse(null);
    }
    
}

在上面的例子中,getUserById() 方法是一个只读方法,因此将 readOnly 属性设置为 true。

需要注意的是,只读事务只适用于读取数据的场景,如果需要修改数据,则需要使用读写事务。此外,只读事务不能保证数据的一致性,因为在只读事务中,数据可能已经被其他事务修改了,因此在使用只读事务时需要注意这一点。
结论

在 Spring Boot 中,只读事务是一种特殊的事务,它可以提高事务的并发性能。只读事务是通过 @Transactional 注解的 readOnly 属性来实现的。只读事务的实现原理与普通事务的实现原理类似,都是通过 AOP 机制来实现的。只需要在方法上加上 @Transactional(readOnly = true) 注解即可将该方法设置为只读事务。但需要注意的是,只读事务只适用于读取数据的场景,如果需要修改数据,则需要使用读写事务。此外,只读事务不能保证数据的一致性,因为在只读事务中,数据可能已经被其他事务修改了,因此在使用只读事务时需要注意这一点。

除了在方法上加上 @Transactional(readOnly = true) 注解之外,还可以在类上加上 @Transactional(readOnly = true) 注解,这样该类中所有的方法都将被设置为只读事务。下面是一个例子:

@Service
@Transactional(readOnly = true)
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    public User getUserById(Long userId) {
        return userRepository.findById(userId).orElse(null);
    }
    
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
    
    @Transactional
    public void saveUser(User user) {
        userRepository.save(user);
    }
    
}

在上面的例子中,getUserById() 和 getAllUsers() 方法都是只读方法,因为在类上加上了 @Transactional(readOnly = true) 注解,而 saveUser() 方法是一个写方法,因此需要使用读写事务。

需要注意的是,在使用只读事务时,需要确保数据的一致性。如果在只读事务中读取了数据,然后在另一个事务中修改了该数据,那么在只读事务中再次读取该数据时,将会看到修改后的数据。因此,在使用只读事务时,需要根据实际情况来决定是否使用只读事务,并且需要对数据的一致性进行仔细的考虑。
总结

在本文中,我们介绍了 Spring Boot 中的事务只读属性是什么,原理以及如何使用。只读事务可以提高事务的并发性能,在读取数据时非常有用,但需要注意数据一致性的问题。只需要在方法上加上 @Transactional(readOnly = true) 注解即可将该方法设置为只读事务,也可以在类上加上 @Transactional(readOnly = true) 注解,将该类中所有的方法都设置为只读事务。在使用只读事务时,需要根据实际情况来决定是否使用只读事务,并且需要对数据的一致性进行仔细的考虑。

原文链接:
https://blog.csdn.net/albert_xjf/article/details/131434383
https://blog.csdn.net/z69183787/article/details/76208998

  • 26
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot 事务是指在 Spring Boot 应用程序使用的事务管理。事务是指在数据库执行的一组操作,要么全部成功,要么全部失败。如果在事务的任何一步操作失败,则回滚事务,回到事务开始前的状态。使用事务可以确保数据的完整性和一致性。 在 Spring Boot ,可以使用 @Transactional 注解在方法级别实现事务管理。只需要将注解添加到需要进行事务管理的方法上,Spring Boot 就会自动为该方法启用事务。 总之,Spring Boot 事务是一种有效的方法,用于管理数据库的操作,以确保数据的完整性和一致性。 ### 回答2: Spring Boot是一个开源的Java开发框架,它简化了Spring应用程序的创建和配置过程。在Spring Boot事务是一种用于管理数据库操作的机制。事务允许开发者将多个数据库操作组合在一起,确保它们要么全部成功,要么全部失败。 Spring Boot使用了Spring Framework的事务管理机制来实现事务。在Spring Boot,可以使用@Transactional注解来标记一个方法或类,以指示该方法或类要在一个事务运行。 使用@Transactional注解标记的方法会在被调用时开启一个事务,并在方法执行完毕后根据方法执行结果来决定事务的提交或回滚。如果方法执行成功,则事务会被提交,数据库的所有操作都会被应用到数据库。如果方法执行失败,事务会被回滚,数据库的所有操作都会被撤销,保持数据库的一致性。 除了@Transactional注解,Spring Boot还提供了其他事务管理相关的注解,例如@Propagation、@Isolation、@ReadOnly等,它们允许开发者进一步定义事务传播行为、隔离级别以及只读属性。 总而言之,Spring Boot事务管理机制提供了一种简单而强大的方式来管理数据库操作。它使得开发者能够在应用程序处理复杂的数据库事务,保证数据的完整性和一致性。使用Spring Boot事务,可以避免许多潜在的并发更新和数据不一致的问题,提高了应用程序的稳定性和性能。 ### 回答3: Spring Boot 是一个为了简化 Spring 应用开发的框架,它在 Spring 框架的基础上做了很多封装和优化。Spring Boot 不仅提供了快速搭建项目的能力,还提供了丰富的功能,其就包括了事务管理。 Spring Boot事务管理基于 Spring Framework 的事务管理机制,并进行了简化和自动配置。在 Spring Boot ,我们可以通过注解的方式来声明事务的边界,方便地管理数据库事务。 在 Spring Boot ,我们可以使用 @Transactional 注解来声明事务,它可以用在类上或方法上。当使用 @Transactional 注解时,如果方法抛出了 RuntimeException 或 Error,Spring 会回滚事务;如果方法没有抛出异常,Spring 会提交事务Spring Boot事务管理支持多种事务传播行为,包括 PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED 等。通过设置事务传播行为,我们可以灵活地控制事务在调用链传播方式。 另外,Spring Boot 还支持使用编程式事务管理,即通过编写代码来手动控制事务的提交和回滚。可以通过编写 TransactionTemplate 或 PlatformTransactionManager 的实例来实现编程式事务管理。 总之,Spring Boot 提供了便捷的事务管理机制,使得开发者能够更加方便地管理数据库事务。无论是通过注解的方式还是编程式的方式,Spring Boot 都能够满足不同场景下的事务需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值