git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@9072 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
JongDae Kim 2006-01-17 04:13:35 +00:00
parent 63815f5970
commit a6607faacc
13 changed files with 811 additions and 281 deletions

View File

@ -236,5 +236,73 @@
</para>
</sect1>
<sect1 id="architecture-current-session" revision="1">
<title>컨텍스트 상의 세션들</title>
<para>
Hibernate를 사용하는 대부분의 어플리케이션들은 어떤 양식의 "컨텍스트상의(contextual)" 세션들을 필요로 한다. 여기서
주어진 세션은 주어진 컨텍스트의 영역에 걸쳐 활동을 한다. 하지만 어플리케이션들을 가로질러 컨텍스트를 구성하는 것에 대한 정의는
일반적으로 다르다; 그리고 다른 컨텍스트들은 현재라고 하는 개념에 대해 다른 영역들을 정의한다. 버전 3.0 전의 Hibernate를
사용하는 어플리케이션들은 자가생산된 <literal>ThreadLocal</literal>-기반의 컨텍스상의 세션들,
<literal>HibernateUtil</literal>과 같은 helper 클래스들을 활용했거나
프락시/인터셉션 기반의 컨텍스트상의 세션들을 제공해주었던 (Spring 또는 Pico와 같은 )제 3의 프레임웍들을 활용하는 경향이 있었다.
</para>
<para>
버전 3.0.1에서부터 시작하여, Hibernate는 <literal>SessionFactory.getCurrentSession()</literal> 메소드를
추가했다. 초기에 이것은 <literal>JTA</literal> 트랜잭션들을 사용하는 것을 전제했다. 여기서 <literal>JTA</literal>
트랜잭션은 현재 세션의 영역과 컨텍스트를 정의했다. Hibernate 팀은 성숙된 다수의 스탠드얼론 <literal>JTA TransactionManager</literal>
구현들이 발표되면, (전부는 아니겠지만) 대부분의 어플리케이션들이 그것들이 <literal>J2EE</literal> 컨테이너 내로 배치되든
그렇지 않든 간에 <literal>JTA</literal> 트랜잭션 관리를 사용하게 될 것이라고 주장한다. 그것에 기초하여, <literal>JTA</literal>에 기반한
컨텍스트상의 세션들은 언젠가 당신이 사용을 필요로 하게 될 전부다.
</para>
<para>
하지만 버전 3.1 이후로 <literal>SessionFactory.getCurrentSession()</literal> 이면의 처리과정은
이제 플러그 가능하다. 그것을 끝내기 위해, 하나의 새로운 확장 인터페이스
(<literal>org.hibernate.context.CurrentSessionContext</literal>)와
하나의 새로운 구성 파라미터(<literal>hibernate.current_session_context_class</literal>)가 현재 세션들을
정의하는 영역과 컨텍스트의 플러그 가능성을 허용하기 위해 추가되었다.
</para>
<para>
그것의 계약에 대한 상세한 논의는 <literal>org.hibernate.context.CurrentSessionContext</literal> 인터페이스에
관한 javadocs를 보라. 그것은 하나의 메소드, <literal>currentSession()</literal>를 정의하며, 그 구현은
현재의 컨텍스트 상의 세션을 추적할 책임이 있다. 비공식적으로, Hibernate는 이 인터페이스에 대한 구 개의 구현들을 부수적으로
포함하고 있다.
</para>
<itemizedlist>
<listitem>
<para>
<literal>org.hibernate.context.JTASessionContext</literal> - 현재의 세션들은 하나의
<literal>JTA</literal>에 의해 추적되고 영역화 된다. 여기서 처리과정은 이전의 JTA-전용 접근과 정확하게
동일하다. 상세한 것은 javadocs를 보라.
</para>
</listitem>
<listitem>
<para>
<literal>org.hibernate.context.ThreadLocalSessionContext</literal> - 현재의 세션들은
실행 쓰레드에 의해 추적된다. 상세한 것은 다시 javadocs를 보라.
</para>
</listitem>
</itemizedlist>
<para>
두 구현들은 <emphasis>session-per-request</emphasis>로 알려지고 사용되고 있는
"하나의 세션 - 하나의 데이터베이스 트랜잭션" 프로그래밍 모형을 제공한다. 하나의 Hibernate 세션의 시작과 끝은
데이터베이스 트랜잭션의 존속 기간에 의해 정의된다. 만일 (예를 들면 순수 J2SE에서 또는 JTA/UserTransaction/BMT의 경우에)
당신이 프로그램 상의 트랜잭션 경계구분을 사용할 경우, 당신은 당신의 코드로부터 기본 트랜잭션 시스템을 은폐시키는데
Hibernate <literal>Transaction</literal> API를 사용하는 것이 권장된다. 만일 당신이 CMT를 지원하는
하나의 EJB 컨테이너에서 실행할 경우, 트랜잭션 경계들이 선언적으로 정의되고 당신은 당신의 코드 내에 어떤 트랜잭션도
세션 경계구분 오퍼레이션들을 필요로 하지 않는다. 추가 정보와 코드 예제들은 <xref linkend="transactions"/>를 참조하라.
</para>
<para>
<literal>hibernate.current_session_context_class</literal> 구성 파라미터는
<literal>org.hibernate.context.CurrentSessionContext</literal> 구현이 사용될 것임을 정의한다. 역호환을 위해.
만일 이 구성 파라미터가 설정되지 않았지만 하나의 <literal>org.hibernate.transaction.TransactionManagerLookup</literal>
구성되어 있을 경우, Hibernate는 <literal>org.hibernate.context.JTASessionContext</literal>를 사용할 것임을
노트하라. 일반적으로, 이 파라미터의 값은 단지 사용할 구현 클래스를 명명할 것이다; 하지만 두 가지 비공식적인 구현들로서 두 개의
대응하는 짧은 이름들 "jta"와 "thread"이 존재한다.
</para>
</sect1>
</chapter>

View File

@ -292,7 +292,7 @@ create table Address ( addressId bigint not null primary key )
<sect1 id="assoc-bidirectional" revision="1">
<title>양방향 연관들</title>
<sect2 id="assoc-bidirectional-m21" revision="1">
<sect2 id="assoc-bidirectional-m21" revision="2">
<title>one to many / many to one</title>
<para>
@ -352,6 +352,13 @@ create table Address ( addressId bigint not null primary key )
</list>
</class>]]></programlisting>
<para>
만일 기본 외래 키 컬럼이 <literal>NOT NULL</literal>일 경우 콜렉션 매핑의 <literal>&lt;key&gt;</literal>
요소 상에 <literal>not-null="true"</literal>를 정의하는 것이 중요하다. 내포된
<literal>&lt;column&gt;</literal> 요소 상에 <literal>not-null="true"</literal>
선언하지 말고, <literal>&lt;key&gt;</literal> 요소 상에 선언하라.
</para>
</sect2>
<sect2 id="assoc-bidirectional-121">

View File

@ -990,7 +990,7 @@
</sect2>
<sect2 id="mapping-declaration-version" revision="3">
<sect2 id="mapping-declaration-version" revision="4">
<title>version (옵션)</title>
<para>
@ -1015,7 +1015,7 @@
type="typename"
access="field|property|ClassName"
unsaved-value="null|negative|undefined"
generated="true|false"
generated="never|always"
insert="true|false"
node="element-name|@attribute-name|element/@attribute|."
/>]]></programlisting>
@ -1052,7 +1052,7 @@
</callout>
<callout arearefs="version6">
<para>
<literal>generated</literal> (옵션 - 디폴트는 <literal>false</literal>):
<literal>generated</literal> (옵션 - 디폴트는 <literal>never</literal>):
이 version 프로퍼티 값이 데이터베이스에 의해 실제로 산출되는지를 지정한다.
<xref linkend="mapping-generated">산출되는 프로퍼티들</xref>에 관한 논의를 보라.
</para>
@ -1082,7 +1082,7 @@
</para>
</sect2>
<sect2 id="mapping-declaration-timestamp" revision="2" >
<sect2 id="mapping-declaration-timestamp" revision="3" >
<title>timestamp (옵션)</title>
<para>
@ -1106,7 +1106,7 @@
access="field|property|ClassName"
unsaved-value="null|undefined"
source="vm|db"
generated="true|false"
generated="never|always"
node="element-name|@attribute-name|element/@attribute|."
/>]]></programlisting>
<calloutlist>
@ -1150,7 +1150,7 @@
</callout>
<callout arearefs="timestamp6">
<para>
<literal>generated</literal> (옵션 - 디폴트는 <literal>false</literal>):
<literal>generated</literal> (옵션 - 디폴트는 <literal>never</literal>):
이 timestamp 프로퍼티 값이 데이터베이스에 의해 실제로 산출되는지를 지정한다.
<xref linkend="mapping-generated">산출되는 프로퍼티들</xref>에 대한 논의들 보라.
</para>
@ -1166,7 +1166,7 @@
</sect2>
<sect2 id="mapping-declaration-property" revision="3">
<sect2 id="mapping-declaration-property" revision="4">
<title>프로퍼티</title>
<para>
@ -1202,7 +1202,7 @@
unique="true|false"
not-null="true|false"
optimistic-lock="true|false"
generated="true|false"
generated="never|insert|always"
node="element-name|@attribute-name|element/@attribute|."
index="index_name"
unique_key="unique_key_id"
@ -1278,7 +1278,7 @@
</callout>
<callout arearefs="property12">
<para>
<literal>generated</literal> (옵션 - 디폴트는 <literal>false</literal>):
<literal>generated</literal> (옵션 - 디폴트는 <literal>never</literal>):
이 프로퍼티 값이 데이터베이스에 의해 실제로 산출되는지를 지정한다.
<xref linkend="mapping-generated">산출되는 프로퍼티들</xref>에 대한 논의를 보라.
</para>
@ -3059,7 +3059,7 @@ public class Customer implements Serializable {
</sect2>
</sect1>
<sect1 id="mapping-generated">
<sect1 id="mapping-generated" revision="1">
<title>산출되는 프로퍼티들</title>
<para>
산출되는 프로퍼티들은 데이터베이스에 의해 산출되는 그것들의 값들을 갖는 프로퍼티들이다. 전형적으로,
@ -3077,6 +3077,19 @@ public class Customer implements Serializable {
<xref linkend="mapping-declaration-property">단순 프로퍼티들</xref> 만이 generated로
마크될 수 있다.
</para>
<para>
<literal>never</literal> (디폴트) - 는 주어진 프로퍼티 값이 데이터베이스 내에 생성되지 않을 것임을 의미한다.
</para>
<para>
<literal>insert</literal> - 는 주어진 프로퍼티 값이 insert 시에 생성되지만, 차후의 업데이트들에서 다시
생성되지 않을 것임을 기술한다. 생성-날짜와 같은 것들이 이 카테고리 내로 포함될 것이다. 비록
<xref linkend="mapping-declaration-version">version</xref>
<xref linkend="mapping-declaration-timestamp">timestamp</xref> 프로퍼티들이 생성되는 것으로서
마크될 수 있을 지라도, 이 옵션은 거기서 이용 불가능함을 노트하라...
</para>
<para>
<literal>always</literal> - 는 프로퍼티 값이 insert 시와 update 시 모두에 생성됨을 기술한다.
</para>
</sect1>
<sect1 id="mapping-database-object">

View File

@ -171,7 +171,8 @@ session.close();]]></programlisting>
</listitem>
<listitem>
<para>
join들은 (함축적이든 명시적이든) 대량 HQL 질의 속에 지정될 수 없다. 서브-질의들이 where-절에 사용될 수 있다;
<xref linkend="queryhql-joins-forms">join들</xref>은 (함축적이든 명시적이든)
대량 HQL 질의 속에 지정될 수 없다. 서브-질의들이 where-절에 사용될 수 있다;
서브질의들 그 자신들은 조인들을 포함할 수 있다.
</para>
</listitem>

View File

@ -144,9 +144,9 @@ kittens = cat.getKittens(); // Okay, kittens collection is a Set
</callout>
<callout arearefs="mappingcollection4">
<para>
<literal>lazy</literal> (옵션 - 디폴트는 <literal>true</literal>)
<literal>lazy</literal> (옵션 - 디폴트는 <literal>true</literal>)
lazy 페칭을 사용 불가능하도록 하고 그 연관이 항상 eagerly 페치됨을 지정하는데 , 또는 대부분의
연산들이 콜렉션을 초기화시키지 않는 "extra-lazy" 페칭을 이용 가능하도록 하는데(매우 큰 콜렉션들에
연산들이 콜렉션을 초기화시키지 않는 곳에서 "extra-lazy" 페칭을 이용 가능하도록 하는데(매우 큰 콜렉션들에
적당함) 사용될 수 있다
</para>
</callout>

View File

@ -782,7 +782,7 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
</tgroup>
</table>
<table frame="topbot" id="configuration-transaction-properties" revision="8">
<table frame="topbot" id="configuration-transaction-properties" revision="9">
<title>Hibernate 트랜잭션 프로퍼티들</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="1*"/>
@ -838,8 +838,9 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
<literal>hibernate.transaction.flush_before_completion</literal>
</entry>
<entry>
만일 사용가능토록 하면, 트랜잭션의 before completion 단계 동안에 세션이 자동적으로 flush 될 것이다.
(CMT에 대해 Hibernate를 사용할 때 매우 유용하다.)
만일 사용가능하도록 되면, 세션은 트랜잭션의 before completion 단계 동안에 자동적으로 flush 될 것이다.
빌드되어 있는 자동적인 세션 컨텍스트 관리가 선호되는데,
<xref linkend="architecture-current-session"/>를 보라.
<para>
<emphasis role="strong">예.</emphasis>
<literal>true</literal> | <literal>false</literal>
@ -863,7 +864,7 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
</tgroup>
</table>
<table frame="topbot" id="configuration-misc-properties" revision="8">
<table frame="topbot" id="configuration-misc-properties" revision="9">
<title>여러가지 프로퍼티들</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="1*"/>
@ -875,6 +876,21 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
</row>
</thead>
<tbody>
<row>
<entry>
<literal>hibernate.current_session_context_class</literal>
</entry>
<entry>
"현재" <literal>Session</literal>의 영역화를 위한 하나의 (맞춤) 방도를
공급한다. 빌드되어 있는 방도들에 대한 추가 정보는
<xref linkend="architecture-current-session"/>를 보라.
<para>
<emphasis role="strong">예.</emphasis>
<literal>jta</literal> | <literal>thread</literal> |
<literal>custom.Class</literal>
</para>
</entry>
</row>
<row>
<entry>
<literal>hibernate.query.factory_class</literal>
@ -1331,10 +1347,10 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
<itemizedlist>
<listitem>
<para>
<emphasis>JTA Session 바인딩</emphasis>: Hibernate <literal>Session</literal>당신이 EJB들을 사용하고자
원할 경우에 JTA 트랜잭션들의 영역(scope)에 자동적으로 바인드 시킬 수도 있다. 간단하게 JNDI로부터 <literal>SessionFactory</literal>
<emphasis>JTA Session 바인딩</emphasis>: Hibernate <literal>Session</literal>
JTA 트랜잭션들의 영역(scope)에 자동적으로 바인드 시킬 수도 있다. 간단하게 JNDI로부터 <literal>SessionFactory</literal>
룩업하고 현재 <literal>Session</literal>을 얻어라. Hibernate로 하여금 당신의 JTA 트랜잭션이 완료될 때 <literal>Session</literal>
flush시키고 닫는 것을 처리하도록 하라. 트랜잭션 경계 설정은 EJB 배치 디스크립터들 내에서 선언적이다.
flush시키고 닫는 것을 처리하도록 하라. 트랜잭션 경계 구분은 선언적(CMT)이거나 프로그래밍적((BMT/UserTransaction))이다.
</para>
</listitem>
</itemizedlist>
@ -1405,7 +1421,7 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
</para>
<para>
Hibernate에 있는 몇몇 특징들(예를 들면. second level 캐시, 자동적인 JTA 및 Session 바인딩, 기타)은 관리되는 환경에서
Hibernate에 있는 몇몇 특징들(예를 들면. second level 캐시, JTA를 가진 컨텍스트 상의 세션들, 기타.)은 관리되는 환경에서
JTA <literal>TransactionManager</literal>에 대한 접근을 필요로 한다. 어플리케이션 서버에서 당신은 Hibernate가
<literal>TransactionManager</literal>에 대한 참조를 획득하는 방법을 지정해야 한다. 왜냐하면 J2EE가 한 개의 메커니즘을
표준화 시키고 있지 않기 때문이다:
@ -1469,7 +1485,7 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
</sect2>
<sect2 id="configuration-optional-jndi" revision="2">
<sect2 id="configuration-optional-jndi" revision="3">
<title>JNDI-bound <literal>SessionFactory</literal></title>
<para>
@ -1501,40 +1517,48 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect]]></programlisting>
<para>
만일 당신이 하나의 JNDI <literal>SessionFactory</literal>를 사용할 경우, 하나의 EJB 또는 어떤 다른 클래스는 JNDI
룩업을 사용하여 <literal>SessionFactory</literal>를 얻을 수 있다. 만일 당신이 제 1장에서 소개된 하나의 Singleton
레지스트리로서 행동하는 <literal>HibernateUtil</literal> helper 클래스를 사용할 경우 이 셋업이 필수적이지 않음을
노트하라. 하지만 <literal>HibernateUtil</literal>은 관리되지 않는 환경에서 보다 공통적이다.
룩업을 사용하여 <literal>SessionFactory</literal>를 얻을 수 있다.
</para>
<para>
우리는 관리되는 환경들에서 <literal>SessionFactory</literal>를 JNDI에 바인드 시키고 그 밖의 경우에는
하나의 <literal>static</literal> 싱글톤을 사용하는 것을 권장한다. 이들 상세로부터 당신의 어플리케이션 코드를
은폐시키기 위해, 우리는 또한 <literal>HibernateUtil.getSessionFactory()</literal>과 같은 하나의
helper 클래스 내에서 <literal>SessionFactory</literal>에 대한 실제 룩업 코드를 은폐시키기를 권장한다.
그런 클래스는 또한 Hibernate를 시작하는 편리한 방법임을 노트하라&mdash; 1장을 보라.
</para>
</sect2>
<sect2 id="configuration-j2ee-currentsession" revision="2">
<title>자동적인 JTA 및 Session 바인딩</title>
<sect2 id="configuration-j2ee-currentsession" revision="4">
<title>Current Session context management with JTA</title>
<para>
관리되는 환경들에서 우리는 <literal>SessionFactory</literal>를 JNDI에 바인드 시키는 것을 권장한다. 트랜잭션과
<literal>Session</literal> 핸들링을 위해, 당신은 이미 소개된 <literal>HibernateUtil</literal> helper
클래스를 사용할 수 있다. 하지만 EJB들은 동일한 쓰레드에서 실행되지 않을 수도 있는데, 그것은 항상 적절하게
<literal>ThreadLocal</literal> 핸들링을 행하지 않는다(예를 들면 두 개의 세션이 서로를 호출할 때).
</para>
당신 자신의 <literal>ThreadLocal</literal> 유틸리티를 작동시키는 대신에, 우리는 또한 Hibernate <literal>Session</literal>
얻기 위해 <literal>SessionFactory</literal> 상의 <literal>getCurrentSession()</literal> 메소드 사용을
권장한다. <xref linkend="architecture-current-session">현재 세션들</xref>에 관한 논의를 보라. <literal>"jta"</literal>
세션 컨텍스트를 사용하고 있는 경우에, 현재의 JTA 트랜잭션으로 시작되고 연관된 Hibernate <literal>Session</literal>
존재하지 않을 경우, 우리는 JTA 트랜잭션으로 시작되고 연관될 것이다. <literal>"jta"</literal> 컨텍스트에서
<literal>getCurrentSession()</literal>를 통해 검색된 <literal>Session</literal>들은 그 트랜잭션이 완료되기 전에
자동적으로 flush될 것이고 트랜잭션 완료 후에 닫혀질 것이고, 그리고 각각의 문장 뒤에 JDBC 커넥션들을 적극적으로 해제할 것이다.
이것은 그런 관리 사항들에 대해 사용자 코드를 명료하게 유지시켜서, 연관되어 있는 JTA 트랜잭션의 생명주기에 의해 <literal>Session</literal>들이
관리되도록 허용해준다. 이것은 그런 관리 사항들에 대해 사용자 코드를 명료하게 유지시켜서, 세션이 연관되어 있는 JTA 트랜잭션의
생명주기에 의해 관리되는 것을 <literal>Session</literal>들에게 허용해준다. 당신의 코드는
<para>
당신 자신의 <literal>ThreadLocal</literal> 유틸리티를 조작하는 대신, Hibernate <literal>Session</literal>
얻는데 <literal>SessionFactory</literal> 상의 <literal>getCurrentSession()</literal> 메소드를 사용하라.
만일 현재의 JTA 트랜잭션 내에 Hibernate <literal>Session</literal>이 존재하지 않을 경우, 세션이 시작되고 할당될 것이다.
<literal>hibernate.transaction.flush_before_completion</literal>
<literal>hibernate.transaction.auto_close_session</literal> 구성 옵션 둘 다 당신이
<literal>getCurrentSession()</literal>으로 검색하는 모든 <literal>Session</literal>에 대해 자동적으로 설정될
것이고, 따라서 세션들은 또한 컨테이너가 JTA 트랜잭션들을 끝낼 때 자동적으로 flush되고 닫혀질 것이다. 이것은
<literal>ThreadLocal</literal> 관리에 대한 대안이다. 당신이 <emphasis>CaveatEmptor</emphasis> 어플리케이션에서
찾을 수 있는 <literal>HibernateUtil</literal> 클래스는 실제로 두 개의 방도들 사이를 자동적으로 전환할 수 있으므로,
transaction-local, BMT, 그리고 CMT 환경들 사이에서 당신의 코드를 이식가능하게 유지할 수 있다.
</para>
<para>
예를 들어 만일 당신이 당신의 영속 계층을 작성하는데 DAO 디자인 패턴을 사용할 경우, 모든 DAO는 필요할 때 <literal>SessionFactory</literal>
룩업하고 "현재(current)" Session을 연다. 제어 코드와 DAO 코드 사이에서 <literal>SessionFactory</literal> 또는
<literal>Session</literal>의 인스턴스들을 전달할 필요가 없다.
<literal>Session</literal>들과 트랜잭션들을 처리하는 가장 손쉬운 방법은 Hibernate의 자동적인 "현재"
<literal>Session</literal>이다.
<xref linkend="architecture-current-session">current sessions</xref>에 관한 논의를 보라.
<literal>"jta"</literal> 세션 컨텍스트를 사용하는 경우, 현재의 JTA 트랜잭션으로 시작되고 연관된
Hibernate <literal>Session</literal>들이 존재하지 않을 경우, 당신이
<literal>sessionFactory.getCurrentSession()</literal>을 처음 호출할 때 하나의 세션이 현재의 JTA 트랜잭션에 대해
시작되고 연관될 것이다. <literal>"jta"</literal> 컨텍스트에서 <literal>getCurrentSession()</literal>
통해 검색된 <literal>Session</literal>들은 그 트랜잭션이 완료되기 전에 자동적으로 flush될 것이고
그 트랜잭션 완료들 후에 닫혀질 것이고 각각의 문장 후에 JDBC 커넥션들을 적극적으로 해제할 것이다.
이것은 그런 관리 사항들에 대해 사용자 코드를 명료하게 유지시켜서, 연관되어 있는 JTA 트랜잭션의 생명주기에 의해 <literal>Session</literal>들이
관리되도록 허용해준다. 이것은 그런 관리 사항들에 대해 사용자 코드를 명료하게 유지시켜서, 세션이 연관되어 있는 JTA 트랜잭션의
생명주기에 의해 관리되는 것을 <literal>Session</literal>들에게 허용해준다. 당신의 코드는 트랜잭션 경계들을 설정하는데
<literal>UserTransaction</literal>을 통해 프로그램 상으로 JTA를 사용하거나, Hibernate <literal>Transaction</literal> API를 (이식성을 위한 코드로 권장됨)
사용할 수 있다. 만일 당신이 하나의 EJB 컨테이너에서 실행하고 있을 경우, CMT의 경우에 선언적인 트랜잭션 경계설정이 선호된다.
</para>
</sect2>

View File

@ -359,7 +359,7 @@ Cat fritz = (Cat) iter.next();]]></programlisting>
</sect2>
<sect2 id="performance-fetching-initialization">
<sect2 id="performance-fetching-initialization" revision="1">
<title>콜렉션들과 프락시들을 초기화 시키기</title>
<para>
@ -393,11 +393,9 @@ Cat fritz = (Cat) iter.next();]]></programlisting>
<para>
웹 기반 어플리케이션에서, 서블릿 필터는 뷰 렌더링이 완료되는, 사용자 요청의 바로 끝에서만 <literal>Session</literal>
닫는데 사용될 수 있다(<emphasis>Open Session in View</emphasis> 패턴). 물론 이것은 당신의 어플리케이션
인프라스트럭처의 예외상황 처리의 정정에 관한 무거운 요구를 부과한다. 사용자에게 반환하기 전에, 심지어 예외상황이
뷰의 렌더링 동안 발생할 때, <literal>Session</literal>이 닫혀지고 트랜잭션이 종료되는 것은 지극히 중요하다.
서블릿 필터는 이 접근법으로 <literal>Session</literal>에 접근하는 것이 가능해야 한다. 우리는
<literal>ThreadLocal</literal> 변수가 현재의 <literal>Session</literal>을 보관하는데 사용되는 것을
권장한다(예제 구현에 대해서는 1장, <xref linkend="quickstart-playingwithcats"/>를 보라).
인프라스트럭처의 예외상황 처리의 정정에 관한 무거운 요구를 부과한다.
뷰 렌더링 동안에 하나의 예외상황이 발생할때에도 사용자에게 반환되기 전에 <literal>Session</literal>이 닫혀지고
트랜잭션이 종료되는 것은 지극히 중요하다. 이 "Open Session in View" 패턴에 관한 예제들은 Hibernate 위키를 보라.
</para>
</listitem>
<listitem>

View File

@ -61,7 +61,7 @@ List cats = crit.list();]]></programlisting>
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.sql("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
.add( Restrictions.sqlRestriction("lower({alias}.name) like lower(?)", "Fritz%", Hibernate.STRING) )
.list();]]></programlisting>
<para>
@ -119,9 +119,9 @@ List cats = sess.createCriteria(Cat.class)
</para>
<programlisting><![CDATA[List cats = sess.createCriteria(Cat.class)
.add( Restrictions.like("name", "F%")
.add( Restrictions.like("name", "F%") )
.createCriteria("kittens")
.add( Restrictions.like("name", "F%")
.add( Restrictions.like("name", "F%") )
.list();]]></programlisting>
<para>

View File

@ -67,7 +67,7 @@
</sect1>
<sect1 id="queryhql-joins" revision="1">
<sect1 id="queryhql-joins" revision="2">
<title>연관들과 조인들</title>
<para>
@ -149,9 +149,11 @@
left join fetch child.kittens]]></programlisting>
<para>
<literal>fetch</literal> 구조체는 <literal>scroll()</literal>
또는 <literal>iterate()</literal>를 사용하여 호출된 질의들 내에 사용될 수 없음을 노트하라. <literal>fetch</literal>
<literal>setMaxResults()</literal> 또는 <literal>setFirstResult()</literal>와 함께 사용될 수도 없을 것이다.
(비록 <literal>scroll()</literal>이 사용될 수 있을지라도) <literal>fetch</literal> 구조체는
<literal>iterate()</literal>를 사용하여 호출되는 질의들 내에 사용될 수 없음을 노트하라. 이들 오퍼레이션들이 결과 행들에
기초하고 있기 때문에 <literal>fetch</literal><literal>setMaxResults()</literal> 또는
<literal>setFirstResult()</literal>과 함께 사용되지 말아야 하며, 그것들(결과 행들)은 대개 각각의 eager 콜렉션 페칭에 대해
중복들을 포함하므로 많은 행들이 당신이 기대하는 것이 아니다.
<literal>fetch</literal>는 특별한 용도의 <literal>with</literal> 조건과도 함께 사용될 수 없다.한 개의 질의 내에 하나
이상의 콜렉션을 조인 페칭시켜 카티젼 곱을 생성시키는 것이 가능한데, 이 경우에 주의하라. 다중 콜렉션 role들을 조인 페칭시키는 것은
또한 때때로 bag 매핑들에 대해 예기치 않은 결과들을 가져다주기 때문에, 당신이 이 경우에 당신의 질의들을 처방하는 방법에 대해 주의하라.
@ -168,6 +170,27 @@
</sect1>
<sect1 id="queryhql-joins-forms">
<title>join 구문의 형식들</title>
<para>
HQL은 두 가지 형식의 연관 조인을 지원한다: <literal>암묵적</literal> 그리고 <literal>명시적</literal>.
</para>
<para>
앞의 절에서 보여진 질의들은 모두 join 키워드가 from 절 내에 명시적으로 사용되는 <literal>명시적인</literal> 형식을
사용한다. 이것은 권장되는 형식이다.
</para>
<para>
<literal>함축적인</literal> 형식은 join 키워드를 사용하지 않는다. 대신에, 연관들은 dot(.) 표기를
사용하여 "dot-참조된다(dereferenced)". <literal>함축적인</literal> 조인들은 임의의 HQL 절들내에
나타날 수 있다. <literal>함축적인</literal> join은 결과되는 SQL 문장에서 inner join으로 귀결된다.
</para>
<programlisting><![CDATA[from Cat as cat where cat.mate.name like '%s%']]></programlisting>
</sect1>
<sect1 id="queryhql-select">
<title>select 절</title>

View File

@ -342,14 +342,15 @@ List loggedCats = sess.createSQLQuery(sql)
</para>
</sect2>
<sect2 id="sp_query">
<sect2 id="sp_query" revision="1">
<title>질의를 위한 내장 프로시저 사용하기</title>
<para>
Hibernate 3은 내장 프로시저들을 통한 질의들에 대한 지원을 도입한다. 내장 프로시저들은 Hibernate와 동작하는 것이
가능하도록 첫 번째 출력-파라미터로서 한 개의 결과셋을 반환해야 한다. Oracle9 이상의 버전에서 그런 내장 프로시저에
대한 예제는 다음과 같다:
Hibernate 3은 내장 프로시저들과 함수들을 통한 질의 지원을 도입한다. 대부분의 다음 문서는 양자 모두에 동일하게 적용된다.
내장 프로시저/함수는 Hibernate와 동작하는 것이 가능하도록 첫 번째 out-파라미터로서 한 개의 결과셋을 반환해야 한다.
Oracle9 이상의 버전에서 그런 내장 프로시저에 대한 예제는 다음과 같다:
</para>
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
@ -388,11 +389,11 @@ BEGIN
<literal>&lt;load-collection&gt;</literal>은 지원되지 않는다.
</para>
<sect3 id="querysql-limits-storedprocedures">
<sect3 id="querysql-limits-storedprocedures" revision="1">
<title>내장 프로시저들을 사용하는 규칙들/제약들</title>
<para>
Hibernate에서 내장 프로시저들을 사용하기 위해서 프로시저들은 다음 몇몇 규칙들을 따라야 한다. 만일 그것들이 그들 규칙들을
Hibernate에서 내장 프로시저들을 사용하기 위해서 프로시저들/함수들은 다음 몇몇 규칙들을 따라야 한다. 만일 그것들이 그들 규칙들을
따르지 않을 경우 그것들은 Hibernate와 함께 사용 불가능하다. 만일 당신이 여전히 이들 프로시저들을 사용하고자 원할 경우,
당신은 <literal>session.connection()</literal>을 통해 그것들을 실행시켜야 한다. 데이터베이스 벤더들이 다른 내장
프로시저 의미론/구문을 갖고 있기 때문에, 규칙들은 각각의 데이터베이스에 따라 차이가 난다.
@ -402,21 +403,19 @@ BEGIN
내장 프로시저 질의들은 <literal>setFirstResult()/setMaxResults()</literal>로서 쪽매김 될 수 없다.
</para>
<para>권장되는 호출 형식은 표준 SQL92이다: <literal>{ ? = call
functionName(&lt;parameters&gt;) }</literal> 또는 <literal>{ ? = call
procedureName(&lt;parameters&gt;}</literal>. Native 호출 구문은 지원되지 않는다.</para>
<para>
Oracle의 경우 다음 규칙들이 적용된다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
프로시저는 한 개의 결과 셋을 반환해야 한다. 이것은 Oracle 9 또는 10에서 한 개의 <literal>SYS_REFCURSOR</literal>
반환함으로써 행해진다. Oracle에서 당신은 한 개의 <literal>REF CURSOR</literal> 타입을 정의할 필요가 있다.
</para>
</listitem>
<listitem>
<para>
권장되는 형식은 <literal>{ ? = call procName(&lt;parameters&gt;) }</literal> 또는
<literal>{ ? = call procName }</literal>이다.(이것은 Hibernate 규칙이라기 보다는 Oracle 규칙이다.)
<para>하나의 함수는 하나의 결과 셋을 반환해야 한다. 프로시저의 첫 번째 파라미터는 하나의 결과 셋을 반환하는
하나의 <literal>OUT</literal>이어야 한다. 이것은 Oracle 9 또는 10에서 하나의 <literal>SYS_REFCURSOR</literal>
사용하여 행해진다. Oracle에서 당신이 <literal>REF CURSOR</literal> 타입을 정의할 필요가 있다면, Oracle 보고서를 보라.
</para>
</listitem>
</itemizedlist>

View File

@ -195,7 +195,7 @@ sess.refresh(cat); //re-read the state (after the trigger executes)]]></programl
native SQL 속에 당신의 질의를 표현할 수도 있다.
</para>
<sect2 id="objectstate-querying-executing">
<sect2 id="objectstate-querying-executing" revision="1">
<title>질의들을 실행하기</title>
<para>
@ -222,12 +222,18 @@ List kittens = session.createQuery(
Cat mother = (Cat) session.createQuery(
"select cat.mother from Cat as cat where cat = ?")
.setEntity(0, izi)
.uniqueResult();]]></programlisting>
.uniqueResult();]]
Query mothersWithKittens = (Cat) session.createQuery(
"select mother from Cat as mother left join fetch mother.kittens");
Set uniqueMothers = new HashSet(mothersWithKittens.list());]]></programlisting>
<para>
하나의 질의는 대개 <literal>list()</literal>를 호출하여 실행되고, 질의의 결과는 메모리 내에서 하나의 콜렉션 속으로
전체적으로 로드될 것이다. 하나의 질의에 의해 검색된 엔티티 인스턴스들은 영속(persistent) 상태에 있다. 당신의 질의가
하나의 객체를 오직 반환할 것임을 당신이 알고 있을 경우에 <literal>uniqueResult()</literal> 메소드는 단축을 제공한다.
콜렉션들에 대해 eager 페칭을 사용하는 질의들은 대개 (그것들의 초기화된 콜렉션들을 가진) 루트 객체들에 대한 중복들을 대개
반환한다. 당신은 <literal>Set</literal>을 통해 이들 중복들을 간단하게 필터링할 수 있다.
</para>
<sect3 id="objectstate-querying-executing-iterate">
@ -951,7 +957,7 @@ sess.close();]]></programlisting>
</sect1>
<sect1 id="objectstate-transitive">
<sect1 id="objectstate-transitive" revision="1">
<title>Transitive persistence(전이 영속)</title>
<para>
@ -1080,6 +1086,13 @@ sess.close();]]></programlisting>
</sect1>
<para>
마지막으로 오퍼레이션들의 케스케이딩이 <emphasis>호출 시점</emphasis>에서 또는 <emphasis>flush 시점</emphasis>에서
객체 그래프에 적용될 수 있음을 노트하라. 이용 가능할 경우에 모든 오퍼레이션들은 그 오퍼레이션이 실행될 때 도달 가능한 연관된
엔티티들에 대해 케스케이드 된다. 하지만 <literal>save-upate</literal><literal>delete-orphan</literal>
<literal>Session</literal>의 flush 동안에 도달 가능한 모든 연관된 엔티티들에 대해 이행적(transitive)이다.
</para>
<sect1 id="objectstate-metadata">
<title>메타데이터 사용하기</title>

View File

@ -1,25 +1,30 @@
<chapter id="transactions" revision="1">
<chapter id="transactions" revision="2">
<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는 이 장의 뒷 부분에서 논의된다.
Hibernate는 메모리 내에서 객체들을 잠그지 않는다. 당신의 어플리케이션은 격리 레벨에 의해 정의된 대로 행위를 기대할 수 있다.
또한 transaction-영역의 캐시인 <literal>Session</literal> 덕분에, Hibernate는 (스칼라 값들을 반환하는 질의들을 보고하지 않는)
식별자와 엔티티 질의들에 의한 룩업을 위해 반복 가능한 읽기를 제공한다.
</para>
<para>
자동적인 optimistic 동시성 제어를 위한 버전화에 덧붙여, Hibernate는 또한 <literal>SELECT FOR UPDATE</literal>
구문을 사용하여 행들에 대한 pessimistic 잠금을 위한 하나의 (마이너)API를 제공한다. optimistic 동시성 제어와 이 API는
이 장의 뒷부분에서 논의된다.
</para>
<para>
우리는 <literal>Configuration</literal>, <literal>SessionFactory</literal>, <literal>Session</literal>,
알갱이를 가진 Hibernate에서의 동시성 제어 뿐만 아니라 데이터베이스 트랜잭션과 긴 어플리케이션 트랜잭션에 대한 논의를 시작한다.
알갱이를 가진 Hibernate에서의 동시성 제어 뿐만 아니라 데이터베이스 트랜잭션과 장기간에 걸친 (컴퓨터와의)대화들에 대한 논의를 시작한다.
</para>
<sect1 id="transactions-basics">
<sect1 id="transactions-basics" revision="1">
<title>세션 영역과 트랜잭션 영역</title>
<para>
@ -29,17 +34,17 @@
</para>
<para>
<literal>Session</literal>은 단일 비지니스 프로세스, 하나의 작업 단위를 위해 한번만 사용되고 나서 폐기될 예정인,
비용이 들지 않는, 쓰레드 안전하지 않은 객체이다. <literal>Session</literal>은 그것이 필요하지 않으면 JDBC
<literal>Connection</literal>(또는 <literal>Datasource</literal>)를 얻지 않을 것이어서, 데이터 접근이
특별한 요청에 서비스할 필요가 있을 것이라고 당신이 확신하지 않을 경우에 당신은 <literal>Session</literal>을 안전하게
열고 닫을 수 있다. (이것은 당신이 요청 인터셉션을 사용하여 다음 패턴들 중 어떤 것을 구현하자마자 중요하게 된다.)
<literal>Session</literal>은 하나의 요청, 하나의 대화 , 하나의 작업 단위를 위해 한번만 사용되고 나서 폐기될 예정인,
비용이 들지 않는, 쓰레드 안전하지 않은 객체이다. <literal>Session</literal>은 커넥션이 필요하지 않으면 하나의 JDBC
<literal>Connection</literal>(또는 <literal>Datasource</literal>)를 얻지 않을 것이므로, 사용될 때까지
리소스들을 소비하지 않는다.
</para>
<para>
이 그림을 완성하기 위해 당신은 또한 데이터베이스 트랜재션들에 대해 생각해야 한다. 데이터베이스 트랜잭션은 데이터베이스에서
잠금 다툼을 줄이기 위해 가능한 짧아야 한다. 긴 데이터베이스 트랜잭션들은 당신의 어플리케이션이 고도의 동시성 로드로의 가용성을
높이는 것을 방해할 것이다.
높이는 것을 방해할 것이다. 그러므로 사용자가 생각하는 시간 동안 단위 작업이 완료될 때까지 데이터베이스 트랜잭션을 열려진채로
소유하는 것은 대개 결코 좋은 설계는 아니다.
</para>
<para>
@ -58,7 +63,10 @@
작업 단위 속으로 그룹지워진다. (이것은 또한 모든 하나의 SQL 문장 뒤의 auto-commit(자동-커밋)이 어플리케이션 내에서
무용지물임을 의미하고, 이 모드가 SQL 콘솔 작업을 돕도록 고안되었음을 노트하라. Hibernate는
의미하고, 이 모드는 Hibernate는 즉시 자동-커밋 모드를 사용 불가능하게 하거나, 어플리케이션 서버가 그렇게 행하고,
즉시 자동-커밋시키는 것을 사용불가능하게 하거나 ,그렇게 행하는 것을 기대한다.)
즉시 자동-커밋시키는 것을 사용불가능하게 하거나 ,그렇게 행하는 것을 기대한다.) 데이터베이스 트랜잭션들은 결코 옵션이 아니며,
하나의 데이터베이스와의 모든 통신은 당신이 데이터를 읽든 쓰단간에 상관없이 하나의 트랜잭션 내에서 발생해야 한다.
설명하였듯이, 데이터 읽기를 위한 auto-commit 특징을 피해야 할 것이며, 많은 작은 트랜잭션들은 하나의 명료하게 정의된
작업 단위보다 더 좋게 수행될 것 같지 않다. 후자가 또한 훨씬 더 유지가능하고 확장적이다.
</para>
<para>
@ -71,21 +79,35 @@
</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 in View</emphasis>으로 알려져 있고 Hibernate Wiki 상에서 찾을 수 있다. 물론 당신은
하나의 인터셉터를 구현하고 그것을 당신의 환경에 설정하는 방법을 찾아야 한다. 팁들과 예제들은 Hibernate 웹 사이트를 보라.
난제는 구현에 놓여있다: Hibernate는 이 패턴을 단순화 시키기 위해 "현재 세션"에 관한 미리 빌드된 관리를 제공한다. 당신이
행해야할 모든 것은 서버 요청이 처리되어야 할 때 트랜잭션을 시작하고, 그 응답이 클라이언트로 전송되기 전에 트랜잭션을 끝내는
것이다. 당신은 당신이 좋아하는 임의의 방법으로 이것을 행할 수 있으며, 공통된 해결책들은 서비스 메소드들 상의 첨단, 또는
하나의 프락시/인터셉션 컨테이너를 가진 APO 인터셉터인, <literal>ServletFilter</literal>이다. 하나의 EJB
컨테이너는 CMT의 경우에 선언적으로 EJB session beans 상에 트랜잭션 경계구분과 같은 동시 대조 측면들을 구현하는
표준 방법이다. 만일 당신이 프로그램 상의 트랜잭션 경계구분을 사용하고자 결정한다면, 사용의 용이성과 코드 이식성을 위해
이 장의 뒷 부분에서 보여진 Hibernate <literal>Transaction</literal>을 선호하라.
</para>
<para>
당신의 어플리케이션 코드는 어디서든 필요할 때 종종 <literal>sessionFactory.getCurrentSession()</literal>
간단히 호출함으로써 요청을 처리할 "현재 세션"에 접근할 수 있다. 당신은 현재 데이터베이스 트랜잭션으로 영역화된 하나의
<literal>Session</literal>을 항상 얻게 될 것이다. 이것은 resource-local 환경 또는 JTA 환경에 대해 구성되어야
하며, <xref linkend="architecture-current-session"/>을 보라.
</para>
<para>
때때로 "뷰가 렌더링될 때까지" 하나의 <literal>Session</literal> 과 데이터베이스 트랜잭션의 영역을 확장시키는 것이
편리하다. 이것은 요청이 처리된 후에 하나의 별도의 렌더링 단계를 활용하는 서블릿 어플리케이션들에서 특히 유용하다.
뷰 렌더링이 완료될 때까지 데이터베이스 트랜잭션을 확장하는 것은 당신이 당신 자신의 인터셉터를 구현하는 경우에 행하기가 쉽다.
하지만 만일 당신이 컨테이너에 의해 관리되는 트랜잭션들을 가진 EJB들에 의존할 경우에, 하나의 EJB 메소드가 반환될 때
임의의 뷰 렌더링이 시작될 수 있기 전에 하나의 트랜잭션이 완료되기 때문에, 행하는 것이 쉽지 않다. 이
<emphasis>Open Session in View</emphasis> 패턴을 둘러싼 팁들과 예제들은 Hibernate 웹 사이트와 포럼을 보라.
</para>
</sect2>
<sect2 id="transactions-basics-apptx">
<title>어플리케이션 트랜잭션들</title>
<sect2 id="transactions-basics-apptx" revision="1">
<title>장기간의 대화</title>
<para>
session-per-request 패턴은 당신이 작업 단위들을 설계하는데 사용할 수 있는 유일한 유용한 개념이 아니다. 많은 비지니스
@ -110,7 +132,8 @@
</itemizedlist>
<para>
우리는 사용자의 관점에서, 이것을 작업 단위, 장기간 실행되는 <emphasis>어플리케이션 트랜잭션</emphasis>이라고 명명한다.
우리는 사용자의 관점에서, 이것을 작업 단위, 장기간 실행되는 <emphasis>대화</emphasis>
(또는 <emphasis>어플리케이션 트랜잭션</emphasis>)이라고 명명한다.
당신이 당신의 어플리케이션에서 이것을 어떻게 구현할 수 있는 많은 방법들이 존재한다.
</para>
@ -122,19 +145,20 @@
</para>
<para>
명료하게, 우리는 어플리케이션 트랜잭션을 구현하는데 몇몇 데이터베이스 트랜잭션들을 사용해야 한다. 이 경우에,
비지니스 프로세스들의 격리를 유지하는 것은 어플리케이션 티어의 부분적인 책임이 된다. 단일 어플리케이션 트랜잭션은
대개 여러 개의 데이터베이스 트랜잭션들에 걸친다. 그것은 이들 데이터베이스 트랜잭션들 중 오직 한 개(마지막 트랜잭션)가
업데이트된 데이터를 저장하고, 모든 다른 트랜잭션들이 단순히 데이터를 읽는 (예를 들면, 몇몇 요청/응답 주기에 걸치는
마법사 스타일의 대화 상자에서) 경우에만 원자단위가 될 것이다. 특히 당신이 Hibernate의 특징들을 사용할 경우에 , 이것은
들리는 것보다 구현하기가 더 쉽다:
명료하게, 우리는 대화(<emphasis>어플리케이션 트랜잭션</emphasis>)를 구현하는데 몇몇 데이터베이스 트랜잭션들을
사용해야 한다. 이 경우에, 비지니스 프로세스들의 격리를 유지하는 것은 어플리케이션 티어의 부분적인 책임이 된다.
단일 대화는 대개 여러 개의 데이터베이스 트랜잭션들에 걸친다. 그것은 이들 데이터베이스 트랜잭션들 중
오직 한 개(마지막 트랜잭션)가 업데이트된 데이터를 저장하고, 모든 다른 트랜잭션들이 단순히 데이터를 읽는 (예를 들면,
몇몇 요청/응답 주기에 걸치는 마법사 스타일의 대화 상자에서) 경우에만 원자단위가 될 것이다. 특히 당신이 Hibernate의
특징들을 사용할 경우에 , 이것은 들리는 것보다 구현하기가 더 쉽다:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>자동적인 버전화</emphasis> - Hibernate는 당신을 위해 자동적인 optimistic
동시성 제어를 행할 수 있고, 그것은 사용자가 생각하는 시간 동안 동시적인 변경이 발생했는지를 자동적으로 검출할 수 있다..
동시성 제어를 행할 수 있고, 그것은 사용자가 생각하는 시간 동안 동시적인 변경이 발생했는지를 자동적으로 검출할 수 있다.
대개 우리는 오직 대화의 끝에서 체크한다.
</para>
</listitem>
<listitem>
@ -148,17 +172,18 @@
</listitem>
<listitem>
<para>
<emphasis>Long Session</emphasis> - Hibernate <emphasis>Session</emphasis>은 데이터베이스
<emphasis>확장된 (또는 Long) Session</emphasis> - Hibernate <emphasis>Session</emphasis>은 데이터베이스
트랜잭션이 커밋된 후에 기본 JDBC 커넥션이 연결 해제될 수도 있고, 새로운 클라이언트 요청이 발생할 때 다시 연결될 수
있다. 이 패턴은 <emphasis>session-per-application-transaction(어플리케이션 트랜잭션 당 세션)</emphasis>으로
알려져 있고 재첨부를 불필요하게 만든다. 자동적인 버전화는 동시성 변경들을 격리시키는데 사용된다.
있다. 이 패턴은 <emphasis>session-per-conversation(대화 당 세션)</emphasis>으로
알려져 있고 재첨부를 불필요하게 만든다. 자동적인 버전화는 동시성 변경들을 격리시키는데 사용되고 <literal>Session</literal>
자동적으로 flush되는 것이 허용되지 않지만 명시적으로 flush되는 것은 허용된다.
</para>
</listitem>
</itemizedlist>
<para>
<emphasis>session-per-request-with-detached-objects</emphasis>
<emphasis>session-per-application-transaction</emphasis> 양자는 장점들과 단점들을 갖는데, 우리는 이 장의
<emphasis>session-per-conversation</emphasis> 양자는 장점들과 단점들을 갖는데, 우리는 이 장의
뒷 부분에서 optimistic 동시성 제어 단락에서 그것들을 논의한다.
</para>
@ -279,7 +304,8 @@
데이터베이스 (또는 시스템) 트랜잭션 경계들은 항상 필수적이다. 데이터베이스와의 통신은 데이터베이스 트랜잭션의 외부에서 발생할 수
없다(이것은 자동-커밋 모드로 사용되는 많은 개발자들에게는 혼동스러워 보인다). 항상 심지어 읽기 전용 오퍼레이션들에 대해서도 명료한
트랜잭션 경계들을 사용하라. 당신의 격리 레벨과 데이터베이스 가용성들에 따라, 이것은 필요하지 않을 수 있지만, 만일 당신이 항상
트랜잭션들을 명시적으로 경계 설정할 경우에는 하강하는 결점들이 존재하지 않는다.
트랜잭션들을 명시적으로 경계 설정할 경우에는 하강하는 결점들이 존재하지 않는다. 확실히, 하나의 데이터베이스 트랜잭션은 심지어
데이터 읽기조차도 많은 작은 트랜잭션들의 경우보다는 더 좋게 수행될 것이다.
</para>
<para>
@ -287,14 +313,19 @@
관리되는 J2EE 환경에서 실행될 수 있다. 관리되지 않는 환경에서, Hibernate는 대개 그것 자신의 데이터베이스 커넥션 풀에 대한
책임이 있다. 어플리케이션 개발자는 트랜잭션 경계들을 손수 설정해야 한다. 달리 말해, 개발자 스스로 데이터베이스 트랜잭션들을
시작하고, 커밋시키거나 롤백시켜야 한다. 관리되는 환경은 대개 예를 들어 EJB 세션 빈즈의 배치 디스크립터 속에 선언적으로 정의된
트랜잭션 어셈블리를 가진, 컨테이너에 의해-관리되는 트랜잭션들을 제공한다. 그때 프로그램 상의 트랜잭션 경계 설정은 더 이상 필요하지
않다. 심지어 <literal>Session</literal>을 flush 시키는 것이 자동적으로 행해진다.
트랜잭션 어셈블리를 가진, 컨테이너에 의해-관리되는 트랜잭션들(CMT)을 제공한다. 그때 프로그램 상의 트랜잭션 경계 설정은 더 이상 필요하지
않다.
</para>
<para>
하지만, 당신의 영속 계층이 이식성을 유지하게끔 자주 희망된다. Hibernate는 당신의 배치 환경의 고유한 트랜잭션 시스템 속으로
변환되는 <literal>Transaction</literal>이라 명명되는 wrapper API 를 제공한다. 이 API는 실제로 옵션이지만 우리는
당신이 CMT session bean 속에 있지 않는 한 그것의 사용을 강력하게 권장한다.
However, it is often desirable to keep your persistence layer portable between non-managed
resource-local environments, and systems that can rely on JTA but use BMT instead of CMT.
In both cases you'd use programmatic transaction demaracation.
하지만, CMT 대신 BMT를 사용하는 JTA에 의존할 수 있는 시스템들, 그리고 관리되지 않는 resource-local 환경들 사이에서
당신의 영속 계층에 이식성을 유지시키는 것이 자주 희망된다. 두 경우들에서 당신은 프로그램 상의 트랜잭션 경계설정을 사용할 것이다.
Hibernate는 당신의 배치 환경의 고유한 트랜잭션 시스템 속으로 변환되는 <literal>Transaction</literal>이라 명명되는
wrapper API 를 제공한다. 이 API는 실제로 옵션이지만 우리는 당신이 CMT session bean 속에 있지 않는 한 그것의 사용을
강력하게 권장한다.
</para>
<para>
@ -330,12 +361,13 @@
</para>
<sect2 id="transactions-demarcation-nonmanaged">
<sect2 id="transactions-demarcation-nonmanaged" revision="2">
<title>관리되지 않는 환경</title>
<para>
만일 Hibernate 영속 계층이 관리되지 않는(non-managed) 환경에서 실행될 경우, 데이터베이스 커넥션들은 대개 Hibernate의
풀링 메커니즘에 의해 처리된다. session/transaction 처리 관용구는 다음과 같다:
만일 Hibernate 영속 계층이 관리되지 않는(non-managed) 환경에서 실행될 경우, 데이터베이스 커넥션들은 대개 Hibernate가
필요로할 때 커넥션들을 획득하는 간단한 (예를 들면 DataSource가 아닌) 커넥션 풀(pool)들로부터 처리된다.
session/transaction 처리 관용구는 다음과 같다:
</para>
<programlisting><![CDATA[// Non-managed environment idiom
@ -358,64 +390,54 @@ finally {
}]]></programlisting>
<para>
또는 다음과 같을 수 있다:
당신은 <literal>Session</literal>을 명시적으로 <literal>flush()</literal> 하지 말아야 한다 -
<literal>commit()</literal>에 대한 호출은 (그 세션에 대한
<xref linkend="objectstate-flushing">FlushMode</xref>에 따라)자동적으로 동기화를 트리거시킨다.
<literal>close()</literal>에 대한 호출은 세션의 끝을 마크한다. <literal>close()</literal>
주된 구현은 JDBC 커넥션이 그 세션에 의해 포기될 것이라는 점이다. 이 Java 코드는 관리되지 않는 환경과
JTA 환경 양자에서 이식성이 있고 실행된다.
</para>
<programlisting><![CDATA[// Non-managed environment idiom
Session sess = factory.openSession();
<para>
보다 더 유연한 해결책은 앞서 설명했듯이 Hibernate의 미리 빌드되어 있는 "현재 세션" 컨텍스트 관리이다:
</para>
<programlisting><![CDATA[// Non-managed environment idiom with getCurrentSession()
try {
sess.getTransaction().begin();
factory.getCurrentSession().beginTransaction();
// do some work
...
sess.getTransaction().commit()
factory.getCurrentSession().getTransaction().commit();
}
catch (RuntimeException e) {
if ( sess.getTransaction().isActive() ) {
sess.getTransaction().rollback();
}
factory.getCurrentSession().getTransaction().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 컨테이너 서비스들을 사용할 것이다. 예외상황
"상단"에서 잡혀야 한다. 달리 말해, (영속 계층에서) Hibernate 호출들을 실행시키는 코드와 <literal>RuntimeException</literal>
처리하(고 대개 오직 제거하고 빠져나갈 수 있는) 코드는 다른 계층들 속에 있다. Hibernate에 의한 현재 컨텍스트 관리는 이 설계를
현격하게 단순화시켜서, 당신이 필요로 하는 모든 것은 <literal>SessionFactory</literal>에 대한 접근이다.예외상황
처리는 이 장의 뒷부분에서 논의된다.
</para>
<para>
당신은 (디폴트인) <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>를 선택해야 함을 노트하라.
당신은 (디폴트인) <literal>org.hibernate.transaction.JDBCTransactionFactory</literal>를 선택해야 하고,
두번째 예제의 경우 당신의 <literal>hibernate.current_session_context_class</literal>를 선택해야 함을 노트하라.
</para>
</sect2>
<sect2 id="transactions-demarcation-jta">
<sect2 id="transactions-demarcation-jta" revision="3">
<title>JTA 사용하기</title>
<para>
만일 당신의 영속 계층이 어플리케이션 서버에서(예를 들어, EJB 세션 빈즈 이면에서) 실행될 경우, Hibernate에 의해
획득된 모든 데이터소스 커넥션은 자동적으로 전역 JTA 트랜잭션의 부분일 것이다. Hibernate는 이 통합을 위한 두 개의
방도들을 제공한다.
획득된 모든 데이터소스 커넥션은 자동적으로 전역 JTA 트랜잭션의 부분일 것이다. 당신은 또한 스탠드얼론 JTA 구현을
설치할 수 있고 EJB 없이 그것을 사용할 수 있다. Hibernate는 JTA 통합을 위한 두 개의 방도들을 제공한다.
</para>
<para>
@ -444,58 +466,32 @@ finally {
}]]></programlisting>
<para>
또는:
만일 당신이 트랜잭션에 묶인 <literal>Session</literal>, 즉 쉬운 컨텍스트 보급을 위한
<literal>getCurrentSession()</literal> 기능을 사용하고자 원할 경우, 당신은
JTA <literal>UserTransaction</literal> API를 직접 사용해야 할 것이다:
</para>
<programlisting><![CDATA[// BMT idiom
Session sess = factory.openSession();
<programlisting><![CDATA[// BMT idiom with getCurrentSession()
try {
sess.getTransaction().begin();
UserTransaction tx = (UserTransaction)new InitialContext()
.lookup("java:comp/UserTransaction");
// do some work
...
tx.begin();
sess.getTransaction().commit()
// Do some work on Session bound to transaction
factory.getCurrentSession().load(...);
factory.getCurrentSession().persist(...);
tx.commit();
}
catch (RuntimeException e) {
if ( sess.getTransaction().isActive() ) {
sess.getTransaction().rollback();
}
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은 다음과 같이 감소된다:
CMT의 경우, 트랜잭션 관할[경계 설정]은 프로그램 상이 아닌, session bean 배치 디스크립터들 속에서 행해진다.
그러므로 코드는 다음으로 감소된다:
</para>
<programlisting><![CDATA[// CMT idiom
@ -503,23 +499,33 @@ Session sess = factory.getCurrentSession();
// do some work
...
]]></programlisting>
<para>
달리 말해, 당신이 관리 환경에서 행해야 하는 모든 것은 <literal>SessionFactory.getCurrentSession()</literal>
호출하고, 당신의 데이터 접근 작업을 행하고, 그리고 나머지를 컨테이너에게 남겨두는 것이다. 트랜잭션 경계들은 당신의
session bean의 배치 디스크립터들 속에 선언적으로 설정된다. 그 세션의 생명주기는 Hibernate에 의해 완전하게 관리된다.
왜냐하면 하나의 세션 빈 메소드에 의해 던져진 처리되지 않은 <literal>RuntimeException</literal>
글로벌 트랜잭션을 rollback으로 설정하도록 컨테이너에게 알려주기 때문에, CMT/EJB에서조차 롤백은 자동적으로 발생된다.
<emphasis>이것은 당신이 BMT 이든 CMT이든 모두에서 Hibernate <literal>Transaction</literal> API를 사용할 필요가
없으며, 당신은 그 트랜잭션에 묶인 "현재" Session의 자동적인 보급(propagation)을 얻게 됨을 의미한다.</emphasis>
</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> 사용을 쉽게 피할 수 있다.)
당신이 Hibernate의 트랜잭션 팩토리를 구성할 때, 당신이 JTA를 직접 사용할 경우(BMT) 당신은
<literal>org.hibernate.transaction.JTATransactionFactory</literal>를 선택해야하고, CMT session bean에서는
<literal>org.hibernate.transaction.CMTTransactionFactory</literal>를 선택해야 함을 노트하라. 또한
<literal>org.hibernate.transaction.manager_lookup_class</literal>를 설정하는 것을 염두에 두라. 게다가
반드시 당신의 <literal>hibernate.current_session_context_class</literal>이 설정되지 않도록 하거나(역호환성),
또는 <literal>"jta"</literal>로 설정되도록 하라.
</para>
<para>
<literal>getCurrentSession()</literal> 오퍼레이션들은 JTA 환경에서 한 가지 단점을 갖고 있다.
디폴트로 사용되는, <literal>after_statement</literal> 커넥션 해제 모드 사용에 대한 하나의 보류 통보가 존재한다.
JTA 명세서의 어리석은 제한으로 인해, Hibernate가 <literal>scroll()</literal> 또는 <literal>iterate()</literal>
의해 반환되는 임의의 닫혀지지 않은 <literal>ScrollableResults</literal> 또는 <literal>Iterator</literal>
인스턴스들을 자동적으로 제거하는 것이 불가능하다. 당신은 <literal>finally</literal> 블록 내에서 명시적으로
<literal>ScrollableResults.close()</literal> 또는 <literal>Hibernate.close(Iterator)</literal>
호출하여 기본 데이터베이스 커서를 해제<emphasis>시켜야 한다</emphasis>.(물론 대부분의 어플리케이션들은 JTA 또는 CMT 코드에서
<literal>scroll()</literal>이나 <literal>iterate()</literal>를 사용하는 것을 쉽게 피할 수 있다.)
</para>
</sect2>
@ -615,9 +621,7 @@ try {
sess.getTransaction().commit()
}
catch (RuntimeException e) {
if ( sess.getTransaction().isActive() ) {
sess.getTransaction().rollback();
}
throw e; // or display error message
}
finally {
@ -639,8 +643,8 @@ finally {
<para>
고도의 동시성과 고도의 가용성을 일치시키는 유일한 접근법은 버전화를 가진 optimistic동시성 제어이다. 버전 체킹은 업데이트 충돌을
검출하기 위해(그리고 업데이트 손실을 방지하기 위해) 버전 번호들 또는 timestamp들을 사용한다. Hibernate는 optimistic 동시성을
사용하는 어플리케이션 코드 작성에 세 가지 가능한 접근법들을 제공한다. 우리가 보여주는 쓰임새들은 긴 어플리케이션 트랜잭션들의 상황
속에 있지만 버전 체킹 또한 단일 데이터베이스 트랜잭션들에서 업데이트 손실을 방지하는 이점을 갖고 있다.
사용하는 어플리케이션 코드 작성에 세 가지 가능한 접근법들을 제공한다. 우리가 보여주는 쓰임새들은 장시간의 대화의 상황
속에 있지만, 버전 체킹 또한 단일 데이터베이스 트랜잭션들에서 업데이트 손실을 방지하는 이점을 갖고 있다.
</para>
<sect2 id="transactions-optimistic-manual">
@ -649,16 +653,18 @@ finally {
<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>
@ -669,27 +675,31 @@ session.close();]]></programlisting>
<para>
물론, 당신이 낮은 데이터 동시성 환경에서 작업하고 있고 버전 체킹을 필요로 하지 않을 경우에, 당신은 이 접근법을 사용할 수 도 있고
단지 버전 체크를 생략할 수도 있다. 그 경우에, <emphasis>마지막의 커밋 성공</emphasis>은 당신의 긴 어플리케이션 트랜잭션들
단지 버전 체크를 생략할 수도 있다. 그 경우에, <emphasis>마지막의 커밋 성공</emphasis>은 당신의 장시간의 대화
대한 디폴트 방도가 될 것이다. 이것이 어플리케이션의 사용자들을 혼동시킬 수 있음을 염두에 두라. 왜냐하면 사용자들은 오류 메시지들
또는 충돌 변경들을 병합시킬 기회 없이 업데이트들 손실을 겪을 수도 있기 때문이다.
</para>
<para>
명료하게 수작업 버전 체킹은 매우 사소한 환경들에서도 공포적이고 대부분의 어플리케이션들에 대해 실제적이지 않다. 흔히 단일
인스턴스 뿐만 아니라 변경된 객체들의 전체 그래프들이 체크되어야 한다. Hibernate는 설계 패러다임으로서 <literal>Session</literal>
인스턴스 뿐만 아니라 변경된 객체들의 전체 그래프들이 체크되어야 한다. Hibernate는 설계 패러다임으로서 하나의 확장된 <literal>Session</literal>
또는 detached 인스턴스들에 대해 자동적인 버전 체킹을 제공한다.
</para>
</sect2>
<sect2 id="transactions-optimistic-longsession">
<title> 세션과 자동적인 버전화</title>
<title>확장된 세션과 자동적인 버전화</title>
<para>
하나의 <literal>Session</literal> 인스턴스와 그것의 영속 인스턴스들은 전체 어플리케이션 트랜잭션에 사용된다. Hibernate는
flush 할 때 인스턴스 버전들을 체크하고 만일 동시성 변경이 검출될 경우에 예외상황을 던진다. 이 예외상황을 잡아내고 처리하는 것을
개발자의 몫이다(공통된 옵션들은 변경들을 병합시키거나 또는 쓸모가 없지 않은 데이터로 비지니스 프로세스를 다시 시작하는 기회를
사용자에게 주는 것이다).
하나의 <literal>Session</literal> 인스턴스와 그것의 영속 인스턴스들은 <emphasis>session-per-conversation</emphasis>
알려진 전체 대화에 사용된다. Hibernate는 flush 시점에서 인스턴스 버전들을 체크하며, 만일 동시적인 변경이 검출될 경우에
하나의 예외상황을 던진다. 이 예외상황을 포착하고 처리하는 것은 개발자의 몫이다(공통된 옵션들은 사용자가 변경들을 병합하거나
손실되지 않은 데이터를 가지고 비지니스 대화를 재시작하는 기회이다).
</para>
<para>
@ -698,40 +708,52 @@ session.close();]]></programlisting>
그 자체 관계할 필요가 없거나 그것은 모든 데이터베이스 트랜잭션에서 인스턴스들을 다시 로드시킬 필요가 없다.
</para>
<programlisting><![CDATA[// foo is an instance loaded earlier by the Session
session.reconnect(); // Obtain a new JDBC connection
Transaction t = session.beginTransaction();
<programlisting><![CDATA[// foo is an instance loaded earlier by the old session
Transaction t = session.beginTransaction(); // Obtain a new JDBC connection, start transaction
foo.setProperty("bar");
t.commit(); // End database transaction, flushing the change and checking the version
session.disconnect(); // Return JDBC connection ]]></programlisting>
session.flush(); // Only for last transaction in conversation
t.commit(); // Also return JDBC connection
session.close(); // Only for last transaction in conversation]]></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> 어떤 데이터에 대한 잠금을 필요로 하지 않는다.
이전 세션 상에서 하나의 새로운 데이터베이스 트랜잭션을 시작하는 것은 하나의 새로운 커넥션을 획득하고 그 세션을 소비한다.
데이터베이스 트랜잭션을 커밋(확약)시키는 것은 그 JDBC 커넥션으로부터 하나의 세션을 연결해제시키고 그 커넥션을 풀(pool)로
반환시킬 것이다. 재연결 후에, 당신이 업데이트하고 있지 않은 데이터에 대한 버전 체크를 강제시키기 위해서, 당신은
또 다른 트랜잭션에 의해 업데이트되었을 수도 있는 임의의 객체들에 대해 <literal>LockMode.READ</literal>로서
<literal>Session.lock()</literal>을 호출할 수도 있다. 당신은 당신이 업데이트 중인 임의의 데이터를 잠금할 필요가 없다.
대개 당신은 마지막 데이터베이스 트랜잭션 주기만이 이 대화 내에서 행해진 모든 변경들을 실제로 영속화시키는 것이 허용되도록 하기 위해,
하나의 확장된 <literal>Session</literal>에 대해 <literal>FlushMode.NEVER</literal>를 설정할 것이다.
그러므로 오직 이 마지막 데이터베이스 트랜잭션 만이 <literal>flush()</literal> 오퍼레이션을 포함할 것이고, 또한
대화를 종료시키기 위해 세션을 <literal>close()</literal>할 것이다.
</para>
<para>
만일 <literal>disconnect()</literal><literal>reconnect()</literal>에 대한 명시적인 호출들이 너무 번거러울 경우,
당신은 대신에 <literal>hibernate.connection.release_mode</literal>를 사용할 수도 있다.
</para>
<para>
만일 사용자가 생각하는시간 동안 <literal>Session</literal>이 저장되기에는 너무 큰 경우 이 패턴은 문제성이 있다. 예를 들어
만일 사용자가 생각하는시간 동안 <literal>Session</literal>이 저장되기에 너무 큰 경우 이 패턴은 문제성이 있다. 예를 들어
<literal>HttpSession</literal>은 가능한 작은 것으로 유지되어야 한다. 또한 <literal>Session</literal>은 (필수의)
첫 번째 레벨 캐시이고 모든 로드된 객체들을 포함하기 때문에, 우리는 아마 적은 요청/응답 주기들에 대해서만 이 방도를 사용할 수 있다.
<literal>Session</literal>이 곧 실효성이 없는 데이터를 갖게 될 것이므로 이것이 진정으로 권장된다.
당신은 하나의 대화에 대해서만 하나의 <literal>Session</literal>을 사용해야 한다. 왜냐하면 그것은 또한 곧 실없는 데이터가
될 것이기 때문이다.
</para>
<para>
또한 당신이 연결해제된 <literal>Session</literal>을 영속 계층에 가깝게 유지해야함을 노트하라. 달리말해,
<literal>Session</literal>을 보관하는데 EJB stateful session bean을 사용하고 <literal>HttpSession</literal>
내에 그것을 저장하기 위해서 그것을 웹 계층에 전송하지 말라(또는 그것을 별도의 티어에 직렬화 시키지도 말라).
(초기의 Hibernate 버전들은 <literal>Session</literal>에 대한 명시적인 연결해제와 재연결을 필요로 했음을
노트하라. 트랜잭션을 시작하고 끝내는 것이 동일한 효과를 가지므로, 이들 방법들은 진부하게 되었다.)
</para>
<para>
또한 당신은 영속 계층에 대해 연결해제된 <literal>Session</literal>을 닫혀진채로 유지해야함을 노트하라. 달
리말해, 하나의 3-tier 환경에서 <literal>Session</literal>을 소유하는데 EJB stateful session bean을
사용하고, <literal>HttpSession</literal> 내에 그것을 저장하기 위해 그것을 웹 계층에 전송하지 말라
(또는 그것을 별도의 티어에 직렬화 시키지도 말라).
</para>
<para>
확장된 세션 패턴, 또는 <emphasis>session-per-conversation</emphasis>은 자동적인 현재 세션 컨텍스트 관리에 대해
구현하기가 더 어렵다. 당신은 이를 위해 당신 자신의 <literal>CurrentSessionContext</literal> 구현을 공급할 필요가
있으며, 예제들은 Hibernate Wiki를 보라.
</para>
</sect2>
@ -901,5 +923,86 @@ session.close();]]></programlisting>
</sect1>
<sect1 id="transactions-connection-release">
<title>연결 해제 모드들</title>
<para>
JDBC 커넥션 관리에 관한 Hibernate의 리거시(2.x) 특징은 그것이 처음으로 필요로 했을 때
하나의 <literal>Session</literal>이 하나의 커넥션을 획득할 것이고, 그런 다음 그 커넥션은 그 세션이
닫혀질때까지 보관된다는 것이었다.
Hibernate 3.x는 세션에게 그것의 JDBC 커넥션들을 처리하는 방법을 알려주기 위해 연결 해제 모드들에 관한
개념을 도입했다. 다음 논의는 구성된 <literal>ConnectionProvider</literal>를 통해 제공되는 커넥션들에
대해서만 적절하다는 점을 노트하라; 사용자가 제공하는 커넥션들은
<literal>org.hibernate.ConnectionReleaseMode</literal>의 열거된 값들에 의해 식별된다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>ON_CLOSE</literal> - 는 본질적으로 위에 설명된 리거시 특징이다. Hibernate 세션은 그것이
어떤 JDBC 접근을 수행하고 세션이 닫혀질 때까지 그 커넥션을 보관할 필요가 있을 때 하나의 커넥션을 획득한다.
</para>
</listitem>
<listitem>
<para>
<literal>AFTER_TRANSACTION</literal> - 은 하나의
<literal>org.hibernate.Transaction</literal>이 완료된 후에 연결들을 해제하라고 말한다.
</para>
</listitem>
<listitem>
<para>
<literal>AFTER_STATEMENT</literal> (또한 적극적인 해제라고 언급됨) - 는 각각의 모든 문장 실행 후에
커넥션들을 해제하라고 말한다. 이 적극적인 해제는 그 문장이 주어진 세션과 연관된 리소스들을 열려진채로 남겨둘
경우에는 건너뛰게(skip) 된다; 현재 이것이 일어나는 유일한 상황은
<literal>org.hibernate.ScrollableResults</literal>의 사용을 통해서이다.
</para>
</listitem>
</itemizedlist>
<para>
사용할 해제 모드를 지정하기 위해 구성 파라미터 <literal>hibernate.connection.release_mode</literal>가 사용된다.
가능한 값들은 다음과 같다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>auto</literal> (디폴트) - 이 선택은
<literal>org.hibernate.transaction.TransactionFactory.getDefaultReleaseMode()</literal>
메소드에 의해 반환된 해제 모드로 위임시킨다. JTATransactionFactory인 경우, 이것은
ConnectionReleaseMode.AFTER_STATEMENT를 반환한다; JDBCTransactionFactory인 경우, 이것은
ConnectionReleaseMode.AFTER_TRANSACTION을 반환한다. 이 설정의 값이 사용자 코드 내의 버그들 그리고/또는
유효하지 않은 가정들을 가리키는 경향이 있음으로 인해 이 디폴트 특징을 실패로 변경하는 것은 거의 좋은 생각이 아니다.
</para>
</listitem>
<listitem>
<para>
<literal>on_close</literal> - 는 ConnectionReleaseMode.ON_CLOSE를 사용하라고 말한다. 이 설정은
역호환성을 위해 남겨졌지만, 그것의 사용은 매우 권장되지 않는다.
</para>
</listitem>
<listitem>
<para>
<literal>after_transaction</literal> - 은 ConnectionReleaseMode.AFTER_TRANSACTION을 사용하라고 말한다.
이 설정은 JTA 환경들에서 사용되지 않을 것이다. 또한
ConnectionReleaseMode.AFTER_TRANSACTION인 경우에 만일 세션이 auto-commit 모드에 있도록 고려될 경우,
커넥션들은 마치 해제 모드가 AFTER_STATEMENT인 것처럼 해제될 것임을 또한 노트하라.
</para>
</listitem>
<listitem>
<para>
<literal>after_statement</literal> - 는 ConnectionReleaseMode.AFTER_STATEMENT를 사용하라고 말한다.
추가적으로 구성된 <literal>ConnectionProvider</literal>는 그것이 이 설정
(<literal>supportsAggressiveRelease()</literal>)을 지원하는지 여부를 알기 위해 참고된다.
만일 지원하지 않을 경우, 해제 모드는 ConnectionReleaseMode.AFTER_TRANSACTION으로 재설정된다. 이 설정은
우리가 <literal>ConnectionProvider.getConnection()</literal>을 호출할 때마다 우리가 동일한 기본 JDBC
커넥션을 다시 필요로 할 수 있는 환경들에서 또는 우리가 동일한 커넥션을 얻는 것에 상관없는 auto-commit 환경에서 오직
안전하다.
</para>
</listitem>
</itemizedlist>
</sect1>
</chapter>

View File

@ -17,7 +17,7 @@
</sect1>
<sect1 id="tutorial-firstapp">
<sect1 id="tutorial-firstapp" revision="2">
<title>파트 1 - 첫 번째 Hibernate 어플리케이션</title>
<para>
@ -26,7 +26,7 @@
</para>
<para>
우리가 우리가 출석을 원하는 이벤트들을 저장할 수 있는 작은 데이터베이스 어플리케이션과 이들
우리가 우리가 수반하고자 원하는 이벤트들을 저장할 수 있는 작은 데이터베이스 어플리케이션과 이들
이벤트들의 호스트들에 대한 정보를 필요로 한다고 가정하자.
</para>
@ -40,7 +40,7 @@
<programlisting><![CDATA[.
+lib
antlr.jar
cglib-full.jar
cglib.jar
asm.jar
asm-attrs.jars
commons-collections.jar
@ -51,10 +51,11 @@
log4j.jar ]]></programlisting>
<para>
이것은 Hibernate에 필요한 최소한의 세트이다(우리는 또한 메인 아카이브인 hibernate3.jar를
복사했음을 노트하라). 필수적인 제 3의 라이브러리와 선택적인 제 3의 라이브러리들에 대한 추가 정보는
Hibernate 배포본의 <literal>lib/</literal> 디렉토리 속에 있는 <literal>README.txt</literal> 파일을 보라.
(실제로, Log4j는 필수적이지는 않지만 많은 개발자들에 의해 선호된다.)
이것은 <emphasis>글의 작성 시점에서</emphasis> Hibernate에 필수적인 최소한의 세트이다(우리는
또한 메인 아카이브인 hibernate3.jar를 복사했음을 노트하라). 당신이 사용 중인 Hibernate 배포본이
더 많거나 보다 적은 라이브러리들을 필요로 할 수도 있다. 필수 라이브러리들과 선택적인 제3의 라이브러리들에 대한
추가 정보는 Hibernate 배포본의 <literal>lib/</literal> 디렉토리 내에 있는 <literal>README.txt</literal>
파일을 보라. (실제로, Log4j는 필수는 아니지만 많은 개발자들에 의해 선호된다.)
</para>
<para>
@ -285,7 +286,7 @@ public class Event {
</sect2>
<sect2 id="tutorial-firstapp-configuration">
<sect2 id="tutorial-firstapp-configuration" revision="2">
<title>Hibernate 구성</title>
<para>
@ -297,7 +298,12 @@ public class Event {
<para>
개발 디렉토리의 루트에 <literal>data</literal>로 명명된 디렉토리를 생성시켜라 - 이 디렉토리는
HSQL DB가 그것의 데이터 파일들을 저장하게 될 장소이다.
HSQL DB가 그것의 데이터 파일들을 저장하게 될 장소이다. 이제 이 데이터 디렉토리에서
<literal>java -classpath lib/hsqldb.jar org.hsqldb.Server</literal>를 실행시켜서
데이터베이스를 시작시켜라. 당신은 그것이 시작되고 이것은 우리의 어플리케이션이 나중에 연결하게 될 장소인,
하나의 TCP/IP 소켓에 바인드 되는 것을 볼 수 있다. 만일 이 튜토리얼 동안에 당신이 새 데이터베이스로
시작하고자 원할 경우, HSQL DB를 셧다운시키고(왼도우에서 <literal>CTRL + C</literal>를 눌러라),
<literal>data/</literal> 디렉토리 내에 있는 모든 파일들을 삭제하고 다시 HSQL DB를 시작하라.
</para>
<para>
@ -802,19 +808,19 @@ else if (args[0].equals("list")) {
</sect2>
<sect2 id="tutorial-associations-unidirset">
<sect2 id="tutorial-associations-unidirset" revision="3">
<title>단방향 Set-기반의 연관</title>
<para>
우리는 <literal>Person</literal> 클래스에 이벤트들을 가진 한 개의 콜렉션을 추가할 것이다. 그 방법으로 우리는
명시적인 질의를 실행시키지 않고서 -<literal>aPerson.getEvents()</literal>를 호출하여- 특정 개인에 대한
이벤트들을 쉽게 네비게이트할 수 있다. 우리는 하나의 Java 콜렉션, <literal>Set</literal>를 사용한다. 왜냐하면
명시적인 질의-<literal>aPerson.getEvents()</literal>를 호출함으로써-를 실행시키지 않고서 특정 개인에 대한
이벤트들을 쉽게 네비게이트할 수 있다. 우리는 하나의 Java 콜렉션, 하나의 <literal>Set</literal>를 사용한다. 왜냐하면
그 콜렉션은 중복 요소들을 포함하기 않을 것이고 그 순서가 우리와 관련되어 있지 않기 때문이다.
</para>
<para>
지금까지 우리는 <literal>Set</literal>으로 구현된 단방향이고 다중 값을 가진 연관들을 설계했다.
Java 클래스들 속에 이를 위한 코드를 작성하고 그런 다음 그것을 매핑시키자:
우리는 하나의 <literal>Set</literal>으로 구현된, 하나의 단방향, 다중값 연관들을 필요로 한다. Java 클래스들 내에
이를 위한 코드를 작성하고 그런 다음 그것을 매핑시키자:
</para>
<programlisting><![CDATA[public class Person {
@ -839,9 +845,32 @@ else if (args[0].equals("list")) {
사용한다:
</para>
<programlisting><![CDATA[<class name="Person" table="PERSON">
<programlisting><![CDATA[public class Person {
private Set events = new HashSet();
public Set getEvents() {
return events;
}
public void setEvents(Set events) {
this.events = events;
}
}]]></programlisting>
<para>
이 연관을 매핑시키기 전에, 다른 측에 대해 생각해보자. 명료하게 우리는 이것을 단방향으로 유지시킬 수 있다. 또는
만일 우리가 양방향으로 네비게이트하는 것을 가능하도록 하고자 원할 경우에, 우리는 <literal>Event</literal> 상의
또 다른 콜렉션을 생성시킬 수 있다. 예를 들면.<literal>anEvent.getParticipants()</literal>. 이것은
기능적인 관점에서 필수적이지 않다. 당신은 특정 이벤트에 대한 참여자들을 검색하기 위해 하나의 명시적인 질의를 항상
실행시킬 수 있다. 이것은 당신에게 설계상의 선택으로 남겨두지만, 이 논의에서 명료해지는 것은 그 연관의 다중성이다:
양 측들이 "many" 값을 가질 경우, 우리는 이것을 <emphasis>many-to-many</emphasis> 연관이라 명명한다. 그러므로,
우리는 Hibernate의 many-to-many 매핑을 사용한다:
</para>
<programlisting><![CDATA[<class name="events.Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="increment"/>
<generator class="native"/>
</id>
<property name="age"/>
<property name="firstname"/>
@ -849,19 +878,18 @@ else if (args[0].equals("list")) {
<set name="events" table="PERSON_EVENT">
<key column="PERSON_ID"/>
<many-to-many column="EVENT_ID" class="Event"/>
<many-to-many column="EVENT_ID" class="events.Event"/>
</set>
</class>]]></programlisting>
<para>
Hibernate는 모든 종류의 콜렉션 매핑들, 가장 공통적인 <literal>&lt;set&gt;</literal>을 지원한다. many-to-many
연관 (또는 <emphasis>n:m</emphasis> 엔티티 관계)의 경우, 한 개의 연관 테이블이 필요하다. 이 테이블 에 있는 각각의
연관 (또는 <emphasis>n:m</emphasis> 엔티티 관계)의 경우, 한 개의 연관 테이블이 필요하다. 이 테이블 에 있는 각각의
행은 한 명의 개인과 한 개의 이벤트 사이의 링크를 표현한다. 테이블 이름은 <literal>set</literal> 요소의 <literal>table</literal>
속성으로 구성된다. 연관 내의 식별자 컬럼 이름은 개인 측에 대해 <literal>&lt;key&gt;</literal> 요소로 정의되고
이벤트 측에 대한 컬럼 이름은 <literal>&lt;many-to-many&gt;</literal><literal>column</literal> 속성으로
정의된다. 당신은 또한 당신의 콜렉션 내에 있는 객체들의 클래스(정확하게 : 참조들을 가진 콜렉션의 다른 측 상에 있는 클래스)를
Hibernate에게 알려주어야 한다
Hibernate에게 알려주어야 한다.
</para>
<para>
@ -1136,20 +1164,273 @@ public void removeFromEvent(Event event) {
모든 양방향 연관들은 한 쪽이 <literal>inverse</literal>일 필요가 있다. one-to-many 연관에서 그것은 many-측이어야 하고,
many-to-many 연관에서 당신은 어느 측이든 선택할 수 있으며 차이점은 없다.
</para>
<!--
</sect2>
<para>
Let's turn this into a small web application.
</para>
</sect1>
<sect1 id="tutorial-webapp">
<title>파트 3 - EventManager 웹 어플리케이션</title>
<para>
Hibernate 웹 어플리케이션은 대부분의 스탠드얼론 어플리케이션과 같이 <literal>Session</literal>
<literal>Transaction</literal>을 사용한다. 하지만 몇몇 공통 패턴들이 유용하다. 우리는 이제
<literal>EventManagerServlet</literal>를 작성한다. 이 서블릿은 데이터베이스 내에 저장된 모든 이벤트들을 나열할 수 있고,
그것은 새로운 이벤트들을 입력하기 위한 HTML form을 제공한다.
</para>
<sect2 id="tutorial-webapp-servlet">
<title>기본 서블릿 작성하기</title>
<para>
다음 장에서 우리는 Hibernate를 Tomcat 및 WebWork와 통합시킨다. <literal>EventManager</literal>는 우리의 성장하는
어플리케이션을 더이상 감당하지 못한다.
당신의 소스 디렉토리에서 <literal>events</literal> 패키지 내에 새로운 클래스를 생성시켜라:
</para>
-->
<programlisting><![CDATA[package events;
// Imports
public class EventManagerServlet extends HttpServlet {
private final SimpleDateFormat dateFormatter =
new SimpleDateFormat("dd.MM.yyyy");
// Servlet code
}]]></programlisting>
<para>
<literal>dateFormatter</literal>는 나중에 문자열들로부터 그리고 문자열로 <literal>Date</literal> 객체들을
변환하는데 나중에 사용하게 될 도구이다. 서블릿의 멤버로서 오직 한 개의 formatter를 갖는 것이 도리에 맞다.
</para>
<para>
서블릿은 HTTP <literal>GET</literal> 요청들 만을 처리하므로, 우리가 구현하는 메소드는 <literal>doGet()</literal>이다:
</para>
<programlisting><![CDATA[protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
try {
// Begin unit of work
HibernateUtil.getSessionFactory()
.getCurrentSession().beginTransaction();
// Process request and render page...
// End unit of work
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().commit();
} catch (Exception ex) {
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().rollback();
throw new ServletException(ex);
}
}]]></programlisting>
<para>
우리가 여기서 적용하는 패턴은 <emphasis>session-per-request</emphasis>이다. 하나의 요청이
서블릿에 도달할 때, 하나의 새로운 Hibernate <literal>Session</literal>
<literal>SessionFactory</literal> 상의 <literal>getCurrentSession()</literal>에 대한
첫번째 호출을 통해 열린다. 그때 하나의 데이터베이스 트랜잭션이 시작되고, 모든 데이터 접근이 하나의 트랜잭션
내에서 발생하는 한, 데이터가 읽혀지거나 기록되는데 문제가 없다(우리는 어플리케이션들 내에서 auto-commit 모드를
사용하지 않는다).
</para>
<para>
다음으로, 요청의 가능한 액션들이 처리되고 응답 HTML이 렌더링된다. 우리는 곧장 그부분으로 갈 것이다.
</para>
<para>
마지막으로, 프로세싱과 렌더링이 완료될 때 작업 단위가 종료된다. 만일 어떤 문제가 프로세싱과 렌더링 동안에 발생될 경우,
하나의 예외상황이 던져질 것이고 데이터베이스 트랜잭션은 롤백될 것이다. 이것은 <literal>session-per-request</literal>
완료시킨다. 모든 서블릿 내에 있는 트랜잭션 구획 코드 대신에 당신은 또한 서블릿 필터를 사용할 수 있다.
<emphasis>Open Session in View</emphasis>로 명명되는 이 패턴에 대한 추가 정보는 Hibernate 웹 사이트와 위키를 보라.
당신은 서블릿 내에서가 아닌 JSP 내에 당신의 뷰를 렌더링하는 것을 고려할 때 그것을 필요로 할 것이다.
</para>
</sect2>
<sect2 id="tutorial-webapp-processing">
<title>프로세싱과 렌더링</title>
<para>
요청의 처리와 페이지의 렌더링을 구현하자.
</para>
<programlisting><![CDATA[// Write HTML header
PrintWriter out = response.getWriter();
out.println("<html><head><title>Event Manager</title></head><body>");
// Handle actions
if ( "store".equals(request.getParameter("action")) ) {
String eventTitle = request.getParameter("eventTitle");
String eventDate = request.getParameter("eventDate");
if ( "".equals(eventTitle) || "".equals(eventDate) ) {
out.println("<b><i>Please enter event title and date.</i></b>");
} else {
createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
out.println("<b><i>Added event.</i></b>");
}
}
// Print page
printEventForm(out);
listEvents(out);
// Write HTML footer
out.println("</body></html>");
out.flush();
out.close();]]></programlisting>
<para>
Java와 HTML이 혼합된 이 코딩이 보다 복잡한 어플리케이션에서 기준이 될 수 없다 할지라도, 우리는
단지 이 튜토리얼 내에서 기본 Hibernate 개념들을 설명하고 있음을 염두에 두라. 코드는
하나의 HTML 헤더와 하나의 footer를 프린트한다. 이 페이지 내에 이벤트 엔트리를 위한
하나의 HTML form과 데이터베이스 내에 있는 모든 이벤트들의 목록이 프린트된다. 첫 번째 메소드는
시행적이고 오직 HTML을 출력한다:
</para>
<programlisting><![CDATA[private void printEventForm(PrintWriter out) {
out.println("<h2>Add new event:</h2>");
out.println("<form>");
out.println("Title: <input name='eventTitle' length='50'/><br/>");
out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
out.println("<input type='submit' name='action' value='store'/>");
out.println("</form>");
}]]></programlisting>
<para>
<literal>listEvents()</literal> 메소드는 하나의 질의를 실행하기 위해서 현재의
쓰레드에 결합된 Hibernate <literal>Session</literal>을 사용한다:
</para>
<programlisting><![CDATA[private void listEvents(PrintWriter out) {
List result = HibernateUtil.getSessionFactory()
.getCurrentSession().createCriteria(Event.class).list();
if (result.size() > 0) {
out.println("<h2>Events in database:</h2>");
out.println("<table border='1'>");
out.println("<tr>");
out.println("<th>Event title</th>");
out.println("<th>Event date</th>");
out.println("</tr>");
for (Iterator it = result.iterator(); it.hasNext();) {
Event event = (Event) it.next();
out.println("<tr>");
out.println("<td>" + event.getTitle() + "</td>");
out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
out.println("</tr>");
}
out.println("</table>");
}
}]]></programlisting>
<para>
마지막으로, <literal>store</literal> 액션은 <literal>createAndStoreEvent()</literal>
메소드로 디스패치된다. 그것은 현재 쓰레드의 <literal>Session</literal>을 사용한다:
</para>
<programlisting><![CDATA[protected void createAndStoreEvent(String title, Date theDate) {
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
HibernateUtil.getSessionFactory()
.getCurrentSession().save(theEvent);
}]]></programlisting>
<para>
즉 서블릿이 완성된다. 서블릿에 대한 요청은 하나의 단일 <literal>Session</literal>
<literal>Transaction</literal> 내에서 처리될 것이다. 이전처럼 스탠드얼론 어플리케이션에서,
Hibernate는 이들 객체들을 실행 중인 현재 쓰레드에 자동적으로 바인드시킬 수 있다. 이것은 당신의 코드를
계층화 시키고 당신이 좋아하는 임의의 방법으로 <literal>SessionFactory</literal>에 접근하는 자유를
당신에게 부여한다. 대개 당신은 보다 세련된 설계를 사용할 것이고 데이터 접근 코드를 데이터 접근 객체들 내로
이동시킬 것이다(DAO 패턴). 추가 예제들은 Hibernate 위키를 보라.
</para>
</sect2>
<sect2 id="tutorial-webapp-deploy">
<title>배치하기 그리고 테스트하기</title>
<para>
이 어플리케이션을 배치하기 위해서 당신은 하나의 웹 아카이브, WAR를 생성시켜야 한다. 다음 Ant target을
당신의 <literal>build.xml</literal> 내에 추가하라:
</para>
<programlisting><![CDATA[<target name="war" depends="compile">
<war destfile="hibernate-tutorial.war" webxml="web.xml">
<lib dir="${librarydir}">
<exclude name="jsdk*.jar"/>
</lib>
<classes dir="${targetdir}"/>
</war>
</target>]]></programlisting>
<para>
이 target은 당신의 프로젝트 디렉토리 내에 <literal>hibernate-tutorial.war</literal>로 명명된
하나의 파일을 생성시킨다. 그것은 당신의 프로젝트의 기본 디렉토리 내에 기대되는 모든 라이브러리들과
<literal>web.xml</literal> 디스크립터를 패키징한다:
</para>
<programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>Event Manager</servlet-name>
<servlet-class>events.EventManagerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Event Manager</servlet-name>
<url-pattern>/eventmanager</url-pattern>
</servlet-mapping>
</web-app>]]></programlisting>
<para>
당신이 웹 어플리케이션을 컴파일하고 배치하기 전에, 하나의 부가적인 라이브러리가 필요함을 노트하라:
<literal>jsdk.jar</literal>. 당신이 이미 이 라이브러리를 갖고 있지 않을 경우, 이것은 Java servlet
development kit이며, Sun 웹 사이트로부터 그것을 얻어서 그것을 당신의 라이브러리 디렉토리에 복사하라.
하지만 그것은 오직 컴파일 시에만 사용될 것이고 WAR 패키지에서는 제외된다.
</para>
<para>
빌드하고 배치하기 위해 당신의 프로젝트 디렉토리 내에서 <literal>ant war</literal>를 호출하고
<literal>hibernate-tutorial.war</literal> 파일을 당신의 Tomcat <literal>webapp</literal>
디렉토리로 복사하라. 만일 당신이 Tomcat을 설치하지 않았다면, 그것을 내려받아 설치 지침들을 따르라. 당신은
이 어플리케이션을 배치하기 위해 임의의 Tomcat 구성을 변경하지 않아야 한다.
</para>
<para>
일단 배치했고 Tomcat이 실행중이면, <literal>http://localhost:8080/hibernate-tutorial/eventmanager</literal>
어플리케이션에 접근하라. 첫 번째 요청이 당신의 서블릿에 도달할 때 Hibernate가 초기화(<literal>HibernateUtil</literal>
내에 있는 static initializer가 호출된다) 되는 것을 보기 위해 그리고 만일 어떤 예외상황들이 발생할 경우
상세한 출력을 얻기 위해서 Tomcat 로그를 지켜보도록 하라.
</para>
</sect2>
</sect1>
<sect1 id="tutorial-summary">
<sect1 id="tutorial-summary" revision="1">
<title>요약</title>
<para>
이 튜토리얼은 간단한 스탠드얼론 Hibernate 어플리케이션을 작성하는 기초를 다루었다.
이 튜토리얼은 간단한 스탠드얼론 Hibernate 어플리케이션과 하나의 작은 웹 어플리케이션을 작성하는 기초를 다루었다.
</para>
<para>