Spring Boot订单创建触发ObjectOptimisticLockingFailureException问题求助
ObjectOptimisticLockingFailureException 在创建Spring Boot订单时持续触发
开发Spring Boot应用时,调用createOrder方法创建订单,每次执行都会抛出ObjectOptimisticLockingFailureException异常。已通过@Version实现乐观锁,且方法标注了@Transactional保证事务一致性。
相关代码
OrderService 代码
public class OrderService { OrderRepository orderRepository; ItemRepository itemRepository; UserRepository userRepository; private OrderView mapToOrderView(Order order) { return OrderView.builder().orderId(order.getOrderId()) .userId(order.getUserId()) .createdTime(order.getCreatedTime()) .updatedTime(order.getUpdatedTime()) .completedTime(order.getCompletedTime()) .orderItems(order.getOrderItems()) .deliveryMethod(order.getDeliveryMethod()) .orderStatus(order.getOrderStatus()) .additionalInstructions(order.getAdditionalInstructions()) .rating(order.getRating()) .feedback(order.getFeedback()) .build(); } @Transactional public OrderView createOrder(PostOrderRequest request) { Order order = Order.builder() .orderId(UUID.randomUUID()) .userId(request.getUserId()) .createdTime(LocalDateTime.now()) .deliveryMethod(request.getDeliveryMethod()) .orderStatus(OrderStatus.PLACED) .build(); List<OrderItem> orderItems = mapOrderItems(request.getOrderItems(), order.getOrderId()); order.setOrderItems(orderItems); order.setUpdatedTime(LocalDateTime.now()); orderRepository.save(order); return mapToOrderView(order); } private List<OrderItem> mapOrderItems(List<OrderItem> itemRequests, UUID orderId) { if (itemRequests == null || itemRequests.isEmpty()) { throw new IllegalArgumentException("Order must contain at least one item."); } List<OrderItem> orderItems = new ArrayList<>(); for (OrderItem itemRequest : itemRequests) { Optional<Item> item = itemRepository.findById(itemRequest.getItemId()); if (item.isEmpty()) { throw new IllegalArgumentException("Item not available: " + itemRequest.getItemId()); } orderItems.add(OrderItem.builder() .orderItemId(UUID.randomUUID()) .itemId(item.get().getItemId()) .quantity(itemRequest.getQuantity()) .build()); } return orderItems; } }
Order 实体类代码
@Data @Builder @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "orders") public class Order { @Id @GeneratedValue(strategy = GenerationType.AUTO) private UUID orderId; private UUID userId; @Column(updatable = false, insertable = false) private LocalDateTime createdTime; private LocalDateTime updatedTime; private LocalDateTime completedTime; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true) private List<OrderItem> orderItems; private DeliveryMethod deliveryMethod; private OrderStatus orderStatus; @OneToOne @JoinColumn(name = "payment_id") private Payment payment; private String additionalInstructions; private int rating; private String feedback; @Version private int version; }
错误日志
org.springframework.orm.ObjectOptimisticLockingFailureException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [order.mgt.core.model.Order#be58d23c-010b-4f3c-9917-f9aa0df95c4f]The resource you are trying to update has been modified by another transaction.
Hibernate 日志
Hibernate: select u1_0.user_id, u1_0.country, u1_0.county, u1_0.number, u1_0.post_code, u1_0.street_address, u1_0.street_address2, u1_0.town, u1_0.email, u1_0.phone_number, u1_0.created_date, u1_0.name from user u1_0 where u1_0.user_id=? Hibernate: select i1_0.item_id, i1_0.category, i1_0.description, i1_0.image, i1_0.is_available, i1_0.name, i1_0.tag from item i1_0 where i1_0.item_id=? Hibernate: select o1_0.order_id, o1_0.additional_instructions, o1_0.completed_time, o1_0.created_time, o1_0.delivery_method, o1_0.feedback, o1_0.order_status, p1_0.paymentid, p1_0.amount, p1_0.currency, p1_0.order_id, p1_0.status, p1_0.type, o1_0.rating, o1_0.updated_time, o1_0.user_id, o1_0.version, oi1_0.order_id, oi1_0.order_item_id, c1_0.customization_id, c1_0.customization_option, c1_0.price_change, i1_0.item_id, i1_0.category, i1_0.description, i1_0.image, i1_0.is_available, i1_0.name, i1_0.tag, oi1_0.item_id, oi1_0.portion_size, oi1_0.quantity from orders o1_0 left join payment p1_0 on p1_0.paymentid=o1_0.payment_id left join order_item oi1_0 on o1_0.order_id=oi1_0.order_id left join customization c1_0 on c1_0.customization_id=oi1_0.customization_id left join item i1_0 on i1_0.item_id=oi1_0.item_id2 where o1_0.order_id=? Hibernate: select o1_0.order_id, o1_0.additional_instructions, o1_0.completed_time, o1_0.created_time, o1_0.delivery_method, o1_0.feedback, o1_0.order_status, p1_0.paymentid, p1_0.amount, p1_0.currency, p1_0.order_id, p1_0.status, p1_0.type, o1_0.rating, o1_0.updated_time, o1_0.user_id, o1_0.version from orders o1_0 left join payment p1_0 on p1_0.paymentid=o1_0.payment_id where o1_0.order_id=?
解决建议
1. 修复OrderItem与Order的关联关系
Order实体的@OneToMany(mappedBy = "order")表明OrderItem是关系的维护端,必须显式设置OrderItem的order属性指向对应的Order。当前代码创建OrderItem时未关联Order,导致Hibernate在级联保存时执行额外的查询和更新操作,触发乐观锁冲突。
修改mapOrderItems方法,传入Order对象而非orderId,创建OrderItem时设置关联:
private List<OrderItem> mapOrderItems(List<OrderItem> itemRequests, Order order) { if (itemRequests == null || itemRequests.isEmpty()) { throw new IllegalArgumentException("Order must contain at least one item."); } List<OrderItem> orderItems = new ArrayList<>(); for (OrderItem itemRequest : itemRequests) { Optional<Item> item = itemRepository.findById(itemRequest.getItemId()); if (item.isEmpty()) { throw new IllegalArgumentException("Item not available: " + itemRequest.getItemId()); } orderItems.add(OrderItem.builder() .orderItemId(UUID.randomUUID()) .itemId(item.get().getItemId()) .quantity(itemRequest.getQuantity()) .order(order) // 新增:关联当前Order .build()); } return orderItems; }
同时在createOrder方法中调用时传入Order对象:
List<OrderItem> orderItems = mapOrderItems(request.getOrderItems(), order);
2. 确保@Version字段正确初始化
Order实体的version字段是乐观锁的核心,需保证创建时初始化为0:
- 在@Builder中添加默认值:
@Builder public Order(..., @Builder.Default int version = 0) { // 构造逻辑 } - 或者在数据库表中设置
version字段的默认值为0,避免版本号不一致。
3. 修正createdTime字段的映射
当前createdTime标注了@Column(updatable = false, insertable = false),但代码中尝试设置该值,这是无效的。需二选一:
- 如果希望数据库自动生成创建时间:保留
insertable=false, updatable=false,并在数据库表中设置created_time字段的默认值为CURRENT_TIMESTAMP。 - 如果希望代码控制创建时间:移除
insertable=false,让Hibernate插入时写入该值。
4. 排查额外的实体修改逻辑
检查是否存在以下逻辑在事务中修改Order实体:
- Spring Data JPA的Auditing监听器(如
@CreatedDate、@LastModifiedDate) - 自定义切面或拦截器
- 其他关联实体的级联操作
这些逻辑可能在save前后修改Order,导致版本号更新,触发乐观锁异常。
内容的提问来源于stack exchange,提问作者Sarangan
相关产品推荐
相关产品推荐

