Spring事务详解:从原理到实战
前言
事务(Transaction)是数据库操作的核心概念,它保证了一组数据库操作要么全部成功,要么全部失败。Spring框架提供了强大的声明式事务管理功能,极大地简化了事务处理的复杂度。本文将深入讲解Spring事务的核心概念、实现原理和最佳实践。
一、事务基础
1.1 什么是事务
事务是一组原子性的数据库操作序列,这些操作要么全部执行成功,要么全部不执行。
事务示例:银行转账
┌─────────────────────────────────────────┐
│ 无事务的问题: │
│ 1. 从A账户扣款 -100元 ✓ │
│ 2. 系统崩溃 ✗ │
│ 3. 向B账户增加 +100元 ✗(未执行) │
│ 结果:钱凭空消失! │
├─────────────────────────────────────────┤
│ 有事务的保障: │
│ BEGIN TRANSACTION │
│ 1. 从A账户扣款 -100元 │
│ 2. 向B账户增加 +100元 │
│ 3. 如果都成功 → COMMIT │
│ 如果有失败 → ROLLBACK(全部撤销) │
│ 结果:数据一致性得到保障 │
└─────────────────────────────────────────┘
1.2 ACID特性
ACID事务特性
┌─────────────────────────────────────────┐
│ A - Atomicity(原子性) │
│ - 事务中的所有操作要么全部完成 │
│ - 要么全部不完成,不会停在中间状态 │
│ - 例如:转账操作不可分割 │
├─────────────────────────────────────────┤
│ C - Consistency(一致性) │
│ - 事务前后数据的完整性必须保持一致 │
│ - 例如:转账前后总金额不变 │
├─────────────────────────────────────────┤
│ I - Isolation(隔离性) │
│ - 多个事务并发执行时互不干扰 │
│ - 通过隔离级别控制 │
├─────────────────────────────────────────┤
│ D - Durability(持久性) │
│ - 事务提交后,对数据的修改是永久的 │
│ - 即使系统故障也不会丢失 │
└─────────────────────────────────────────┘
1.3 事务隔离级别
/**
* Spring支持的事务隔离级别
*/
public enum Isolation {
/**
* 使用数据库默认隔离级别
*/
DEFAULT(-1),
/**
* 读未提交(最低级别)
* 问题:脏读、不可重复读、幻读
*/
READ_UNCOMMITTED(1),
/**
* 读已提交(Oracle默认)
* 解决:脏读
* 问题:不可重复读、幻读
*/
READ_COMMITTED(2),
/**
* 可重复读(MySQL默认)
* 解决:脏读、不可重复读
* 问题:幻读
*/
REPEATABLE_READ(4),
/**
* 串行化(最高级别)
* 解决:脏读、不可重复读、幻读
* 问题:性能最低
*/
SERIALIZABLE(8);
}
并发问题说明:
┌──────────────┬────────────────────────────────┐
│ 并发问题 │ 说明 │
├──────────────┼────────────────────────────────┤
│ 脏读 │ 读取到未提交的数据 │
│ (Dirty Read)│ 事务A读取了事务B未提交的数据 │
├──────────────┼────────────────────────────────┤
│ 不可重复读 │ 同一事务中两次读取结果不同 │
│ (Non- │ 事务A先读取数据,事务B修改并 │
│ Repeatable │ 提交,事务A再次读取时数据变化 │
│ Read) │ │
├──────────────┼────────────────────────────────┤
│ 幻读 │ 同一事务中两次查询记录数不同 │
│ (Phantom │ 事务A查询记录,事务B插入新记录 │
│ Read) │ 事务A再次查询时记录数增加 │
└──────────────┴────────────────────────────────┘
二、Spring事务管理
2.1 编程式事务 vs 声明式事务
/**
* 编程式事务(不推荐)
*/
@Service
public class ProgrammaticTransactionService {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private AccountDao accountDao;
public void transfer(Long fromId, Long toId, BigDecimal amount) {
transactionTemplate.execute(status -> {
try {
// 扣款
accountDao.deduct(fromId, amount);
// 加款
accountDao.add(toId, amount);
return true;
} catch (Exception e) {
// 回滚
status.setRollbackOnly();
throw e;
}
});
}
}
/**
* 声明式事务(推荐)
*/
@Service
public class DeclarativeTransactionService {
@Autowired
private AccountDao accountDao;
@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
// 扣款
accountDao.deduct(fromId, amount);
// 加款
accountDao.add(toId, amount);
// Spring自动管理事务
}
}
2.2 @Transactional注解详解
/**
* @Transactional注解的各个属性
*/
@Service
public class TransactionalAttributesDemo {
/**
* 1. propagation:事务传播行为
*/
@Transactional(propagation = Propagation.REQUIRED)
public void requiredExample() {
// 如果当前有事务,加入该事务;否则创建新事务(默认)
}
/**
* 2. isolation:事务隔离级别
*/
@Transactional(isolation = Isolation.READ_COMMITTED)
public void isolationExample() {
// 使用读已提交隔离级别
}
/**
* 3. timeout:事务超时时间(秒)
*/
@Transactional(timeout = 30)
public void timeoutExample() {
// 30秒内必须完成,否则回滚
}
/**
* 4. readOnly:只读事务
*/
@Transactional(readOnly = true)
public List<User> queryUsers() {
// 只读事务,优化性能
return null;
}
/**
* 5. rollbackFor:指定回滚异常
*/
@Transactional(rollbackFor = Exception.class)
public void rollbackForExample() throws Exception {
// 遇到Exception及其子类都回滚
}
/**
* 6. noRollbackFor:指定不回滚异常
*/
@Transactional(noRollbackFor = BusinessException.class)
public void noRollbackForExample() {
// 遇到BusinessException不回滚
}
/**
* 7. 组合使用
*/
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30,
rollbackFor = Exception.class
)
public void comprehensiveExample() throws Exception {
// 完整的事务配置
}
}
2.3 事务传播行为
/**
* 事务传播行为详解
*/
@Service
@Slf4j
public class TransactionPropagationDemo {
@Autowired
private UserService userService;
/**
* REQUIRED(默认):如果有事务就加入,没有就创建
*/
@Transactional(propagation = Propagation.REQUIRED)
public void required() {
log.info("REQUIRED事务");
userService.saveUser(new User()); // 加入当前事务
}
/**
* REQUIRES_NEW:总是创建新事务,挂起当前事务
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNew() {
log.info("REQUIRES_NEW事务");
userService.saveLog(new Log()); // 在新事务中执行
}
/**
* SUPPORTS:如果有事务就加入,没有就以非事务方式执行
*/
@Transactional(propagation = Propagation.SUPPORTS)
public void supports() {
log.info("SUPPORTS事务");
}
/**
* NOT_SUPPORTED:以非事务方式执行,如果有事务就挂起
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void notSupported() {
log.info("NOT_SUPPORTED - 非事务执行");
}
/**
* MANDATORY:必须在事务中执行,否则抛出异常
*/
@Transactional(propagation = Propagation.MANDATORY)
public void mandatory() {
log.info("MANDATORY - 必须有事务");
}
/**
* NEVER:必须在非事务中执行,否则抛出异常
*/
@Transactional(propagation = Propagation.NEVER)
public void never() {
log.info("NEVER - 不能有事务");
}
/**
* NESTED:嵌套事务,可以独立提交或回滚
*/
@Transactional(propagation = Propagation.NESTED)
public void nested() {
log.info("NESTED - 嵌套事务");
}
}
传播行为示意图:
REQUIRED传播行为
┌─────────────────────────────────────────┐
│ methodA() @Transactional(REQUIRED) │
│ ┌───────────────────────────────────┐ │
│ │ Transaction │ │
│ │ ┌───────────────────────────────┐│ │
│ │ │ methodA操作 ││ │
│ │ │ methodB() @Transactional ││ │
│ │ │ ┌─────────────────────────┐ ││ │
│ │ │ │ 加入methodA的事务 │ ││ │
│ │ │ │ methodB操作 │ ││ │
│ │ │ └─────────────────────────┘ ││ │
│ │ └───────────────────────────────┘│ │
│ │ 一起提交或回滚 │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
REQUIRES_NEW传播行为
┌─────────────────────────────────────────┐
│ methodA() @Transactional │
│ ┌───────────────────────────────────┐ │
│ │ Transaction A │ │
│ │ methodA操作 │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ 挂起Transaction A │ │ │
│ │ └─────────────────────────────┘ │ │
│ └───────────────────────────────────┘ │
│ methodB() @Transactional(REQUIRES_NEW)│
│ ┌───────────────────────────────────┐ │
│ │ Transaction B (新事务) │ │
│ │ methodB操作 │ │
│ │ 独立提交或回滚 │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ Transaction A 恢复 │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
三、实战案例
3.1 案例1:订单创建(多表操作)
/**
* 订单实体
*/
@Data
public class Order {
private Long id;
private String orderNo;
private Long userId;
private BigDecimal totalAmount;
private OrderStatus status;
private LocalDateTime createTime;
}
/**
* 订单项实体
*/
@Data
public class OrderItem {
private Long id;
private Long orderId;
private Long productId;
private Integer quantity;
private BigDecimal price;
}
/**
* 订单服务
*/
@Service
@Slf4j
public class OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private OrderItemDao orderItemDao;
@Autowired
private ProductService productService;
@Autowired
private AccountService accountService;
/**
* 创建订单(事务保证原子性)
*/
@Transactional(rollbackFor = Exception.class)
public Order createOrder(Long userId, List<OrderItem> items) {
log.info("开始创建订单,用户: {}", userId);
// 1. 计算订单总金额
BigDecimal totalAmount = items.stream()
.map(item -> item.getPrice().multiply(new BigDecimal(item.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 2. 创建订单
Order order = new Order();
order.setOrderNo(generateOrderNo());
order.setUserId(userId);
order.setTotalAmount(totalAmount);
order.setStatus(OrderStatus.PENDING);
order.setCreateTime(LocalDateTime.now());
orderDao.insert(order);
log.info("订单创建成功: {}", order.getOrderNo());
// 3. 创建订单项
for (OrderItem item : items) {
item.setOrderId(order.getId());
orderItemDao.insert(item);
// 4. 扣减库存(如果库存不足会抛出异常,导致整个事务回滚)
productService.deductStock(item.getProductId(), item.getQuantity());
}
// 5. 扣减账户余额
accountService.deduct(userId, totalAmount);
log.info("订单创建完成: {}", order.getOrderNo());
return order;
}
private String generateOrderNo() {
return "ORD" + System.currentTimeMillis();
}
}
/**
* 商品服务
*/
@Service
@Slf4j
public class ProductService {
@Autowired
private ProductDao productDao;
/**
* 扣减库存(加入外部事务)
*/
@Transactional(propagation = Propagation.REQUIRED)
public void deductStock(Long productId, Integer quantity) {
Product product = productDao.findById(productId);
if (product.getStock() < quantity) {
throw new InsufficientStockException("库存不足");
}
product.setStock(product.getStock() - quantity);
productDao.update(product);
log.info("扣减库存: 商品[{}], 数量[{}], 剩余[{}]",
productId, quantity, product.getStock());
}
}
/**
* 账户服务
*/
@Service
@Slf4j
public class AccountService {
@Autowired
private AccountDao accountDao;
/**
* 扣减余额
*/
@Transactional(propagation = Propagation.REQUIRED)
public void deduct(Long userId, BigDecimal amount) {
Account account = accountDao.findByUserId(userId);
if (account.getBalance().compareTo(amount) < 0) {
throw new InsufficientBalanceException("余额不足");
}
account.setBalance(account.getBalance().subtract(amount));
accountDao.update(account);
log.info("扣减余额: 用户[{}], 金额[{}], 剩余[{}]",
userId, amount, account.getBalance());
}
}
3.2 案例2:转账操作(REQUIRES_NEW)
/**
* 转账服务
*/
@Service
@Slf4j
public class TransferService {
@Autowired
private AccountDao accountDao;
@Autowired
private TransferLogService transferLogService;
/**
* 转账(主事务)
*/
@Transactional(rollbackFor = Exception.class)
public void transfer(Long fromUserId, Long toUserId, BigDecimal amount) {
log.info("开始转账: {} -> {}, 金额: {}", fromUserId, toUserId, amount);
try {
// 1. 扣款
Account fromAccount = accountDao.findByUserId(fromUserId);
if (fromAccount.getBalance().compareTo(amount) < 0) {
throw new InsufficientBalanceException("余额不足");
}
fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
accountDao.update(fromAccount);
// 模拟异常
// if (amount.compareTo(new BigDecimal("1000")) > 0) {
// throw new RuntimeException("转账金额过大");
// }
// 2. 加款
Account toAccount = accountDao.findByUserId(toUserId);
toAccount.setBalance(toAccount.getBalance().add(amount));
accountDao.update(toAccount);
// 3. 记录成功日志(新事务,即使转账失败也会记录)
transferLogService.logSuccess(fromUserId, toUserId, amount);
log.info("转账成功");
} catch (Exception e) {
// 4. 记录失败日志(新事务,即使转账回滚也会记录)
transferLogService.logFailure(fromUserId, toUserId, amount, e.getMessage());
throw e;
}
}
}
/**
* 转账日志服务
*/
@Service
@Slf4j
public class TransferLogService {
@Autowired
private TransferLogDao transferLogDao;
/**
* 记录成功日志(新事务,独立提交)
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logSuccess(Long fromUserId, Long toUserId, BigDecimal amount) {
TransferLog log = new TransferLog();
log.setFromUserId(fromUserId);
log.setToUserId(toUserId);
log.setAmount(amount);
log.setStatus("SUCCESS");
log.setCreateTime(LocalDateTime.now());
transferLogDao.insert(log);
this.log.info("转账成功日志已记录");
}
/**
* 记录失败日志(新事务,独立提交)
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logFailure(Long fromUserId, Long toUserId, BigDecimal amount, String errorMsg) {
TransferLog log = new TransferLog();
log.setFromUserId(fromUserId);
log.setToUserId(toUserId);
log.setAmount(amount);
log.setStatus("FAILURE");
log.setErrorMsg(errorMsg);
log.setCreateTime(LocalDateTime.now());
transferLogDao.insert(log);
this.log.info("转账失败日志已记录");
}
}
3.3 案例3:批量处理(嵌套事务)
/**
* 批量数据导入服务
*/
@Service
@Slf4j
public class BatchImportService {
@Autowired
private UserDao userDao;
@Autowired
private ErrorLogService errorLogService;
/**
* 批量导入用户(主事务)
*/
@Transactional(rollbackFor = Exception.class)
public void batchImport(List<User> users) {
log.info("开始批量导入,总数: {}", users.size());
int successCount = 0;
int failureCount = 0;
for (User user : users) {
try {
// 导入单个用户(嵌套事务)
importUser(user);
successCount++;
} catch (Exception e) {
// 单个用户导入失败不影响其他用户
failureCount++;
log.error("用户导入失败: {}", user.getUsername(), e);
}
}
log.info("批量导入完成,成功: {}, 失败: {}", successCount, failureCount);
}
/**
* 导入单个用户(嵌套事务,可以独立回滚)
*/
@Transactional(propagation = Propagation.NESTED)
public void importUser(User user) {
// 数据校验
if (user.getUsername() == null || user.getUsername().isEmpty()) {
throw new ValidationException("用户名不能为空");
}
// 检查是否已存在
if (userDao.existsByUsername(user.getUsername())) {
throw new DuplicateException("用户名已存在");
}
// 保存用户
userDao.insert(user);
log.info("用户导入成功: {}", user.getUsername());
}
}
3.4 案例4:分布式事务(Seata)
/**
* 使用Seata实现分布式事务
*/
@Service
@Slf4j
public class DistributedOrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private AccountServiceClient accountServiceClient; // Feign客户端
@Autowired
private InventoryServiceClient inventoryServiceClient; // Feign客户端
/**
* 创建订单(分布式事务)
* @GlobalTransactional 开启Seata分布式事务
*/
@GlobalTransactional(
name = "create-order",
timeoutMills = 30000,
rollbackFor = Exception.class
)
public Order createOrder(OrderDTO orderDTO) {
log.info("开始创建订单(分布式事务)");
// 1. 创建订单(本地事务)
Order order = new Order();
order.setOrderNo(generateOrderNo());
order.setUserId(orderDTO.getUserId());
order.setTotalAmount(orderDTO.getTotalAmount());
order.setStatus(OrderStatus.PENDING);
orderDao.insert(order);
log.info("订单创建成功: {}", order.getOrderNo());
// 2. 扣减库存(远程调用库存服务)
boolean stockDeducted = inventoryServiceClient.deduct(
orderDTO.getProductId(),
orderDTO.getQuantity()
);
if (!stockDeducted) {
throw new BusinessException("扣减库存失败");
}
log.info("库存扣减成功");
// 3. 扣减账户余额(远程调用账户服务)
boolean balanceDeducted = accountServiceClient.deduct(
orderDTO.getUserId(),
orderDTO.getTotalAmount()
);
if (!balanceDeducted) {
throw new BusinessException("扣减余额失败");
}
log.info("余额扣减成功");
// 4. 更新订单状态
order.setStatus(OrderStatus.PAID);
orderDao.update(order);
log.info("订单创建完成(分布式事务): {}", order.getOrderNo());
return order;
}
private String generateOrderNo() {
return "ORD" + System.currentTimeMillis();
}
}
/**
* 库存服务(参与分布式事务)
*/
@Service
@Slf4j
public class InventoryService {
@Autowired
private InventoryDao inventoryDao;
/**
* 扣减库存
* 被@GlobalTransactional标记的方法调用,自动加入分布式事务
*/
@Transactional(rollbackFor = Exception.class)
public boolean deduct(Long productId, Integer quantity) {
Inventory inventory = inventoryDao.findByProductId(productId);
if (inventory.getStock() < quantity) {
log.error("库存不足");
return false;
}
inventory.setStock(inventory.getStock() - quantity);
inventoryDao.update(inventory);
log.info("库存扣减成功: 商品[{}], 数量[{}]", productId, quantity);
return true;
}
}
3.5 案例5:只读事务优化查询
/**
* 查询服务(只读事务优化)
*/
@Service
@Slf4j
public class QueryService {
@Autowired
private UserDao userDao;
@Autowired
private OrderDao orderDao;
/**
* 查询用户(只读事务)
* 优势:
* 1. 不需要脏检查
* 2. 某些数据库可以优化只读事务
* 3. 明确表达这是只读操作
*/
@Transactional(readOnly = true)
public User findUserById(Long id) {
log.info("查询用户: {}", id);
return userDao.findById(id);
}
/**
* 复杂查询(只读事务)
*/
@Transactional(readOnly = true)
public UserOrderVO findUserWithOrders(Long userId) {
log.info("查询用户及订单: {}", userId);
User user = userDao.findById(userId);
List<Order> orders = orderDao.findByUserId(userId);
UserOrderVO vo = new UserOrderVO();
vo.setUser(user);
vo.setOrders(orders);
return vo;
}
/**
* 分页查询(只读事务)
*/
@Transactional(readOnly = true)
public Page<Order> findOrdersByPage(Long userId, int page, int size) {
log.info("分页查询订单: 用户[{}], 页码[{}], 大小[{}]", userId, page, size);
long total = orderDao.countByUserId(userId);
List<Order> orders = orderDao.findByUserIdWithPage(userId, page, size);
Page<Order> result = new Page<>();
result.setTotal(total);
result.setRecords(orders);
result.setCurrent(page);
result.setSize(size);
return result;
}
}
四、事务失效场景
4.1 常见失效场景
/**
* 事务失效场景汇总
*/
@Service
@Slf4j
public class TransactionFailureDemo {
@Autowired
private UserDao userDao;
/**
* 场景1:方法不是public(事务失效)
*/
@Transactional
private void privateMethod() { // ❌ 事务不生效
userDao.save(new User());
}
@Transactional
protected void protectedMethod() { // ❌ 事务不生效
userDao.save(new User());
}
/**
* 场景2:同类内部调用(事务失效)
*/
public void outerMethod() {
// ❌ 直接调用,绕过代理,事务不生效
this.innerMethod();
}
@Transactional
public void innerMethod() {
userDao.save(new User());
}
/**
* 场景3:异常被捕获(事务不回滚)
*/
@Transactional
public void catchException() {
try {
userDao.save(new User());
throw new RuntimeException("测试异常");
} catch (Exception e) {
// ❌ 异常被捕获,事务不会回滚
log.error("异常被捕获", e);
}
}
/**
* 场景4:抛出检查异常(默认不回滚)
*/
@Transactional // ❌ 默认只回滚RuntimeException
public void checkedExceptionMethod() throws Exception {
userDao.save(new User());
throw new Exception("检查异常"); // 不会回滚
}
/**
* 场景5:错误的传播行为
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void notSupportedMethod() { // ❌ 以非事务方式执行
userDao.save(new User());
}
/**
* 场景6:数据库不支持事务
*/
@Transactional
public void myisamTable() { // ❌ MyISAM引擎不支持事务
// 操作MyISAM表
}
/**
* 场景7:未启用事务管理
*/
// ❌ 配置类未添加@EnableTransactionManagement
}
4.2 解决方案
/**
* 事务失效的解决方案
*/
@Service
@Slf4j
public class TransactionSolution {
@Autowired
private UserDao userDao;
@Autowired
private ApplicationContext context;
/**
* 解决方案1:方法改为public
*/
@Transactional
public void publicMethod() { // ✓ 事务生效
userDao.save(new User());
}
/**
* 解决方案2:注入自己,通过代理调用
*/
@Autowired
private TransactionSolution self;
public void outerMethodSolution1() {
self.innerMethod(); // ✓ 通过代理调用,事务生效
}
/**
* 解决方案3:从容器获取代理对象
*/
public void outerMethodSolution2() {
TransactionSolution proxy = context.getBean(TransactionSolution.class);
proxy.innerMethod(); // ✓ 通过代理调用
}
/**
* 解决方案4:使用AopContext
*/
public void outerMethodSolution3() {
((TransactionSolution) AopContext.currentProxy()).innerMethod(); // ✓
}
@Transactional
public void innerMethod() {
userDao.save(new User());
}
/**
* 解决方案5:异常后手动回滚
*/
@Transactional
public void catchExceptionSolution() {
try {
userDao.save(new User());
throw new RuntimeException("测试异常");
} catch (Exception e) {
log.error("异常被捕获", e);
// ✓ 手动标记回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
/**
* 解决方案6:指定回滚异常
*/
@Transactional(rollbackFor = Exception.class) // ✓ 指定回滚所有异常
public void checkedExceptionSolution() throws Exception {
userDao.save(new User());
throw new Exception("检查异常"); // 会回滚
}
}
五、最佳实践
5.1 事务粒度控制
/**
* 事务粒度最佳实践
*/
@Service
@Slf4j
public class TransactionGranularityDemo {
@Autowired
private OrderDao orderDao;
@Autowired
private EmailService emailService;
/**
* ❌ 不好:事务粒度太大
*/
@Transactional
public void badExample(Order order) {
// 数据库操作
orderDao.save(order);
// 发送邮件(耗时操作,不需要在事务中)
emailService.sendOrderConfirmation(order);
// 调用第三方API(耗时操作,不需要在事务中)
callThirdPartyApi(order);
}
/**
* ✓ 好:事务粒度适中
*/
public void goodExample(Order order) {
// 数据库操作在事务中
saveOrder(order);
// 非事务操作
emailService.sendOrderConfirmation(order);
callThirdPartyApi(order);
}
@Transactional(rollbackFor = Exception.class)
public void saveOrder(Order order) {
orderDao.save(order);
}
private void callThirdPartyApi(Order order) {
// 调用第三方API
}
}
5.2 异常处理策略
/**
* 事务异常处理最佳实践
*/
@Service
@Slf4j
public class TransactionExceptionDemo {
@Autowired
private OrderDao orderDao;
@Autowired
private NotificationService notificationService;
/**
* 策略1:让异常向上抛出
*/
@Transactional(rollbackFor = Exception.class)
public void strategy1(Order order) throws Exception {
orderDao.save(order);
if (order.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("订单金额必须大于0");
}
// 异常会导致事务回滚
}
/**
* 策略2:捕获并重新抛出
*/
@Transactional(rollbackFor = Exception.class)
public void strategy2(Order order) {
try {
orderDao.save(order);
processPayment(order);
} catch (PaymentException e) {
log.error("支付失败", e);
// 转换为运行时异常,触发回滚
throw new BusinessException("订单创建失败:" + e.getMessage(), e);
}
}
/**
* 策略3:部分回滚
*/
@Transactional(rollbackFor = Exception.class)
public void strategy3(Order order) {
orderDao.save(order);
try {
// 发送通知失败不影响订单创建
notificationService.sendNotification(order);
} catch (Exception e) {
log.error("发送通知失败,但不影响订单", e);
// 不抛出异常,事务继续
}
}
private void processPayment(Order order) throws PaymentException {
// 支付处理
}
}
5.3 性能优化
/**
* 事务性能优化
*/
@Service
@Slf4j
public class TransactionPerformanceDemo {
@Autowired
private OrderDao orderDao;
@Autowired
private CacheService cacheService;
/**
* 优化1:使用只读事务
*/
@Transactional(readOnly = true)
public List<Order> findOrders(Long userId) {
// 只读事务可以提高性能
return orderDao.findByUserId(userId);
}
/**
* 优化2:合理设置超时时间
*/
@Transactional(timeout = 10, rollbackFor = Exception.class)
public void updateOrder(Order order) {
// 10秒超时,防止长时间锁表
orderDao.update(order);
}
/**
* 优化3:减少事务范围
*/
public void createOrder(Order order) {
// 准备数据(不在事务中)
validateOrder(order);
enrichOrderData(order);
// 数据库操作(在事务中)
saveOrderInTransaction(order);
// 后续处理(不在事务中)
sendNotification(order);
}
@Transactional(rollbackFor = Exception.class)
public void saveOrderInTransaction(Order order) {
orderDao.save(order);
}
/**
* 优化4:批量操作
*/
@Transactional(rollbackFor = Exception.class)
public void batchInsert(List<Order> orders) {
// 批量插入比逐条插入更高效
orderDao.batchInsert(orders);
}
/**
* 优化5:使用缓存减少数据库访问
*/
@Transactional(readOnly = true)
public Order findOrderWithCache(Long orderId) {
// 先查缓存
Order cached = cacheService.get("order:" + orderId, Order.class);
if (cached != null) {
return cached;
}
// 缓存未命中,查数据库
Order order = orderDao.findById(orderId);
// 更新缓存
if (order != null) {
cacheService.put("order:" + orderId, order);
}
return order;
}
private void validateOrder(Order order) { }
private void enrichOrderData(Order order) { }
private void sendNotification(Order order) { }
}
六、监控与调试
6.1 事务监控
/**
* 事务监控切面
*/
@Aspect
@Component
@Slf4j
public class TransactionMonitorAspect {
@Around("@annotation(transactional)")
public Object monitorTransaction(ProceedingJoinPoint joinPoint,
Transactional transactional) throws Throwable {
String methodName = joinPoint.getSignature().toShortString();
long startTime = System.currentTimeMillis();
log.info("事务开始: {}, 传播行为: {}, 隔离级别: {}",
methodName,
transactional.propagation(),
transactional.isolation());
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
log.info("事务提交: {}, 耗时: {}ms", methodName, duration);
// 记录慢事务
if (duration > 1000) {
log.warn("慢事务: {}, 耗时: {}ms", methodName, duration);
}
return result;
} catch (Throwable e) {
long duration = System.currentTimeMillis() - startTime;
log.error("事务回滚: {}, 耗时: {}ms, 异常: {}",
methodName, duration, e.getMessage());
throw e;
}
}
}
6.2 事务调试
/**
* 事务调试工具
*/
@Component
@Slf4j
public class TransactionDebugger {
/**
* 检查是否在事务中
*/
public boolean isInTransaction() {
return TransactionSynchronizationManager.isActualTransactionActive();
}
/**
* 获取当前事务名称
*/
public String getCurrentTransactionName() {
return TransactionSynchronizationManager.getCurrentTransactionName();
}
/**
* 获取当前事务隔离级别
*/
public Integer getCurrentTransactionIsolationLevel() {
return TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
}
/**
* 事务是否只读
*/
public boolean isCurrentTransactionReadOnly() {
return TransactionSynchronizationManager.isCurrentTransactionReadOnly();
}
/**
* 打印事务信息
*/
public void printTransactionInfo() {
if (isInTransaction()) {
log.info("当前在事务中");
log.info("事务名称: {}", getCurrentTransactionName());
log.info("隔离级别: {}", getCurrentTransactionIsolationLevel());
log.info("是否只读: {}", isCurrentTransactionReadOnly());
} else {
log.info("当前不在事务中");
}
}
}
/**
* 使用调试工具
*/
@Service
@Slf4j
public class DebugService {
@Autowired
private TransactionDebugger debugger;
@Transactional
public void testTransaction() {
debugger.printTransactionInfo();
// 输出:
// 当前在事务中
// 事务名称: com.example.service.DebugService.testTransaction
// 隔离级别: null (使用默认)
// 是否只读: false
}
}
七、总结
核心知识点回顾
Spring事务核心要点
│
├── 事务基础
│ ├── ACID特性
│ ├── 隔离级别
│ └── 并发问题(脏读、不可重复读、幻读)
│
├── 事务管理
│ ├── 编程式事务
│ └── 声明式事务(@Transactional)
│
├── 传播行为
│ ├── REQUIRED(默认)
│ ├── REQUIRES_NEW
│ ├── NESTED
│ └── 其他(SUPPORTS/NOT_SUPPORTED/MANDATORY/NEVER)
│
├── 实战应用
│ ├── 多表操作事务
│ ├── 分布式事务
│ ├── 批量处理
│ └── 只读优化
│
├── 失效场景
│ ├── 非public方法
│ ├── 同类调用
│ ├── 异常捕获
│ └── 检查异常
│
└── 最佳实践
├── 合理粒度
├── 异常处理
├── 性能优化
└── 监控调试
Spring事务管理是企业级应用开发的核心技能。理解事务的原理和特性,掌握正确的使用方法,避免常见的陷阱,才能开发出健壮、高效的应用系统。