【第九章】 Spring的事务 之 9.3 编程式事务

Spring 提供了多种事务管理方式,其中编程式事务管理(Programmatic Transaction Management)是最为底层的一种方式。在编程式事务管理中,开发者需要手动控制事务的开启、提交和回滚。这种方式相较于声明式事务管理(Declarative Transaction Management)更加灵活,但也更为复杂。接下来我们将详细介绍 Spring 中编程式事务的实现方式。


1. 编程式事务的基本概念

编程式事务管理要求开发者在代码中显式地管理事务生命周期。通过 PlatformTransactionManager 接口和 TransactionStatus 来控制事务的启动、提交、回滚等操作。编程式事务使开发者能够精确控制事务的行为,但同时也增加了代码的复杂性。

2. 编程式事务的实现步骤

在 Spring 中,通过 PlatformTransactionManager 和 TransactionTemplate 实现编程式事务管理。实现的基本步骤如下:

  1. 获取事务管理器:通过 PlatformTransactionManager 获取事务管理器。
  2. 开启事务:调用 PlatformTransactionManager 的 getTransaction() 方法开启事务。
  3. 执行操作:在事务中执行数据库操作。
  4. 提交事务:通过 commit() 方法提交事务。
  5. 回滚事务:如果发生异常,调用 rollback() 方法回滚事务。

3. 代码实现

3.1 使用 PlatformTransactionManager 实现编程式事务

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Service
public class ProgrammaticTransactionService {

    @Autowired
    private PlatformTransactionManager transactionManager;

    public void executeTransaction() {
        // 创建事务定义,设置事务的传播行为等
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);

        // 获取事务状态
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            // 执行一些数据库操作
            performDatabaseOperation();

            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // 如果发生异常,回滚事务
            transactionManager.rollback(status);
            throw e; // 将异常抛出,以便外层处理
        }
    }

    private void performDatabaseOperation() {
        // 执行数据库操作的代码
        System.out.println("Performing database operation...");
    }
}

3.2 使用 TransactionTemplate 简化事务管理

Spring 提供了 TransactionTemplate 来简化编程式事务管理的操作。通过 TransactionTemplate,可以将事务管理的细节封装起来,使得开发者更专注于业务逻辑的编写。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionTemplate;

@Service
public class TransactionTemplateService {

    private final TransactionTemplate transactionTemplate;

    @Autowired
    public TransactionTemplateService(PlatformTransactionManager transactionManager) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }

    public void executeTransaction() {
        // 使用 TransactionTemplate 执行事务
        transactionTemplate.execute(status -> {
            try {
                // 执行数据库操作
                performDatabaseOperation();
                return null; // 提交事务
            } catch (Exception e) {
                status.setRollbackOnly(); // 发生异常时标记回滚
                return null;
            }
        });
    }

    private void performDatabaseOperation() {
        // 执行数据库操作的代码
        System.out.println("Performing database operation...");
    }
}

4. 事务管理中的重要概念

4.1 事务的传播行为

事务的传播行为定义了在事务方法调用过程中,事务是如何被传播的。Spring 提供了多种传播行为,常用的有:

  • PROPAGATION_REQUIRED:如果当前没有事务,则开启一个新的事务;如果当前存在事务,则加入该事务。
  • PROPAGATION_REQUIRES_NEW:每次都会创建一个新的事务,无论当前是否存在事务。
  • PROPAGATION_NESTED:如果当前存在事务,则在当前事务中创建一个嵌套事务,否则行为同 PROPAGATION_REQUIRED

4.2 事务的隔离级别

事务的隔离级别定义了不同事务之间的并发访问如何处理。Spring 提供了以下几种隔离级别:

  • ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许脏读。
  • ISOLATION_READ_COMMITTED:避免脏读,但允许不可重复读。
  • ISOLATION_REPEATABLE_READ:避免脏读和不可重复读,但可能会导致幻读。
  • ISOLATION_SERIALIZABLE:最高的隔离级别,完全避免脏读、不可重复读和幻读,但性能较差。

4.3 事务的回滚规则

默认情况下,Spring 会在运行时发生 RuntimeException 或 Error 时进行回滚。如果想要自定义回滚规则,可以通过 @Transactional 注解的 rollbackFor 属性或 noRollbackFor 属性来指定哪些异常会触发事务回滚。


5. 编程式事务的优缺点

优点

  • 灵活性:开发者可以精确控制事务的行为,例如嵌套事务、跨多个数据源的事务等。
  • 精确控制:事务的开启、提交、回滚等操作都可以由开发者控制,适用于复杂的业务场景。

缺点

  • 代码冗余:相比声明式事务,编程式事务需要手动管理事务的各个方面,代码量较大,且容易出错。
  • 可维护性差:当事务逻辑较为复杂时,编程式事务可能导致代码的可读性和可维护性降低。

6. 总结

  • 编程式事务管理适用于那些需要开发者精确控制事务行为的复杂业务场景。它提供了最大的灵活性,可以自定义事务的各个细节。
  • 然而,编程式事务管理的代码较为繁琐,需要手动管理事务的生命周期,因此在事务逻辑较为简单的场景中,声明式事务(使用 @Transactional)会是更优的选择。

如果你的应用程序的事务处理比较简单,建议使用 声明式事务管理,但在面对复杂的事务处理需求时,编程式事务管理无疑是更好的选择。