在 Spring 项目中处理事务

在 Spring 项目中,处理事务主要有两种方式:编程式事务管理和声明式事务管理,下面分别进行介绍。

1. 编程式事务管理

编程式事务管理是通过编写代码来控制事务的边界,这种方式比较灵活,但代码量较大,维护成本较高。

示例代码

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.sql.DataSource;

@Service
public class ProgrammaticTransactionService {

    @Autowired
    private DataSource dataSource;

    public void performTransaction() {
        // 创建事务管理器
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
        // 定义事务属性
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 获取事务状态
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            // 执行数据库操作
            // ...
            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(status);
        }
    }
}

2. 声明式事务管理

声明式事务管理是通过 AOP(面向切面编程)来实现的,它将事务管理代码从业务逻辑中分离出来,使得代码更加简洁和易于维护。Spring 提供了基于 XML 配置和基于注解两种方式来实现声明式事务管理。

基于注解的声明式事务管理示例

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class DeclarativeTransactionService {

    @Transactional
    public void performTransaction() {
        // 执行数据库操作
        // ...
    }
}

同时,需要在配置类中启用事务管理:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration
@EnableTransactionManagement
public class TransactionConfig {

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

事务的传播行为

事务的传播行为定义了在嵌套事务调用时,事务如何进行传播。Spring 定义了 7 种事务传播行为,下面分别进行介绍:

1. PROPAGATION_REQUIRED(默认)

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

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 业务逻辑
    methodB();
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // 业务逻辑
}

在上述示例中,methodA 调用 methodB 时,如果 methodA 已经开启了一个事务,methodB 会加入到该事务中;如果 methodA 没有开启事务,则 methodAmethodB 会共同创建一个新的事务。

2. PROPAGATION_SUPPORTS

如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 业务逻辑
    methodB();
}

@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // 业务逻辑
}

在上述示例中,如果 methodA 开启了事务,methodB 会加入到该事务中;如果 methodA 没有开启事务,methodB 会以非事务方式执行。

3. PROPAGATION_MANDATORY

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

@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
    // 业务逻辑
}

如果调用 methodB 时没有开启事务,会抛出 IllegalTransactionStateException 异常。

4. PROPAGATION_REQUIRES_NEW

无论当前是否存在事务,都会创建一个新的事务,并挂起当前事务(如果存在)。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 业务逻辑
    methodB();
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // 业务逻辑
}

在上述示例中,methodB 会创建一个新的事务,并且 methodA 的事务会被挂起,直到 methodB 的事务完成。

5. PROPAGATION_NOT_SUPPORTED

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

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 业务逻辑
    methodB();
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodB() {
    // 业务逻辑
}

在上述示例中,methodB 会以非事务方式执行,并且 methodA 的事务会被挂起,直到 methodB 执行完成。

6. PROPAGATION_NEVER

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

@Transactional(propagation = Propagation.NEVER)
public void methodB() {
    // 业务逻辑
}

如果调用 methodB 时存在事务,会抛出 IllegalTransactionStateException 异常。

7. PROPAGATION_NESTED

如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。嵌套事务是外部事务的一部分,外部事务回滚时,嵌套事务也会回滚,但嵌套事务回滚不会影响外部事务。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 业务逻辑
    methodB();
}

@Transactional(propagation = Propagation.NESTED)
public void methodB() {
    // 业务逻辑
}

在上述示例中,methodB 会在 methodA 的事务内部创建一个嵌套事务。