낙관적 락이란?
두번의 갱신 분실 문제(Second Lost Updates Problem)
A,B사용자 화면에서 모두 오류가 나지 않았기 때문에 A입장에서는 X=10으로 변경되었겠지…라고 생각함. A 입장에서는 오류라고 판단될 수 있음
고려할 수 있는 방법
⇒ JPA에서는 @Version 을 사용해서 “먼저 Commit된 것을 DB에 반영함” 전략을 구현할 수 있다.
@Version
@Entity
public class Board {
@Id
private String id;
private String title;
@Version
private Integer version;
// 엔티티를 수정할 때마다 version필드값이 자동으로 증가
// 엔티티를 수정할 때 조회 시점의 version필드값과 수정 시점의 version필드값이 다르면 예외를 발생시킴
}
낙관적 락 사용하기
NONE
OPTIMISTIC
엔티티를 조회만 할때에도 버전을 체크
Commit된 Transaction만 읽기 때문에 두번의 갱신 분실 문제 뿐 아니라, Dirty Read와 Non-repeatable read를 방지함
사용 예시(@NamedQuery)
@Lock(LockModeType.OPTIMISTIC)
public CustomerEntity findFirstById(int id);
테스트 시나리오(테스트 코드는 맨 아래 git url 참고)
스레드1(이름을 홍길동으로 변경하는 코드가 1초동안 실행됨)
스레드2(스레드 시작과 끝에 엔티티를 조회하는 코드가 3초동안 실행됨)
OPTIMISTIC_FORCE_INCREMENT
낙관적 락을 사용하면서 버전 정보를 강제로 증가시킴
강제로 버전을 증가시켜서 논리적 단위의 엔티티 묶음을 버전 관리할 수 있음
사용 예시(@NamedQuery)
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
public CustomerEntity findFirstById(int id);
PESSIMISTIC_WRITE
다른 트랜잭션에서 읽기도 못하고 쓰기도 못함
사용예시(@NamedQuery)
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select c from CustomerEntity c where c.id = :id")
public CustomerEntity findByIdWithPessimistic(int id);
테스트 시나리오(테스트 코드는 맨 아래 git url 참고)
동시에 마일리지 -1
PESSIMISTIC_READ
다른 트랜잭션에서 읽기만 가능
일반적으로 사용안함(데이터베이스 대부분은 방언에 의해서 PESSIMISTIC_WRITE로 동작하기 때문에)
사용예시(@NamedQuery)
@Lock(LockModeType.PESSIMISTIC_READ)
@Query("select c from CustomerEntity c where c.id = :id")
public CustomerEntity findByIdWithPessimistic(int id);
PESSIMISTIC_FORCE_INCREMENT
다른 트랜잭션에서 읽기도 못하고 쓰기도 못함 + 비관적 락이지만 버전정보를 사용해서 엔티티의 버전관리를 함
사용예시(@NamedQuery)
@Lock(LockModeType.PESSIMISTIC_FORCE_INCREMENT)
@Query("select c from CustomerEntity c where c.id = :id")
public CustomerEntity findByIdWithPessimistic(int id);
테스트 시나리오(테스트 코드는 맨 아래 git url 참고)