Concurrency

동시에 동일한 데이터에 접근할 때에 데이터에 대한 접근을 제어하기 위해 Optimistic Locking을 지원한다. 한편 Hibernate의 Native API를 통해서는 지원 가능한 Pessimistic Locking 은 JPA2.0 버전에 정의될 예정이다.

Optimistic Locking

Without Locking Source

@Test
public void testUpdateUserWithoutOptimisticLocking() throws Exception {
 
   // 1. 테스트를 위한 신규 데이터를 입력
   newTransaction();
   addDepartmentUserAtOnce();
   closeTransaction();
 
   // 2. 동일한 식별자를 이용하여 User 정보를 두번 조회
   newTransaction();
   User fstUser = (User) em.find(User.class,"User1");
   User scdUser = (User) em.find(User.class,"User1");
   closeTransaction();
 
   // 3. Detached 상태에서의 변경처리
   fstUser.setUserName("First : Kim");
 
   // 4. 별도의 트랜잭션으로 변경처리
   newTransaction();
   scdUser.setUserName("Second : Kim");
   closeTransaction();
 
   // 5. 3에서 작업한 내용이 반영되어 변경.
   newTransaction();
   em.merge(fstUser);
   closeTransaction();
}

위에서 제시한 로직에 대해 자세히 살펴보자.

  1. #1, #2번 코드에 의해 각각 동일한 식별자를 이용하여 같은 데이터 조회
  2. 두번째 트랜잭션이 종료된 후, #3번 코드에서는 Detached 상태의 fstUser 객체의 userName 변경
  3. 세번째 트랜잭션 내의 #4번 코드에서는 scdUser 객체의 userName 변경, 세번째 트랜잭션 종료시 변경 사항이 DB에 반영
  4. 네번째 트랜잭션 내에서 #3번 코드를 통해 변경된 fstUser 객체에 대해 update 수행
  5. fstUser에 대한 수정 작업 또한 성공적으로 처리

결론적으로 보면, userId가 “User1”인 User의 userName은 “First : Kim”이 되어 앞서 scdUser에서 요청했던 수정 작업은 무시된 것이다. 이러한 현상을 Lost Update라고 하며, 이를 해결하기 위한 방법은 3가지가 있다.

  1. Last Commit Wins : Optimistic Locking 을 수행하지 않게 되면 기본적으로 수행되는 유형으로 2개의 트랜잭션 모두 성공적으로 commit된다. 그러므로 두번째 commit은 첫번째 commit 내용을 덮어쓸 수 있다. (위의 예의 경우)
  2. First Commit Wins : Optimistic Locking을 적용한 유형으로 첫번째 commit만이 성공적으로 이루어지며, 두번째 commit 시에는 Error를 얻게 된다.
  3. Merge : 첫번째 commit만이 성공적으로 이루어지며, 두번째 commit 시에는 Error를 얻게 된다. 그러나 First Commit Wins와는 달리 두번째 commit을 위한 작업을 처음부터 다시 하지 않고 개발자의 선택에 의해 선택적으로 변경될 수 있도록 한다. 가장 좋은 전략이나 변경 사항을 merge 할 수 있는 화면이나 방법을 직접 제공해 줄 수 있어야 한다.(추가 구현 필요함)

JPA에서는 Versioning 기반의 Automatic Optimistic Locking을 통해 First Commit Wins 전략을 취할 수 있도록 지원한다. JPA에서 Optimistic Locking을 수행하기 위해서는 해당 테이블에 Version을 추가해야 한다. 그러한 경우 해당 테이블과 매핑된 객체를 로드할 때 Version 정보도 함께 로드되고 객체 수정시 테이블의 현재 값과 비교하여 처리 여부를 결정하게 된다.

With Optimistic Locking Source

@Test
public void testUpdateDepartmentWithOptimisticLocking() throws Exception {
 
   // 1. 테스트를 위한 신규 데이터를 입력
   newTransaction();
   addDepartmentUserAtOnce();
   closeTransaction();
 
   // 2. Department 정보를 두번 조회
   newTransaction();
   Department fstDepartment = (Department) em.find(Department.class,"Dept1");
   assertEquals("fail to check a version of department.", 0, fstDepartment.getVersion());
   Department scdDepartment = (Department) em.find(Department.class,"Dept1");
   closeTransaction();
 
   // 3. 두번째 조회한 Department 정보에 다른 deptName을 셋팅하여 DB에 반영
   fstDepartment.setDeptName("First : Dept.");
 
   // 4. 첫번째 조회한 Department 정보에 대해 merge() 메소드를 호출
   newTransaction();
   scdDepartment.setDeptName("Second : Dept.");
   closeTransaction();
 
   // 5. 세번째 트랜잭션에서의 수정으로 인해 DEPARTMENT_VERSION이 이미 변경되었기 때문에
   //    StaleObjectStateException 발생이 예상
   newTransaction();
   try {
      em.merge(fstDepartment);
      closeTransaction();
   } catch (Exception e) {
      e.printStackTrace();
      assertTrue("fail to throw StaleObjectStateException.",e instanceof StaleObjectStateException);
   }
}

위와같이 다음의 testUpdateDepartmentWithOptimisticLocking() 메소드를 수행하였을 때 첫번째 수정 작업은 성공적으로 이루어지나 두번째 수정 작업에 대해서는 #6번 코드에서처럼 StaleObjectStateException이 throw될 것이다. 이를 위한 entity 클래스의 설정의 일부분은 다음과 같다.

Entity Class Source

@Entity
@Table(name="DEPARTMENT")
public class Department {
 
   private static final long serialVersionUID = 1L;
 
   @Id
   @Column(name = "DEPT_ID", length = 10)
   private String deptId;
 
   @Version
   @Column(name = "DEPT_VERSION")
   private int version;
...
}

위에서 보는 것 같이 DEPT_VERSION이라는 컬럼을 추가하여 버전관리를 하게 함으로써 Optimistic Locking처리를 할 수 있다.

 
egovframework/rte2/psl/orm/concurrency.txt · 마지막 수정: 2023/12/21 05:21 (외부 편집기)
 
이 위키의 내용은 다음의 라이센스에 따릅니다 :CC Attribution-Noncommercial-Share Alike 3.0 Unported
전자정부 표준프레임워크 라이센스(바로가기)

전자정부 표준프레임워크 활용의 안정성 보장을 위해 위험성을 지속적으로 모니터링하고 있으나, 오픈소스의 특성상 문제가 발생할 수 있습니다.
전자정부 표준프레임워크는 Apache 2.0 라이선스를 따르고 있는 오픈소스 프로그램입니다. Apache 2.0 라이선스에 따라 표준프레임워크를 활용하여 발생된 업무중단, 컴퓨터 고장 또는 오동작으로 인한 손해 등에 대해서 책임이 없습니다.
Recent changes RSS feed CC Attribution-Noncommercial-Share Alike 3.0 Unported Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki