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));
}
}事务失效场景
| 场景 | 原因 | 解决 |
|---|---|---|
| 方法非 public | AOP 代理不拦截 | 改为 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) { ... }
}