【揭秘】Spring框架中的7大神秘事务策略,你了解多少?

2023-12-13 15:55:41

Spring框架中的7大神秘事务策略 - 程序员古德

Spring定义了七种事务传播规则,确保方法在不同事务上下文中的执行方式。其中,REQUIRED最为常用,保证数据一致性;SUPPORTS适应有无事务环境;MANDATORY强制事务执行;REQUIRES_NEW独立新事务;NOT_SUPPORTEDNEVER分别避免和禁止事务;NESTED允许嵌套子事务。选择适当规则,确保数据完整性、一致性,提升系统健壮性。

Spring框架中的7大神秘事务策略 - 程序员古德

PROPAGATION_REQUIRED

这是最最最常用的方式。这是最最最常用的方式。这是最最最常用的方式。

如果当前存在事务,则加入该事务;如果当前没有事务,就新建一个事务。这是最常用的选择,它可以确保被调用方法运行在同一个事务上下文中,从而保持数据的一致性

例如,在订单处理中,当用户下订单后需要减库存和增加订单记录。这两个操作必须同时成功或失败,以确保数据的一致性。通过PROPAGATION_REQUIRED,这两个操作可以在同一个事务中执行。

1、Repository类(MyRepository.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.data.jpa.repository.JpaRepository;  
import org.springframework.stereotype.Repository;  
  
@Repository  
public interface MyRepository extends JpaRepository<MyEntity, Long> {  
    // 添加适合您项目的JPA存储库方法  
}

2、Service类(MyService.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Propagation;  
import org.springframework.transaction.annotation.Transactional;  
  
@Service  
public class MyService {  
  
    @Autowired  
    private MyRepository myRepository;  
      
    @Transactional(propagation = Propagation.REQUIRED)  
    public void methodB() {  
        System.out.println("Method B started");  
        // 执行一些业务逻辑,可能涉及数据库操作  
        myRepository.updateData("Method B");  
        System.out.println("Method B completed successfully");  
    }  
      
    @Transactional(propagation = Propagation.REQUIRED)  
    public void methodA() {  
        System.out.println("Method A started");  
        methodB(); // 在methodA()中调用methodB()  
        System.out.println("Method A completed successfully");  
    }  
}

3、Controller类(MyController.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
@RestController  
public class MyController {  
  
    @Autowired  
    private MyService myService;  
      
    @GetMapping("/execute")  
    public String execute() {  
        myService.methodA(); // 调用Service类中的methodA()方法,它将进一步调用methodB()方法  
        return "Execution completed";  
    }  
} 

在这个示例中,MyService类中的methodA()methodB()都使用了PROPAGATION_REQUIRED传播规则。

这意味着它们将加入当前存在的事务,如果不存在事务,它们将创建一个新的事务。当MyController类中的execute()方法被调用时,它将调用MyService类中的methodA()方法,然后methodA()方法将调用methodB()方法。如果任何一个方法执行失败并抛出异常,整个事务将回滚

PROPAGATION_SUPPORTS

如果当前存在事务,则加入该事务;如果当前没有事务,就以非事务的方式继续运行。这主要用于那些可以在事务中运行,但也可以单独运行的操作。

例如,日志记录功能,该功能可以在事务中记录操作,但如果没有事务,它仍然可以记录。这种情况下可以使用PROPAGATION_SUPPORTS。

1、Repository类(MyRepository.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.data.jpa.repository.JpaRepository;  
  
public interface MyRepository extends JpaRepository<MyEntity, Long> {  
    // 添加适合您项目的JPA存储库方法  
}

2、Service类(MyService.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Propagation;  
import org.springframework.transaction.annotation.Transactional;  
  
@Service  
public class MyService {  
  
    @Autowired  
    private MyRepository myRepository;  
      
    @Transactional(propagation = Propagation.SUPPORTS)  
    public void methodB() {  
        System.out.println("Method B started");  
        // 执行一些业务逻辑,可能涉及数据库操作  
        myRepository.updateData("Method B");  
        System.out.println("Method B completed successfully");  
    }  
      
    @Transactional(propagation = Propagation.SUPPORTS)  
    public void methodA() {  
        System.out.println("Method A started");  
        methodB(); // 在methodA()中调用methodB()  
        System.out.println("Method A completed successfully");  
    }  
}

3、Controller类(MyController.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
@RestController  
public class MyController {  
  
    @Autowired  
    private MyService myService;  
      
    @GetMapping("/execute")  
    public String execute() {  
        myService.methodA(); // 调用Service类中的methodA()方法  
        return "Execution completed";  
    }  
}  

在这个示例中,MyService类中的methodA()methodB()都使用了PROPAGATION_SUPPORTS传播规则。这意味着如果当前存在事务,那么它们将加入该事务;如果不存在事务,它们将以非事务方式执行。当MyController类中的execute()方法被调用时,它将调用MyService类中的methodA()方法,然后methodA()方法将调用methodB()方法。

PROPAGATION_MANDATORY

如果当前存在事务,则加入该事务;如果当前没有事务,就抛出异常。这确保了方法只能在事务上下文中运行。

例如,某个操作只能在事务中进行以确保数据的完整性。如果该操作在非事务上下文中被调用,就会抛出异常。

1、Repository类(MyRepository.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.data.jpa.repository.JpaRepository;  
import org.springframework.stereotype.Repository;  
  
@Repository  
public interface MyRepository extends JpaRepository<MyEntity, Long> {  
    // 添加适合您项目的JPA存储库方法  
}

2、Service类(MyService.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Propagation;  
import org.springframework.transaction.annotation.Transactional;  
import org.springframework.transaction.TransactionSystemException;  
  
@Service  
public class MyService {  
  
    @Autowired  
    private MyRepository myRepository;  
      
    @Transactional(propagation = Propagation.MANDATORY)  
    public void methodB() {  
        System.out.println("Method B started");  
        // 执行一些业务逻辑,可能涉及数据库操作  
        myRepository.updateData("Method B");  
        System.out.println("Method B completed successfully");  
    }  
      
    @Transactional(propagation = Propagation.MANDATORY)  
    public void methodA() {  
        System.out.println("Method A started");  
        try {  
            methodB(); // 在methodA()中调用methodB()  
            System.out.println("Method A completed successfully");  
        } catch (TransactionSystemException e) {  
            System.out.println("Transaction not active, cannot call methodB() from methodA()");  
            throw e;  
        }  
    }  
}

3、Controller类(MyController.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
import org.springframework.transaction.TransactionDefinition;  
import org.springframework.transaction.TransactionStatus;  
import org.springframework.transaction.support.TransactionTemplate;  
import org.springframework.transaction.support.DefaultTransactionDefinition;  
  
@RestController  
public class MyController {  
  
    @Autowired  
    private MyService myService;  
      
    @Autowired  
    private TransactionTemplate transactionTemplate;  
      
    @GetMapping("/execute")  
    public String execute() {  
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();  
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); // 设置事务传播行为为REQUIRES_NEW,确保在调用methodA()时存在活动事务。  
        transactionTemplate.execute(def, status -> {  
            myService.methodA(); // 调用Service类中的methodA()方法,它将进一步调用methodB()方法。这里使用TransactionTemplate确保存在活动事务。  
            return null; // 事务执行完成,返回null。实际项目中可能需要根据需求返回合适的值。  
        });  
        return "Execution completed";  
    }  
}  

在这个示例中,MyService类中的methodA()methodB()都使用了PROPAGATION_MANDATORY传播规则。这意味着它们必须在已经存在的事务上下文中执行。如果调用它们时没有活动的事务,将会抛出TransactionSystemException异常。

PROPAGATION_REQUIRES_NEW

新建一个事务,如果当前存在事务,把当前事务挂起。这可以确保被调用方法在新的独立事务中运行,与调用者的事务隔离。

例如,用户注册后需要发送欢迎邮件。即使注册失败,邮件仍然应该发送。通过PROPAGATION_REQUIRES_NEW,发送邮件的操作可以在一个新的事务中进行,不受注册事务的影响。

1、Repository类(MyRepository.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.data.jpa.repository.JpaRepository;  
import org.springframework.stereotype.Repository;  
  
@Repository  
public interface MyRepository extends JpaRepository<MyEntity, Long> {  
    // 添加适合您项目的JPA存储库方法  
}

2、Service类(MyService.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Propagation;  
import org.springframework.transaction.annotation.Transactional;  
  
@Service  
public class MyService {  
  
    @Autowired  
    private MyRepository myRepository;  
      
    @Transactional(propagation = Propagation.REQUIRES_NEW)  
    public void methodB() {  
        System.out.println("Method B started");  
        // 执行一些业务逻辑,可能涉及数据库操作  
        myRepository.updateData("Method B");  
        System.out.println("Method B completed successfully");  
    }  
      
    @Transactional(propagation = Propagation.REQUIRES_NEW)  
    public void methodA() {  
        System.out.println("Method A started");  
        try {  
            methodB(); // 在methodA()中调用methodB()  
            System.out.println("Method A completed successfully");  
        } catch (Exception e) {  
            System.out.println("An error occurred while executing methodB() from methodA()");  
            throw e;  
        }  
    }  
}

3、Controller类(MyController.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
import org.springframework.transaction.TransactionDefinition;  
import org.springframework.transaction.TransactionStatus;  
import org.springframework.transaction.support.TransactionCallbackWithoutResult;  
import org.springframework.transaction.support.TransactionTemplate;  
import org.springframework.transaction.support.DefaultTransactionDefinition;  
  
@RestController  
public class MyController {  
  
    @Autowired  
    private MyService myService;  
      
    @GetMapping("/execute")  
    public String execute() {  
        myService.methodA(); // 调用Service类中的methodA()方法,它将进一步调用methodB()方法。由于使用了PROPAGATION_REQUIRES_NEW,每次调用都会启动一个新的事务。  
        return "Execution completed"; // 执行完成,返回字符串。实际项目中可能需要根据需求返回合适的值。  
    }  
}  

在这个示例中,MyService类中的methodA()methodB()都使用了PROPAGATION_REQUIRES_NEW传播规则。这意味着每次调用这些方法时,无论是否已经存在事务,都会启动一个新的事务。请注意,PROPAGATION_REQUIRES_NEW会挂起当前事务(如果存在),并创建一个新的事务。新的事务将独立于原始事务进行提交或回滚。调用结束后,原始事务将继续执行。这样确保了每个方法调用都在独立的事务中执行,互不影响。

PROPAGATION_NOT_SUPPORTED

如果当前存在事务,就把当前事务挂起。这主要用于那些不应该在事务中运行的操作。这种传播行为意味着目标方法将在没有事务的情况下运行,即使调用者方法处于事务上下文中。这可以用于在事务的边界内执行一些非事务性的操作,或者临时跳出事务以执行特定的代码逻辑。

例如,某个长时间的报告生成操作不应该在事务中运行,以免影响其他操作的性能。这种情况下可以使用PROPAGATION_NOT_SUPPORTED。

1、Repository类(MyRepository.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.data.jpa.repository.JpaRepository;  
import org.springframework.stereotype.Repository;  
  
@Repository  
public interface MyRepository extends JpaRepository<MyEntity, Long> {  
    // 添加适合您项目的JPA存储库方法  
}

2、Service类(MyService.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Propagation;  
import org.springframework.transaction.annotation.Transactional;  
  
@Service  
public class MyService {  
  
    @Autowired  
    private MyRepository myRepository;  
      
    @Transactional(propagation = Propagation.NOT_SUPPORTED)  
    public void methodB() {  
        System.out.println("Method B started");  
        // 执行一些业务逻辑,可能涉及数据库操作,但由于使用了PROPAGATION_NOT_SUPPORTED,该操作将在非事务环境中执行。  
        myRepository.updateData("Method B");  
        System.out.println("Method B completed successfully");  
    }  
      
    @Transactional(propagation = Propagation.REQUIRED) // 使用默认的REQUIRED传播规则,确保methodA()在事务环境中执行。  
    public void methodA() {  
        System.out.println("Method A started");  
        try {  
            methodB(); // 在methodA()中调用methodB()。由于methodB()使用了PROPAGATION_NOT_SUPPORTED,它将在非事务环境中执行。  
            System.out.println("Method A completed successfully");  
        } catch (Exception e) {  
            System.out.println("An error occurred while executing methodB() from methodA()");  
            throw e;  
        }  
    }  
}

3、Controller类(MyController.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
import org.springframework.transaction.TransactionDefinition;  
import org.springframework.transaction.TransactionStatus;  
import org.springframework.transaction.support.TransactionCallbackWithoutResult;  
import org.springframework.transaction.support.TransactionTemplate;  
import org.springframework.transaction.support.DefaultTransactionDefinition;  
  
@RestController
public class MyController {

  @Autowired private MyService myService;

  @GetMapping("/execute")
  public String execute() {
    /*
    	调用Service类中的methodA()方法,它将进一步调用methodB()方法,由于使用了PROPAGATION_NOT_SUPPORTED,methodB()将在非事务环境中执行。
    	而methodA()使用默认的REQUIRED传播规则,确保它在事务环境中执行。
    	这样可以在同一个方法中实现事务和非事务操作的混合执行。
    */
    myService.methodA(); 
    return "Execution completed"; 
  }
}

思考:

1、那PROPAGATION_NOT_SUPPORTED和PROPAGATION_NEVER有什么区别:

  1. PROPAGATION_NOT_SUPPORTED:
    1. 当使用PROPAGATION_NOT_SUPPORTED传播规则时,如果当前存在事务,则挂起该事务,并在没有事务的上下文中执行目标方法。如果当前没有事务,则目标方法仍然在没有事务的上下文中执行。
    2. 这种传播行为意味着目标方法将在没有事务的情况下运行,即使调用者方法处于事务上下文中。这可以用于在事务的边界内执行一些非事务性的操作,或者临时跳出事务以执行特定的代码逻辑。
  2. PROPAGATION_NEVER:
    1. 当使用PROPAGATION_NEVER传播规则时,如果当前存在事务,则抛出异常。该方法只能在没有事务的上下文中执行,如果调用者方法处于事务上下文中,将会引发异常。
    2. 这种传播行为强制要求目标方法在没有事务的情况下运行,如果当前存在事务,则会中断调用并抛出异常。这可以用于确保某些特定的代码逻辑不会在事务上下文中执行,以避免潜在的事务管理问题。

综上所述,PROPAGATION_NOT_SUPPORTED和PROPAGATION_NEVER的主要区别在于它们对当前存在的事务的处理方式。PROPAGATION_NOT_SUPPORTED会挂起当前事务并在没有事务的上下文中执行目标方法,而PROPAGATION_NEVER则会检查当前是否存在事务,如果存在则抛出异常。

PROPAGATION_NEVER

以非事务方式运行,如果当前存在事务,则抛出异常。这确保了方法永远不会在事务上下文中运行。

考虑一个性能测试的操作,该操作不应该受到任何事务的约束。如果该操作在事务中被调用,就应该抛出异常。

1、Repository类(MyRepository.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.data.jpa.repository.JpaRepository;  
import org.springframework.stereotype.Repository;  
  
@Repository  
public interface MyRepository extends JpaRepository<MyEntity, Long> {  
    // 添加适合您项目的JPA存储库方法  
}

2、Service类(MyService.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Propagation;  
import org.springframework.transaction.annotation.Transactional;  
import org.springframework.transaction.UnexpectedRollbackException;  
  
@Service  
public class MyService {  
  
    @Autowired  
    private MyRepository myRepository;  
      
    @Transactional(propagation = Propagation.NEVER)  
    public void methodB() {  
        System.out.println("Method B started");  
        try {  
            // 尝试执行一些业务逻辑,但由于使用了PROPAGATION_NEVER,如果当前存在事务,则会抛出UnexpectedRollbackException异常。  
            myRepository.updateData("Method B");  
            System.out.println("Method B completed successfully");  
        } catch (UnexpectedRollbackException e) {  
            System.out.println("An UnexpectedRollbackException occurred while executing methodB() because it was called within an existing transaction.");  
            throw e;  
        }  
    }  
      
    @Transactional(propagation = Propagation.REQUIRED) // 使用默认的REQUIRED传播规则,确保methodA()在事务环境中执行。  
    public void methodA() {  
        System.out.println("Method A started");  
        try {  
            methodB(); // 在methodA()中调用methodB()。由于methodB()使用了PROPAGATION_NEVER,它将尝试在非事务环境中执行,但如果已经存在一个事务,将会抛出UnexpectedRollbackException异常。  
            System.out.println("Method A completed successfully");  
        } catch (Exception e) {  
            System.out.println("An error occurred while executing methodB() from methodA()");  
            throw e;  
        }  
    }  
}

3、Controller类(MyController.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
import org.springframework.transaction.TransactionDefinition;  
import org.springframework.transaction.TransactionStatus;  
import org.springframework.transaction.support.TransactionCallbackWithoutResult;  
import org.springframework.transaction.support.TransactionTemplate;  
import org.springframework.transaction.support.DefaultTransactionDefinition;  
  
@RestController
public class MyController {

  @Autowired private MyService myService;

  @GetMapping("/execute")
  public String execute() {
    /*
    	调用Service类中的methodA()方法,它将进一步调用methodB()方法。
    	由于methodB()使用了PROPAGATION_NEVER,如果当前存在事务,methodB()将抛出UnexpectedRollbackException异常。
    */
    myService.methodA();
  }
}

在上面的代码中,methodB()使用PROPAGATION_NEVER传播规则,这意味着它应该在非事务环境中执行。但是,如果它已经在一个事务中(由methodA()的REQUIRED传播规则确保),那么它会抛出UnexpectedRollbackException异常。

以下是几个约束点:

  1. methodA()使用REQUIRED传播规则,确保它在事务中执行。
  2. methodB()使用PROPAGATION_NEVER传播规则,试图在非事务环境中执行。如果它已经在一个事务中,就会抛出异常。

所以,如果methodA()被调用并且已经存在一个事务(由其他方法或外部因素引起),那么当methodB()被调用时,它会抛出UnexpectedRollbackException异常。这是因为PROPAGATION_NEVER的规则是不允许在已经存在的事务中执行的。

PROPAGATION_NESTED

如果当前存在事务,则嵌套事务作为一个子事务运行;如果当前没有事务,则该嵌套事务即表现为REQUIRED属性。这允许在一个已存在的事务中创建一个新的嵌套事务,这个嵌套事务可以独立于父事务进行提交或回滚。

例如:在订单处理中,用户下订单后除了减库存和增加订单记录外,还需要更新用户的积分,积分更新操作可以作为一个嵌套事务执行,这样即使积分更新失败,也不会影响到订单的处理。

1、Repository类(MyRepository.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.data.jpa.repository.JpaRepository;  
import org.springframework.stereotype.Repository;  
  
@Repository  
public interface MyRepository extends JpaRepository<MyEntity, Long> {  
    // 添加适合您项目的JPA存储库方法  
}

2、Service类(MyService.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Propagation;  
import org.springframework.transaction.annotation.Transactional;  
  
@Service  
public class MyService {  
  
    @Autowired  
    private MyRepository myRepository;  
      
    @Transactional(propagation = Propagation.NESTED)  
    public void methodB() {  
        System.out.println("Method B started");  
        try {  
            // 尝试执行一些业务逻辑,由于使用了PROPAGATION_NESTED,它将参与父事务,或者如果父事务不存在,则创建新的事务。  
            myRepository.updateData("Method B");  
            System.out.println("Method B completed successfully");  
        } catch (Exception e) {  
            System.out.println("An error occurred while executing methodB()");  
            throw e;  
        }  
    }  
      
    @Transactional(propagation = Propagation.REQUIRED) // 使用默认的REQUIRED传播规则,确保methodA()在事务环境中执行。  
    public void methodA() {  
        System.out.println("Method A started");  
        try {  
            methodB(); // 在methodA()中调用methodB()。由于methodB()使用了PROPAGATION_NESTED,它将参与methodA()所在的事务,或者如果methodA()所在的事务不存在,则创建新的事务。这样可以在同一个方法中实现嵌套事务。  
            System.out.println("Method A completed successfully");  
        } catch (Exception e) {  
            System.out.println("An error occurred while executing methodB() from methodA()");  
            throw e;  
        }  
    }  
}

3、Controller类(MyController.java)

/**
 * @版权 Copyright by 程序员古德 <br>
 * @创建人 程序员古德 <br>
 * @创建时间 2023/09/12 15:37 <br>
 */

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
import org.springframework.transaction.TransactionDefinition;  
import org.springframework.transaction.TransactionStatus;  
import org.springframework.transaction.support.TransactionCallbackWithoutResult;  
import org.springframework.transaction.support.TransactionTemplate;  
import org.springframework.transaction.support.DefaultTransactionDefinition;  
  
@RestController
public class MyController {

  @Autowired private MyService myService;

  @GetMapping("/execute")
  public String execute() {
    /*
    	调用Service类中的methodA()方法,它将进一步调用methodB()方法。由于使用了PROPAGATION_NESTED,如果methodA()所在的事务存在,则methodB()将参与该事务;否则,它将创建新的事务。
    
    */
    myService.methodA();
  }
}

思考:

1、PROPAGATION_NESTED和PROPAGATION_REQUIRED有什么区别?

PROPAGATION_NESTED和PROPAGATION_REQUIRED都是Spring框架中事务传播行为的选项,它们在事务处理方面有一些区别:

  1. 事务存在性:
    1. PROPAGATION_REQUIRED:如果当前没有事务,则新建一个事务;如果当前存在事务,则加入该事务。
    2. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
  2. 嵌套事务:
    1. PROPAGATION_NESTED允许在父事务中执行子事务,形成一个嵌套事务。子事务可以独立于父事务进行提交或回滚,而父事务不受子事务的影响。这种传播行为要求数据库和JDBC驱动程序支持保存点(Savepoint)功能,以便能够在嵌套事务中进行回滚操作
    2. PROPAGATION_REQUIRED不支持嵌套事务的概念。如果当前存在事务,则该方法将加入该事务;否则,将新建一个事务。它没有提供像PROPAGATION_NESTED那样的子事务独立提交或回滚的能力。

综上所述,PROPAGATION_NESTED和PROPAGATION_REQUIRED的主要区别在于嵌套事务的处理。PROPAGATION_NESTED允许在父事务中执行子事务,并提供子事务的独立提交或回滚能力;而PROPAGATION_REQUIRED则不支持嵌套事务的概念,只能加入当前存在的事务或新建一个事务。在选择传播行为时,应根据具体的业务需求和数据库支持来确定使用哪种传播规则。如果用父、子事务场景来说:父事务回滚的同时子事务也会回滚,子事务回滚但不影响父事务提交。

核心总结

Spring定义了七种事务传播规则,确保方法在不同事务上下文中的执行方式。其中,REQUIRED最为常用,保证数据一致性;SUPPORTS适应有无事务环境;MANDATORY强制事务执行;REQUIRES_NEW独立新事务;NOT_SUPPORTEDNEVER分别避免和禁止事务;NESTED允许嵌套子事务。选择适当规则,确保数据完整性、一致性,提升系统健壮性。

文章来源:https://blog.csdn.net/qusikao/article/details/134963492
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。