You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 21:20:53