트랜잭션은 다음을 보장해야 한다.
트랜잭션 간에 격리성을 완벽히 보장하려면 트랜잭션을 거의 차례대로 실행해야 한다. 이는 동시성 처리 성능이 매우 나빠지게 하므로 ANSI 표준은 트랜잭션의 격리 수준을 4단계로 나누어 정의한다.
위에서부터 아래로 갈수록 격리 수준이 노아지며, 격리 수준이 낮을수록 동시성은 증가하지만 격리 수준에 따라 다음과 같이 다양한 문제가 발생할 수 있다.
낙관적 락은 트랜잭션 대부분은 충돌이 발생하지 않는다고 가정하는 방법이다. 이것은 데이터베이스가 제공하는 락 기능을 사용하는 것이 아니라 JPA가 제공하는 버전 관리 기능을 사용한다.
낙관적 락은 트랜잭션을 커밋하기 전까지는 트랜잭션의 충돌을 알 수 없다.
비관적 락은 트랜잭션의 충돌이 발생한다고 가정하고 우선 락을 거는 방법이다. 이것은 데이터베이스가 제공하는 락 기능을 사용한다.
+ 두 번의 갱신 분실 문제(second lost updates problem) : 먼저 완료한 수정사항이 사라지고 나중에 완료한 수정사항만 남게 되는 문제
이를 해결하기 위한 방법은 3가지가 있다.
락은 다음 위치에 적용할 수 있다.
EntityManager.lock()
, EntityManager.find()
, EntityManager.refresh()
Query.setLockMode()
(TypeQuery 포함)JPA 사용 시 추천 전략은 READ COMMITTED 트랜잭션 격리 수준 + 낙관적 버전 관리다. -> 두 번의 갱신 내역 분실 문제 예방
비관적 락을 사용하면 락을 획득할 때까지 트랜잭션이 대기한다. 무한정 기다릴 수 없으므로 타임아웃 시간을 줄 수 있다.
타임아웃은 데이터베이스 특성에 따라 동작하지 않을 수 있다.
영속성 컨텍스트 내부에는 엔터티를 보관하는 저장소가 있는데 이것을 1차 캐시라 한다. 일반적인 웹 애플리케이션 환경은 트랜잭션을 시작하고 종료할 때까지만 1차 캐시가 유효하다.
하이버네이트를 포함한 대부분의 JPA 구현체들은 애플리케이션 범위의 캐시를 지원하는데 이것을 공유 캐시 또는 2차 캐시라 한다.