Đây là bài toán lost update, xử lý bằng optimistic locking với @Version — phù hợp vì đọc nhiều hơn ghi và người dùng giữ dữ liệu trên form khá lâu (entity ở trạng thái detached), không thể giữ lock DB suốt thời gian đó.
@Entity
public class Order {
@Id @GeneratedValue
private Long id;
@Version
private Long version;
// ...
}Cơ chế: mỗi UPDATE Hibernate sinh ra đều kèm điều kiện version và tự tăng nó:
update orders set status = ?, version = 6 where id = ? and version = 5Nếu người khác đã ghi trước, version trong DB không còn là 5 → 0 dòng bị ảnh hưởng → JPA ném OptimisticLockException, Spring bọc thành ObjectOptimisticLockingFailureException.
Luồng đầy đủ ở tầng ứng dụng:
- API trả version hiện tại về client cùng dữ liệu đơn; client gửi lại version đó khi lưu.
- Bắt ObjectOptimisticLockingFailureException → trả HTTP 409 Conflict kèm dữ liệu mới nhất để người dùng xem thay đổi rồi quyết định.
- Với thao tác chạy nền, tự động nạp lại entity trong transaction mới và thử lại 2-3 lần thay vì báo lỗi.
Chỉ chuyển sang pessimistic lock (SELECT ... FOR UPDATE) khi tranh chấp thực sự cao và transaction ngắn, vì nó giữ lock ở DB và có nguy cơ deadlock.