在复杂的系统设计中,我们有时会遇到数据库循环依赖的问题,这通常是一个棘手的挑战。
情景:当我们购买时商品时我们需要形成对应的订单以及物流,生成订单需要物流Id,但是此时物流还没形成,同时生先成物流也会出现这个问题
解决方法:
1. 延迟设置外键
在初始创建订单时,我们可以暂时不设置物流ID(作为外键)。一旦物流信息被插入,我们再更新这个外键。
//形成订单
Order order = Order.builder()
.status("已支付")
.orderTime(LocalDateTime.now())
.userId(userId)
.productId(productId)
.build();
userMapper.purchaseProduct(order);
2. 利用返回的订单ID生成物流信息
这里有一个小tips:当我们成功插入数据时(Insert),开启 useGeneratedKeys 后,MyBatis会自动将数据库生成的ID值回填到相应的Java对象中,在这个例子中就是order。值得注意是对象的实体类有一个orderId 属性,并且这个属性有相应的getter和setter方法。
useGeneratedKeys="true" keyProperty="orderId">
我们就可以用order调用get方法获取orderId了
//生成物流信息
String address = userMapper.getUserAddress(order.getUserId());
Logistic logistics = Logistic.builder()
.orderId(order.getOrderId())
.address(address)
.logisticsCompany("顺丰")
.status("待发货")
.build();
// 插入新的物流信息
userMapper.insertLogisticInfo(logistics);
3. 更新订单的物流ID
order.setLogisticId(logistics.getLogisticId()); // 设置新的物流信息ID
userMapper.updateOrderLogisticId(order); // 更新订单信息
这样,我们已经成功地同时生成了订单和物流信息,而没有陷入循环依赖的问题。
体会
尽管上述方法是一个有效的解决方案,但它同时揭示了设计上的一个问题。在设计数据库时,我们应该尽量避免物理外键导致的循环依赖,而考虑在应用逻辑中实现所谓的“逻辑外键”,这意味着,我们在代码中维护外键关系,但在数据库中不实施外键约束。