v31final
git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@9072 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
63815f5970
commit
a6607faacc
|
@ -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>
|
||||
|
||||
|
|
|
@ -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><key></literal>
|
||||
요소 상에 <literal>not-null="true"</literal>를 정의하는 것이 중요하다. 내포된
|
||||
<literal><column></literal> 요소 상에 <literal>not-null="true"</literal>를
|
||||
선언하지 말고, <literal><key></literal> 요소 상에 선언하라.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="assoc-bidirectional-121">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -171,7 +171,8 @@ session.close();]]></programlisting>
|
|||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
join들은 (함축적이든 명시적이든) 대량 HQL 질의 속에 지정될 수 없다. 서브-질의들이 where-절에 사용될 수 있다;
|
||||
<xref linkend="queryhql-joins-forms">join들</xref>은 (함축적이든 명시적이든)
|
||||
대량 HQL 질의 속에 지정될 수 없다. 서브-질의들이 where-절에 사용될 수 있다;
|
||||
서브질의들 그 자신들은 조인들을 포함할 수 있다.
|
||||
</para>
|
||||
</listitem>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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를 시작하는 편리한 방법임을 노트하라— 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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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><load-collection></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(<parameters>) }</literal> 또는 <literal>{ ? = call
|
||||
procedureName(<parameters>}</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(<parameters>) }</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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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><set></literal>을 지원한다. many-to-many
|
||||
연관 (또는 <emphasis>n:m</emphasis> 엔티티 관계)의 경우, 한 개의 연관 테이블이 필요하다. 이 테이블 속에 있는 각각의
|
||||
연관 (또는 <emphasis>n:m</emphasis> 엔티티 관계)의 경우, 한 개의 연관 테이블이 필요하다. 이 테이블 내에 있는 각각의
|
||||
행은 한 명의 개인과 한 개의 이벤트 사이의 링크를 표현한다. 테이블 이름은 <literal>set</literal> 요소의 <literal>table</literal>
|
||||
속성으로 구성된다. 연관 내의 식별자 컬럼 이름은 개인 측에 대해 <literal><key></literal> 요소로 정의되고
|
||||
이벤트 측에 대한 컬럼 이름은 <literal><many-to-many></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>
|
||||
|
|
Loading…
Reference in New Issue