Java Persistence API(비공식적으로 JPA라고도 함)는 자바 EE 및 자바 SE 애플리케이션을 위해 POJO(Plain Old Java Object) 기반의 지속성 모델을 제공합니다. 이것은 관계형 데이터가 자바 객체에 매핑되는 방식에 대한 모든 세부사항을 다루며 객체/관계 매핑(O/R)을 표준화합니다. JPA의 최신 업데이트인 Java Persistence 2.0은 추가적인 O/R 매핑 기능과 새로운 쿼리 언어 기능과 같은 다수의 새로운 기능이 추가되어 있습니다. JPA 2.0에서 강화된 또 다른 영역은 잠금과 동시성입니다.
이 테크팁에서는 JPA 2.0의 새로운 잠금 및 동시성 기능을 강조하고 이런 기능을 보여주는 애플리케이션을 제공합니다.
잠금과 동시성
잠금은 데이터베이스의 트랜잭션들이 동시성(concurrency)을 지니면서 처리되도록 하는 기법입니다. 둘 이상의 데이터베이스 트랜잭션이 같은 데이터에 동시에 액세스할 때, 잠금은 한 번에 한 트랜잭션만 데이터를 변경할 수 있도록 하기 위해 사용됩니다.
일반적으로, 낙관적 잠금(Optimistic locking)과 비관적 잠금(Pessimistic locking)이라는 두 가지 잠금 접근 방식이 있습니다. 낙관적 잠금에서는 동시 트랜잭션 사이에 충돌이 자주가 아닌 가끔 발생할 것으로 가정합니다. 즉, 이들 트랜잭션이 동시에 같은 데이터를 읽고 변경하려는 시도를 자주 하지는 않을 것이라고 가정을 하는 것입니다. 낙관적 잠금의 목표는 동시 트랜잭션이 동시에 처리될 수 있도록 충분한 자유를 보장하는 한편 충돌을 감지하고 예방하는 것입니다. 두 트랜잭션은 같은 데이터에 동시에 액세스할 수 있습니다. 하지만, 충돌을 예방하기 위해 데이터를 마지막으로 읽은 이후 데이터에 발생한 모든 변경 사항을 검색하기 위한 확인 작업이 이루어집니다.
비관적 잠금에서는 트랜잭션이 자주 충돌을 일으킬 것으로 가정합니다. 비관적 잠금에서는 트랜잭션이 데이터를 읽게 되면 읽음과 동시에 그 데이터를 잠급니다. 첫 번째 트랜잭션이 읽기를 커밋할 때까지는 다른 트랜잭션이 그 데이터를 변경할 수 없습니다.
낙관적 잠금은 동시 트랜잭션이 충돌하지 않는 애플리케이션에 가장 적합합니다. 비관적 잠금은 동시 트랜잭션이 충돌하는 애플리케이션에 가장 적합합니다.
JPA를 사용하면 엔티티를 잠글 수 있습니다. 이를 통해 어떤 엔티티에 대해 사용할 잠금의 종류, 잠금 시점 그리고 잠금 위치를 제어할 수 있습니다. JPA에서는 엔티티가 경량의 지속성 도메인 객체임을 명심해야 합니다. 일반적으로, 엔티티는 관계형 데이터베이스의 테이블을 나타내고, 각각의 엔티티 인스턴스는 그 테이블의 한 행에 해당합니다.
JPA 1.0에서의 잠금 지원
JPA 1.0에서는 낙관적 읽기 또는 낙관적 쓰기 잠금만을 지원합니다. 이러한 지원에서는 어떤 트랜잭션이라도 엔티티를 읽고 업데이트할 수 있습니다. 하지만, 트랜잭션이 커밋될 때 JPA는 엔티티의 version 속성을 확인하여 해당 엔티티를 마지막으로 읽은 후 업데이트되었는지 판단합니다. 엔티티를 마지막으로 읽은 이후로 version 속성이 업데이트된 경우 JPA는 예외를 스로우(throw)합니다. 이 접근 방식의 이점은 어떠한 데이터베이스 잠금도 유지되지 않는다는 점입니다. 따라서 비관적 잠금보다 확장성이 훨씬 뛰어납니다. 이 접근 방식의 단점은 사용자나 애플리케이션이 실패한 업데이트를 새로 고치고 다시 시도해야 한다는 점입니다.
버전이 지정된 엔티티는 다음 코드에서 보듯이 @Version 주석으로 표시됩니다.
그리고 해당하는 데이터베이스 스키마에는 다음 SQL 문으로 작성되는 것과 같은 버전 열이 있습니다.
version 속성은 int, short, long 또는 timestamp가 될 수 있습니다. 이 속성은 트랜잭션이 정상적으로 커밋될 때 증가됩니다. 따라서 다음과 같은 SQL 연산이 이루어집니다.
그림 1은 낙관적 잠금을 보여줍니다.
![]() |
| 그림 1. 낙관적 잠금 |
여기서는 두 개의 동시 트랜잭션이 파트 p 업데이트를 시도합니다. Transaction 1이 먼저 커밋됩니다. 이에 대해 JPA가 p 엔티티에 대한 version 속성을 증가시킵니다. Transaction 2가 커밋되면 p 엔티티에 대한 version 속성이 Transaction 2가 p 엔티티를 마지막으로 읽었을 때보다 높기 때문에 JPA는 OptimisticLockException을 스로우(throw)합니다. 결과적으로, Transaction 2가 롤백됩니다.
잠금 모드를 지정하면 JPA가 버전이 지정된 엔티티에 대한 잠금을 관리하는 방식을 더욱 세밀하게 제어할 수 있습니다. EntityManager 클래스의 lock() 메소드를 통해 제어합니다. 메소드 서명은 다음과 같습니다.
첫 번째 메소드 매개변수는 이 트랜잭션을 통해 잠궈야 하는 엔티티 인스턴스를 나타냅니다. 두 번째 메소드 매개 변수는 잠금 모드입니다.
JPA 1.0에서는 잠금 모드 값이 다음 중 하나만 될 수 있었습니다.
READ. 이 경우에는 JPA 엔티티 관리자가 이전에 설명한 것처럼 낙관적 잠금 작업을 수행합니다. JPA 엔티티 관리자는 엔티티를 잠그고, 트랜잭션이 커밋되기 전에 엔티티의 버전 속성을 확인하여 해당 엔티티를 마지막으로 읽은 후 업데이트되었는지 판단합니다. 버전 속성이 업데이트된 경우 엔티티 관리자는OptimisticLockException을 스로우(throw)하고 트랜잭션을 롤백합니다.WRITE. 이 경우에는 엔티티 관리자가 READ 잠금 모드의 경우와 동일한 낙관적 잠금 작업을 수행합니다. 하지만, 엔티티의 버전 열을 업데이트하기도 합니다.
JPA 2.0의 추가 잠금 지원
JPA 2.0에는 5가지 새로운 잠금 모드가 추가됩니다. 이들 중 두 가지는 낙관적 잠금에 사용됩니다. JPA 2.0에서는 비관적 잠금에 대한 지원도 추가되고 비관적 잠금에 대한 3가지 잠금 모드를 제공합니다. 새로 추가된 2가지 낙관적 잠금 모드는 다음과 같습니다.
OPTIMISTIC. 이것은READ잠금 모드와 동일합니다.READ잠금 모드는 JPA 2.0에서 계속 지원되지만, 새로운 애플리케이션에 대해OPTIMISTIC을 지정하는 것이 좋습니다.OPTIMISTIC_FORCE_INCREMENT. 이것은WRITE잠금 모드와 동일합니다.WRITE잠금 모드는 JPA 2.0에서 계속 지원되지만, 새로운 애플리케이션에 대해OPTIMISTIC_FORCE_INCREMENT를 지정하는 것이 좋습니다.
새로 추가된 3가지 비관적 잠금 모드는 다음과 같습니다.
PESSIMISTIC_READ. 엔티티 관리자는 트랜잭션이 엔티티를 읽자마자 바로 그 엔티티를 잠급니다. 트랜잭션이 완료될 때까지 잠금 상태가 계속 유지됩니다. 이 잠금 모드는 반복 가능한 읽기 의미(repeatable-read semantics)를 사용하여 데이터를 쿼리하려고 할 때 사용됩니다. 다시 말해, 연속적인 읽기 작업 간에 데이터가 업데이트되지 않도록 하는 것입니다. 이 잠금 모드에서는 다른 트랜잭션이 데이터를 읽는 작업을 차단하지 않습니다.PESSIMISTIC_WRITE. 엔티티 관리자는 트랜잭션이 엔티티를 업데이트하자마자 바로 그 엔티티를 잠급니다. 이 잠금 모드에서는 해당 엔티티 데이터를 업데이트하려는 트랜잭션들이 강제로 직렬화(serialization)됩니다. 동시에 업데이트하는 트랜잭션 사이에서 업데이트 실패 가능성이 높을 때 이 잠금 모드가 종종 사용됩니다.PESSIMISTIC_FORCE_INCREMENT. 엔티티 관리자는 트랜잭션이 엔티티를 읽을 때 그 엔티티를 잠급니다. 또한 엔티티가 수정되지 않았다 하더라도, 트랜잭션이 종료되면 엔티티의version속성도 증가시킵니다.
JPA 2.0에서는 엔티티에 대한 잠금 모드를 지정할 수 있는 여러 가지 방법이 있습니다. EntityManager의 lock() 및 find() 메소드에서 잠금 모드를 지정할 수 있습니다. 뿐만 아니라, EntityManager.refresh() 메소드를 호출하면 데이터베이스에서 엔티티 인스턴스의 상태를 새로 고치고 엔티티의 잠금 모드를 기준으로 엔티티를 잠급니다.
Query 인터페이스의 setLockMode() 메소드를 통해 쿼리에 대한 잠금 모드를 설정할 수도 있습니다. 그리고 @NamedQuery 주석의 setLockMode 요소를 통해 명명된 쿼리에서 반환하는 결과에 대해 잠금 모드를 지정할 수 있습니다.
JPA 2.0에서 지원되는 새로운 잠금 모드에 대해 몇 가지 예를 살펴봅시다.
OPTIMISTIC 잠금 모드
OPTIMISTIC 잠금 모드에 대한 전형적인 사용 사례는, 예컨대 두 엔티티 사이에 어떤 관계가 있을 때 일관성을 보장하기 위해 한 엔티티가 하나 이상의 엔티티에 대해 고유의 종속성을 가지는 경우입니다. 그림 2에 표시된 예에서, 왼쪽 업데이트의 Transaction 1은 파트 p1에 대한 가격을 업데이트합니다. 이에 따라 p1의 version 속성이 증가됩니다. 오른쪽의 Transaction 2는 사용자 u1에 대한 입찰을 제출합니다. 파트 가격이 사용자의 현재 입찰가보다 낮으면 Transaction 2가 입찰가를 증가시킵니다.
![]() |
그림 2. OPTIMISTIC 잠금 모드 사용 |
이 시나리오에서는 Transaction T2가 가격을 읽은 후 Transaction T1이 해당 파트의 가격을 변경하는 경우 Transaction 2가 커밋되지 않도록 하려 합니다. 따라서 OPTIMISTIC 잠금 모드를 선택하는 것이 좋습니다.
Transaction 2를 커밋하기 전, 엔티티 관리자는 p1 엔티티에 대한 version 속성을 확인합니다. p1 version 속성은 p1을 마지막으로 읽었을 때보다 높으므로, 엔티티 관리자는 OptimisticLockException을 스로우(throw)하고 Transaction2를 롤백합니다. u1의 version 속성 업데이트 여부를 확인할 때는 예외가 스로우(throw)되지 않습니다. 그것은 Transaction 1이 p1의 version 속성을 업데이트하기 때문이며, 따라서 u1의 version 속성을 증가시키지 않습니다.
OPTIMISTIC_FORCE_INCREMENT 잠금 모드
OPTIMISTIC_FORCE_INCREMENT 잠금 모드에서는 다른 트랜잭션이 이미 잠긴 엔티티를 수정하려 하는 경우 낙관적 잠금 실패로 이어집니다. 이 잠금 모드의 일반적인 용도는 어떤 관계가 있는 엔티티들 사이의 일관성을 보장하기 위한 것입니다.
그림 3은 OPTIMISTIC_FORCE_INCREMENT 잠금 모드의 예를 나타낸 것입니다.
![]() |
그림 3. OPTIMISTIC_FORCE_INCREMENT 잠금 모드 사용 |
오른쪽의 Transaction 2는 파트 p1의 가격이 트랜잭션 중에 바뀌지 않도록 하기 위한 것이므로, 다음과 같이 p1 엔티티를 잠급니다.
그런 다음 Transaction 2는 em.flush()를 호출하는데, 이럴 경우 데이터베이스에서 p1의 version 속성이 증가됩니다. 이와 함께 p1을 업데이트하려 하면 OptimisticLockException이 스로우(throw)되면서 롤백됩니다. 보시다시피, Transaction 1은 Transaction 2가 em.flush()를 호출한 후 p1의 가격을 업데이트하려 합니다. Transaction T1이 커밋을 시도하면 엔티티 관리자가 p1 version 속성을 확인합니다. 마지막 읽기 작업 이후로 이 속성이 업데이트되었기 때문에, 엔티티 관리자는 OptimisticLockException을 스로우(throw)하고 Transaction T1을 롤백합니다.
PESSIMISTIC 잠금 모드
비관적 잠금 모드에서는 데이터를 읽을 때 데이터베이스 행을 잠급니다. 이것은 SQL 문 SELECT . . . FOR UPDATE [NOWAIT]에 대한 응답으로 이루어지는 동작과 같습니다. 비관적 잠금은 트랜잭션이 같은 엔티티를 동시에 업데이트하지 못하게 합니다. 이렇게 하면 애플리케이션 코드를 간소화할 수 있지만, 데이터에 대한 동시 액세스 즉, 데이터의 동시성(concurrency)이 제한되어 확장성을 떨어뜨리고 교착 상태(deadlock)를 유발할 수 있습니다. 비관적 잠금은 동시 트랜잭션들 사이에 충돌이 발생할 위험이 높은 애플리케이션에 적합합니다.
다음 그림은 PESSIMISTIC 잠금 모드의 다양한 예를 나타낸 것입니다.
- 그림 4는 엔티티를 읽고 이후 단계에서 이 엔티티를
PESSIMISTIC_READ잠금 모드에서 설정하는 예를 나타냅니다. - 그림 5는 엔티티를 읽고 그와 동시에 이 엔티티를
PESSIMISTIC_WRITE잠금 모드에서 설정하는 예를 나타냅니다. - 그림 6은 엔티티를 읽고 이후 단계에서 이 엔티티를
PESSIMISTIC_WRITE잠금 모드에서 설정하는 예를 나타냅니다.
![]() |
그림 4. 엔티티를 읽은 후 PESSIMISTIC_READ 잠금 모드 설정 |
![]() |
그림 5. 엔티티를 읽는 동안 PESSIMISTIC_WRITE 잠금 모드 설정 |
![]() |
그림 6. 엔티티를 읽은 후 PESSIMISTIC_WRITE 잠금 모드 설정 |
어떤 잠금 접근 방식을 사용하는 것이 적당한지는 해당 애플리케이션마다 다릅니다. 아마, 올바른 결정을 내리기 위해서는 다음과 같은 질문을 하실 수도 있습니다.
- 동시 트랜잭션 간의 충돌 위험이 무엇입니까?
- 확장성의 요건은 무엇입니까?
- 실패 후 다시 시도하는 사용자의 요구 사항은 무엇입니까?
샘플 애플리케이션
이 팁에서는 JPA 2.0에서 지원되는 잠금 모드를 보여주는 샘플 애플리케이션을 제시합니다. 이 애플리케이션은 자바 EE 6 SDK 프리뷰 릴리스에서도 제공됩니다. 자바 EE 6 SDK 프리뷰 릴리스 다운로드 패키지의 samples 디렉토리에서 "자바 지속성 API 잠금 샘플 애플리케이션(The Java Persistence API Locking Sample Application)"을 찾아보십시오.
이 애플리케이션은 클라이언트, 서블릿, 파트 및 사용자 데이터에 대한 엔티티 클래스, 데이터 액세스와 업데이트를 위한 논리를 제공하는 무상태 세션 빈(stateless session beans)으로 구성됩니다. 클라이언트는 서블릿을 호출하여 데이터를 초기화합니다. 그런 다음, 클라이언트는 병렬 읽기(parallel read) 및 업데이트 작업을 시뮬레이트하도록 서블릿에 여러 가지 요청을 합니다. 이런 작업은 빈에 의해 수행됩니다. 작업 중 일부는 낙관적 잠금을, 일부는 비관적 잠금을 사용하여 수행됩니다. 예를 들어, 다음의 updateWithOptimisticReadLock() 메소드는 낙관적 잠금을 사용하여 수행되는 병렬 작업을 보여 줍니다.
updateWithOptimisticReadLock() 메소드는 partEJB 빈의 updatePrice() 메소드를 호출하여 사용자를 찾은 다음, 파트 가격을 업데이트합니다. 그런 다음, updateWithOptimisticReadLock() 메소드는 병렬 메소드 호출을 통해 다른 사용자를 찾을 수 있도록 기다린 후 userEJB 빈에서 updateBid() 메소드를 호출합니다. updateBid() 메소드는 해당 파트에 대해 낙관적 잠금을 설정한 다음, 아래에 나타낸 것처럼 파트 가격을 기준으로 사용자 입찰을 제출합니다.
그 다음, updateWithOptimisticReadLock() 메소드가 em.flush()를 호출합니다. 이때, 엔티티 관리자가 해당 파트 엔티티에 대한 버전 확인을 수행합니다. 다른 사용자가 제출한 임의의 트랜잭션이 특정 파트가 잠겨 있는 동안 그 파트를 업데이트하는 경우 엔티티 관리자는 그 파트의 버전 속성을 증가시킵니다. 해당 파트의 버전 속성이 설정된 입찰 트랜잭션에서 마지막으로 파트를 읽었을 때의 버전 속성보다 높은 경우 updateWithOptimisticReadLock() 메소드는 OptimisticLockException을 스로우(throw)하고 그 트랜잭션을 롤백합니다.
/samples/javaee6/jpa/locking 디렉토리에서 애플리케이션의 소스 코드를 찾을 수 있습니다.
샘플 애플리케이션을 실행하려면 다음과 같이 하십시오.
- 자바 EE 6 SDK 프리뷰 릴리스를 아직 다운로드하지 않았으면 다운로드합니다. 자바 플랫폼 스탠다드 에디션(자바 SE) 6 SDK가 설치되어 있는지도 확인합니다.
- 샘플 애플리케이션을 다운로드합니다.
- 공통 빌드 지침에 따라 빌드 환경을 설정하고 애플리케이션 서버를 구성합니다.
- samples_install_dir
/javaee6/jpa/locking디렉토리로 이동합니다. 여기서, samples_install_dir은 샘플 애플리케이션을 설치한 곳입니다. - 명령줄에 다음 명령을 입력하여 샘플 애플리케이션을 빌드하고 배포한 후 실행합니다.
ant all
ant all명령을 아래의 명령 집합으로 바꿀 수 있습니다.ant default— 애플리케이션을 컴파일하고 패키지로 만듭니다.ant dir— 애플리케이션을 애플리케이션 서버에 배포합니다.ant run— 자바 클라이언트 테스트를 실행합니다.그러면 다음과 비슷한 결과가 출력됩니다.
[java] LockingJavaClient: Test is starting [java] Calling URL:http://localhost:8080/locking/test/?tc=initData&nc=6 &ns=3&np=3 [java] [java] Starting parallel updates with 9 users for operation: updateWOL [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWOL&uid=1 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWOL&uid=2 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWOL&uid=7 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWOL&uid=9 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWOL&uid=5 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWOL&uid=3 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWOL&uid=8 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWOL&uid=6 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWOL&uid=4 [java] Result for operation updateWOL for userId 1 is Success [java] Result for operation updateWOL for userId 2 is Success [java] Result for operation updateWOL for userId 5 is Failure [java] Result for operation updateWOL for userId 6 is Failure [java] Result for operation updateWOL for userId 7 is Failure [java] Result for operation updateWOL for userId 8 is Success [java] Result for operation updateWOL for userId 9 is Success [java] Result for operation updateWOL for userId 3 is Success [java] Result for operation updateWOL for userId 4 is Failure [java] Parallel updates executed with 9 users for operation: updateWOL Time taken:6146 miliseconds [java] ... [java] [java] Starting parallel updates with 9 users for operation: updateWPL [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWPL&uid=2 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWPL&uid=5 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWPL&uid=3 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWPL&uid=9 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWPL&uid=1 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWPL&uid=4 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWPL&uid=7 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWPL&uid=6 [java] Calling URL:http://localhost:8080/locking/test/?tc=updateWPL&uid=8 [java] Result for operation updateWPL for userId 5 is Success [java] Result for operation updateWPL for userId 3 is Success [java] Result for operation updateWPL for userId 9 is Success [java] Result for operation updateWPL for userId 2 is Success [java] Result for operation updateWPL for userId 1 is Success [java] Result for operation updateWPL for userId 8 is Success [java] Result for operation updateWPL for userId 4 is Success [java] Result for operation updateWPL for userId 6 is Success [java] Result for operation updateWPL for userId 7 is Success [java] Parallel updates executed with 9 users for operation: updateWPL Time taken:15054 miliseconds [java] LockingJavaClient: Test is endedURL 호출에서
tc매개 변수 값에서 식별되는 작업은 다음과 같습니다.updateWOL. 파트를 찾습니다. 병렬 스레드가 병렬로 사용자를 찾을 수 있도록 생각할 시간을 시뮬레이트합니다. 낙관적 잠금을 사용하여 파트를 업데이트합니다.updateWOR. 파트와 사용자를 찾습니다. 병렬 스레드가 병렬로 사용자를 찾을 수 있도록 생각할 시간을 시뮬레이트합니다. 낙관적 잠금을 사용하여 파트를 잠급니다. 사용자를 업데이트합니다.updateWOW. 파트와 사용자를 찾습니다. 병렬 스레드가 병렬로 사용자를 찾을 수 있도록 생각할 시간을 시뮬레이트합니다. OPTIMISTIC_FORCE_INCREMENT 잠금을 사용하여 파트를 잠급니다. 사용자를 업데이트합니다.updateWRP. 파트를 찾습니다. 병렬 스레드가 병렬로 사용자를 찾을 수 있도록 생각할 시간을 시뮬레이트합니다. PESSIMISTIC_READ 잠금을 사용하여 파트를 잠급니다. 파트를 업데이트합니다.updateWRR. 파트를 찾습니다. 병렬 스레드가 병렬로 사용자를 찾을 수 있도록 생각할 시간을 시뮬레이트합니다. PESSIMISTIC_WRITE 잠금을 사용하여 새로 고칩니다. 파트를 업데이트합니다.updateWPL. PESSIMISTIC_WRITE 잠금을 사용하여 파트를 찾습니다. 병렬 스레드가 병렬로 사용자를 찾을 수 있도록 생각할 시간을 시뮬레이트합니다. 파트를 업데이트합니다.
updateWOL과 같은 낙관적 잠금을 사용하는 업데이트 작업은 일부 실패하지만,updateWPL과 같은 비관적 잠금을 사용하는 업데이트 작업은 전부 성공합니다. 하지만, 비관적 잠금을 사용하여 업데이트하는 데 걸리는 시간은 낙관적 잠금을 사용할 때보다 훨씬 깁니다.ant clean명령을 사용하면 샘플 애플리케이션 배포를 해제하고 임시 디렉토리를 제거할 수 있습니다.
추가 자료
자세한 내용은 다음 자료를 참조하십시오.
- EclipseLink를 사용하여 JPA에서 반복 불가능한 읽기 방지
- Java Persistence API 2.0: 새로운 기능
- JPA 2.0의 새로운 기능과 특징
- GlassFish 3과 함께 자바 EE 6 플랫폼 시작: 초보자부터 전문가까지
- Java Persistence API: 모범 사례와 팁
저자 정보
Carol McDonald는 썬마이크로시스템즈의 자바 기술 전문가입니다. 1986년부터 소프트웨어 개발자로 일해 온 Carol은 자바 EE 기술, XML, 인터넷/인트라넷 애플리케이션, LDAP, 분산 네트워크 관리(CMIP, SNMP) 및 전자 메일(X.400, X.500)을 비롯한 분산 네트워크 애플리케이션 및 프로토콜 기술 분야에서 경력을 쌓아왔습니다. Carol은 자바 외에도 프랑스어와 독일어에 능통합니다. Carol McDonald의 블로그를 둘러보십시오.
"Java EE" 카테고리의 다른 글
- JAX-WS 핸들러 구성, 패키징, 배치하기 (댓글 2개 / 트랙백 0개) 2006/10/19
- Enterprise Bean에서 보안 주석 사용하기 (댓글 4개 / 트랙백 0개) 2007/05/28
- 엔터프라이즈용 Java Application Verification Kit, 2부 (댓글 1개 / 트랙백 0개) 2005/10/05
- Java Persistence를 최상으로 구현하는 방법 (댓글 12개 / 트랙백 0개) 2007/07/23
- SAAJ 소개 (댓글 1개 / 트랙백 0개) 2005/06/08
- 커스텀 ELResolver를 이용하여 통합 EL 확장하기 (댓글 4개 / 트랙백 0개) 2006/10/19
- JAX-WS를 이용한 웹 서비스 개발 (댓글 1개 / 트랙백 0개) 2006/01/18
- JAXR (JAVA API FOR XML REGISTRIES) (댓글 1개 / 트랙백 0개) 2005/05/18
- GlassFish에서 호출 흐름 모니터링하기 (댓글 3개 / 트랙백 0개) 2006/06/16
- Groovy, Grails, MySQL 및 Java Persistence API의 조합 (댓글 0개 / 트랙백 0개) 2008/08/20






JPA 2에 동시성 제어를 위한 기능이 추가되어 좋습니다. 하지만, 이 기사에서는 동시성 제어가 바로 애플리케이션 구현에 특정한 것이며 사용된 동시성 스키마를 이해하지 못하면 보안에 대한 잘못된 인식으로 이어진다는 사실이 전혀 언급되어 있지 않습니다.
데이터베이스에서 너무 멀리 Imho 추출을 하면 갖가지 문제(성능/정확성)로 이어지지만, 라이브 잠금(livelocking) 및 기아 현상(starvation)과 같은 더욱 미묘한 라이브 관련 문제도 발생합니다.
따라서 종합적인 API를 보유한다고 해도 여전히 모든 문제를 해결하지는 못합니다. 보이지 않는 곳에서 무슨 일이 일어나는지 확인하려면 데이터베이스에 정말 정통할 필요가 있고, 예상하는 대로 이루어지는지 확인하려면 생성된 SQL을 확인하는 작업도 필요합니다.
게시자: Peter Veentjer, 게시일: 2009/9/15 02:40 AM PDT #