您的位置:

Spring 事务传播详解

一、事务概念

事务是一组由一个或多个操作组成的不可分割的工作单元,这些操作要么全部成功,要么全部失败。在关系型数据库中,事务是指一组SQL语句组成的操作序列,具有四个特性:原子性、一致性、隔离性和持久性。

二、Spring 事务处理

Spring 通过TransactionManager来统一管理事务,支持声明式事务和编程式事务两种方式。其中,声明式事务可以通过AOP实现,而编程式事务可以通过编写代码来实现。

三、Spring 事务传播行为

Spring 定义了七种事务传播行为,用于配置事务的传播属性,确保多个事务之间的正确性和一致性。

1、PROPAGATION_REQUIRED(默认值)

如果当前存在事务,则加入该事务;如果不存在事务,则创建一个新事务。

@Transactional(propagation = Propagation.REQUIRED)
public void method(){
   // 事务代码
}

2、PROPAGATION_SUPPORTS

如果当前存在事务,则加入该事务;如果不存在事务,则以非事务的方式继续执行。

@Transactional(propagation = Propagation.SUPPORTS)
public void method(){
   // 事务代码
}

3、PROPAGATION_MANDATORY

如果当前存在事务,则加入该事务;如果不存在事务,则抛出异常。

@Transactional(propagation = Propagation.MANDATORY)
public void method(){
   // 事务代码
}

4、PROPAGATION_REQUIRES_NEW

创建一个新事务,并且暂停当前事务(如果存在)。

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void method(){
   // 事务代码
}

5、PROPAGATION_NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,则挂起该事务。

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void method(){
   // 事务代码
}

6、PROPAGATION_NEVER

以非事务方式执行操作,如果当前存在事务,则抛出异常。

@Transactional(propagation = Propagation.NEVER)
public void method(){
   // 事务代码
}

7、PROPAGATION_NESTED

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则相当于PROPAGATION_REQUIRED。

@Transactional(propagation = Propagation.NESTED)
public void method(){
   // 事务代码
}

四、Spring 事务传播案例

下面我们通过一个简单的案例来说明 Spring 事务传播的使用。

1、创建数据库表

创建一个名为 user 的表,用于演示 Spring 事务传播功能的使用。

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

2、添加依赖

Pom.xml 文件中添加 Spring Boot 相关依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

3、配置数据源

在 application.properties 文件中添加数据源相关配置。

spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

4、创建User实体类

创建 User 实体类。

public class User {
    private int id;
    private String name;
    private int age;
    // 省略 getter 和 setter 方法
}

5、添加数据访问对象

创建 UserDAO 接口和实现类,用于操作数据库。

public interface UserDAO {
    void addUser(User user);
    void addUserWithRequired(User user);
    void addUserWithRequiresNew(User user);
}
@Repository
public class UserDAOImpl implements UserDAO {
    @Autowired
    private JdbcTemplate jdbcTemplate;
 
    @Override
    public void addUser(User user) {
        String sql = "INSERT INTO user(name, age) VALUES(?, ?)";
        jdbcTemplate.update(sql, new Object[]{user.getName(), user.getAge()});
    }
    
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addUserWithRequired(User user) {
        String sql = "INSERT INTO user(name, age) VALUES(?, ?)";
        jdbcTemplate.update(sql, new Object[]{user.getName(), user.getAge()});
        throw new RuntimeException();
    }
    
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addUserWithRequiresNew(User user) {
        String sql = "INSERT INTO user(name, age) VALUES(?, ?)";
        jdbcTemplate.update(sql, new Object[]{user.getName(), user.getAge()});
        throw new RuntimeException();
    }
}

6、添加服务层

创建 UserService 接口和实现类,用于操作数据库。

public interface UserService {
    void addUser(User user);
    void addUserWithRequired(User user);
    void addUserWithRequiresNew(User user);
}
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDAO userDAO;
 
    @Override
    @Transactional
    public void addUser(User user) {
        userDAO.addUser(user);
        throw new RuntimeException();
    }
 
    @Override
    @Transactional
    public void addUserWithRequired(User user) {
        userDAO.addUserWithRequired(user);
    }
 
    @Override
    @Transactional
    public void addUserWithRequiresNew(User user) {
        userDAO.addUserWithRequiresNew(user);
    }
}

7、写测试类

创建一个测试类,测试 Spring 事务传播的效果。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class UserServiceImplTest {
 
    @Autowired
    private UserService userService;
    @Autowired
    private UserDAO userDAO;
    
    @Test(expected = RuntimeException.class)
    public void testTransactionRequired() {
        User user = new User();
        user.setName("John");
        user.setAge(20);
        userService.addUserWithRequired(user);
    }
    
    @Test(expected = RuntimeException.class)
    public void testTransactionRequiresNew() {
        User user = new User();
        user.setName("John");
        user.setAge(20);
        userService.addUserWithRequiresNew(user);
    }
}

总结

本文详细介绍了 Spring 事务传播的概念、配置方式和七种传播行为,并提供了一个简单的案例,演示了 Spring 事务传播机制的实际使用。掌握了事务传播的知识,能够在必要的时候进行正确的配置,确保多个事务之间的正确性和一致性。