Spring 事务管理机制介绍

1. 事务的基本概念

事务是一组不可分割的数据库操作序列,这些操作要么全部成功执行,要么全部失败回滚。事务具有四个特性,即 ACID 特性:

  • 原子性(Atomicity):事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。
  • 一致性(Consistency):事务执行前后,数据库的状态必须保持一致。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应该影响其他事务的执行。
  • 持久性(Durability):事务一旦提交,其对数据库的改变是永久性的。

2. Spring 事务管理的核心接口

Spring 事务管理主要基于以下几个核心接口:

  • PlatformTransactionManager:这是事务管理器的核心接口,它定义了事务的基本操作,如获取事务、提交事务和回滚事务。不同的持久化框架有不同的实现类,例如 DataSourceTransactionManager 用于 JDBC 事务管理,HibernateTransactionManager 用于 Hibernate 事务管理。
  • TransactionDefinition:该接口定义了事务的属性,包括隔离级别、传播行为、超时时间和是否只读等。
  • TransactionStatus:它代表了当前事务的状态,通过该接口可以进行事务的提交、回滚等操作。

3. Spring 事务管理的实现方式

Spring 提供了两种事务管理的实现方式:

  • 编程式事务管理:通过编写代码来管理事务,需要手动获取事务、提交事务或回滚事务。这种方式比较灵活,但代码冗余度高,不适合大规模使用。
  • 声明式事务管理:通过配置的方式来管理事务,无需在业务代码中显式地编写事务管理代码。声明式事务管理又分为基于 XML 配置和基于注解两种方式,其中基于注解的方式更为常用,使用 @Transactional 注解可以方便地为方法或类添加事务支持。

Spring 事务的嵌套实现

1. 事务传播行为

事务的嵌套涉及到事务的传播行为,TransactionDefinition 接口定义了 7 种事务传播行为,常用的有以下几种:

  • PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认的传播行为。
  • PROPAGATION_REQUIRES_NEW:无论当前是否存在事务,都会创建一个新的事务,并挂起当前事务(如果存在)。
  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务中执行;如果当前没有事务,则创建一个新的事务。嵌套事务是外部事务的一部分,外部事务回滚时,嵌套事务也会回滚,但嵌套事务回滚不会影响外部事务。

2. 示例代码

下面是一个使用 Spring Boot 和 JPA 实现事务嵌套的示例:

首先,确保在 pom.xml 中添加必要的依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

定义实体类 User

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    public User() {
    }

    public User(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

定义 UserRepository 接口:

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}

定义服务类 UserService,其中包含嵌套事务的方法:

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 UserService {
    @Autowired
    private UserRepository userRepository;

    @Transactional(propagation = Propagation.REQUIRED)
    public void outerTransaction() {
        User user1 = new User("User1");
        userRepository.save(user1);
        try {
            innerTransaction();
        } catch (Exception e) {
            System.out.println("Inner transaction failed, but outer transaction may continue.");
        }
        User user2 = new User("User2");
        userRepository.save(user2);
    }

    @Transactional(propagation = Propagation.NESTED)
    public void innerTransaction() {
        User user3 = new User("User3");
        userRepository.save(user3);
        throw new RuntimeException("Inner transaction exception");
    }
}

在控制器中调用服务方法:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/testTransaction")
    public String testTransaction() {
        userService.outerTransaction();
        return "Transaction test completed.";
    }
}

代码解释

  • outerTransaction 方法使用 @Transactional(propagation = Propagation.REQUIRED) 注解,表示如果当前没有事务,则创建一个新的事务。
  • innerTransaction 方法使用 @Transactional(propagation = Propagation.NESTED) 注解,表示在嵌套事务中执行。当 innerTransaction 方法抛出异常时,嵌套事务会回滚,但外部事务不会受到影响,user2 仍然会被保存到数据库中。

通过以上示例,你可以看到如何在 Spring 中实现事务的嵌套。根据不同的业务需求,选择合适的事务传播行为可以确保事务的正确执行。