강의 컨설팅 트레이닝 무료진단 무료책자 마케팅편지 마케팅정보공유 다이어리 서비스제휴 고객센터

EclipseLink를 사용하여 JPA에서 반복 불가능한 읽기 방지
작성자 : 99 단국강토
등록날짜 : 2009.02.10 10:02
2,693

출처: http://sdnkorea.com/blog/627

이 팁에서는 EclipseLink가 제공하는 JPA(Java Persistence API) 및 JPA 확장 기능을 통해 반복 불가능한 읽기를 차단하는 방법에 대해 설명합니다.


반복 불가능한 읽기

반복 불가능한 읽기란 데이터베이스 트랜잭션과 관련된 개념입니다. 반복 불가능한 읽기에서는 어떤 데이터 소스에서 데이터 항목을 여러 번 읽을 때 항상 같은 값이 반환되지 않습니다. JPA 용어로 말하자면, 반복 불가능한 읽기란 어떤 트랜잭션을 통해 데이터 소스에서 같은 엔터티를 여러 번 읽는 애플리케이션이 엔터티를 읽는 사이 엔터티 상태가 바뀌었음을 발견한다는 뜻입니다.

반복 불가능한 읽기가 나타나는 상황은 예를 들어 다음과 같습니다. T1 트랜잭션이 데이터베이스에서 한 행을 읽습니다. 그리고 T1이 커밋하기 전에 다른 트랜잭션 T2가 해당 행을 수정하거나 삭제합니다. 그 결과 두 트랜잭션 모두 성공적으로 커밋됩니다.

대개 이러한 상황을 예방하기 위해 다음 두 방법 중 하나를 사용합니다.

  • 낙관적 접근 방식(Optimistic). 이 접근 방식에서는 같은 데이터를 동시에 읽고 쓰지 않는다고 가정합니다. 쓰기 트랜잭션은 커밋이 허용됩니다. 그러나 읽기 트랜잭션을 커밋하려면 먼저 변경된 값을 감지해야 합니다.
  • 비관적 접근 방식(Pessimistic) . 이 접근 방식에서는 같은 데이터를 동시에 읽고 쓸 수 있다고 가정합니다. 읽기 트랜잭션은 기본 데이터 소스에서 해당 데이터를 나타내는 행을 잠급니다. 쓰기 트랜잭션은 읽기가 커밋된 후에만 커밋할 수 있습니다. 읽기 트랜잭션이 이루어지는 동안 잠긴 데이터 값은 변경되지 않습니다.

예를 들어, 다음과 같이 간단한 데이터베이스 테이블을 생각해 보십시오.
설명 가격 720 고가 품목582.99 721 추천 품목 66.99

애플리케이션 A가 트랜잭션 T1을 시작하고 "고가 품목"의 가격을 검색하기 위해 테이블을 검색합니다. 그런 다음, A는 "고가 품목" 가격을 업데이트하는 트랜잭션 T2를 시작합니다.

그림 1은 낙관적 접근 방식을 보여 줍니다. T1 트랜잭션은 데이터를 동시에 수정하지 않는다고 가정합니다. 그러나 사실상 T2 트랜잭션이 데이터를 동시에 수정하고 있으며 커밋을 진행합니다. T1은 커밋을 시도하다가 이러한 변경 사실을 감지하고 애플리케이션에 통지하여 T1 트랜잭션을 롤백하게 합니다.


 
1327039732.jpg

그림 1. 낙관적 접근 방식을 사용하여 반복 불가능한 읽기 방지

그림 2는 비관적 접근 방식을 보여 줍니다. 여기서는 T1 트랜잭션이 커밋될 때까지 T2 업데이트를 차단합니다.

 
1102266027.jpg

그림 2. 비관적 접근 방식을 사용하여 반복 불가능한 읽기 방지


JPA에서 반복 불가능한 읽기 방지

JPA를 사용하여 버전 지정된 엔터티에 대한 반복 불가능한 읽기를 방지할 수 있습니다. 버전 지정된 엔터티는 다음 코드에서 보듯이 @Version 주석으로 표시됩니다.

<PRE> @Entity

public class StockQuote implements Serializable {

@Version
public Long getVersion() {
return version;
}
</PRE>

그리고 해당하는 데이터베이스 스키마에는 다음 SQL 문으로 작성되는 것과 같은 버전 열이 있습니다.

<PRE> CREATE TABLE STOCKQUOTE
(ID NUMBER NOT NULL, VERSION NUMBER, PRICE FLOAT, DESCRIPTION VARCHAR(255),
PRIMARY KEY (ID));
</PRE>

JPA는 버전 지정을 토대로 낙관적 잠금을 관리할 수 있습니다. 낙관적 잠금에서는 동시 트랜잭션 사이에 간헐적인 충돌이 발생할 것으로 가정합니다. 낙관적 잠금의 목표는 동시 트랜잭션이 동시에 처리될 수 있도록 충분한 자유를 보장하는 한편 충돌을 감지하고 예방하는 것입니다.

JPA에서 버전 지정된 엔터티에 대한 반복 불가능한 읽기를 방지하는 방법은 EntityManager 클래스의 lock() 메소드를 사용하는 것입니다. 메소드 서명은 다음과 같습니다.

<PRE> public void lock(Object entity, LockModeType lockMode);
</PRE>

첫 번째 메소드 매개변수는 이 트랜잭션을 통해 잠궈야 하는 엔터티 인스턴스입니다.

두 번째 메소드 매개변수는 잠금 모드이며, 다음과 같은 값을 사용할 수 있습니다.

  • READ
  • WRITE

어떤 잠금 모드로나 반복 불가능한 읽기를 방지할 수 있습니다. 그러나 WRITE 잠금 모드에서는 버전 열도 업데이트됩니다.

그림 1에서 보듯이 T1 트랜잭션으로 읽기 잠금이 확보되더라도 T2 트랜잭션은 변경 사항을 커밋할 수 있습니다. T1 트랜잭션이 커밋하려고 하면 EclipseLink JPA가 버전 열을 확인하여 마지막 읽기 이후 변경된 데이터를 감지합니다. 그런 다음, OptimisticLockException을 애플리케이션 A로 스로우(throw)합니다. 이때 애플리케이션은 엔터티 값을 새로 고친 뒤 작업을 다시 시도할 수 있습니다. 또는 작업을 취소하고 트랜잭션을 롤백할 수 있습니다.


EclipseLink 확장 기능 활용

EclipseLink는 모든 자바 환경에서 실행 가능하며 문자 그대로 어떤 종류의 데이터 소스에서나 개체의 읽고 쓰기를 지원하는 종합적이고 일관성 있는 프레임워크를 제공할 목적으로 시작된 오픈 소스 프로젝트입니다. 이 프로젝트로 얻은 결과 중 하나가 JPA의 고급 확장 기능입니다. 이 확장 기능을 활용하면 현재 JPA 1.0 사양에서 지원되지 않는 비관적 잠금을 통한 반복 읽기를 보장할 수 있습니다. 그러나 이 솔루션은 EclipseLink 고유의 확장 기능을 사용하므로 적용 범위가 넓지 않습니다.

EclipseLink로 반복 읽기를 보장하려면 JPA 쿼리 언어(JPA QL)로 작성된 쿼리에 대한 비관적 잠금 지원을 이용해야 합니다. 비관적 잠금에서는 동시 트랜잭션 사이에 잦은 충돌이 발생할 것으로 가정합니다. 비관적 잠금 도중 충돌을 방지하기 위해 애플리케이션 메모리가 읽는 순간부터 끝날 때까지 데이터베이스에서 해당 엔터티를 잠급니다.

EclipseLink는 쿼리 힌트, 즉 각 업체의 쿼리 기능에 사용되는 JPA 확장 지점을 통해 JPA QL 쿼리에 대한 비관적 잠금을 지원합니다.

JPA QL 쿼리 힌트를 통해 비관적 잠금을 사용하는 방법은 다음 두 가지입니다. 하나는 다음 예제와 같이 @NamedQuery 주석을 사용하는 방법입니다.

<PRE> @NamedQuery(
name="GetStock"
query="select sq from StockQuote as sq where sq.id = :id"
hints={@QueryHint(name=EclipseLinkQueryHints.PESSIMISTIC_LOCK,
value=PessimisticLock.Lock)})
</PRE>

다른 하나는 다음 예제와 같이 Query API를 사용하는 방법입니다.

<PRE> Query q = em.createNamedQuery("GetStock");
q.setHint(EclipseLinkQueryHints.PESSIMISTIC_LOCK,
PessimisticLock.Lock);
q.setParameter("id", 1);
</PRE>

이러한 방법 중 하나를 택하여 비관적 잠금을 실시하면 대개 데이터베이스에서 해당 행이 잠깁니다. 그림 2와 같이 T1 트랜잭션이 위의 두 쿼리 중 하나를 실행하면 데이터 소스의 해당 행이 잠기고 T2 트랜잭션의 업데이트 커밋이 차단됩니다. T1의 커밋이 완료된 후 T2도 변경 사항을 커밋할 수 있습니다.


샘플 애플리케이션

이제 EclipseLink JPA 확장 기능을 사용하여 반복 불가능한 읽기를 방지하는 샘플 애플리케이션을 살펴보겠습니다. 실제로 이 애플리케이션은 반복 불가능한 읽기를 허용하므로 두 종류의 읽기 결과를 비교해 볼 수 있습니다. 이 애플리케이션은 이 테크팁의 부록인 샘플 패키지에 들어 있습니다.

이 샘플 애플리케이션은 간단한 버전의 주식 시장용 애플리케이션으로, 보통 다수의 동시 트랜잭션을 처리하며 엔터티에 대한 동시 읽기 및 쓰기 서비스를 제공합니다. 이 애플리케이션의 기능은 일반적으로 다음과 같습니다.

  1. 데이터베이스에서 테이블을 작성합니다. 다음은 테이블을 작성하는 SQL 문입니다(이 코드는 createDDL.ddbc 파일에 있음).
    <PRE>CREATE TABLE STOCKQUOTE
    (ID NUMBER NOT NULL,
    VERSION INTEGER,
    PRICE FLOAT,
    DESCRIPTION VARCHAR(255),
    PRIMARY KEY (ID))
    ;

    INSERT INTO STOCKQUOTE (ID, PRICE, VERSION, DESCRIPTION)
    VALUES(1, 23, 1, 'JAVA')
    ;

    CREATE TABLE SEQUENCE (
    SEQ_NAME VARCHAR(50) NOT NULL,
    SEQ_COUNT INTEGER,
    PRIMARY KEY (SEQ_NAME))
    ;

    INSERT INTO SEQUENCE(SEQ_NAME, SEQ_COUNT) values ('SEQ_GEN', 1);
    </PRE>
     
    작성된 테이블에는 VERSION 열이 있습니다.

  2. 지속적 엔터티에 대한 메타데이터 파일, 클래스, JAR 파일을 지정하는 지속성 단위를 설정합니다. 지속성 단위는 항상 애플리케이션의 META-INF 디렉토리에 있는 persistence.xml 파일에 지정됩니다. 다음은 샘플 애플리케이션의 persistence.xml 파일 내용입니다.
    <PRE><?xml version="1.0" encoding="UTF-8"?>
    <persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="tech-tip-4-samplePU" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>com.sun.techtip.sample.jpa.entity.StockQuote</class>
    <properties>
    <property name="eclipselink.jdbc.user" value="spec"/>
    <property name="eclipselink.jdbc.password" value="spec"/>
    <property name="eclipselink.jdbc.url" value="jdbc:oracle:thin:@floater.sfbay:1521:specjdb"/>
    <property name="eclipselink.jdbc.driver" value="oracle.jdbc.driver.OracleDriver"/>
    </properties>
    </persistence-unit>
    </persistence>
    </PRE>
     
    이 지속성 단위는 EclipseLink를 JPA 지속성 공급자로 지정하고 있습니다.

  3. 두 개의 작업용 스레드, ReaderThreadWriterThread 스레드를 작성합니다. 이 애플리케이션은 낙관적 잠금의 반복 읽기, 비관적 잠금의 반복 읽기, 반복 불가능한 읽기의 세 가지 모드로 실행할 수 있습니다. 애플리케이션을 시작할 때 rro(낙관적 잠금의 반복 읽기), rrp(비관적 잠금의 반복 읽기), 또는 nrr(반복 불가능한 읽기)을 지정하여 모드를 선택할 수 있습니다. 애플리케이션은 입력 항목을 인수로 사용하여 ReaderThread를 시작합니다. 애플리케이션의 이 부분에 대한 코드는 Main.java 파일에 들어 있습니다. 다음은 해당 코드의 일부입니다.
    <PRE>public class Main {

    private static final String NRR = "nrr";
    private static final String RR="rr";
    private static final String P="p";
    private static final String O="o";

    public Main() {
    }

    public static void main(String[] args){
    if(args==null || args.length < 1){
    displayUsage();
    System.exit(-1);
    }

    boolean bLock = RR.equals(args[0].trim());

    if(args.length < 2 && bLock){
    displayUsage();
    System.exit(-1);
    }

    boolean isPessimistic = bLock? P.equals(args[1].trim()): false;

    System.out.println("Running sample for " +
    (bLock? "Repeatable read ":"Non-repeatable read ") +
    (bLock? (isPessimistic? "in pessimistic mode":"in optimisitic mode"):
    ""));

    //create two worker threads with lock options
    ReaderThread rt = new ReaderThread(bLock, isPessimistic);
    WriterThread wt = new WriterThread();

    //start the run
    new Thread(rt).start();
    new Thread(wt).start();
    </PRE>
     

    낙관적 잠금의 반복 읽기 모드에서는 ReaderThread가 엔터티에 대한 트랜잭션을 시작합니다. 그런 다음, WriterThreadReaderThread 트랜잭션이 이루어지는 동안 주가를 업데이트하고 성공적으로 커밋합니다. ReaderThread도 트랜잭션을 커밋하려고 합니다. 그러나 JPA가 데이터베이스의 해당 행이 마지막 읽기 이후 다른 스레드에 의해 업데이트되었음을 감지하고 OptimisticLockException을 스로우(throw)합니다. 애플리케이션은 이 예외를 포착하고 해당 트랜잭션에 실패 플래그를 붙입니다.

    비관적 잠금의 반복 읽기 모드에서는 ReaderThread가 엔터티에 대한 트랜잭션을 시작합니다. 그런 다음, WriterThreadReaderThread 트랜잭션이 이루어지는 동안 주가를 업데이트하려고 시도합니다. 그러나 ReaderThread가 엔터티 개체와 해당 개체를 나타내는 데이터베이스 행을 모두 잠궜기 때문에 업데이트 시도는 차단됩니다.

    반복 불가능한 읽기 모드에서는 ReaderThread가 엔터티에 대한 트랜잭션을 시작합니다. 그런 다음, ReaderThread가 트랜잭션을 커밋하기 전에 WriterThread가 같은 엔터티에 대해 주가를 업데이트하고 트랜잭션을 커밋합니다. ReaderThread는 가격 차이를 감지하여 충돌을 확인하고 경고 메시지를 표시합니다.

    다음은 ReaderThread의 코드 중 일부입니다.

    <PRE>public class ReaderThread implements Runnable{

    private EntityManagerFactory emf;
    private boolean bLock;
    private boolean isPessimistic;

    public ReaderThread(boolean bLock, boolean isPessimistic) {
    this.bLock = bLock;
    this.isPessimistic=isPessimistic;
    }

    public void run() {
    emf = DataSourceManager.getInstance().getEMF();
    read();
    }

    private void read(){

    EntityManager em = emf.createEntityManager();
    em.getTransaction().begin();

    StockQuote sq;

    if(bLock){
    if (isPessimistic) {
    Query q = em.createNamedQuery("GetStock");
    q.setParameter("id", 1);
    q.setHint(EclipseLinkQueryHints.PESSIMISTIC_LOCK,
    PessimisticLock.Lock);
    sq = (StockQuote) q.getSingleResult();

    } else {
    sq = em.find(StockQuote.class, new Long(1));
    em.lock(sq, LockModeType.READ);
    }

    } else {
    sq = em.find(StockQuote.class, new Long(1));
    }

    double price = sq.getPrice();

    System.out.println("ReaderThread: original price - "+ price);

    //now wait for write thread to do it's update
    while(!WriterThread.readyToWrite.get()){
    try {
    System.out.println("ReaderThread: waiting for writer thread to" +
    " be ready...");
    Thread.currentThread().sleep(100);
    }catch(InterruptedException iex){
    iex.printStackTrace();
    }
    }

    WriterThread.attemptWrite.set(true);
    System.out.println("ReaderThread: giving WriterThread OK to commit...");

    //write thread has attempted it's write
    System.out.println("ReaderThread: hit enter to continue....");
    try {
    System.in.read();
    } catch(Exception ex) {
    ex.printStackTrace();
    }

    if(!bLock || (bLock && isPessimistic)){
    em.refresh(sq);
    }
    double _price = sq.getPrice();

    try {
    em.getTransaction().commit();
    System.out.println("ReaderThread: latest price - " + _price);

    if (_price != price && !bLock) {
    System.out.println("Price was modified by the writer thread, " +
    "repeatable read failed as expected.");
    } else if (!bLock) {
    System.out.println("Price did not change inspite of " +
    "concurrent update!");
    } else if (_price == price && bLock) {
    System.out.println("Repeatable read succeeded!");
    } else {
    System.out.println("The app failed in testing " +
    "repeatable reads, please check your DB supports " +
    "row locking.");
    }
    } catch(Exception ex) {
    System.out.println("ReaderThread: Transaction failed with following" +
    " message -- "+ex.getMessage());
    }

    }
    }
    </PRE>
     
    ReaderThread가 JPA QL 쿼리 힌트에서 비관적 잠금을 통해 반복 읽기를 허용하고 있습니다.
    <PRE>if(bLock){
    if (isPessimistic) {
    Query q = em.createNamedQuery("GetStock");
    q.setParameter("id", 1);
    q.setHint(EclipseLinkQueryHints.PESSIMISTIC_LOCK,
    PessimisticLock.Lock);
    sq = (StockQuote) q.getSingleResult();

    } else {
    sq = em.find(StockQuote.class, new Long(1));
    em.lock(sq, LockModeType.READ);
    }

    }
    </PRE>


샘플 애플리케이션 실행

이 팁에는 샘플 패키지가 제공됩니다. 샘플 패키지의 애플리케이션을 설치하고 실행하려면 다음과 같이 하십시오.

  1. 샘플 패키지를 다운로드하고 압축을 풉니다. 새로 압축된 디렉토리 <sample_install_dir>/jpa-repeatable-read.tech-tip이 보입니다. 여기서 <sample_install_dir>은 샘플 패키지를 설치한 디렉토리입니다. 예를 들어, Windows 시스템의 C:\에 압축을 풀었다면 새로 만들어진 디렉토리는 C:\jpa-repeatable-read.tech-tip에 있습니다.

  2. jpa-repeatable-read.tech-tip 디렉토리로 이동한 뒤 build.xml 파일에서 jdbc.url, db.userid, db.passwordjavaee.home의 속성 값을 해당 운영 환경에 맞게 적절히 설정합니다. 애플리케이션은 이러한 속성 설정을 사용하여 persistence.xml 파일을 업데이트하고 데이터베이스의 초기 설정을 수행합니다.

  3. jpa-repeatable-read.tech-tip 디렉토리에 다음 명령을 입력하여 데이터베이스를 설정하고 지속성 단위를 지정합니다. <PRE> ant setup
    </PRE>
  4. 낙관적 잠금의 반복 읽기 모드로 애플리케이션을 실행합니다. build.xml 파일에서 실행 대상의 인수 값이 다음과 같이 rro여야 합니다.
    <PRE><target name="run" depends="clean, pusetup, compile">
    <java classname="com.sun.techtip.sample.Main" fork="true">
    <arg value="rr"/>
    <arg value="o"/>
    </PRE>
    이제 jpa-repeatable-read.tech-tip 디렉토리에서 다음 명령을 입력합니다. <PRE> ant run all
    </PRE>그러면 다음과 비슷한 결과가 출력됩니다.
    <PRE>Running sample for Repeatable read in optimisitic mode
    [EL Info]: 2008.07.01 12:46:16.500--ServerSession(17423963)--EclipseLink, version: Eclipse Persistence Services - 1.0 (Build SNAPSHOT - 20080604)
    [EL Info]: 2008.07.01 12:46:19.343--ServerSession(17423963)--file:/D:/work/articles/jpa-repeatable-read.tech-tip/build/classes/-tech-tip-4-samplePU login successful
    WriterThread: original price - 40.745903
    WriterThread: ready, need OK from ReaderThread...
    ReaderThread: original price - 40.745903
    ReaderThread: giving WriterThread OK to commit...
    ReaderThread: hit enter to continue....
    WriterThread: starting to commit now...
    WriterThread: committed tx...
    WriterThread: updated price - 44.8204933

    [EL Warning]: 2008.07.01 12:47:37.906--UnitOfWork(24118161)--Exception [EclipseLink-5006] (Eclipse Persistence Services - 1.0 (Build SNAPSHOT - 20080604)): org.eclipse.persistence.exceptions.OptimisticLockException
    Exception Description: The object [com.sun.techtip.sample.jpa.entity.StockQuote@878c4c] cannot be updated because it has changed or been deleted since it was last read.
    Class> com.sun.techtip.sample.jpa.entity.StockQuote Primary Key> [1]
    [EL Warning]: 2008.07.01 12:47:37.921--UnitOfWork(24118161)--javax.persistence.OptimisticLockException: Exception [EclipseLink-5006] (Eclipse Persistence Services - 1.0 (Build SNAPSHOT - 20080604)): org.eclipse.persistence.exceptions.OptimisticLockException
    Exception Description: The object [com.sun.techtip.sample.jpa.entity.StockQuote@878c4c] cannot be updated because it has changed or been deleted since it was last read.
    Class> com.sun.techtip.sample.jpa.entity.StockQuote Primary Key> [1]
    ReaderThread: Transaction failed with following message -- javax.persistence.OptimisticLockException: Exception [EclipseLink-5006] (Eclipse Persistence Services - 1.0 (Build SNAPSHOT - 20080604)): org.eclipse.persistence.exceptions.OptimisticLockException
    Exception Description: The object [com.sun.techtip.sample.jpa.entity.StockQuote@878c4c] cannot be updated because it has changed or been deleted since it was last read.
    Class> com.sun.techtip.sample.jpa.entity.StockQuote Primary Key> [1]
    </PRE>

    반복 불가능한 읽기가 차단되었습니다. 설치된 JPA는 WriterThread에 의해 가격이 변경되었음을 감지하고 OptimisticLockException을 스로우(throw)합니다. 그러면 ReaderThread가 이 예외를 포착하고 해당 트랜잭션에 실패 플래그를 붙입니다.

  5. 비관적 잠금의 반복 읽기 모드로 애플리케이션을 실행합니다. build.xml 파일에서 실행 대상의 인수 값이 다음과 같이 rrp여야 합니다.
    <PRE> <target name="run" depends="clean, pusetup, compile">
    <java classname="com.sun.techtip.sample.Main" fork="true">
    <arg value="rr"/>
    <arg value="p"/>
    </PRE>

이제 jpa-repeatable-read.tech-tip 디렉토리에서 다음 명령을 입력합니다. <PRE> ant run all
</PRE>그러면 다음과 비슷한 결과가 출력됩니다.
<PRE>Running sample for Repeatable read in pessimistic mode
[EL Info]: 2008.07.01 12:53:50.078--ServerSession(17423963)--EclipseLink, version: Eclipse Persistence Services - 1.0 (Build SNAPSHOT - 20080604)
[EL Info]: 2008.07.01 12:53:51.875--ServerSession(17423963)--file:/D:/work/articles/jpa-repeatable-read.tech-tip/build/classes/-tech-tip-4-samplePU login successful
WriterThread: original price - 44.8204933
WriterThread: ready, need OK from ReaderThread...
ReaderThread: original price - 44.8204933
ReaderThread: giving WriterThread OK to commit...
ReaderThread: hit enter to continue....
WriterThread: starting to commit now...

ReaderThread: latest price - 44.8204933
Repeatable read succeeded!
WriterThread: committed tx...
WriterThread: updated price - 49.302542630000005
</PRE>

반복 읽기가 성공했습니다. WriterThread의 트랜잭션 커밋은 ReaderThread 트랜잭션이 커밋될 때까지 차단됩니다.
  • 반복 불가능한 읽기 모드로 애플리케이션을 실행합니다. build.xml 파일에서 실행 대상의 값을 nrr로 변경합니다. 이제 jpa-repeatable-read.tech-tip 디렉토리에서 다음 명령을 입력합니다.
<PRE> ant run all
</PRE>그러면 다음과 비슷한 결과가 출력됩니다.
<PRE>Running sample for Non-repeatable read
[EL Info]: 2008.07.01 12:55:33.906--ServerSession(17423963)--EclipseLink, version: Eclipse Persistence Services - 1.0 (Build SNAPSHOT - 20080604)
[EL Info]: 2008.07.01 12:55:35.734--ServerSession(17423963)--file:/D:/work/articles/jpa-repeatable-read.tech-tip/build/classes/-tech-tip-4-samplePU login successful
ReaderThread: original price - 49.30254263
ReaderThread: waiting for writer thread to be ready...
WriterThread: original price - 49.30254263
WriterThread: ready, need OK from ReaderThread...
ReaderThread: giving WriterThread OK to commit...
ReaderThread: hit enter to continue....
WriterThread: starting to commit now...
WriterThread: committed tx...
WriterThread: updated price - 54.232796893

ReaderThread: latest price - 54.232796893
Price was modified by the writer thread, repeatable read failed as expected.
</PRE>

반복 읽기가 실패했습니다. ReaderThread는 가격을 요청한 첫 번째 시기의 값 49.30을 검색합니다. WriterThreadReaderThread 트랜잭션과 동시에 이 값을 54.23으로 업데이트할 수 있습니다. 두 번째 ReaderThreadWriterThread가 트랜잭션을 커밋한 이후에 실행되므로 업데이트된 가격을 검색하게 됩니다.

참고: 이 애플리케이션은 다양한 유형의 데이터베이스에서 실행할 수 있지만 테스트에 사용한 DB는 오라클 데이터베이스뿐입니다.

추가 자료


저자 정보

Rahul Biswas는 자바 성능 엔지니어링 그룹에서 활동 중인 썬의 직원입니다.


이 글의 영문 원본은
Preventing Non-Repeatable Reads in JPA Using EclipseLink에서 보실 수 있습니다.

"쇼핑몰·홈페이지·오픈마켓
블로그·페이스북·이메일 등의 각종 마케팅 글쓰기,
각종 광고, 영업, 판매, 제안서, 전단지
반응율 3배×10배 이상 높이는 마법의 8단계 공식"
자세히보기

Comments

번호 제목 글쓴이 날짜 조회
3045 색깔별로 분류 99 단국강토 02.10 1959
3044 선택자(Selector) 99 단국강토 02.10 1645
3043 HTML 강좌 STYLE SHEET 문단제어 99 단국강토 02.10 1689
3042 문서내에 작은 스크롤상자 만들기(iframe 대신) 99 단국강토 02.10 2497
3041 이미지 정중앙 99 단국강토 02.10 1746
3040 마우스 커서 변경 99 단국강토 02.10 1592
3039 make flexible width(example) 99 단국강토 02.10 2251
3038 리스트 스타일 99 단국강토 02.10 1935
3037 테이블,이미지 테두리 스타일 99 단국강토 02.10 1975
3036 테이블 넓이보다 긴 텍스트 처리(자동 줄 바꿈) 99 단국강토 02.10 3423
3035 마우스 올렸을때 사각테두리 만드는 법 99 단국강토 02.10 2243
3034 서로 다르게 CSS소스 적용하는 방법 99 단국강토 02.10 2973
3033 마우스 On 시에 표 색상 바꾸기 99 단국강토 02.10 3351
3032 해상도 상관 없이 팝업 중앙에 위치시키기 99 단국강토 02.10 3417
3031 해상도 상관 없이 팝업 중앙에 위치시키기 99 단국강토 02.10 2348
3030 EclipseLink를 사용하여 JPA에서 반복 불가능한 읽기 방지 99 단국강토 02.10 2297
열람중 EclipseLink를 사용하여 JPA에서 반복 불가능한 읽기 방지 99 단국강토 02.10 2694
3028 자바스크립트의기본 99 단국강토 02.10 1438
3027 자바스크립트의기본 99 단국강토 02.10 2939
3026 table 태그를 알자 99 단국강토 02.10 1421
3025 table 태그를 알자 99 단국강토 02.10 2788
3024 레이어를 제대로알자 99 단국강토 02.10 2451
3023 레이어를 제대로알자 99 단국강토 02.10 1601
3022 다음부터..창띄우지않기[쿠키이용] 99 단국강토 02.10 2545
3021 다음부터..창띄우지않기[쿠키이용] 99 단국강토 02.10 2954
3020 프린트관련스크립 99 단국강토 02.10 1526
3019 프린트관련스크립 99 단국강토 02.10 2011
3018 원하는 창 크기 ,즐겨찾기등록하기 99 단국강토 02.10 1833
3017 원하는 창 크기 ,즐겨찾기등록하기 99 단국강토 02.10 2199
3016 새창,새창내려온후진동 99 단국강토 02.10 1341
마케팅
특별 마케팅자료
다운로드 마케팅자료
창업,경영
기획,카피,상품전략
동기부여,성취