事务管理实战:从@Transactional到分布式事务

目录

引言

一、事务管理基础

1. 什么是事务?

2. 事务管理的方式

二、声明式事务管理:@Transactional

1. 基础使用

2. 事务传播行为

3. 事务隔离级别

三、分布式事务入门:Seata 的基本使用

1. 什么是分布式事务?

2. Seata 简介

3. 集成 Seata 到 Spring Boot 应用

步骤 1:添加依赖

步骤 2:配置 Seata

步骤 3:配置数据源

步骤 4:编写业务代码

4. 测试分布式事务


引言

在现代企业级应用开发中,事务管理是确保数据一致性和完整性的重要环节。无论是单体应用还是微服务架构,事务管理都扮演着不可或缺的角色。本篇文章将从声明式事务管理入手,结合Spring框架的@Transactional注解,深入探讨事务传播和隔离级别的配置与使用。随后,我们将扩展到分布式事务领域,介绍Seata的基本使用和配置方法。


一、事务管理基础

1. 什么是事务?

事务是数据库操作的一个逻辑单元,必须满足以下四个特性(ACID):

  • 原子性(Atomicity):事务中的所有操作要么全部成功提交,要么全部失败回滚。
  • 一致性(Consistency):事务完成后,数据库处于一个合法的一致状态。
  • 隔离性(Isolation):一个事务的执行不应受到其他事务的影响。
  • 持久性(Durability):一旦事务提交,其对数据库的修改将永久保存。
2. 事务管理的方式

在Java应用中,事务管理主要有两种方式:

  • 编程式事务管理:通过手动编写代码来控制事务的开始、提交和回滚。
  • 声明式事务管理:通过配置或注解(如@Transactional)来声明事务边界,由容器自动管理。

由于声明式事务管理简洁易用且符合“关注点分离”原则,本文将重点介绍其使用方法。


二、声明式事务管理:@Transactional

1. 基础使用

@Transactional 是Spring框架提供的一个注解,用于声明式地管理事务。它可以应用于类或方法级别。

示例代码:

import org.springframework.transaction.annotation.Transactional; 
 
@Service 
public class UserService {
 
    @Autowired 
    private UserMapper userMapper;
 
    @Transactional 
    public void addUserAndLog(String username) {
        User user = new User();
        user.setUsername(username); 
        userMapper.insert(user); 
 
        // 记录日志 
        Log log = new Log();
        log.setUsername(username); 
        logMapper.insert(log); 
    }
}

@Transactional:默认情况下,该注解会开启一个事务,并在方法执行完毕后提交事务。如果方法抛出异常,则回滚事务。

2. 事务传播行为

Spring支持多种事务传播行为,用于控制方法之间的事务关联方式。常见的传播行为包括:

  • REQUIRED:如果当前存在事务,则加入该事务;否则创建新事务。
  • SUPPORTS:如果当前存在事务,则加入该事务;否则以非事务方式执行。
  • MANDATORY:如果当前不存在事务,则抛出异常。
  • NOT_SUPPORTED:以非事务方式执行;如果当前存在事务,则暂停当前事务。
  • NESTED:如果当前存在事务,则创建一个嵌套事务;否则创建新事务。

示例代码:

@Service 
public class OrderService {
 
    @Autowired 
    private OrderMapper orderMapper;
    @Autowired 
    private PaymentMapper paymentMapper;
 
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order, Payment payment) {
        orderMapper.insert(order); 
 
        // 子方法自动加入同一事务 
        pay(order.getId(),  payment);
    }
 
    @Transactional(propagation = Propagation.REQUIRED)
    private void pay(Long orderId, Payment payment) {
        payment.setOrderId(orderId); 
        paymentMapper.insert(payment); 
    }
}

 Propagation.REQUIRED:确保两个方法在一个事务中执行。

3. 事务隔离级别

事务隔离级别决定了不同事务之间可见性的程度。Spring支持以下几种隔离级别:

  • DEFAULT:使用数据库默认的隔离级别(通常是REPEATABLE_READ)。
  • READ_UNCOMMITTED:最低的隔离级别,允许脏读、不可重复读和幻读。
  • READ_COMMITTED:允许不可重复读和幻读。
  • REPEATABLE_READ:禁止脏读和不可重复读。
  • SERIALIZABLE:最高的隔离级别,禁止所有并发问题。

示例代码:

@Service 
public class AccountService {
 
    @Autowired 
    private AccountMapper accountMapper;
 
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void transferMoney(Long fromId, Long toId, Double amount) {
        Account fromAccount = accountMapper.selectById(fromId); 
        Account toAccount = accountMapper.selectById(toId); 
 
        if (fromAccount.getBalance()  >= amount) {
            fromAccount.setBalance(fromAccount.getBalance()  - amount);
            accountMapper.updateById(fromAccount); 
 
            toAccount.setBalance(toAccount.getBalance()  + amount);
            accountMapper.updateById(toAccount); 
        }
    }
}

 Isolation.READ_COMMITTED:防止脏读,但允许不可重复读和幻读。

三、分布式事务入门:Seata 的基本使用

1. 什么是分布式事务?

在微服务架构中,一个业务操作可能需要多个服务协同完成。例如,在电商系统中,下单操作可能需要调用订单服务、库存服务和支付服务。这种跨服务的操作被称为分布式事务。

传统的JTA(Java Transaction API)在分布式环境下表现不佳,因此出现了新的解决方案——Seata

2. Seata 简介

Seata 是一个开源的分布式事务解决方案,支持多种分布式事务模式:

  • TCC(Try-Confirm-Cancel):适用于复杂的业务逻辑。
  • Saga:长流程业务。
  • AT(Automatic Transaction):基于数据库的自动提交机制。

本文将重点介绍 AT 模式的使用。

3. 集成 Seata 到 Spring Boot 应用
步骤 1:添加依赖

pom.xml 中添加 Seata 和相关的依赖:


    
    
        io.seata 
        seata-spring-boot-starter
        1.7.0
    
 
    
    
        org.springframework.boot 
        spring-boot-starter-web
    
    
        org.springframework.boot 
        spring-boot-starter-jdbc
    

步骤 2:配置 Seata

application.properties 文件中添加以下配置:

# Seata 配置 
seata.enabled=true  
seata.application-id=order-service  
seata.tx-service-group=my_tx_group  
seata.service.vgroup-mapping.my_tx_group=1  
seata.service.node=192.168.1.100:8091  

步骤 3:配置数据源

确保你的数据源配置支持分布式事务。例如:

# 数据源配置 
spring.datasource.url=jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC  
spring.datasource.username=root  
spring.datasource.password=root  
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver  
 
# 配置连接池 
spring.datasource.hikari.minimum-idle=5  
spring.datasource.hikari.maximum-pool-size=20  

步骤 4:编写业务代码

在需要进行分布式事务的方法上添加 @GlobalTransactional 注解:

import io.seata.spring.annotation.GlobalTransactional; 
 
@Service 
public class OrderService {
 
    @Autowired 
    private OrderMapper orderMapper;
    @Autowired 
    private InventoryService inventoryService;
    @Autowired 
    private PaymentService paymentService;
 
    @GlobalTransactional(timeout = 30000)
    public void createOrder(Order order, Payment payment) {
        // 创建订单 
        orderMapper.insert(order); 
 
        // 扣减库存 
        inventoryService.decreaseInventory(order.getItemId(),  order.getNum()); 
 
        // 支付 
        paymentService.pay(order.getUserId(),  payment.getAmount()); 
    }
}

  • @GlobalTransactional:声明这是一个全局事务。
  • timeout:设置事务超时时间(单位:毫秒)。
4. 测试分布式事务

为了验证分布式事务的有效性,可以设计一个测试场景:

  1. 创建一个订单。
  2. 扣减库存。
  3. 执行支付。

如果任何一个步骤失败,整个事务应回滚。

@RunWith(SpringRunner.class) 
@SpringBootTest 
public class OrderServiceTest {
 
    @Autowired 
    private OrderService orderService;
 
    @Test 
    public void testCreateOrder() {
        Order order = new Order();
        order.setItemId(1L); 
        order.setNum(2); 
        order.setUserId(1L); 
 
        Payment payment = new Payment();
        payment.setAmount(100.0); 
 
        orderService.createOrder(order,  payment);
    }
}

你可能感兴趣的:(SpringBoot框架学习,spring,boot)