在 Spring 框架中,事务(Transaction)管理是保障数据一致性和完整性的重要机制。Spring 支持两种主要的事务实现方式:


✅ 一、编程式事务(Programmatic Transaction)

手动控制事务的开启、提交与回滚。更灵活但更冗长,推荐用于底层框架或需要动态控制事务粒度的地方。

✳️ 实现方式 1:使用 TransactionTemplate

@Service
public class UserService {
    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private UserRepository userRepository;

    public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
        transactionTemplate.executeWithoutResult(status -> {
            try {
                userRepository.decreaseBalance(fromId, amount);
                userRepository.increaseBalance(toId, amount);
            } catch (Exception e) {
                status.setRollbackOnly();  // 手动回滚
                throw e;
            }
        });
    }
}

✳️ 实现方式 2:使用 PlatformTransactionManager

@Service
public class UserService {
    @Autowired
    private PlatformTransactionManager transactionManager;

    public void updateUser() {
        TransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            // your db operations
            transactionManager.commit(status);
        } catch (RuntimeException ex) {
            transactionManager.rollback(status);
            throw ex;
        }
    }
}

✅ 二、声明式事务(Declarative Transaction)

基于注解或 XML 配置,由 Spring AOP 自动管理事务。推荐方式,更简洁、更易维护。


✳️ 注解方式(最常用)

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
        // 如果出现异常,事务自动回滚
    }
}

常用注解参数:

属性说明
propagation事务的传播行为,默认 REQUIRED
isolation事务隔离级别,默认 DEFAULT
timeout超时时间(秒)
readOnly是否只读,优化性能
rollbackFor指定哪些异常触发回滚(如:Exception.class
noRollbackFor指定哪些异常触发回滚

示例:

@Transactional(
    propagation = Propagation.REQUIRED,
    isolation = Isolation.READ_COMMITTED,
    rollbackFor = {SQLException.class}
)

✳️ XML 配置方式(老项目中可能见到)

<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
    <tx:method name="get*" read-only="true"/>
    <tx:method name="save*" propagation="REQUIRED"/>
    <tx:method name="*" rollback-for="Exception"/>
  </tx:attributes>
</tx:advice>

<aop:config>
  <aop:pointcut id="serviceMethods" expression="execution(* com.example.service..*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>

✅ 三、编程式 vs 声明式事务 对比

对比项编程式事务声明式事务
控制粒度细,灵活控制事务边界粒度固定,统一处理事务
实现复杂度代码多、冗余使用简单、维护性强
推荐场景动态事务、框架底层逻辑大多数业务服务逻辑推荐使用

✅ 四、使用声明式事务的注意事项

  • 方法必须是 public 且被 Spring 容器管理(即由容器代理)才能生效
  • 默认只对 运行时异常(RuntimeException)回滚
  • 事务方法间内部调用不会生效(需使用代理或注入自身)

好的!下面我帮你写一个完整的 Spring Boot + MyBatis + 声明式事务示例项目,展示事务的标准用法、异常回滚和事务传播特性。示例中包含:

  • 两个 Service 方法,模拟转账业务
  • 声明式事务注解 @Transactional
  • 事务异常回滚演示
  • 事务传播测试

Spring Boot + MyBatis 事务示例


1. 项目依赖(pom.xml

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <!-- MyBatis Spring Boot Starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>

    <!-- MySQL Connector -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- Spring Boot Starter JDBC -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2. 数据库表(MySQL)

CREATE TABLE account (
  id INT PRIMARY KEY AUTO_INCREMENT,
  user_name VARCHAR(50),
  balance DECIMAL(10,2)
);

INSERT INTO account (user_name, balance) VALUES ('Alice', 1000.00), ('Bob', 1000.00);

3. 实体类 Account.java

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
    private Integer id;
    private String userName;
    private BigDecimal balance;
}

4. Mapper 接口 AccountMapper.java

@Mapper
public interface AccountMapper {
    Account findByUserName(String userName);

    void updateBalance(@Param("userName") String userName, @Param("amount") BigDecimal amount);
}

5. Mapper XML AccountMapper.xml

<mapper namespace="com.example.mapper.AccountMapper">

  <select id="findByUserName" resultType="com.example.domain.Account">
    SELECT * FROM account WHERE user_name = #{userName}
  </select>

  <update id="updateBalance">
    UPDATE account SET balance = balance + #{amount} WHERE user_name = #{userName}
  </update>

</mapper>

6. Service 层 AccountService.java

@Service
public class AccountService {

    @Autowired
    private AccountMapper accountMapper;

    /**
     * 转账操作,声明式事务控制
     */
    @Transactional(rollbackFor = Exception.class)
    public void transfer(String fromUser, String toUser, BigDecimal amount) {
        // 扣款
        accountMapper.updateBalance(fromUser, amount.negate());

        // 模拟异常,测试事务回滚
        if (amount.compareTo(BigDecimal.valueOf(500)) > 0) {
            throw new RuntimeException("转账金额过大,模拟异常回滚");
        }

        // 收款
        accountMapper.updateBalance(toUser, amount);
    }

    /**
     * 测试事务传播,外部方法不加事务,调用内部加事务方法
     */
    public void testPropagation(String fromUser, String toUser, BigDecimal amount) {
        saveWithoutTransaction();
        saveWithTransaction(fromUser, toUser, amount);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void saveWithTransaction(String fromUser, String toUser, BigDecimal amount) {
        accountMapper.updateBalance(fromUser, amount.negate());
        accountMapper.updateBalance(toUser, amount);
    }

    public void saveWithoutTransaction() {
        // 一般操作,不开启事务
        System.out.println("无事务操作");
    }
}

7. Controller 层 AccountController.java

@RestController
@RequestMapping("/account")
public class AccountController {

    @Autowired
    private AccountService accountService;

    @PostMapping("/transfer")
    public String transfer(@RequestParam String from, @RequestParam String to, @RequestParam BigDecimal amount) {
        try {
            accountService.transfer(from, to, amount);
            return "转账成功";
        } catch (Exception e) {
            return "转账失败,原因:" + e.getMessage();
        }
    }

    @PostMapping("/testPropagation")
    public String testPropagation(@RequestParam String from, @RequestParam String to, @RequestParam BigDecimal amount) {
        try {
            accountService.testPropagation(from, to, amount);
            return "事务传播测试成功";
        } catch (Exception e) {
            return "事务传播测试失败:" + e.getMessage();
        }
    }
}

8. 说明

  • @Transactional 注解控制事务,异常自动回滚
  • rollbackFor = Exception.class 表示所有异常都会回滚,不限于 RuntimeException
  • 测试转账金额大于 500 时抛异常,演示事务回滚效果
  • 事务传播示例展示外部无事务,内部有事务的方法调用

好的!下面是完整的 Spring Boot + MyBatis + 事务管理 示例,包括:

  • application.yml 配置
  • 编程式事务示例
  • 声明式事务示例(回顾)
  • 简单测试用例示例

1. Spring Boot 配置文件(src/main/resources/application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

  # MyBatis 配置(可选)
  mybatis:
    mapper-locations: classpath:mapper/*.xml
    type-aliases-package: com.example.domain

# 开启事务管理(Spring Boot 默认开启)
spring:
  main:
    allow-bean-definition-overriding: true

2. 编程式事务示例(基于 PlatformTransactionManager

@Service
public class ProgrammaticTransactionService {

    private final PlatformTransactionManager transactionManager;
    private final AccountMapper accountMapper;

    public ProgrammaticTransactionService(PlatformTransactionManager transactionManager, AccountMapper accountMapper) {
        this.transactionManager = transactionManager;
        this.accountMapper = accountMapper;
    }

    public void transferMoney(String fromUser, String toUser, BigDecimal amount) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            accountMapper.updateBalance(fromUser, amount.negate());
            // 模拟异常
            if (amount.compareTo(BigDecimal.valueOf(500)) > 0) {
                throw new RuntimeException("金额过大,回滚事务");
            }
            accountMapper.updateBalance(toUser, amount);
            transactionManager.commit(status);
        } catch (RuntimeException e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}

3. 声明式事务示例(回顾)

@Service
public class DeclarativeTransactionService {

    @Autowired
    private AccountMapper accountMapper;

    @Transactional(rollbackFor = Exception.class)
    public void transfer(String fromUser, String toUser, BigDecimal amount) {
        accountMapper.updateBalance(fromUser, amount.negate());
        if (amount.compareTo(BigDecimal.valueOf(500)) > 0) {
            throw new RuntimeException("金额过大,回滚事务");
        }
        accountMapper.updateBalance(toUser, amount);
    }
}

4. 简单测试用例示例(JUnit 5)

@SpringBootTest
public class TransactionTest {

    @Autowired
    private DeclarativeTransactionService declarativeTransactionService;

    @Autowired
    private ProgrammaticTransactionService programmaticTransactionService;

    @Autowired
    private AccountMapper accountMapper;

    @Test
    void testDeclarativeTransactionRollback() {
        BigDecimal amount = BigDecimal.valueOf(600);
        Assertions.assertThrows(RuntimeException.class, () -> {
            declarativeTransactionService.transfer("Alice", "Bob", amount);
        });

        // 验证余额未变(假设初始为1000)
        Account alice = accountMapper.findByUserName("Alice");
        Account bob = accountMapper.findByUserName("Bob");
        Assertions.assertEquals(BigDecimal.valueOf(1000), alice.getBalance());
        Assertions.assertEquals(BigDecimal.valueOf(1000), bob.getBalance());
    }

    @Test
    void testProgrammaticTransactionRollback() {
        BigDecimal amount = BigDecimal.valueOf(600);
        Assertions.assertThrows(RuntimeException.class, () -> {
            programmaticTransactionService.transferMoney("Alice", "Bob", amount);
        });

        // 验证余额未变
        Account alice = accountMapper.findByUserName("Alice");
        Account bob = accountMapper.findByUserName("Bob");
        Assertions.assertEquals(BigDecimal.valueOf(1000), alice.getBalance());
        Assertions.assertEquals(BigDecimal.valueOf(1000), bob.getBalance());
    }
}

5. 说明

  • 编程式事务通过 PlatformTransactionManager 手动开启提交或回滚,适合复杂场景
  • 声明式事务通过 @Transactional 注解声明,代码简洁,自动管理事务边界
  • 测试用例验证转账超额时事务回滚,余额不变