hibernate-orm/reference/ko/modules/transactions.xml
JongDae Kim 7672eaa82b *** empty log message ***
git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@7183 1b8cb986-b30d-0410-93ca-fae66ebed9b2
2005-06-18 12:39:09 +00:00

817 lines
60 KiB
XML

<chapter id="transactions" revision="1">
<title>트랜잭션들과 동시성</title>
<para>
Hibernate와 동시성 제어에 대한 가장 중요한 점은 이해하기가 매우 쉽다는 점이다. Hibernate는 어떤 추가적인 잠금 행위 없이
JDBC 커넥션들과 JTA 리소스들을 직접 사용한다. 우리는 당신의 데이터베이스 관리 시스템의 JDBC, ANSI, 그리고 트랜잭션 격리 명세에
약간의 시간을 할애할 것을 매우 권장한다. Hibernate는 단지 자동적인 버전화를 추가하지만 메모리 내에서 객체들을 잠그지 않거나 당신의
데이터베이스 트랜잭션들의 격리 레벨을 변경시키지 않는다. 기본적으로, 당신이 당신의 데이터베이스 리소스들에 직접 JDBC(또는 JTA/CMT)를
사용하는 것처럼 Hibernate를 사용하라.
</para>
<para>
하지만, 자동적인 버전화에 덧붙여, Hibernate는 또한 <literal>SELECT FOR UPDATE</literal> 구문을 사용하여,
행들에 대한 pessimistic 잠금을 위한 (마이너) API를 제공한다. 이 API는 이 장의 뒷 부분에서 논의된다.
</para>
<para>
우리는 <literal>Configuration</literal>, <literal>SessionFactory</literal>, <literal>Session</literal>,
알갱이를 가진 Hibernate에서의 동시성 제어 뿐만 아니라 데이터베이스 트랜잭션과 긴 어플리케이션 트랜잭션에 대한 논의를 시작한다.
</para>
<sect1 id="transactions-basics">
<title>세션 영역과 트랜잭션 영역</title>
<para>
<literal>SessionFactory</literal>는 모든 어플리케이션 쓰레드들에 의해 공유되도록 고안된 생성에 비용이 드는,
쓰레드안전(threadsafe) 객체이다. 그것은 대개 어플리케이션 시작 시에 <literal>Configuration</literal> 인스턴스로부터
한번 생성된다.
</para>
<para>
<literal>Session</literal>은 단일 비지니스 프로세스, 하나의 작업 단위를 위해 한번만 사용되고 나서 폐기될 예정인,
비용이 들지 않는, 쓰레드 안전하지 않은 객체이다. <literal>Session</literal>은 그것이 필요하지 않으면 JDBC
<literal>Connection</literal>(또는 <literal>Datasource</literal>)를 얻지 않을 것이어서, 데이터 접근이
특별한 요청에 서비스할 필요가 있을 것이라고 당신이 확신하지 않을 경우에 당신은 <literal>Session</literal>을 안전하게
열고 닫을 수 있다. (이것은 당신이 요청 인터셉션을 사용하여 다음 패턴들 중 어떤 것을 구현하자마자 중요하게 된다.)
</para>
<para>
이 그림을 완성하기 위해 당신은 또한 데이터베이스 트랜재션들에 대해 생각해야 한다. 데이터베이스 트랜잭션은 데이터베이스에서
잠금 다툼을 줄이기 위해 가능한 짧아야 한다. 긴 데이터베이스 트랜잭션들은 당신의 어플리케이션이 고도의 동시성 로드로의 가용성을
높이는 것을 방해할 것이다.
</para>
<para>
하나의 작업 단위의 영역은 무엇인가? 하나의 Hibernate <literal>Session</literal>은 몇몇 데이터베이스 트랜잭션들에
걸칠 수 있는가 또는 이것은 영역들의 one-to-one 관계인가? 당신은 언제 <literal>Session</literal>을 열고 닫는가
그리고 당신은 데이터베이스 트랜잭션 경계들을 어떻게 한정하는가?
</para>
<sect2 id="transactions-basics-uow">
<title>작업 단위</title>
<para>
첫번째로, <emphasis>session-per-operation</emphasis> anti-패턴을 사용하지 말라. 즉, 단일 쓰레드 내에서
모든 간단한 데이터베이스 호출에 대해 <literal>Session</literal>을 열고 닫지 말라! 물론 같은 것이 데이터베이스
트랜잭션들에 대해서도 참이다. 어플리케이션 내의 데이터베이스 호출들은 계획된 순서를사용하여 행해지며, 그것들은 원자
작업 단위 속으로 그룹지워진다. (이것은 또한 모든 하나의 SQL 문장 뒤의 auto-commit(자동-커밋)이 어플리케이션 내에서
무용지물임을 의미하고, 이 모드가 SQL 콘솔 작업을 돕도록 고안되었음을 노트하라. Hibernate는
의미하고, 이 모드는 Hibernate는 즉시 자동-커밋 모드를 사용 불가능하게 하거나, 어플리케이션 서버가 그렇게 행하고,
즉시 자동-커밋시키는 것을 사용불가능하게 하거나 ,그렇게 행하는 것을 기대한다.)
</para>
<para>
다중 사용자 클라이언트/서버 어플리케이션에서 가장 공통된 패턴은 <emphasis>session-per-request</emphasis>이다.
이 모형에서, 클라이언트로부터의 요청은 (Hibernate 영속 계층이 실행되는) 서버로 전송되고, 새로운 Hibernate
<literal>Session</literal>이 열려지고, 모든 데이터베이스 오퍼레이션들이 이 작업 단위 내에서 실행된다. 일단 그 작업이
완료되었다면(그리고 클라이언트에 대한 응답이 준비되었다면), 그 세션은 flush 되고 닫혀진다. 당신은 또한 당신이
<literal>Session</literal>을 열고 닫을 때 그것을 시작하고 커밋시켜서 클라이언트 요청에 서비스하는데 한 개의 데이터베이스
트랜잭션을 사용하게 될 것이다. 둘 사이의 관계는 일대일 대응이고 이 모형은 많은 어플리케이션들에서 완전하게 적합하다.
</para>
<para>
도전점은 구현에 놓여있다: <literal>Session</literal>과 트랜잭션이 정확하게 시작되고 끝나야 할 뿐만 아니라, 그것들은
또한 데이터 접근 오퍼레이션들에 대해 접근 가능해야 한다. 작업의 단위에 대한 경계 구분은 하나의 요청이 서버에 도착하고
응답이 전송되기 전에 실행되는 인터셉터(예를 들면 <literal>ServletFilter</literal>)를 사용하여 이상적으로 구현된다.
우리는 하나의 <literal>ThreadLocal</literal> 변수를 사용하여 그 <literal>Session</literal>을 요청에 서비스하는
쓰레드에 바인드 시킬 것을 권장한다. 이것은 이 쓰레드 내에서 실행되는 모든 코드에서 (static 변수에 접근하는 것처럼) 쉽게
접근을 허용해준다. 당신이 선택하는 데이터베이스 트랜잭션 경계 구분 메커니즘에 따라, 당신은 또한 <literal>ThreadLocal</literal>
변수 내에 트랜잭션 컨텍스트를 유지할 수도 있다. 이것을 위한 구현 패턴들은<emphasis>ThreadLocal Session</emphasis>
<emphasis> 뷰 내의 Open Session</emphasis>으로 알려져 있다. 당신은 이것을 구현하기 위해 이 문서의 앞 쪽에 보였던
<literal>HibernateUtil</literal> helper 클래스를 쉽게 확장할 수 있다. 물론 당신은 당신의 환경에서 인터셉터를 구현하고 그것을
설정하는 방법을 찾아야 한다. 팁들과 예제들은 Hibernate 웹 사이트를 보라.
</para>
</sect2>
<sect2 id="transactions-basics-apptx">
<title>어플리케이션 트랜잭션들</title>
<para>
session-per-request 패턴은 당신이 작업 단위들을 설계하는데 사용할 수 있는 유일한 유용한 개념이 아니다. 많은 비지니스
프로세스들은 데이터베이스 접근들을 중재하는 사용자 사이의 전체 일련의 상호작용들을 필요로 한다. 웹과 엔터프라이즈
어플리케이션에서 사용자 상호작용에 걸치는 것은 데이터베이스 트랜잭션에 허용되지 않는다. 다음 예제를 검토하자:
</para>
<itemizedlist>
<listitem>
<para>
대화상자의 첫 번째 화면이 열리고, 사용자에게 보여진 데이터는 특정 <literal>Session</literal>과 데이터베이스
트랜잭션 속에 로드되었다. 사용자가 객체들을 변경시키는 것이 자유롭다.
</para>
</listitem>
<listitem>
<para>
사용자는 5분 후에 "저장"을 클릭하고 그의 변경들이 영속화 되기를 기대한다; 그는 또한 그가 이 정보를 편집하는 유일한
개인이고 변경 충돌이 발생하지 않기를 기대한다.
</para>
</listitem>
</itemizedlist>
<para>
우리는 사용자의 관점에서, 이것을 작업 단위, 장기간 실행되는 <emphasis>어플리케이션 트랜잭션</emphasis>이라고 명명한다.
당신이 당신의 어플리케이션에서 이것을 어떻게 구현할 수 있는 많은 방법들이 존재한다.
</para>
<para>
첫 번째 naive 구현은 동시성 변경을 방지하고, 격리와 atomicity(원자 단위성)을 보장하기 위해 데이터베이스에 의해
소유된 잠금으로 사용자가 생각하는동안 <literal>Session</literal>과 데이터베이스 트랜잭션을 유지할 수도 있다.
이것은 물론 anti-패턴이다. 왜냐하면 잠금 다툼은 어플리케이션이 동시 사용자들의 가용 숫자를 높이는 것을 허용하지
않을 것이기 때문이다.
</para>
<para>
명료하게, 우리는 어플리케이션 트랜잭션을 구현하는데 몇몇 데이터베이스 트랜잭션들을 사용해야 한다. 이 경우에,
비지니스 프로세스들의 격리를 유지하는 것은 어플리케이션 티어의 부분적인 책임이 된다. 단일 어플리케이션 트랜잭션은
대개 여러 개의 데이터베이스 트랜잭션들에 걸친다. 그것은 이들 데이터베이스 트랜잭션들 중 오직 한 개(마지막 트랜잭션)가
업데이트된 데이터를 저장하고, 모든 다른 트랜잭션들이 단순히 데이터를 읽는 (예를 들면, 몇몇 요청/응답 주기에 걸치는
마법사 스타일의 대화 상자에서) 경우에만 원자단위가 될 것이다. 특히 당신이 Hibernate의 특징들을 사용할 경우에 , 이것은
들리는 것보다 구현하기가 더 쉽다:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>자동적인 버전화</emphasis> - Hibernate는 당신을 위해 자동적인 optimistic
동시성 제어를 행할 수 있고, 그것은 사용자가 생각하는 시간 동안 동시적인 변경이 발생했는지를 자동적으로 검출할 수 있다..
</para>
</listitem>
<listitem>
<para>
<emphasis>Detached 객체들</emphasis> - 만일 당신이 이미 논의된 <emphasis>session-per-request</emphasis>
패턴을 사용하고자 결정하는 경우, 모든 로드된 인스턴스들은 사용자가 생각하는 시간 동안 detached 상태에 있을 것이다.
Hibernate는 그 객체들을 재첨부시키고 변경들을 영속화 시키는 것을 허용해주며, 그 패턴은
<emphasis>session-per-request-with-detached-objects(detached-객체들을 가진 요청 당 세션)</emphasis>으로 명명된다.
자동적인 버전화는 동시성 변경들을 격리시키는데 사용된다.
</para>
</listitem>
<listitem>
<para>
<emphasis>Long Session</emphasis> - Hibernate <emphasis>Session</emphasis>은 데이터베이스
트랜잭션이 커밋된 후에 기본 JDBC 커넥션이 연결 해제될 수도 있고, 새로운 클라이언트 요청이 발생할 때 다시 연결될 수
있다. 이 패턴은 <emphasis>session-per-application-transaction(어플리케이션 트랜잭션 당 세션)</emphasis>으로
알려져 있고 재첨부를 불필요하게 만든다. 자동적인 버전화는 동시성 변경들을 격리시키는데 사용된다.
</para>
</listitem>
</itemizedlist>
<para>
<emphasis>session-per-request-with-detached-objects</emphasis>
<emphasis>session-per-application-transaction</emphasis> 양자는 장점들과 단점들을 갖는데, 우리는 이 장의
뒷 부분에서 optimistic 동시성 제어 단락에서 그것들을 논의한다.
</para>
</sect2>
<sect2 id="transactions-basics-identity">
<title>객체 identity 고려하기</title>
<para>
어플리케이션은 두 개의 다른 <literal>Session</literal>들 내에 있는 동일한 영속 상태에 동시에 접근할 수도 있다.
하지만 영속 클래스의 인스턴스는 두 개의 <literal>Session</literal> 인스턴스들 사이에 결코 공유되지 않는다.
그러므로 identity에 대한 두 개의 다른 개념들이 존재한다:
</para>
<variablelist spacing="compact">
<varlistentry>
<term>데이터베이스 Identity</term>
<listitem>
<para>
<literal>foo.getId().equals( bar.getId() )</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>JVM Identity</term>
<listitem>
<para>
<literal>foo==bar</literal>
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
그때 (예를 들어 <literal>Session</literal> 영역에서) <emphasis>특정</emphasis> <literal>Session</literal>
첨부된 객체들의 경우 두 개의 개념들은 동등한 것이고, 데이터베이스 identity에 대한 JVM identity가 Hibernate에 의해 보장된다.
하지만, 어플리케이션이 두 개의 다른 세션들에서 "동일한" (영속 identity) 비지니스 객체에 동시에 접근하는 동안, 두 개의 인스턴스들은
실제로 "다르다"(JVM identity). 충돌들은 flush/커밋 시에 (자동적인 버전화)를 사용하여, optimistic 접근법을 사용하여 해결된다.
</para>
<para>
이 접근법은 Hibernate와 데이터베이스가 동시성에 대해 걱정하지 않도록 해준다; 그것은 또한 최상의 scalability를 제공한다.
왜냐하면 단일 쓰레드-작업 단위 내에서 identity 보장은 단지 비용이 드는 잠금이나 다른 동기화 수단들을 필요로 하지 않기 때문이다.
어플리케이션은 그것이 <literal>Session</literal> 당 단일 쓰레드를 강제하는 한, 어떤 비지니스 객체에 대해 결코 동기화 시킬
필요가 없다. 하나의 <literal>Session</literal> 내에서 어플리케이션은 객체들을 비교하는데 <literal>==</literal>
안전하게 사용할 수가 있다.
</para>
<para>
하지만, 하나의 <literal>Session</literal> 외부에서 <literal>==</literal>를 사용하는 어플리케이션은 예기치 않은
결과들을 보게 될 수도 있다. 이것은 어떤 예기치 않은 장소들에서, 예를 들어 당신이 두 개의 detached 인스턴스들을 동일한
<literal>Set</literal> 내에 집어넣을 경우에 발생할 수도 있다. 둘 다 동일한 데이터베이스 identity를 가질 수 있지만
(예를 들어 그것들은 동일한 행을 표현한다), JVM identity는 정의 상 detached 상태에 있는 인스턴스들을 보장하지 않는다.
개발자는 영속 클래스들내에 <literal>equals()</literal> 메소드와 <literal>hashCode()</literal> 메소드를 오버라이드 시켜야 하고
객체 equality에 대한 그 자신의 개념을 구현해야 한다. 하나의 경고가 존재한다: equality를 구현하는데 데이터베이스 identifier를 결코
사용하지 말고, 하나의 비지니스 키, 유일한, 대개 불변인 속성들의 조합을 사용하라. 데이터베이스 식별자는 만일 transient 객체가 영속화되는
경우에 변경될 것이다. 만일 transient 인스턴스가(대개 detached 인스턴스들과 함께) <literal>Set</literal> 내에 보관되는 경우에,
hashcode 변경은 <literal>Set</literal>의 계약을 파기시킨다. 비지니스 키들에 대한 속성들은 데이터베이스 프라이머리 키들 만큼
안정적이어서는 안되며, 당신은 오직 객체들이 동일한 <literal>Set</literal> 내에 있는 한에서 안정성을 보장해야만 한다.
이 쟁점에 관한 논의에 대한 더 많은 것을 Hibernate 웹 사이트를 보라. 또한 이것이 Hibernate 쟁점이 아니며, 단지 자바 객체
identity와 equality가 구현되어야 하는 방법임을 노트하라.
</para>
</sect2>
<sect2 id="transactions-basics-issues">
<title>공통된 쟁점들</title>
<para>
안티-패턴들 <emphasis>session-per-user-session</emphasis> 또는
<emphasis>session-per-application</emphasis>을 결코 사용하지 말라(물론 이 규칙에 대한 드문 예외상황들이 존재한다).
다음 쟁점들 중 몇몇이 또한 권장되는 패턴들로 나타날 수 있음을 노트하고, 당신이 설계 결정을 내리기 전에 내포된 의미들을
확실히 이해하라:
</para>
<itemizedlist>
<listitem>
<para>
<literal>Session</literal>은 쓰레드-안전하지 않다. HTTP 요청들, 세션 빈즈, 또는 Swing worker들처럼
동시에 작업하는 것으로 가정되는 것들은 하나의 <literal>Session</literal> 인스턴스가 공유될 경우에 경쟁 조건들을
발생시킬 것이다. 만일 당신이 당신의 <literal>HttpSession</literal> 내에 Hibernate
<literal>Session</literal>을 유지시키는 경우(나중에 논의됨), 당신은 당신의 Http 세션에 대한 접근을 동기화
시키는 것을 고려해야 한다. 그 밖의 경우, 충분히 빠르게 reload를 클릭하는 사용자는 두 개의 동시적으로 실행되는
쓰레드들 내에서 동일한 <literal>Session</literal>을 사용할 수도 있다.
</para>
</listitem>
<listitem>
<para>
Hibernate에 의해 던져진 예외상황은 당신이 당신의 데이터베이스 트랜잭션을 롤백 시키고 즉시 <literal>Session</literal>
닫아야 함을 의미한다(나중에 상세히 논의됨). 만일 당신의 <literal>Session</literal>이 어플리케이션에 바인드 되어 있는 경우,
당신은 어플리케이션을 중지시켜야 한다. 데이터베이스 트랜잭션 롤백은 당신의 비지니스 객체들을 그것들이 트랜잭션의 시작 시에
머물렀던 상태로 되돌리지는 않는다. 이것은 데이터베이스 상태와 비지니스 객체들이 동기화를 벗어남을 의미한다. 대개 이것은 문제가 아니다.
왜냐하면 예외상황들은 회복가능한 것이 아니고 당신이 어떻게든 롤백 후에 시작해야 하기 때문이다.
</para>
</listitem>
<listitem>
<para>
<literal>Session</literal>은 (Hibernate에 의해 dirty 상태로 관찰되었거나 체크된) 영속 상태에 있는
모든 객체를 캐시 시킨다. 이것은 당신이 오랜 시간 동안 <literal>Session</literal>을 열어둔 채로 유지하거나
단순하게 너무 많은 데이터를 로드시킬 경우에, 당신이 OutOfMemoryException을 얻기 전까지, 그것이 끝없이
성장한다는 점을 의미한다. 이것에 대한 하나의 해결책은 <literal>Session</literal> 캐시를 관리하기 위해
<literal>clear()</literal><literal>evict()</literal>를 호출하는 것이지만, 당신이 대용량 데이터
오퍼레이션들을 필요로 하는 경우에 당신은 대개 내장 프로시저를 고려해야 할 것이다. 몇몇 해결책들이
<xref linkend="batch"/>에 보여져 있다. 사용자 세션 동안에 <literal>Session</literal>을 열려진 채로
유지하는 것은 또한 실효성이 떨어진 데이터에 대한 높은 확률을 의미한다.
</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1 id="transactions-demarcation">
<title>데이터베이스 트랜잭션 경계 설정</title>
<para>
데이터베이스 (또는 시스템) 트랜잭션 경계들은 항상 필수적이다. 데이터베이스와의 통신은 데이터베이스 트랜잭션의 외부에서 발생할 수
없다(이것은 자동-커밋 모드로 사용되는 많은 개발자들에게는 혼동스러워 보인다). 항상 심지어 읽기 전용 오퍼레이션들에 대해서도 명료한
트랜잭션 경계들을 사용하라. 당신의 격리 레벨과 데이터베이스 가용성들에 따라, 이것은 필요하지 않을 수 있지만, 만일 당신이 항상
트랜잭션들을 명시적으로 경계 설정할 경우에는 하강하는 결점들이 존재하지 않는다.
</para>
<para>
Hibernate 어플리케이션은 관리되지 않는 환경(예를 들면. 스탠드얼론, 간단히 웹 어플리케이션들 또는 Swing 어플리케이션들)과
관리되는 J2EE 환경에서 실행될 수 있다. 관리되지 않는 환경에서, Hibernate는 대개 그것 자신의 데이터베이스 커넥션 풀에 대한
책임이 있다. 어플리케이션 개발자는 트랜잭션 경계들을 손수 설정해야 한다. 달리 말해, 개발자 스스로 데이터베이스 트랜잭션들을
시작하고, 커밋시키거나 롤백시켜야 한다. 관리되는 환경은 대개 예를 들어 EJB 세션 빈즈의 배치 디스크립터 속에 선언적으로 정의된
트랜잭션 어셈블리를 가진, 컨테이너에 의해-관리되는 트랜잭션들을 제공한다. 그때 프로그램 상의 트랜잭션 경계 설정은 더 이상 필요하지
않다. 심지어 <literal>Session</literal>을 flush 시키는 것이 자동적으로 행해진다.
</para>
<para>
하지만, 당신의 영속 계층이 이식성을 유지하게끔 자주 희망된다. Hibernate는 당신의 배치 환경의 고유한 트랜잭션 시스템 속으로
변환되는 <literal>Transaction</literal>이라 명명되는 wrapper API 를 제공한다. 이 API는 실제로 옵션이지만 우리는
당신이 CMT session bean 속에 있지 않는 한 그것의 사용을 강력하게 권장한다.
</para>
<para>
대개 <literal>Session</literal> 종료는 네 개의 구분되는 단계들을 수반한다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
세션을 flush 시킨다
</para>
</listitem>
<listitem>
<para>
트랜잭션을 커밋 시킨다
</para>
</listitem>
<listitem>
<para>
세션을 닫는다
</para>
</listitem>
<listitem>
<para>
예외상황들을 처리한다
</para>
</listitem>
</itemizedlist>
<para>
세션을 flush 시키는 것은 앞서 논의되었고, 우리는 이제 관리되는 환경과 관리되지 않는 환경 양자에서 트랜잭션 경계 설정과 예외상황을
더 자세히 살펴볼 것이다.
</para>
<sect2 id="transactions-demarcation-nonmanaged">
<title>관리되지 않는 환경</title>
<para>
만일 Hibernate 영속 계층이 관리되지 않는(non-managed) 환경에서 실행될 경우, 데이터베이스 커넥션들은 대개 Hibernate의
풀링 메커니즘에 의해 처리된다. session/transaction 처리 관용구는 다음과 같이 보여진다:
</para>
<programlisting><![CDATA[//Non-managed environment idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}]]></programlisting>
<para>
당신은 <literal>Session</literal>을 명시적으로 <literal>flush()</literal>시키지 말아야 한다 -
<literal>commit()</literal> 호출은 동기화를 자동적으로 트리거 시킨다.
</para>
<para>
<literal>close()</literal> 호출은 세션의 종료를 마크한다. <literal>close()</literal>의 주된 의미는
JDBC 커넥션이 그 세션에 의해 포기될 것이라는 점이다.
</para>
<para>
이 자바 코드는 이식성이 있고 관리되지 않는 환경과 JTA 환경 양자에서 실행된다.
</para>
<para>
당신은 통상의 어플리케이션에서 비지니스 코드 속에 이 관용구를 결코 보지 않을 것이다; 치명적인(시스템) 예외상황들은 항상
"상단"에서 잡혀야 한다. 달리 말해, Hibernate를 실행하는 코드가 (영속 계층에서) 호출되고 <literal>RuntimeException</literal>
처리하는 (그리고 대개 오직 제거하고 빠져나갈 수 있는) 코드는 다른 계층들 속에 있다. 이것은 당신 자신이 설계하는 도전점일
수 있고 당신은 J2EE/EJB 컨테이너 서비스들이 이용 가능할 때마다 J2EE/EJB 컨테이너 서비스들을 사용할 것이다. 예외상황
처리는 이 장의 뒷부분에서 논의된다.
</para>
<para>
당신은 (디폴트인) <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>를 선택해야 함을 노트하라.
</para>
</sect2>
<sect2 id="transactions-demarcation-jta">
<title>JTA 사용하기</title>
<para>
만일 당신의 영속 계층이 어플리케이션 서버에서(예를 들어, EJB 세션 빈즈 이면에서) 실행될 경우, Hibernate에 의해
획득된 모든 데이터소스 커넥션은 자동적으로 전역 JTA 트랜잭션의 부분일 것이다. Hibernate는 이 통합을 위한 두 개의
방도들을 제공한다.
</para>
<para>
만일 당신이 bean-managed transactions(BMT)를 사용할 경우 Hibernate는 당신이 <literal>Transaction</literal>
API를 사용할 경우에 BMT 트랜잭션을 시작하고 종료하도록 어플리케이션 서버에게 알려줄 것이다. 따라서 트랜잭션 관리 코드는
non-managed 환경과 동일하다.
</para>
<programlisting><![CDATA[// BMT idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}]]></programlisting>
<para>
CMT의 경우, 트랜잭션 관할[경계 설정]은 프로그램 상이 아닌, session bean 배치 디스크립터들 속에서 행해진다. 당신이 스스로
<literal>Session</literal>을 수작업으로 flush 시키고 닫고자 원하지 않을 경우, 단지
<literal>hibernate.transaction.flush_before_completion</literal><literal>true</literal>로 설정하고,
<literal>hibernate.connection.release_mode</literal><literal>after_statement</literal> 또는
<literal>auto</literal>로 설정하고 <literal>hibernate.transaction.auto_close_session</literal>
<literal>true</literal>로 설정하라. Hibernate는 그때 자동적으로 flush 되고 당신을 위해 <literal>Session</literal>
닫을 것이다. 남아 있는 유일한 작업은 예외상황이 발생할 때 트랜잭션을 롤백시키는 것이다. 다행하게도 CMT bean에서, 이것이 자동적으로
일어난다. 왜냐하면 session bean 메소드에 의해 던져진 처리되지 않은 <literal>RuntimeException</literal>은 전역 트랜잭션을
롤백시키도록 컨테이너에게 통보하기 때문이다. <emphasis>이것은 당신이 CMT에서 Hibernate <literal>Transaction</literal> API를
사용할 필요가 전혀 없음을 의미한다.</emphasis>
</para>
<para>
당신이 Hibernate의 트랜잭션 팩토리를 구성할 때, 당신이 BMT session bean에서
<literal>org.hibernate.transaction.JTATransactionFactory</literal>를 선택해야하고, CMT session bean에서
<literal>org.hibernate.transaction.CMTTransactionFactory</literal>를 선택해야 함을 노트하라. 또한
<literal>org.hibernate.transaction.manager_lookup_class</literal>를 설정하는 것을 염두에 두라.
</para>
<para>
만일 당신이 CMT 환경에서 작업하고 있고, 세션을 자동적으로 flushing하고 닫는 것을 사용할 경우, 당신은 또한 당신의 코드의 다른
부분들에서 동일한 세션을 사용하고자 원할 수도 있다. 일반적으로 관리되지 않는 환경에서 당신은 세션을 소유하는데 하나의
<literal>ThreadLocal</literal> 변수를 사용할 것이지만, 한 개의 EJB 요청은 다른 쓰레드들(예를 들면 또 다른 세션 빈을
호출하는 세션 빈) 내에서 실행될 수도 있다. 만일 당신이 당신의 <literal>Session</literal> 인스턴스를 전달하는 것을
고민하고 싶지 않다면, <literal>SessionFactory</literal>이 JTA 트랜잭션 컨텍스트에 바인드되어 있는 한 개의 세션을
반환하는, <literal>getCurrentSession()</literal> 메소드를 제공한다. 이것은 Hibernate를 어플리케이션 속으로
통합시키는 가장 손쉬운 방법이다! "현재" 세션은 (위의 프로퍼티 설정들에 관계없이) auto-flush와 auto-close, 그리고
auto-connection-release를 항상 이용 가능하게 한다. 우리의 session/transaction 관리 idiom은 다음과 같이 감소된다:
</para>
<programlisting><![CDATA[// CMT idiom
Session sess = factory.getCurrentSession();
// do some work
...
]]></programlisting>
<para>
달리 말해, 당신이 관리 환경에서 행해야 하는 모든 것은 <literal>SessionFactory.getCurrentSession()</literal>
호출하고, 당신의 데이터 접근 작업을 행하고, 그리고 나머지를 컨테이너에게 남겨두는 것이다. 트랜잭션 경계들은 당신의
session bean의 배치 디스크립터들 속에 선언적으로 설정된다. 그 세션의 생명주기는 Hibernate에 의해 완전하게 관리된다.
</para>
<para>
<literal>after_statement</literal> 커넥션의 사용에는 한 가지 단서가 존재한다. JTA 명세서의 분별없는 제약성으로 인해,
<literal>scroll()</literal> 또는 <literal>iterate()</literal>에 의해 반환된 어떤 닫혀지지 않은
<literal>ScrollableResults</literal> 또는 <literal>Iterator</literal> 인스턴스들을 Hibernate가 자동적으로
제거하는 것이 불가능하다. 당신은 <literal>finally</literal> 블록에서 <literal>ScrollableResults.close()</literal>
또는 <literal>Hibernate.close(Iterator)</literal>를 명시적으로 호출하여 기본 데이터베이스 커서를
해제<emphasis>시켜야 한다</emphasis>. (물론 대부분의 어플리케이션들은 CMT 코드에서 <literal>scroll()</literal>
또는 <literal>iterate()</literal> 사용을 쉽게 피할 수 있다.)
</para>
</sect2>
<sect2 id="transactions-demarcation-exceptions">
<title>예외상황 처리</title>
<para>
만일<literal>Session</literal>이 (어떤 <literal>SQLException</literal>을 포함하는) 예외상황을 던질 경우,
당신은 데이터베이스 트랜잭션을 즉시 롤백시키고, <literal>Session.close()</literal>를 호출하고
<literal>Session</literal> 인스턴스를 폐기시켜야한다. <literal>Session</literal>의 어떤 메소드들은
그 세션을 일관된 상태로 남겨두지 <emphasis>않을</emphasis> 것이다. Hibernate에 의해 던져진 예외상황은 복구가능한
것으로 취급될 수 없다. 그 <literal>Session</literal><literal>finally</literal> 블록 내에서
<literal>close()</literal>를 호출하여 닫혀지도록 확실히 하라.
</para>
<para>
Hibernate 영속 계층에서 발생할 수 있는 대부분의 오류들을 포장하는, <literal>HibernateException</literal>
체크되지 않은 예외상황이다(그것은 Hibernate의 이전 버전에는 없었다). 우리의 의견으로, 우리는 낮은 계층에서 복구불가능한
예외상황을 붙잡도록 어플리케이션 개발자에게 강제하지 않을 것이다. 대부분의 시스템들에서, 체크되지 않은 치명적인 예외상황들은
(예를 들어, 더 높은 계층에서) 메소드 호출 스택의 첫 번째 프레임들 중 하나 속에서 처리되고, 한 개의 오류 메시지가 어플리케이션
사용자에게 표시된다(또는 어떤 다른 적절한 액션이 취해진다). Hibernate는 또한 <literal>HibernateException</literal>
아닌, 다른 체크되지 않은 예외상황들을 던질 수도 있음을 노트하라. 다시 이것들은 복구가능하지 않고 적절한 액션이 취해져야 한다.
</para>
<para>
Hibernate는 데이터베이스와 상호작용하는 동안에 던져진 <literal>SQLException</literal>들을 하나의
<literal>JDBCException</literal> 속에 포장한다. 사실, Hibernate는 그 예외상황을
<literal>JDBCException</literal>의 보다 의미있는 서브클래스로 변환하려고 시도할 것이다. 기본
<literal>SQLException</literal><literal>JDBCException.getCause()</literal>를 통해 항상 이용 가능하다.
Hibernate는<literal>SessionFactory</literal>에 첨부된 <literal>SQLExceptionConverter</literal>
사용하여 <literal>SQLException</literal>을 적당한 하나의 <literal>JDBCException</literal> 서브클래스로
변환시킨다. 디폴트로 <literal>SQLExceptionConverter</literal>는 구성된 dialect에 의해 정의된다; 하지만
맞춤 구현 속에 플러그인 시키는 것이 또한 가능하다(상세한 것은 <literal>SQLExceptionConverterFactory</literal>
클래스에 관한 javadocs를 보라). 표준 <literal>JDBCException</literal> 서브타입은 다음과 같다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>JDBCConnectionException</literal> - 기본 JDBC 통신에 대한 오류를 나타낸다.
</para>
</listitem>
<listitem>
<para>
<literal>SQLGrammarException</literal> - 생겨난 SQL에 대한 문법 또는 구문 문제점을 나타낸다.
</para>
</listitem>
<listitem>
<para>
<literal>ConstraintViolationException</literal> - 무결성 제약 위반에 관한 어떤 형식을 나타낸다.
</para>
</listitem>
<listitem>
<para>
<literal>LockAcquisitionException</literal> - 요청된 오퍼레이션을 실행하는데 필수적인 잠금 레벨을
획득하는 오류를 나타낸다.
</para>
</listitem>
<listitem>
<para>
<literal>GenericJDBCException</literal> - 다른 카테고리들 중 어떤 것으로 분류되지 않았던 일반적인 예외상황.
</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1 id="transactions-optimistic">
<title>Optimistic 동시성 제어</title>
<para>
고도의 동시성과 고도의 가용성을 일치시키는 유일한 접근법은 버전화를 가진 optimistic동시성 제어이다. 버전 체킹은 업데이트 충돌을
검출하기 위해(그리고 업데이트 손실을 방지하기 위해) 버전 번호들 또는 timestamp들을 사용한다. Hibernate는 optimistic 동시성을
사용하는 어플리케이션 코드 작성에 세 가지 가능한 접근법들을 제공한다. 우리가 보여주는 쓰임새들은 긴 어플리케이션 트랜잭션들의 상황
속에 있지만 버전 체킹 또한 단일 데이터베이스 트랜잭션들에서 업데이트 손실을 방지하는 이점을 갖고 있다.
</para>
<sect2 id="transactions-optimistic-manual">
<title>어플리케이션 버전 체킹</title>
<para>
하나의 구현에서 Hibernate로부터 많은 도움이 없이, 데이터베이스에 대한 각각의 상호작용은 새로운 <literal>Session</literal>
내에서 일어나고, 개발자는 영속 인스턴스들을 처리하기 전에 데이터베이스로부터 모든 영속 인스턴스들을 다시 로드시킬 책임이 있다.
이 접근법은 어플리케이션 트랜잭션을 확실히 격리시키기 위해 그것 자신의 버전 체킹을 수행하도록 어플리케이션에게 강제시킨다.
</para>
<programlisting><![CDATA[// foo is an instance loaded by a previous Session
session = factory.openSession();
Transaction t = session.beginTransaction();
int oldVersion = foo.getVersion();
session.load( foo, foo.getKey() ); // load the current state
if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException();
foo.setProperty("bar");
t.commit();
session.close();]]></programlisting>
<para>
version 프로퍼티는 <literal>&lt;version&gt;</literal>을 사용하여 매핑되고, Hibernate는 만일 엔티티가 dirty일 경우
flush 동안에 그것을 자동적으로 증가시킬 것이다.
</para>
<para>
물론, 당신이 낮은 데이터 동시성 환경에서 작업하고 있고 버전 체킹을 필요로 하지 않을 경우에, 당신은 이 접근법을 사용할 수 도 있고
단지 버전 체크를 생략할 수도 있다. 그 경우에, <emphasis>마지막의 커밋 성공</emphasis>은 당신의 긴 어플리케이션 트랜잭션들에
대한 디폴트 방도가 될 것이다. 이것이 어플리케이션의 사용자들을 혼동시킬 수 있음을 염두에 두라. 왜냐하면 사용자들은 오류 메시지들
또는 충돌 변경들을 병합시킬 기회 없이 업데이트들 손실을 겪을 수도 있기 때문이다.
</para>
<para>
명료하게 수작업 버전 체킹은 매우 사소한 환경들에서도 공포적이고 대부분의 어플리케이션들에 대해 실제적이지 않다. 흔히 단일
인스턴스 뿐만 아니라 변경된 객체들의 전체 그래프들이 체크되어야 한다. Hibernate는 설계 패러다임으로서 긴 <literal>Session</literal>
또는 detached 인스턴스들에 대해 자동적인 버전 체킹을 제공한다.
</para>
</sect2>
<sect2 id="transactions-optimistic-longsession">
<title>긴 세션과 자동적인 버전화</title>
<para>
하나의 <literal>Session</literal> 인스턴스와 그것의 영속 인스턴스들은 전체 어플리케이션 트랜잭션에 사용된다. Hibernate는
flush 할 때 인스턴스 버전들을 체크하고 만일 동시성 변경이 검출될 경우에 예외상황을 던진다. 이 예외상황을 잡아내고 처리하는 것을
개발자의 몫이다(공통된 옵션들은 변경들을 병합시키거나 또는 쓸모가 없지 않은 데이터로 비지니스 프로세스를 다시 시작하는 기회를
사용자에게 주는 것이다).
</para>
<para>
<literal>Session</literal>은 사용자 상호작용을 기다릴 때 어떤 기본 JDBC 커넥션으로부터 연결해제된다. 이 접근법은
데이터베이스 접근의 관점에서 보면 가장 효율적이다. 어플리케이션은 버전 체킹 또는 detached 인스턴스들을 재첨부하는 것에
그 자체 관계할 필요가 없거나 그것은 모든 데이터베이스 트랜잭션에서 인스턴스들을 다시 로드시킬 필요가 없다.
</para>
<programlisting><![CDATA[// foo is an instance loaded earlier by the Session
session.reconnect(); // Obtain a new JDBC connection
Transaction t = session.beginTransaction();
foo.setProperty("bar");
t.commit(); // End database transaction, flushing the change and checking the version
session.disconnect(); // Return JDBC connection ]]></programlisting>
<para>
<literal>foo</literal> 객체는 그것이 로드되었던 <literal>Session</literal>이 어느 것인지를 여전히 알고 있다.
<literal>Session.reconnect()</literal>은 새로운 커넥션을 획득하고(또는 당신이 커넥션을 제공할 수 있다) 그리고
그 세션을 다시 시작시킨다. <literal>Session.disconnect()</literal> 메소드는 JDBC 커넥션으로부터 세션을
연결 해제하고 (당신이 커넥션을 제공하지 않는 한) 그 커넥션을 풀(pool)로 반환할 것이다. 재연결 후에, 당신이 업데이트하고
있는 데이터에 대한 버전 체킹을 강제시키기 위해, 당신은 또 다른 트랜잭션에 의해 업데이트 되었던 어떤 객체들에 대해
<literal>LockMode.READ</literal>로서 <literal>Session.lock()</literal>을 호출할 수도 있다. 당신은 당신이
업데이트 <emphasis>중인</emphasis> 어떤 데이터에 대한 잠금을 필요로 하지 않는다.
</para>
<para>
만일 <literal>disconnect()</literal><literal>reconnect()</literal>에 대한 명시적인 호출들이 너무 번거러울 경우,
당신은 대신에 <literal>hibernate.connection.release_mode</literal>를 사용할 수도 있다.
</para>
<para>
만일 사용자가 생각하는시간 동안 <literal>Session</literal>이 저장되기에는 너무 큰 경우 이 패턴은 문제성이 있다. 예를 들어
<literal>HttpSession</literal>은 가능한 작은 것으로 유지되어야 한다. 또한 <literal>Session</literal>은 (필수의)
첫 번째 레벨 캐시이고 모든 로드된 객체들을 포함하기 때문에, 우리는 아마 적은 요청/응답 주기들에 대해서만 이 방도를 사용할 수 있다.
<literal>Session</literal>이 곧 실효성이 없는 데이터를 갖게 될 것이므로 이것이 진정으로 권장된다.
</para>
<para>
또한 당신이 연결해제된 <literal>Session</literal>을 영속 계층에 가깝게 유지해야함을 노트하라. 달리말해,
<literal>Session</literal>을 보관하는데 EJB stateful session bean을 사용하고 <literal>HttpSession</literal>
내에 그것을 저장하기 위해서 그것을 웹 계층에 전송하지 말라(또는 그것을 별도의 티어에 직렬화 시키지도 말라).
</para>
</sect2>
<sect2 id="transactions-optimistic-detached">
<title>Detached 객체들과 자동적인 버전화</title>
<para>
영속 저장소에 대한 각각의 상호작용은 새로운 <literal>Session</literal>에서 일어난다. 하지만 동일한 영속 인스턴스들은
데이터베이스와의 각각의 상호작용에 재사용된다. 어플리케이션은 원래 로드되었던 detached 인스턴스들의 상태를 또 다른
<literal>Session</literal> 내에서 처리하고 나서 <literal>Session.update()</literal>,
<literal>Session.saveOrUpdate()</literal>, <literal>Session.merge()</literal>를 사용하여 그것들을
다시 첨부시킨다.
</para>
<programlisting><![CDATA[// foo is an instance loaded by a previous Session
foo.setProperty("bar");
session = factory.openSession();
Transaction t = session.beginTransaction();
session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already
t.commit();
session.close();]]></programlisting>
<para>
다시, Hibernate는 flush 동안에 인스턴스 버전들을 체크할 것이고 업데이트 충돌이 발생할 경우에 예외상황을 던질 것이다.
</para>
<para>
당신은 또한 <literal>update()</literal>대신에 <literal>lock()</literal>을 호출할 수도 있고 만일 그 객체가
변경되지 않았음을 당신이 확신하는 경우에 (버전 체킹을 수행하고 모든 캐시들을 무시하는) <literal>LockMode.READ</literal>
사용할 수 있다.
</para>
</sect2>
<sect2 id="transactions-optimistic-customizing">
<title>자동적인 버전화를 맞춤화 시키기</title>
<para>
당신은 <literal>optimistic-lock</literal> 매핑 속성을 <literal>false</literal>로 설정함으로써 특정 프로퍼티들과
콜렉션들에 대한 Hibernate의 자동적인 버전 증가를 불가능하도록 할 수도 있다. 그때 Hibernate는 그 프로퍼티가 dirty 일 경우에
더 이상 버전을 증가시키지 않을 것이다.
</para>
<para>
리거시 데이터베이스 스키마들은 자주 static이고 변경될 수 없다. 또는 다른 어플리케이션들은 또한 동일한 데이터베이스에 접근하고
버전 번호들 또는 심지어 timestamp들을 처리하는 방법을 모를 수도 있다. 두 경우들에서, 버전화는 테이블 내의 특정 컬럼에 의지할 수
없다. version 또는 timestamp 프로퍼티 매핑 없이 행 내의 모든 필드들에 대한 상태를 비교하여 버전 체크를 강제시키기 위해서,
<literal>&lt;class&gt;</literal> 매핑 속에 <literal>optimistic-lock="all"</literal>을 표시하라. 만일
Hibernate가 이전 상태와 새로운 상태를 비교할 수 있을 경우에, 예를 들면 당신이 하나의 긴 <literal>Session</literal>
사용하고 session-per-request-with-detached-objects을 사용하지 않을 경우 이것은 개념적으로만 동작함을 노트하라.
</para>
<para>
때때로 행해졌던 변경들이 중첩되지 않는 한 동시적인 변경이 허용될 수 있다. 만일 <literal>&lt;class&gt;</literal>
매핑할 때 당신이 <literal>optimistic-lock="dirty"</literal>를 설정하면, Hibernate는 flush 동안에 dirty 필드들을
비교만 할 것이다.
</para>
<para>
두 경우들에서, 전용 version/timestamp 컬럼의 경우 또는 full/dirty 필드 비교의 경우, Hibernate는 법전 체크를 실행하고
정보를 업데이트하는데 엔티티 당 (적절한 <literal>WHERE</literal> 절을 가진) 한 개의<literal>UPDATE</literal>
문장을 사용한다. 만일 당신이 연관된 엔티티들에 대한 재첨부를 케스케이드 하는데 transitive 영속을 사용할 경우,
Hibernate는 불필요하게 업데이트들을 실행할 수도 있다. 이것은 대개 문제가 아니지만, 심지어 변경들이 detached 인스턴스들에
대해 행해지지 않았을 때에도 데이터베이스 내에서 <emphasis>on update</emphasis> 트리거들이 실행될 수도 있다. 그 행을
업데이트하기 전에 변경들이 실제로 일어났음을 확인하기 위해 인스턴스를 <literal>SELECT</literal>하는 것을 Hibernate에게
강제시키는, <literal>&lt;class&gt;</literal> 매핑 속에 <literal>select-before-update="true"</literal>
설정함으로써 당신은 이 특징을 맞춤화 시킬 수 있다.
</para>
</sect2>
</sect1>
<sect1 id="transactions-locking">
<title>Pessimistic 잠금</title>
<para>
사용자들은 잠금 방도에 대해 걱정하는데 많은 시간을 할애하하려고 생각하지 않는다. 대개 JDBC 커넥션들에 대한 격리 레벨을 지정하는
것으로 충분하고 그런 다음 단순히 데이터베이스로 하여금 모든 작업을 행하도록 한다. 하지만 진일보한 사용자들은 때때로 배타적인
pessimistic 잠금들을 얻거나 또는 새로운 트랜잭션의 시작 시에 잠금들을 다시 얻고자 원할 수도 있다.
</para>
<para>
Hibernate는 결코 메모리 내에 있는 객체들이 아닌, 데이터베이스의 잠금 메커니즘을 항상 사용할 것이다!
</para>
<para>
<literal>LockMode</literal> 클래스는 Hibernate에 의해 획득될 수 있는 다른 잠금 레벨들을 정의한다. 잠금은 다음 메커니즘들에
의해 얻어진다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>LockMode.WRITE</literal>는 Hibernate가 한 행을 업데이트 하거나 insert 할 때 자동적으로 획득된다.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.UPGRADE</literal><literal>SELECT ... FOR UPDATE</literal> 구문을 지원하는
데이터베이스 상에서 <literal>SELECT ... FOR UPDATE</literal>를 사용하여 명시적인 사용자 요청 상에서
얻어질 수 있다.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.UPGRADE_NOWAIT</literal>는 오라클에서 <literal>SELECT ... FOR UPDATE NOWAIT</literal>
사용하여 명시적인 사용자 요청 상에서 얻어질 수도 있다.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.READ</literal>는 Hibernate가 반복 가능한 읽기(Repeatable Read) 또는 Serialization
격리 레벨에서 데이터를 읽어들일 때 자동적으로 얻어질 수도 있다. 명시적인 사용자 요청에 의해 다시 얻어질 수도 있다.
</para>
</listitem>
<listitem>
<para>
<literal>LockMode.NONE</literal>은 잠금이 없음을 나타낸다. 모든 객체들은 <literal>Transaction</literal>의 끝에서
이 잠금 모드로 전환된다. <literal>update()</literal> 또는 <literal>saveOrUpdate()</literal>에 대한 호출을 통해
세션과 연관된 객체들이 또한 이 잠금 모드로 시작된다.
</para>
</listitem>
</itemizedlist>
<para>
"명시적인 사용자 요청"은 다음 방법들 중 하나로 표현된다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>LockMode</literal>를 지정한 <literal>Session.load()</literal>에 대한 호출.
</para>
</listitem>
<listitem>
<para>
<literal>Session.lock()</literal>에 대한 호출.
</para>
</listitem>
<listitem>
<para>
<literal>Query.setLockMode()</literal>에 대한 호출.
</para>
</listitem>
</itemizedlist>
<para>
만일 <literal>Session.load()</literal><literal>UPGRADE</literal> 또는 <literal>UPGRADE_NOWAIT</literal>
모드로 호출되고 ,요청된 객체가 아직 이 세션에 의해 로드되지 않았다면, 그 객체는 <literal>SELECT ... FOR UPDATE</literal>
사용하여 로드된다. 만일 요청된 것이 아닌 다소 제한적인 잠금으로 이미 로드되어 있는 객체에 대해 <literal>load()</literal>
호출될 경우, Hibernate는 그 객체에 대해 <literal>lock()</literal>을 호출한다.
</para>
<para>
만일 지정된 잠금 모드가 <literal>READ</literal>, <literal>UPGRADE</literal> 또는
<literal>UPGRADE_NOWAIT</literal> 일 경우에 <literal>Session.lock()</literal>은 버전 번호 체크를 수행한다.
(<literal>UPGRADE</literal> 또는 <literal>UPGRADE_NOWAIT</literal> 인 경우에,
<literal>SELECT ... FOR UPDATE</literal>가 사용된다.)
</para>
<para>
만일 데이터베이스가 요청된 잠금 모드를 지원하지 않을 경우, (예외상황을 던지는 대신에) Hibernate는 적절한 대체 모드를 사용할 것이다.
이것은 어플리케이션이 이식 가능할 것임을 확실히 해준다.
</para>
</sect1>
</chapter>