@EntitypublicclassPost{@Id@GeneratedValue(strategy =GenerationType.IDENTITY)privateLong id;privateString title;@Version// JPA가 자동으로 버전 관리privateLong version;// Integer, Long, Short, Timestamp 사용 가능}
-- JPA가 내부적으로 실행하는 UPDATEUPDATE posts
SET title = ?, version =2-- 버전 자동 증가WHERE id =1AND version =1-- 이전 버전 확인
@Service@RequiredArgsConstructorpublicclassPostService{privatefinalPostRepository postRepository;@TransactionalpublicvoidupdateTitle(Long id,String newTitle){Post post = postRepository.findById(id).orElseThrow(()->newResourceNotFoundException("Post not found")); post.setTitle(newTitle);// 커밋 시 버전 충돌이면 OptimisticLockException 발생}}// 컨트롤러에서 처리 (재시도 패턴)@RestControllerAdvicepublicclassGlobalExceptionHandler{@ExceptionHandler(OptimisticLockingFailureException.class)@ResponseStatus(HttpStatus.CONFLICT)publicApiResponsehandleOptimisticLock(OptimisticLockingFailureException e){returnApiResponse.error(409,"다른 사용자가 이미 수정했습니다. 다시 시도해주세요.");}}
// Repository에서 명시적 낙관적 락 설정publicinterfacePostRepositoryextendsJpaRepository<Post,Long>{@Lock(LockModeType.OPTIMISTIC)// 읽기도 버전 체크Optional<Post>findById(Long id);@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)// 읽기 시에도 버전 강제 증가Optional<Post>findWithLock(Long id);}