2012-04-04 4 views
2

원자 적으로 DB 문 집합을 실행하려면 Java 응용 프로그램에 별도의 요구 사항이 있습니다 (&). 예를 들어, 응용 프로그램은 한 테이블에서 데이터 행을 읽고 다른 테이블의 데이터 행을 업데이트해야합니다.Java에서 DB 작업 집합을 잠급니다.

QueryRunner queryRunner = new QueryRunner(); // DBUtils query runner 

Object[] params = new Object[] { param }; 

Connection conn = null; 
try { 
    conn = ...; // get connection 
    conn.setAutoCommit(false); 
    result = queryRunner.query(conn, "select x, y, z from table1 where column1 = ?", new BeanHandler<SomeBean>(SomeBean.class), params); 

    // logic to get value for update 

    queryRunner.update(conn, "update table2 set p = ? where q = ?", some_value, some_id); 
    conn.commit(); 
} catch (SQLException e) { 
    // 
} finally { 
    DBUtils.closeQuietly(conn); 
} 

위와 같이 트랜잭션에 대한 자동 커밋을 false로 설정하고 나중에 명시 적으로 커밋하면 트랜잭션 관리가 가능합니다. 그러나 위의 코드는 다중 스레드 환경에서도 실행될 수 있으며 두 개의 DB 문 (& 선택)을 상호 상호 배타적으로 실행하기를 원합니다.

아래에 설명 된 방법에서 공유 Java Lock 객체를 사용하는 것이 좋습니다. 클래스에서

, 방법에

private Lock lock = new ReentrantLock(); // member variable 

lock.lock(); 
try { 
    conn = ...; // get connection 
    conn.setAutoCommit(false); 

    result = queryRunner.query(conn, "select x, y, z from table1 where column1 = ?", new BeanHandler<SomeBean>(SomeBean.class), params); 

    // logic to get value for update 

    queryRunner.update(conn, "update table2 set p = ? where q = ?", some_value, some_id); 
    conn.commit(); 
} finally { 
    DBUtils.closeQuietly(conn); 
    lock.unlock(); 
} 

그것은 일종의 할 수있는 문제를 해결하는 것 같다. 그러나, 이것이 최선의 관행이고 프레임 워크와 같은 더 나은 대안이 있는지 궁금합니다.

답변

1

제 제안은 데이터베이스가 귀하의 응용 프로그램 대신 이러한 잠금을 관리하도록하는 것입니다. 이것은 여러 JVM이 코드를 실행하는 경우를 처리합니다. 언급 한 잠금 메커니즘은 단일 JVM에서만 효과적 일 수 있습니다.

이 작업을 수행하는 방법은 SELECT ... FOR UPDATE입니다. 이렇게하면 선택한 행에 잠금이 설정되고 트랜잭션이 커밋되거나 롤백 될 때 잠금이 해제됩니다. 이것은 현재 값을 읽지 만 갱신하지 않으려는 다른 트랜잭션이 해당 행을 읽을 수 있기 때문에 테이블 레벨 잠금보다 좋습니다. 다른 트랜잭션이 FOR UPDATE 잠금을 얻으려고하면 첫 번째 트랜잭션이 완료 될 때까지 차단됩니다.

+0

감사합니다, Hiro2k. 우리의 애플리케이션은 서블릿 컨테이너에서 실행되며 하나의 JVM에있을 것이라고 나는 믿는다. SELECT 문이 작동중인 테이블이 아닌 다른 테이블의 행을 업데이트 할 때'SELECT ... FOR UPDATE'가 내 케이스에서 작동하지 않을 수 있습니다. – Chen

+0

그렇지만 부하 분산 또는 확장을 위해 서버의 다른 인스턴스를 추가하기로 결정한 경우이 문제가 발생합니다. 메소드의 맨 처음에 두 번째 테이블의 행에'SELECT ... FOR UPDATE'를 추가하여 다른 사람들이 완료 될 때까지 기다려야합니다. – Hiro2k

+0

좋은 지적, @ Hiro2k. 모든 것을 다룰 수 있는지 알아보기 위해 사용 사례를 평가할 것입니다. – Chen

1

당신이 필요로하는 애트리뷰트를 얻는 유일한 방법은 데이터베이스의 저장 프로 시저를 사용하여 데이터를 분리하고 한꺼번에 잠그는 것입니다. Java 레벨에서의 잠금은 데이터베이스의 잠금이 쉽게 할 수있는 것을 수행 할 수 없습니다.

+0

내 제안은 관련 데이터가 단일 JVM의 동일한 객체에 대한 메소드 호출을 통해 업데이트되는 경우에만 작동합니다. 그것은 데이터베이스 레벨 자체에 잠금을 두지 않습니다. 즉, 다른 메소드 호출, 다른 애플리케이션과 같이 다른 곳에서 트리거 된 동일한 데이터 조각에 대한 예기치 않은 업데이트를 방지하지 못한다는 것입니다. 저장 프로 시저를 활용하면이를 줄일 수 있습니까? – Chen

1

이와 같은 문제를 처리 할 수있는 또 다른 방법은 모든 데이터베이스 트랜잭션에 직렬 가능 트랜잭션 격리 수준을 사용하는 것입니다. 이로 인해 모든 트랜잭션 집합이 실제로 한 번에 하나씩 실행되지 않고 한 번에 하나씩 실행되는 것처럼 작동합니다. 이렇게하면 직렬화 실패 (SQLState 40001)를 catch하고 트랜잭션을 다시 시도하는 프레임 워크를 사용해야합니다. 가장 큰 장점은 트랜잭션 간의 특별한 상호 작용에 대해 걱정할 필요가 없다는 것입니다. 트랜잭션이 유일한 실행 일 때 올바른 일을 수행하면 모든 트랜잭션 혼합에서 올바른 작업을 수행 할 수 있습니다.

이렇게 간단하게 작동하려면 모든 트랜잭션을 직렬 가능해야합니다.

0

제 생각에 선택 및 업데이트 문을 포함하는 코드 블록을 스레드 안전으로 만들고 싶습니까? 그것은 synchronized 키워드가 사용되는 것입니다. 비록이 질문이 오랫동안 물어 왔지만 나는 단지 여기에 기록하고 싶다. 동기화 된 블록 아래에 코드 줄을 넣으십시오.

관련 문제