낙천적인 동시접근 제어
특징
- 모든 게 다 잘 될거라고 가정을 하는 접근법이다.
- unit of work의 마지막에 데이터를 쓰려고 할 때 에러를 발생시킨다.
낙천적인 전략 이해하기
- 두 트랜잭션 모두 read commited는 기본이니까 dirty read는 허용하지 않는다.
- 하지만 non repeatable read는 가능하다. 그리고 둘 중 하나의 update가 분실 될 수도 있다.
- lost updat를 처리할 수 있는 세 가지 방법
- Last commit wins - 마지막에 커밋하는 녀석이 앞선 변경 사항을 덮어쓴다. 에러 메시지 없다.
- First commit wins - 두 번째로 같은 데이터에 커밋하는 녀석이 있을 때 예외를 던진다. 사용자는 새로운 컨버세이션을 시작여 새로운 데이터를 가져와서 다시 작업해야 한다.
- 충돌하는 업데이트 병합하기 - 두 번째로 같은 데이터에 커밋하려는 녀석이 있을 때 예외를 던지고 사용자가 선택적으로 무조건 덮어 쓸 수 도 있고 다시 시작할 수도 있도록 한다.
- optimistic concurrency control이 없거나 기본값인 상태에서는 무조건 last commit wins 전략으로 동작한다. 비추다.
- 하이버네이트와 JP는 automatic optimistic locking을 사용하여 first commit wins 전략을 사용하게 해준다.
- Merge conflicting changes가 가장 사용자 입장에서 좋은데, 이런 창을 띄우고 메시지를 출력하는 것은 개발자의 몫이다.
하이버네이트에서 Versioning 하기
- 각각의 Entity들은 version을 가지고 있고 숫자나 타입스탬프로 표현할 수 있다.
- 하이버네이트는 이 version을 Entity가 수정될 때마다 버전을 증가 시키고 만약 충돌이 발견되면 예외를 던진다.
- 이 version 속성을 Entity에 추가해준다.
public class Item {
...
private int version;
...
}
- XML에서 설정할 때 이 속성에 대한 맵핑은 반드시 id 속성 설정 바로 다음에 위치해야 한다.
- 세터를 사용하지 말고 필드에 직접 쓰도록 설정하고 세터를 만들어 두지 않는게 좋다. 하이버만 쓸 수 있도록...
public class Item {
...
private Date lastUpdated;
...
}
- Timestamp를 사용할 수도 있는데 이건 약간 덜 안전하다. 비추한다.
비추하는 이유
Clustered 환경에서 JVM으로부터 현재 시간을 가져오는 건 위험하다. jvm이 두 개니까 둘이 같을 수도 있겠지. |
자동 버전 관리
자동 버전 관리 동작 원리
두 개의 트랜잭션이 같은 데이터에 가져온다. 이때 버전 넘버를 확인한다. 1이라고 하자. 그뒤에 Update문이 실행 될 |
- 언제 Entity의 버전을 올리는가? Entity 객체가 가지고 있는 속성들이 dirty 할 때에만 올린다.
버전 넘버나 Timestamp 없이 버전 관리하기
- 레거시 DB나 기존의 자바 클래스를 사용하고 있어서 버전 컬럼을 추가할 수 없어도, 하이버네이트는 자동 버전 관리를 할 수 있다.
- 단, 객체를 가져오고 수정하는 Persistent Context(Session)가 같아야 한다. 따라서
Detached 객체를 가지고 Conversation을 구현할 때에는 자동 버전 관리가 불가능하다. 그 때는 버전 넘버나
Timestamp가 필요하다.
- 버전 컬럼 대신에 모든 컬럼을 비교한다. optimistic-lock="dirty" 라고 설정하면 dirty
상태의 속성만 비교한다. 이때에는 update문을 dirty한 컬럼으로만 생성해야 하니까 dynamicUpdate를 true로
설정해야 한다.
Java Persistent 사용하여 버전 관리하기
- Java Persistent는 동시 접근을 Versioning을 사용하여 낙천적으로 관리한다고 가정한다.
@Entity
public class Item {
...
@Version
@Column(name = "OBJ_VERSION")
private int version;
...
}
- JPA는 버전 속성 없이 optimistic versioning을 못하니까 하이버 애노테이션을 사용해야 한다.
@Entity
@org.hibernate.annotations.Entity(
optimisticLock = org.hibernate.annotations.OptimisticLockType.ALL
)
public class Item {
...
}
- 락을 OptimisticLockType.DIRTY로 설정할 수 있다. 그럴 때는 dynamicUpdate 속성의 값도 true로 설정해야 줘야 한다.