spring中事务相关面试题(自用)

1 什么是spring事务

Spring事务管理的实现原理是基于AOP(面向切面编程)和代理模式。Spring提供了两种主要的方式来管理事务:编程式事务管理和声明式事务管理。

  1. 声明式事务管理: Spring的声明式事务管理是通过使用注解或XML配置来实现的。它依赖于AOP代理,允许你将事务管理与业务逻辑解耦。@Transactional可以修饰一个类或者一个方法上面。下面是声明式事务管理的实现原理:

    • 使用@Transactional注解或XML配置来标识哪些方法应该被事务管理。
    • Spring通过AOP代理生成一个代理对象,该代理对象包装了目标对象。
    • 当被标记为事务的方法被调用时,代理对象会在方法执行前和方法执行后分别开启和提交事务。
    • 如果方法执行过程中发生异常,代理对象会回滚事务。
    • 声明式事务管理的配置也可以定义事务传播行为和隔离级别。
  2. 编程式事务管理: 编程式事务管理要求在代码中明确调用事务管理API。它通常涉及使用PlatformTransactionManager接口来控制事务的生命周期。以下是编程式事务管理的实现原理:

    • 创建一个PlatformTransactionManager实例,通常是Spring提供的实现,如DataSourceTransactionManager
    • 手动开始一个事务,使用getTransaction()方法获取一个TransactionStatus对象。
    • 执行一系列操作。
    • 根据操作结果,可以选择提交或回滚事务,使用commit()rollback()方法。
    • 释放事务资源,通常在finally块中执行。

Demo:

在使用编程式事务管理时,Spring会自动处理数据库连接的autocommit。你通常不需要手动将autocommit设置为false,因为Spring的事务管理器会在事务开始时将其设置为false,以确保事务的原子性。


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@SpringBootApplication
public class TransactionExampleApplication {

    @Autowired
    private UserService userService;

    public static void main(String[] args) {
        SpringApplication.run(TransactionExampleApplication.class, args);
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        // Configure your data source here
    }

    public void performTransaction() {
        PlatformTransactionManager transactionManager = transactionManager();
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            userService.createUser("User1");
            userService.createUser("User2");
            // Simulate an error
            int i = 1 / 0; // This will cause an exception

            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
        }
    }
}

@Service
class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void createUser(String username) {
        jdbcTemplate.update("INSERT INTO users (username) VALUES (?)", username);
    }
}

2 什么是spring事务传播行为

大体解释一下事务:在spring中事务可以指一个执行多条sql语句的方法

什么是事务传播行为呢:一个事务方法A在方法体中调用另一个事务方法B,不管事务A、B是在同一个类中还是不同的类中这样都叫事务的传播行为。

Spring定义了以下几种事务传播行为:

  1. REQUIRED(默认)--required:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新事务。
  2. REQUIRES_NEW:创建一个新事务,并挂起当前事务(如果存在)。
  3. SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
  4. MANDATORY:要求当前存在事务,否则抛出异常。
  5. NOT_SUPPORTED:以非事务方式执行操作,挂起当前事务(如果存在)。
  6. NEVER:以非事务方式执行操作,如果当前存在事务,则抛出异常。
  7. NESTED:如果当前存在事务,则创建一个嵌套事务;如果当前没有事务,则创建一个新事务。嵌套事务是独立于外部事务的子事务,可以独立提交或回滚。

通过配置这些传播行为,可以实现复杂的事务嵌套和控制,以满足不同业务场景的需求。Spring的事务管理机制使事务管理变得更容易,同时提供了灵活性和可维护性,使开发者能够更好地控制事务的行为。

光看概念难以很好的理解,下面给出具体情境代码说明:

场景 1:REQUIRED(默认传播行为)

假设你有一个订单服务(OrderService)和一个支付服务(PaymentService)。在处理订单时,你需要创建订单记录并在支付服务中进行付款。订单服务的createOrder方法和支付服务的makePayment方法都使用@Transactional注解,并默认传播行为为REQUIRED

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private PaymentService paymentService;

    @Transactional
    public void createOrder(Order order) {
        // 创建订单记录
        orderRepository.save(order);
        // 调用支付服务
        paymentService.makePayment(order);
    }
}
@Service
public class PaymentService {

    @Transactional
    public void makePayment(Order order) {
        // 处理支付逻辑
    }
}

在这种情况下,当你在订单服务中调用createOrder方法时,它会启动一个新的事务。如果在makePayment方法中发生异常,整个事务将回滚,包括订单创建。这是因为REQUIRED传播行为表示要么加入现有事务,要么创建新事务。

场景 2:REQUIRES_NEW(新事务传播行为)

现在,假设你想要在订单服务中调用支付服务,但无论支付服务的事务成功或失败,订单服务的事务都必须继续。在这种情况下,你可以将支付服务的传播行为配置为REQUIRES_NEW

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private PaymentService paymentService;

    @Transactional
    public void createOrder(Order order) {
        // 创建订单记录
        orderRepository.save(order);
        // 调用支付服务
        paymentService.makePayment(order);
    }
}
@Service
public class PaymentService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void makePayment(Order order) {
        // 处理支付逻辑
    }
}

在这种情况下,当你在订单服务中调用createOrder方法时,订单服务的事务会启动。然后,它调用支付服务的makePayment方法,该方法会启动一个新的独立事务。如果支付服务的事务失败,仅该事务会回滚,而订单服务的事务仍然会继续。

这两个场景说明了不同的事务传播行为如何影响方法之间的事务交互。REQUIRED传播行为将方法加入现有事务或创建新事务,而REQUIRES_NEW传播行为会创建一个新事务,独立于外部事务。选择适当的传播行为取决于你的业务需求。

那么现在最重要的问题来了:

 

还是以两个事务为例,在每个事务都可以设置7中事务传播行为,两个事务传播方式就有49中,所以能能正确理解各个事务类型的概念将十分重要

以下举例一些事务传播的组合方式:

  1. REQUIRED  ------》REQUIRES_NEW   A事务没有就新建,B事务不会加入A,独立执行,B事务发生异常不会回滚到A
  2. REQUIRED  -----》 REQUIRED  A事务没有就创建  B事务会先判断有没有事务,有就加入,所以A,B同在一个事务,B发生异常会回滚到A
  3. REQUIRES_NEW  ------》REQUIRED  A事务没有就创建,B事务加入A事务,A,B同属一个事务,B事务发生异常不会回滚到A

通过三面三个例子就能清楚的了解REQUIRED   REQUIRES_NEW   这两种行为,REQUIRED   如果没有就创建,有就加入,不独立与其他事务。而REQUIRES_NEW   在没有事务时创建事务,有时加入其他事务中,但是REQUIRES_NEW   具有独立性,不会影响其他事务,也不会被其他事务影响而回滚

 解释一下REQUIRES_NEW   与SUPPORTS,容易混淆,比如REQUIRED  --》REQUIRES_NEW   与REQUIRED  --》SUPPORTS 这两种,后者发生异常都不会回滚到之前的事务,具有独立性,REQUIRES_NEW   与SUPPORTS的区别在于之前没有外部事务,REQUIRES_NEW   会创建事务,而SUPPORTS以非事务执行,这就是我认为的唯一区别

综上:REQUIRED   REQUIRES_NEW    SUPPORTS 无论怎么随机组合就能理解了

接下来继续讨论:NOT_SUPPORTED ,  REQUIRES_NEW----》NOT_SUPPORTED 后者不会加入事务,直接是非事务执行,具有独立性。 NOT_SUPPORTED与SUPPORTS的区别就是当之前有外部事务时NOT_SUPPORTED修饰的方法根本不会作为一个事务,而SUPPORTS会加入之前的事务,具有事务的ACID特性

综上我们已经弄懂REQUIRED   REQUIRES_NEW    SUPPORTS  NOT_SUPPORTED

最后再来一个多传播行为的思考:REQUIRED   REQUIRES_NEW    SUPPORTS  NOT_SUPPORTED 分别对应四个事务ABCB

A事务方法体中{

调用B

调用C

调用D

}

这时如果B事务发生异常,会怎样?B会回滚,不影响事务A。C加入A事务执行。D以非事务执行。

我们换一下再来思考:

A事务方法体中{

调用B

}

B事务方法体中 {

调用C

调用D

}

以上调用B出现异常会怎样?B会回滚,那么B中调用的方法都会回滚。

还有3中传播行为没讲,自己去理解吧。

猜你喜欢

转载自blog.csdn.net/qq_52135683/article/details/133846559