Skip to content

Service 编排与事务边界

Service 层负责业务编排和事务管理。你需要理解 @Transactional 的正确用法和 Service 间调用的限制。

Service 定义

java
// 接口
public interface OrderService {
    OrderVO create(OrderDTO dto);
    OrderVO update(OrderDTO dto);
    PageResult<OrderVO> page(OrderQueryDTO query, Integer pageNum, Integer pageSize);
}

// 实现
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderVO create(OrderDTO dto) {
        OrderEntity entity = new OrderEntity();
        BeanUtils.copyProperties(dto, entity);
        orderMapper.insert(entity);
        return toVO(entity);
    }
}

事务边界

@Transactional 基本用法

java
@Transactional(rollbackFor = Exception.class)
public void createOrder(OrderDTO dto) {
    // 1. 创建订单
    orderMapper.insert(order);

    // 2. 扣减库存
    productMapper.updateStock(productId, -quantity);

    // 3. 记录流水
    transactionMapper.insert(record);
    // 任何步骤异常,全部回滚
}

事务传播行为

传播行为说明使用场景
REQUIRED(默认)有事务加入,无事务新建大部分场景
REQUIRES_NEW总是新建事务独立日志记录
NESTED嵌套事务,可部分回滚子操作可独立失败
java
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(String content) {
    // 独立事务,不受外层事务影响
    logMapper.insert(log);
}

避免大事务

大事务会占用数据库连接时间过长,影响并发性能。

java
// 禁止:大事务包含远程调用
@Transactional(rollbackFor = Exception.class)
public void processOrder(Long orderId) {
    OrderEntity order = orderMapper.selectById(orderId);
    // 远程调用(耗时长,不应在事务中)
    String result = httpClient.callExternalApi(order);
    orderMapper.update(order);
}

// 正确:拆分事务范围
public void processOrder(Long orderId) {
    OrderEntity order = orderMapper.selectById(orderId);
    String result = httpClient.callExternalApi(order);
    updateOrder(order, result);  // 事务在此方法内
}

@Transactional(rollbackFor = Exception.class)
public void updateOrder(OrderEntity order, String result) {
    order.setResult(result);
    orderMapper.updateById(order);
}

Service 间调用

禁止循环依赖

java
// 禁止:OrderService 和 ProductService 互相注入
@Service
public class OrderService {
    @Autowired private ProductService productService;
}

@Service
public class ProductService {
    @Autowired private OrderService orderService; // 循环依赖!
}

正确做法:提到 Controller 层

java
@RestController
public class OrderController {
    @Autowired private OrderService orderService;
    @Autowired private ProductService productService;

    @PostMapping("/order")
    public RespInfo create(@RequestBody OrderDTO dto) {
        productService.checkStock(dto.getProductId());
        return RespInfo.success(orderService.create(dto));
    }
}

事务失效场景

场景原因解决
方法非 publicAOP 代理不拦截改为 public
同类内调用不走代理拆到另一个 Service
catch 吞异常事务不知道出错catch 后 rethrow
数据库不支持引擎不支持事务使用 InnoDB
java
// 禁止:同类内调用,事务失效
@Service
public class OrderService {
    public void batchProcess(List<Long> ids) {
        for (Long id : ids) {
            this.process(id); // 不走代理,事务失效
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void process(Long id) { ... }
}

Forge Admin — 基于 Vue3 + Spring Boot 的企业级后台管理框架