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

1116 lines
61 KiB
XML

<chapter id="objectstate">
<title>객체들로 작업하기</title>
<para>
Hibernate는 기본 데이터베이스 관리 시스템의 상세로부터 개발자들을 은폐시켜줄 뿐만 아니라, 또한 객체들에 대한
<emphasis>상태 관리</emphasis>를 제공하는 하나의 완전한 객체/관계형 매핑 솔루션이다. 이것은 공통적인
JDBC/SQL 영속 계층들 내에서의 SQL <literal>문장들</literal>에 대한 관리와는 반대로, 자바 어플리케이션들에서
영속에 관한 매우 고유한 객체-지향적 관점이다.
</para>
<para>
달리 말해, Hibernate 어플리케이션 개발자들은 그들의 객체들의 <emphasis>상태</emphasis>에 대해 항상 생각해야 하고,
SQL 문장들의 실행에 대해서는 필수적이지 않다. 이 부분은 Hibernate에 의해 처리되고 시스템의 퍼포먼스를 튜닝할 때
어플리케이션 개발자와 유일하게 관련된다.
</para>
<sect1 id="objectstate-overview">
<title>Hibernate 객체 상태들</title>
<para>
Hibernate 다음 객체 상태들을 정의하고 지원한다:
</para>
<itemizedlist>
<listitem>
<para>
<emphasis>Transient</emphasis> - 만일 객체가 <literal>new</literal> 연산자를 사용하여 방금 초기화 되었다면
객체는 transient이고, 그것은 Hibernate <literal>Session</literal>과 연관되어 있지 않다. 그것은 데이터베이스
내에서 영속 표상을 갖지 않고 식별자 값이 할당되지 않았다. 만일 어플리케이션이 더 이상 참조를 소유하지 않을 경우
transient 인스턴스들은 쓰레기 수집기에 의해 파괴될 것이다. 객체를 영속화 시키는데
(그리고 이 전이(transition)에 대해 실행될 필요가 있는 SQL 문장들을 Hibernate로 하여금 처리하도록 하는데)
Hibernate <literal>Session</literal>을 사용하라.
</para>
</listitem>
<listitem>
<para>
<emphasis>Persistent</emphasis> - 하나의 영속 인스턴스는 데이터베이스 내에서 하나의 표상을 갖고 하나의 식별자 값을
갖는다. 그것은 방금 저장되었거나 로드되었을 수 있지만, 정의상 그것은 <literal>Session</literal>의 범위 내에 있다.
Hibernate는 영속 상태에서 객체에 대해 행해진 임의의 변경들을 검출해낼 것이고 단위 작업이 완료될 때 그 상태를 데이터베이스와
동기화 시킬 것이다. 개발자들은 하나의 객체가 transient로 되어야 할 때 <literal>UPDATE</literal> 문장들이나
<literal>DELETE</literal> 문장들을 수작업으로 실행하지 않는다.
</para>
</listitem>
<listitem>
<para>
<emphasis>Detached</emphasis> - 하나의 detached 인스턴스는 영속화 되었지만, 그것의
<literal>Session</literal>이 닫혀진 객체이다. 물론 그 객체에 대한 참조는 여전히 유효하고,
그 detached 인스턴스는 이 상태에서도 변경될 수도 있다. 하나의 detached 인스턴스는 나중에
그것(과 모두 변경들)을 다시 영속화 시켜서 새로운 <literal>Session</literal>에 다시 첨부될 수 있다.
이 특징은 사용자가 생각할 시간을 필요로 하는 장 기간 실행되는 작업 단위를 위한 프로그래밍 모형을 가능하게
해준다. 우리는 그것들을 <emphasis>어플리케이션 트랜잭션들</emphasis>, 즉 사용자의 관점의 작업 단위라고
부른다.
</para>
</listitem>
</itemizedlist>
<para>
이제 우리는 상태들과 상태 전이(transition)들(그리고 전이를 트리거 시키는 Hibernate 메소드들)을 상세하게 논의할 것이다.
</para>
</sect1>
<sect1 id="objectstate-makingpersistent" revision="1">
<title>객체들을 영속화 시키기</title>
<para>
하나의 영속 클래스의 새로이 초기화 된 인스턴스들은 Hibernate에 의해 <emphasis>transient</emphasis>로 간주된다.
우리는 그것을 세션과 연관지어서 transient 인스턴스를 <emphasis>영속화</emphasis> 시킬 수 있다:
</para>
<programlisting><![CDATA[DomesticCat fritz = new DomesticCat();
fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
Long generatedId = (Long) sess.save(fritz);]]></programlisting>
<para>
만일 <literal>Cat</literal>이 생성된 식별자를 가질 경우, <literal>save()</literal>가 호출될 때
그 식별자가 생성되고 <literal>cat</literal>에 할당된다. 만일 <literal>Cat</literal>이 하나의
<literal>assigned</literal> 식별자나 하나의 composite key를 가질 경우, <literal>save()</literal>
호출하기 전에 그 식별자가<literal>cat</literal> 인스턴스에 할당될 것이다. 당신은 또한 EJB3 초기 드래프트에서
정의된 의미로 <literal>save()</literal> 대신 <literal>persist()</literal>를 사용할 수도 있다.
</para>
<para>
다른 방법으로, 당신은 <literal>save()</literal>의 오버로드된 버전을 사용하여 식별자를 할당할 수 있다.
</para>
<programlisting><![CDATA[DomesticCat pk = new DomesticCat();
pk.setColor(Color.TABBY);
pk.setSex('F');
pk.setName("PK");
pk.setKittens( new HashSet() );
pk.addKitten(fritz);
sess.save( pk, new Long(1234) );]]></programlisting>
<para>
만일 당신이 영속화 시키는 객체가 연관된 객체들(예를 들면. 앞의 예제에 있는 <literal>kittens</literal> 콜렉션)을 갖고 있다면,
당신이 하나의 foreign 키 컬럼에 대해 하나의 <literal>NOT NULL</literal> 컨스트레인트를 갖지 않는 한,
이들 객체들은 당신이 좋아하는 임의의 순서로 영속화 되었을 수도 있다. foreign 키 컨스트레인트들을 위배하는 위험성이
결코 존재하지 않는다. 하지만 당신이 잘못된 순서로 그 객체들을 <literal>save()</literal> 시킬 경우 당신은
<literal>NOT NULL</literal> 컨스트레인트를 위배할 수도 있다.
</para>
<para>
당신이 연관된 객체들을 자동적으로 저장시키는데 Hibernate의 <emphasis>transitive persistence(전이 영속)</emphasis>
특징을 사용하는 것을 매우 좋아할 것이므로 대개 당신은 이 상세를 내버려둔다. 그때 <literal>NOT NULL</literal> 컨스트레인트
위배들이 발생되지 않을지라도 - Hibernate는 모든 것을 처리할 것이다. Transitive persistence(전이 영속)은 이 장에서
후반부에서 논의된다.
</para>
</sect1>
<sect1 id="objectstate-loading">
<title>객체를 로드시키기</title>
<para>
<literal>Session</literal><literal>load()</literal> 메소드들은 만일 당신이 그것(영속 인스턴스)의 식별자들을
이미 알고 있을 경우에 영속 인스턴스를 검색하는 방법을 당신에게 제공한다. <literal>load()</literal>는 하나의 클래스 객체를
취하고 그 상태를 영속(persistent) 상태로 그 클래스의 새로이 초기화 된 인스턴스 속으로 로드시킬 것이다.
</para>
<programlisting><![CDATA[Cat fritz = (Cat) sess.load(Cat.class, generatedId);]]></programlisting>
<programlisting><![CDATA[// you need to wrap primitive identifiers
long pkId = 1234;
DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) );]]></programlisting>
<para>
다른 방법으로 당신은 주어진 인스턴스 속으로 상태를 로드시킬 수 있다:
</para>
<programlisting><![CDATA[Cat cat = new DomesticCat();
// load pk's state into cat
sess.load( cat, new Long(pkId) );
Set kittens = cat.getKittens();]]></programlisting>
<para>
만일 일치하는 데이터베이스 행이 존재하지 않을 경우에 <literal>load()</literal>가 unrecoverable(복구 불가능한) 예외상황을
던질 것임을 노트하라. 만일 클래스가 프락시를 갖도록 매핑된 경우, <literal>load()</literal>는 초기화 되지 않은 프락시를 단지
반환하고 당신이 그 프락시의 메소드를 호출하기 전까지는 실제로 데이터베이스에 접속하지 않는다. 당신이 데이터베이스로부터
객체에 대한 연관을 실제로 로드시키지 않고서 객체에 대한 연관을 생성시키고자 원할 경우에 이 특징이 매우 유용하다. 만일
<literal>batch-size</literal>가 class 매핑에 정의되는 경우 그것은 또한 다중 인스턴스들이 하나의 배치로서 로드되는 것을
허용해준다.
</para>
<para>
만일 당신이 하나의 일치하는 행이 존재하는지를 확신할 수 없을 경우, 당신은 <literal>get()</literal> 메소드를 사용해야 한다.
그것(<literal>get()</literal> 메소드)는 데이터베이스에 즉시 접속하고 만일 일치하는 행이 없을 경우 null을 반환한다.
</para>
<programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id);
if (cat==null) {
cat = new Cat();
sess.save(cat, id);
}
return cat;]]></programlisting>
<para>
당신은 하나의 <literal>LockMode</literal>를 사용하는, <literal>SELECT ... FOR UPDATE</literal>를 사용하여
하나의 객체를 로드시킬 수도 있다. 추가 정보는 API 문서를 보라.
</para>
<programlisting><![CDATA[Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);]]></programlisting>
<para>
어떤 연관된 인스턴스들이나 포함된 콜렉션들은 당신이 그 연관에 대한 케스케이드 스타일로서 <literal>lock</literal> 또는
<literal>all</literal>을 지정하도록 결정하지 않는 한, <literal>FOR UPDATE</literal>로 선택되지 않음을 노트하라.
</para>
<para>
<literal>refresh()</literal> 메소드를 사용하여, 아무때나 하나의 객체와 모든 그것의 콜렉션들을 다시 로드시키는 것이
가능하다. 데이터베이스 트리거들이 그 객체의 프로퍼티들 중 어떤 것을 초기화 시키는데 사용될 때 이것이 유용하다.
</para>
<programlisting><![CDATA[sess.save(cat);
sess.flush(); //force the SQL INSERT
sess.refresh(cat); //re-read the state (after the trigger executes)]]></programlisting>
<para>
중요한 질문이 대개 이 지점에서 나타난다: Hibernate는 데이터베이로부터 그것을 얼마나 많이 로드시키고 그리고 얼마나 많은
SQL <literal>SELECT</literal>들이 그것을 사용할 것인가? 이것은 <emphasis>페칭 방도</emphasis>에 의존하고
<xref linkend="performance-fetching"/>에 설명되어 있다.
</para>
</sect1>
<sect1 id="objectstate-querying" revision="1">
<title>질의하기</title>
<para>
만일 당신이 당신이 찾고 있는 객체들에 대한 식별자들을 모를 경우, 당신은 하나의 질의를 필요로 한다. Hibernate는 사용이 쉽지만
강력한 객체 지향 질의 언어(HQL)를 지원한다. 프로그램 상의 질의 생성을 위해, Hibernate는 정교한 Criteria 및 Example 질의
특징(QBC와 QBE)를 지원한다. 당신은 또한 객체들로의 결과 셋 변환을 위한 선택적인 Hibernate의 지원으로, 당신의 데이터베이스의
native SQL 속에 당신의 질의를 표현할 수도 있다.
</para>
<sect2 id="objectstate-querying-executing">
<title>질의들을 실행하기</title>
<para>
HQL 질의와 native SQL 질의는 <literal>org.hibernate.Query</literal>의 인스턴스로 표현된다. 이 인터페이스는
파라미터 바인딩, 결과셋 핸들링을 위한, 그리고 실제 질의의 실행을 위한 메소드들을 제공한다. 당신은 항상 현재
<literal>Session</literal>을 사용하여 하나의 <literal>Query</literal>를 얻는다:
</para>
<programlisting><![CDATA[List cats = session.createQuery(
"from Cat as cat where cat.birthdate < ?")
.setDate(0, date)
.list();
List mothers = session.createQuery(
"select mother from Cat as cat join cat.mother as mother where cat.name = ?")
.setString(0, name)
.list();
List kittens = session.createQuery(
"from Cat as cat where cat.mother = ?")
.setEntity(0, pk)
.list();
Cat mother = (Cat) session.createQuery(
"select cat.mother from Cat as cat where cat = ?")
.setEntity(0, izi)
.uniqueResult();]]></programlisting>
<para>
하나의 질의는 대개 <literal>list()</literal>를 호출하여 실행되고, 질의의 결과는 메모리 내에서 하나의 콜렉션 속으로
전체적으로 로드될 것이다. 하나의 질의에 의해 검색된 엔티티 인스턴스들은 영속(persistent) 상태에 있다. 당신의 질의가
하나의 객체를 오직 반환할 것임을 당신이 알고 있을 경우에 <literal>uniqueResult()</literal> 메소드는 단축을 제공한다.
</para>
<sect3 id="objectstate-querying-executing-iterate">
<title>결과들을 반복하기</title>
<para>
종종, 당신은 <literal>iterate()</literal> 메소드를 사용하여 질의를 실행함으로써 더 나은 퍼포먼스를 성취하는
것이 가능할 수 있다. 이것은 오직 대개 질의에 의해 반환되는 실제 엔티티 인스턴스들이 이미 세션 내에 있거나
second-level 캐시 내에 있을 것임을 당신이 예상하는 경우일 것이다. 만일 그것들이 이미 캐시되지 않았다면,
<literal>iterate()</literal><literal>list()</literal> 보다 더 느릴 것이고 간단한 질의에 대해
많은 데이터베이스 접속들을, 대개 오직 식별자들을 반환하는 초기 select에 대해 <emphasis>1</emphasis>번의 접속과
실제 인스턴스들을 초기화 시키는 추가적인 select들에 대해 <emphasis>n</emphasis> 번의 접속을 필요로 할 수 있다.
</para>
<programlisting><![CDATA[// fetch ids
Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
while ( iter.hasNext() ) {
Qux qux = (Qux) iter.next(); // fetch the object
// something we couldnt express in the query
if ( qux.calculateComplicatedAlgorithm() ) {
// delete the current instance
iter.remove();
// dont need to process the rest
break;
}
}]]></programlisting>
</sect3>
<sect3 id="objectstate-querying-executing-tuples">
<title>튜플들을 반환하는 질의들</title>
<para>
Hibernate 질의들은 때때로 객체들의 튜플들을 반환하고, 그 경우에 각각의 튜플은 배열로서 반환된다:
</para>
<programlisting><![CDATA[Iterator kittensAndMothers = sess.createQuery(
"select kitten, mother from Cat kitten join kitten.mother mother")
.list()
.iterator();
while ( kittensAndMothers.hasNext() ) {
Object[] tuple = (Object[]) kittensAndMothers.next();
Cat kitten = tuple[0];
Cat mother = tuple[1];
....
}]]></programlisting>
</sect3>
<sect3 id="objectstate-querying-executing-scalar">
<title>스칼라 결과들</title>
<para>
질의들은 <literal>select</literal> 절 내에 하나의 클래스에 대한 하나의 프로퍼티를 지정할 수 있다.
그것들은 심지어 SQL 집계 함수들을 호출할 수도 있다. 프로퍼티들이나 aggregate들은
"스칼라" 결과들(그리고 영속 상태에 있는 엔티티들이 아닌 것으)로 간주된다.
</para>
<programlisting><![CDATA[Iterator results = sess.createQuery(
"select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
"group by cat.color")
.list()
.iterator();
while ( results.hasNext() ) {
Object[] row = results.next();
Color type = (Color) row[0];
Date oldest = (Date) row[1];
Integer count = (Integer) row[2];
.....
}]]></programlisting>
</sect3>
<sect3 id="objectstate-querying-executing-parameters">
<title>바인드 프라미터들</title>
<para>
<literal>Query</literal> 상의 메소드들은 명명된 파라미터들 또는 JDBC-스타일의 <literal>?</literal>
파라미터들에 바인딩 값들을 제공한다. <emphasis>JDBC와는 대조적으로, Hibernate 숫자 파라미터들은 0에서
시작된다.</emphasis> 명명된 파라미터들은 질의 문자열 속에서 <literal>:name</literal> 형식의 식별자들이다.
명명된 파라미터들의 장점들은 다음과 같다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
명명된 파라미터들은 그것들이 질의 문자열 내에 발생하는 순서에 관계없다
</para>
</listitem>
<listitem>
<para>
그것들은 동일한 질의 내에서 여러 번 발생할 수 있다
</para>
</listitem>
<listitem>
<para>
그것은 자기-설명적이다
</para>
</listitem>
</itemizedlist>
<programlisting><![CDATA[//named parameter (preferred)
Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
q.setString("name", "Fritz");
Iterator cats = q.iterate();]]></programlisting>
<programlisting><![CDATA[//positional parameter
Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
q.setString(0, "Izi");
Iterator cats = q.iterate();]]></programlisting>
<programlisting><![CDATA[//named parameter list
List names = new ArrayList();
names.add("Izi");
names.add("Fritz");
Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
q.setParameterList("namesList", names);
List cats = q.list();]]></programlisting>
</sect3>
<sect3 id="objectstate-querying-executing-pagination">
<title>쪽매김</title>
<para>
만일 당신이 당신의 결과 셋에 경계(당신이 검색하고자 원하는 최대 개수 그리고/또는 당신이 검색하고자 원하는 첫 번째 행)을
지정할 필요가 있다면 당신은 <literal>Query</literal> 인터페이스의 메소드들을 사용해야 한다:
</para>
<programlisting><![CDATA[Query q = sess.createQuery("from DomesticCat cat");
q.setFirstResult(20);
q.setMaxResults(10);
List cats = q.list();]]></programlisting>
<para>
Hibernate는 이 limit 질의를 당신의 DBMS의 native SQL로 번역하는 방법을 알고 있다.
</para>
</sect3>
<sect3 id="objectstate-querying-executing-scrolling">
<title>스크롤 가능한 iteration</title>
<para>
당신의 JDBC 드라이버가 스크롤 가능한 <literal>ResultSet</literal>들을 지원할 경우,
<literal>Query</literal> 인터페이스는 <literal>ScrollableResults</literal> 객체를 얻는데
사용될 수 있고, 그것은 질의 결과들에 대한 유연한 네비게이션을 허용해준다.
</para>
<programlisting><![CDATA[Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
"order by cat.name");
ScrollableResults cats = q.scroll();
if ( cats.first() ) {
// find the first name on each page of an alphabetical list of cats by name
firstNamesOfPages = new ArrayList();
do {
String name = cats.getString(0);
firstNamesOfPages.add(name);
}
while ( cats.scroll(PAGE_SIZE) );
// Now get the first page of cats
pageOfCats = new ArrayList();
cats.beforeFirst();
int i=0;
while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );
}
cats.close()]]></programlisting>
<para>
열려진 데이터베이스 커넥션(과 커서)가 이 기능에 필요함을 노트하고, 만일 당신이 쪽매김 기능을 작동시킬 필요가 있다면
<literal>setMaxResult()</literal>/<literal>setFirstResult()</literal>를 사용하라.
</para>
</sect3>
<sect3 id="objectstate-querying-executing-named">
<title>명명된 질의들을 구체화 시키기</title>
<para>
당신은 또한 매핑 문서 속에 명명된 질의들을 정의할 수 있다.(만일 당신의 질의가 마크업으로서 해석될 수 있는 문자들을
포함할 경우에 <literal>CDATA</literal> 섹션을 사용하는 것을 기억하라))
</para>
<programlisting><![CDATA[<query name="eg.DomesticCat.by.name.and.minimum.weight"><![CDATA[
from eg.DomesticCat as cat
where cat.name = ?
and cat.weight > ?
] ]></query>]]></programlisting>
<para>
파라미터 바인딩과 실행은 프로그램 상으로 행해진다:
</para>
<programlisting><![CDATA[Query q = sess.getNamedQuery("eg.DomesticCat.by.name.and.minimum.weight");
q.setString(0, name);
q.setInt(1, minWeight);
List cats = q.list();]]></programlisting>
<para>
실제 프로그램 코드는 사용되는 질의 언어에 독립적이고, 당신은 또한 메타데이터로 native SQL 질의들을 정의할 수도 있거나
그것들을 매핑 파일들 속에 기존 질의들을 위치지움으로써 기존 질의들을 Hibernate로 이전시킬 수도 있음을 노트하라.
</para>
</sect3>
</sect2>
<sect2 id="objectstate-filtering" revision="1">
<title>콜렉션들을 필터링 하기</title>
<para>
콜렉션 <emphasis>필터</emphasis>는 영속 콜렉션 또는 배열에 적용될 수 있는 질의의 특별한 타입이다. 질의 문자열은
현재의 콜렉션 요소를 의미하는 <literal>this</literal>를 참조할 수 있다.
</para>
<programlisting><![CDATA[Collection blackKittens = session.createFilter(
pk.getKittens(),
"where this.color = ?")
.setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
.list()
);]]></programlisting>
<para>
반환되는 콜렉션은 하나의 bag으로 간주되고, 그것은 주어진 콜렉션에 대한 사본이다. 원래의 콜렉션은 변경되지 않는다
(이것은 이름 "filter"의 의미와는 정반대이지만, 예상되는 행위와 일치된다).
</para>
<para>
필터들은 <literal>from</literal> 절을 필요로 하지 않음을 관찰하라(필요할 경우에 필터들이 한 개의 from 절을 가질 수
있을지라도). 필터들은 콜렉션 요소들 자체들을 반환하는 것으로 한정되지 않는다.
</para>
<programlisting><![CDATA[Collection blackKittenMates = session.createFilter(
pk.getKittens(),
"select this.mate where this.color = eg.Color.BLACK.intValue")
.list();]]></programlisting>
<para>
심지어 하나의 공백의 필터 질의도 예를 들어 거대한 콜렉션 내에 있는 요소들의 부분집합들을 로드시키는데 유용하다:
</para>
<programlisting><![CDATA[Collection tenKittens = session.createFilter(
mother.getKittens(), "")
.setFirstResult(0).setMaxResults(10)
.list();]]></programlisting>
</sect2>
<sect2 id="objecstate-querying-criteria" revision="1">
<title>Criteria 질의들</title>
<para>
HQL은 극히 강력하지만 몇몇 개발자들은 질의 문자열들을 빌드하기 보다, 객체 지향 API를 사용하여 동적으로 질의들을 빌드시키는
것을 선호한다. Hibernate는 이들 경우들을 위한 직관적인 <literal>Criteria</literal> query API를 제공한다:
</para>
<programlisting><![CDATA[Criteria crit = session.createCriteria(Cat.class);
crit.add( Expression.eq( "color", eg.Color.BLACK ) );
crit.setMaxResults(10);
List cats = crit.list();]]></programlisting>
<para>
<literal>Criteria</literal>와 연관된 <literal>Example</literal> API 는
<xref linkend="querycriteria"/>에서 상세하게 논의된다.
</para>
</sect2>
<sect2 id="objectstate-querying-nativesql" revision="2">
<title>native SQL에서 질의들</title>
<para>
당신은 <literal>createSQLQuery()</literal>를 사용하여 SQL 속에 하나의 질의를 표현할 수 있고,
Hibernate로 하여금 결과 셋들로부터 객체들로의 매핑을 처리하도록 할수도 있다. 당신은 아무때나
<literal>session.connection()</literal>을 호출할 수 있고 직접 JDBC <literal>Connection</literal>
사용할 수 있음을 노트하라. 만일 당신이 Hibernate API를 사용하고자 선택한 경우에 , 당신은 SQL alias들을
중괄호들 속에 포함시켜야 한다:
</para>
<programlisting><![CDATA[List cats = session.createSQLQuery(
"SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
"cat",
Cat.class
).list();]]></programlisting>
<programlisting><![CDATA[List cats = session.createSQLQuery(
"SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
"{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
"FROM CAT {cat} WHERE ROWNUM<10",
"cat",
Cat.class
).list()]]></programlisting>
<para>
SQL 질의들은 Hibernate 질의들처럼 명명된 파라미터들과 위치 파라미터들을 포함할 수도 있다. SQL 질의들에 대한
추가 정보는 <xref linkend="querysql"/>에서 찾을 수 있다.
</para>
</sect2>
</sect1>
<sect1 id="objectstate-modifying" revision="1">
<title>영속 객체들을 변경하기</title>
<para>
<emphasis>트랜잭션 상의 영속 인스턴스들</emphasis> (예를들면. <literal>Session</literal>에 의해 로드되고,
저장되고, 생성되거나 질의된 객체들)은 어플리케이션에 의해 처리될 수 있고 영속 상태에 대한 임의의 변경들은
<literal>Session</literal><emphasis>flush될</emphasis> 때 영속화 될 것이다(이 장의 뒷 부분에서 논의됨).
당신의 변경들을 영속화 시키기 위해 (다른 용도를 가진 <literal>update()</literal>와 같은) 특별한 메소드를 호출할
필요가 없다. 따라서 객체의 상태를 업데이트 시키는 가장 간단한 방법은 <literal>Session</literal>이 열려 있는 동안,
그것을 <literal>load()</literal>시키고 나서, 그것을 직접 처리하는 것이다:
</para>
<programlisting><![CDATA[DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
cat.setName("PK");
sess.flush(); // changes to cat are automatically detected and persisted]]></programlisting>
<para>
때때로 이 프로그래밍 모형은 불충분하다. 왜냐하면 그것은 동일한 세션 내에서 (객체를 로드시키는) SQL
<literal>SELECT</literal>와 (그것의 업데이트된 상태를 영속화 시키는) SQL <literal>UPDATE</literal>
양자를 필요로 할 것이기 때문이다. 그러므로 Hibernate는 detached 인스턴스들을 사용하는 대안적인 접근법을 제공한다.
</para>
<para>
<emphasis>Hibernate는 <literal>UPDATE</literal> 문장 또는 <literal>DELETE</literal> 문장의 직접적인 실행을 위한
그것 자신의 API를 제공하지 않음을 노트하라. Hibernate는 하나의 <emphasis>상태 관리</emphasis> 서비스이고, 당신은 그것을
사용할 <emphasis>문장들</emphasis>을 생각하지 말아야 한다. JDBC는 SQL 문장들을 실행시키는 완전한 API이고, 당신은
<literal>session.connection()</literal>을 호출하여 아무때나 한 개의 JDBC <literal>Connection</literal>
얻을 수 있다. 게다가 대량 오퍼레이션의 개념은 온라인 트랜잭션 처리-지향적인 어플리케이션들을 위한 객체/관계형 매핑과 충돌한다.
하지만 Hibernate의 장래 버전들은 특별한 대용량의 오퍼레이션 기능들을 제공할 수도 있다. 몇몇 가능한 배치 오퍼레이션 트릭들에
대해서는 <xref linkend="batch"/>을 보라.</emphasis>
</para>
</sect1>
<sect1 id="objectstate-detached" revision="2">
<title>detached 객체들을 변경시키기</title>
<para>
많은 어플리케이션들은 하나의 트랜잭션 내에서 하나의 객체를 검색하고, 처리를 위한 UI 계층으로 그것을 전송하고,
그런 다음 새로운 트랜잭션 내에서 변경들을 저장할 필요가 있다. 고도의-동시성 환경에서 이런 종류의 접근법을 사용하는
어플리케이션들은 대개 작업의 "긴" 단위를 확실히 격리시키기 위해 버전화 된 데이터를 사용한다.
</para>
<para>
Hibernate는 <literal>Session.update()</literal> 메소드 또는 <literal>Session.merge()</literal> 메소드를
사용하여 detached 인스턴스들의 재첨부를 제공함으로써 이 모형을 지원한다:
</para>
<programlisting><![CDATA[// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catId);
Cat potentialMate = new Cat();
firstSession.save(potentialMate);
// in a higher layer of the application
cat.setMate(potentialMate);
// later, in a new session
secondSession.update(cat); // update cat
secondSession.update(mate); // update mate]]></programlisting>
<para>
만일 <literal>catId</literal> 식별자를 가진 <literal>Cat</literal><literal>secondSession</literal>에 의해
이미 로드되었을 경우에 어플리케이션이 그것을 다시 재첨부하려고 시도할 때, 예외상황이 던져졌을 것이다.
</para>
<para>
만일 그 세션이 동일한 식별자를 가진 영속 인스턴스를 이미 포함하지 않음을 당신이 확신하는 경우에는
<literal>update()</literal>를 사용하고, 만일 당신이 세션의 상태를 고려하지 않은채로 아무때나 당신의 변경을 병합시키고자
원할 경우에는 <literal>merge()</literal>를 사용하라. 달리 말해, 당신의 detached 인스턴스들에 대한 재첨부가 실행되는
첫 번째 오퍼레이션임을 확실히 함으로써, <literal>update()</literal>는 대개 갓 만들어진 세션에서 당신이 호출하게 될
첫 번째 메소드이다.
</para>
<para>
어플리케이션은 만일 그것이 detached 인스턴스들의 상태가 또한 업데이트 되는 것을 원할 경우에<emphasis></emphasis>
주어진 detached 인스턴스로부터 도달 가능한 detached 인스턴스들을 개별적으로<literal>update()</literal> 시킬 것이다.
이것은 물론 <emphasis>transitive persistence(전이 영속)</emphasis>을 사용하여 자동화 될 수 있고,
<xref linkend="objectstate-transitive"/>를 보라.
</para>
<para>
<literal>lock()</literal> 메소드는 또한 하나의 객체를 새로운 세션에 대해 다시 연관시키는것을 어플리케이션에게 허용해준다.
하지만 detached 인스턴스는 변경되지 않아야 한다!
</para>
<programlisting><![CDATA[//just reassociate:
sess.lock(fritz, LockMode.NONE);
//do a version check, then reassociate:
sess.lock(izi, LockMode.READ);
//do a version check, using SELECT ... FOR UPDATE, then reassociate:
sess.lock(pk, LockMode.UPGRADE);]]></programlisting>
<para>
<literal>lock()</literal>이 여러가지 <literal>LockMode</literal>들에 사용될 수 있음을 노트하고,
상세한 것은 API 문서와 트랜잭션 처리에 관한 장을 보라. 재첨부는 <literal>lock()</literal>에 대한
유일한 쓰임새는 아니다.
</para>
<para>
긴 작업 단위에 대한 다른 모형들은 <xref linkend="transactions-optimistic"/>에서 논의된다.
</para>
</sect1>
<sect1 id="objectstate-saveorupdate">
<title>자동적인 상태 검출</title>
<para>
Hibernate 사용자들은 새로운 식별자를 생성시켜서 transient 인스턴스를 저장하거나
그것의 현재 식별자와 연관된 detached 인스턴스들을 업데이트/재첨부 시키는 일반적인 용도의 메소드를 요청했다.
<literal>saveOrUpdate()</literal> 메소드는 이 기능을 구현한다.
</para>
<programlisting><![CDATA[// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catID);
// in a higher tier of the application
Cat mate = new Cat();
cat.setMate(mate);
// later, in a new session
secondSession.saveOrUpdate(cat); // update existing state (cat has a non-null id)
secondSession.saveOrUpdate(mate); // save the new instance (mate has a null id)]]></programlisting>
<para>
<literal>saveOrUpdate()</literal>의 사용 예제와 의미는 초심자들에게는 혼동스러워 보인다. 먼저, 하나의 세션에서 온
인스턴스를 또 다른 새로운 세션 내에서 사용하려고 시도하지 않는 한, 당신은 <literal>update()</literal>,
<literal>saveOrUpdate()</literal>, 또는 <literal>merge()</literal>를 사용할 필요는 없을 것이다.
몇몇 전체 어플리케이션들은 이들 메소드들 중 어느 것도 결코 사용하지 않을 것이다.
</para>
<para>
대개 <literal>update()</literal> 또는 <literal>saveOrUpdate()</literal>는 다음 시나리오에서 사용된다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
어플리케이션이 첫 번째 세션 내에 객체를 로드시킨다
</para>
</listitem>
<listitem>
<para>
객체가 UI 티어로 전달된다
</para>
</listitem>
<listitem>
<para>
몇몇 변경들이 그 객체에 행해진다
</para>
</listitem>
<listitem>
<para>
객체가 비지니스 로직 티어로 전달된다
</para>
</listitem>
<listitem>
<para>
어플리케이션은 두 번째 세션에서 <literal>update()</literal>를 호출함으로써 이들 변경들을 영속화 시킨다
</para>
</listitem>
</itemizedlist>
<para>
<literal>saveOrUpdate()</literal>는 다음을 행한다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
만일 객체가 이 세션 내에서 이미 영속화 되어 있을 경우, 아무것도 행하지 않는다
</para>
</listitem>
<listitem>
<para>
만일 그 세션과 연관된 또 다른 객체가 동일한 식별자를 가질 경우, 예외상황을 던진다
</para>
</listitem>
<listitem>
<para>
만일 그 객체가 식별자 프로퍼티를 갖지 않을 경우, 그것을 <literal>save()</literal> 시킨다
</para>
</listitem>
<listitem>
<para>
만일 객체의 식별자가 새로이 초기화 된 객체에 할당된 값을 가질 경우, 그것을 <literal>save()</literal> 시킨다
</para>
</listitem>
<listitem>
<para>
만일 객체가 (<literal>&lt;version&gt;</literal> 또는 <literal>&lt;timestamp&gt;</literal>에 의해)
버전화 되고, version 프로퍼티 값이 새로이 초기화 된 객체에 할당된 것과 동일한 값일 경우, 그것을 <literal>save()</literal>
시킨다
</para>
</listitem>
<listitem>
<para>
그 밖의 경우 그 객체를 <literal>update()</literal> 시킨다
</para>
</listitem>
</itemizedlist>
<para>
그리고 <literal>merge()</literal>는 매우 다르다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
만일 세션과 현재 연관된 동일한 식별자를 가진 영속 인스턴스가 존재할 경우,
주어진 객체의 상태를 영속 인스턴스 상으로 복사한다
</para>
</listitem>
<listitem>
<para>
만일 세션과 현재 연관된 영속 인스턴스가 존재하지 않을 경우, 데이터베이스로부터 그것을 로드시키려고 시도하거나
새로운 영속 인스턴스를 생성시키려고 시도한다
</para>
</listitem>
<listitem>
<para>
영속 인스턴스가 반환된다
</para>
</listitem>
<listitem>
<para>
주어진 인스턴스는 세션과 연관되지 않고, 그것은 detached 상태에 머무른다
</para>
</listitem>
</itemizedlist>
</sect1>
<sect1 id="objectstate-deleting" revision="1">
<title>영속 객체들을 삭제하기</title>
<para>
<literal>Session.delete()</literal>는 데이터베이스로부터 객체의 상태를 제거할 것이다.
물론 당신의 어플리케이션은 여전히 detached 객체에 대한 참조를 소유할 것이다. 영속 인스턴스를 transient로 만들 때
<literal>delete()</literal>를 생각하는 것이 최상이다.
</para>
<programlisting><![CDATA[sess.delete(cat);]]></programlisting>
<para>
당신은 foreign 키 컨스트레인트 위배들에 대한 위험성 없이 당신이 좋아하는 어떤 순서로 객체들을 삭제할 수도 있다.
잘못된 순서로 객체들을 삭제함으로써 foreign 키 컬럼에 대한 <literal>NOT NULL</literal> 컨스트레인트를 위배할 가능성이
여전히 존재한다. 예를 들어, 당신이 부모를 삭제하지만, 그 자식들을 삭제하는 것을 잊은 경우.
</para>
</sect1>
<sect1 id="objectstate-replicating" revision="1">
<title>두 개의 다른 데이터저장소들 사이에 객체들을 복제하기</title>
<para>
영속 인스턴스들의 그래프를 취하고 식별자 값들을 다시 생성시키지 않고서 그것들을 다른 저장소 속에 영속화 시키는 것을 가능하도록
만드는 것이 종종 유용하다.
</para>
<programlisting><![CDATA[//retrieve a cat from one database
Session session1 = factory1.openSession();
Transaction tx1 = session1.beginTransaction();
Cat cat = session1.get(Cat.class, catId);
tx1.commit();
session1.close();
//reconcile with a second database
Session session2 = factory2.openSession();
Transaction tx2 = session2.beginTransaction();
session2.replicate(cat, ReplicationMode.LATEST_VERSION);
tx2.commit();
session2.close();]]></programlisting>
<para>
<literal>ReplicationMode</literal><literal>replicate()</literal>가 데이터베이스 내에 있는 기존의
행들과의 충돌을 처리하게될 방법을 결정한다.
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>ReplicationMode.IGNORE</literal> - 동일한 식별자를 가진 기존 데이터베이스 행이 존재할
경우에 그 객체를 무시한다
</para>
</listitem>
<listitem>
<para>
<literal>ReplicationMode.OVERWRITE</literal> - 동일한 식별자를 가진 어떤 기존의 데이터베이스 행을 덮어 쓴다
</para>
</listitem>
<listitem>
<para>
<literal>ReplicationMode.EXCEPTION</literal> - 만일 동일한 식별자를 가진 기존 데이터베이스 행이 존재할 경우에
예외상황을 던진다
</para>
</listitem>
<listitem>
<para>
<literal>ReplicationMode.LATEST_VERSION</literal> - 행의 버전 번호가 객체의 버전 번호 보다 이전의 것이면 덮어쓰고,
그 밖의 경우에 그 객체를 무시한다
</para>
</listitem>
</itemizedlist>
<para>
이 특징의 쓰임새들은 다른 데이터베이스 인스턴스들 속으로 입력된 데이터 일치시키기, 제품 업그레이드 동안에 시스템 구성 정보
업데이트 하기, non-ACID 트랜잭션들 동안에 행해진 변경들을 롤백시키기 등을 포함한다.
</para>
</sect1>
<sect1 id="objectstate-flushing">
<title>Session을 flush 시키기</title>
<para>
시간이 지남에 따라 <literal>Session</literal>은 JDBC 커넥션의 상태와 메모리 내에 보관된 객체들의 상태를 동기화 시키는데
필요한 SQL 문장들을 실행시킬 것이다. 이 프로세스 <emphasis>flush</emphasis>는 다음 시점들에서 디폴트로 발생한다
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
몇몇 질의들이 실행되기 전에
</para>
</listitem>
<listitem>
<para>
<literal>org.hibernate.Transaction.commit()</literal> 시점에서
</para>
</listitem>
<listitem>
<para>
<literal>Session.flush()</literal> 시점에서
</para>
</listitem>
</itemizedlist>
<para>
SQL 문장들이 다음 순서로 실행 명령이 내려진다
</para>
<orderedlist spacing="compact">
<listitem>
<para>
대응하는 객체들이 <literal>Session.save()</literal>를 사용하여 저장되었던 것과 같은 순서로, 모든 엔티티 삽입들
</para>
</listitem>
<listitem>
<para>
모든 엔티티 업데이트들
</para>
</listitem>
<listitem>
<para>
모든 콜렉션 삭제들
</para>
</listitem>
<listitem>
<para>
모든 콜렉션 요소 삭제들, 업데이트들 그리고 삽입들
</para>
</listitem>
<listitem>
<para>
모든 콜렉션 삽입들
</para>
</listitem>
<listitem>
<para>
대응하는 객체들이 <literal>Session.delete()</literal>를 사용하여 삭제되었던 것과 같은 순서로 모든 엔티티 삭제들.
</para>
</listitem>
</orderedlist>
<para>
(한가지 예외는 객체들이 저장될 때 <literal>native</literal> ID 생성을 사용하는 객체들이 insert 되는 점이다.)
</para>
<para>
당신이 명시적으로 <literal>flush()</literal> 시킬 때를 제외하면, <literal>Session</literal>이 JDBC 호출들을
실행시키는 <emphasis>시점</emphasis>, 그것들이 실행되는 <emphasis>순서</emphasis>만을 절대적으로 보장하지는 않는다.
하지만 Hibernate는 <literal>Query.list(..)</literal>가 실효성이 없는 데이터를 결코 반환하지 않을 것임을 보장하거나;
그것들이 잘못된 데이터도 반환하지 않을 것임을 보장한다.
</para>
<para>
flush가 너무 자주 발생하지 않도록 디폴트 행위를 변경하는 것이 가능하다. <literal>FlushMode</literal> 클래스는 세 개의
다른 모드들을 정의한다: 오직 커밋 시(그리고 Hibernate <literal>Transaction</literal> API가 사용될 때에만) flush 모드,
설명된 루틴을 사용하는 자동적인 flush 모드, 또는 <literal>flush()</literal>가 명시적으로 호출되지 않는 한 flush 시키지
않는 모드. 마지막 모드는 오래 동안 실행되는 작업 단위에 대해 유용하고, 여기서 <literal>Session</literal>은 열려진채로
유지되고 오랜 시간 동안 연결이 해제된 채로 유지된다. (<xref linkend="transactions-optimistic-longsession"/>를 보라).
</para>
<programlisting><![CDATA[sess = sf.openSession();
Transaction tx = sess.beginTransaction();
sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state
Cat izi = (Cat) sess.load(Cat.class, id);
izi.setName(iznizi);
// might return stale data
sess.find("from Cat as cat left outer join cat.kittens kitten");
// change to izi is not flushed!
...
tx.commit(); // flush occurs]]></programlisting>
<para>
flush 동안에, 하나의 예외상황이 발생할 수도 있다(예를 들면. 만일 DML 오퍼레이션이 컨스트레인트를 위반할 경우). 예외상황들을
처리하는 것은 Hibernatem의 트랜잭션 특징에 관한 어떤 이해를 수반하며, 우리는 <xref linkend="transactions"/>에서 그것을
논의한다.
</para>
</sect1>
<sect1 id="objectstate-transitive">
<title>Transitive persistence(전이 영속)</title>
<para>
특히 당신이 연관된 객체들의 그래프를 다룰 경우에, 특히 개별 객체들을 저장하고, 삭제하거나, 재첨부시키는 것이 꽤 번거롭다.
공통된 경우는 하나의 부모/자식 관계이다. 다음 예제를 검토하자:
</para>
<para>
만일 부모/자식 관계에서 자식들이 값(value) 타입(예를 들면. 주소들 또는 문자열들을 가진 하나의 콜렉션)일 경우, 그것들의 생명주기는
부모에 의존할 것이고 상태 변경들에 대해 편리한 "케스케이딩"에 더 이상의 액션이 필요하지 않을 것이다. 만일 부모가 저장될 때,
값(value)-타입의 자식 객체들도 마찬가지로 저장되고, 부모가 삭제될 때, 자식들이 삭제될 것이다. 이것은 심지어 콜렉션으로부터 하나의
자식을 제거하는 그런 오퍼레이션들에 대해서도 동작한다; Hibernate는 이것을 검출하고, 값(value)-타입의 객체들은 참조를 공유할 수
없으므로, 데이터베이스로부터 그 자식을 삭제시킨다.
</para>
<para>
이제 값(value) 타입이 아닌, 엔티티들인 부모와 자식 객체들을 가진 동일한 시나리오를 검토하자(예를 들면. 카테고리들과 아이템들,
또는 부모 고양이나 자식 고양이). 엔티티들은 그것들 자신의 생명주기를 갖고, 공유된 참조들을 지원하고 (따라서 콜렉션으로부터 하나의
엔티티를 제거하는 것은 그것이 삭제될 수 있음을 의미하지 않는다), 그리고 디폴트로 하나의 엔티티로부터 어떤 다른 연관된 엔티티들로의
상태의 케스케이딩은 존재하지 않는다. Hibernate는 디폴트로 <emphasis>도달가능성에 의한 영속성(persistence by reachability)</emphasis>
구현하지 않는다.
</para>
<para>
- <literal>persist(), merge(), saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()</literal>
포함하는- Hibernate 세션에 대한 각각의 기본 오퍼레이션에 대해서 하나의 대응하는 케스케이딩 스타일이 존재한다. 케스케이드 스타일들
각각은 <literal>create, merge, save-update, delete, lock, refresh, evict, replicate</literal>로 명명된다.
만일 당신이 하나의 오퍼레이션이 하나의 연관에 따라 케스케이딩되는 것을 원할 경우, 당신은 매핑 문서 내에 그것을 지시해야 한다.
예를 들면:
</para>
<programlisting><![CDATA[<one-to-one name="person" cascade="persist"/>]]></programlisting>
<para>
케스케이딩 스타일들이 결합될 수도 있다:
</para>
<programlisting><![CDATA[<one-to-one name="person" cascade="persist,delete,lock"/>]]></programlisting>
<para>
당신은 <emphasis>모든</emphasis> 오퍼레이션들이 그 연관에 따라 케스케이드 되어야 함을 지정하는데
<literal>cascade="all"</literal>을 사용할 수도 있다. 디폴트<literal>cascade="none"</literal>은 오퍼레이션들이
케스케이드 되지 않을 것임을 지정한다.
</para>
<para>
특정한 케스케이드 스타일인, <literal>delete-orphan</literal>은 오직 one-to-many 연관들에만 적용되고,
<literal>delete()</literal> 오퍼레이션이 그 연관으로부터 제거되는 임의의 자식 객체에 적용되어야 함을 나타낸다.
</para>
<para>
권장사항들 :
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
하나의 <literal>&lt;many-to-one&gt;</literal> 또는 <literal>&lt;many-to-many&gt;</literal> 연관에
대해 케스케이드를 가능하게 하는 것은 대개 의미가 없다. 케스케이드는 <literal>&lt;one-to-one&gt;</literal> 연관과
<literal>&lt;one-to-many&gt;</literal> 연관에 대해 종종 유용하다.
</para>
</listitem>
<listitem>
<para>
만일 자식 객체의 수명이 그 부모 객체의 수명에 묶여져 있을 경우, <literal>cascade="all,delete-orphan"</literal>
지정함으로써 그것을 <emphasis>생명 주기 객체</emphasis>로 만들어라.
</para>
</listitem>
<listitem>
<para>
그 밖의 경우, 당신은 케스케이드를 전혀 필요로 하지 않을 수 있다. 그러나 만일 당신이 동일한 트랜잭션 내에서 부모와 자식에
대해 자주 함께 작업하게 될 것이라 생각되고, 당신 스스로 타이핑 하는 것을 절약하고자 원할 경우,
<literal>cascade="persist,merge,save-update"</literal>를 사용하는 것을 고려하라.
</para>
</listitem>
</itemizedlist>
<para>
<literal>cascade="all"</literal>을 가진 (단일 값 연관이든 하나의 콜렉션이든) 하나의 연관을 매핑시키는 것은
그 연관을 부모의 저장/업데이트/삭제가 자식 또는 자식들의 저장/업데이트/삭제로 귀결되는 <emphasis>부모/자식</emphasis>
스타일의 관계로 마크한다.
</para>
<para>
게다가, 하나의 영속 부모로부터 하나의 자식에 대한 단순한 참조는 자식의 저장/업데이트로 귀결될 것이다. 하지만 이 메타포는 불완전하다.
그것의 부모에 의해 참조 해제되는 자식은 <literal>cascade="delete-orphan"</literal>으로 매핑된 하나의
<literal>&lt;one-to-many&gt;</literal> 연관의 경우를 제외하면, 자동적으로 삭제되지 <emphasis>않는다</emphasis>.
하나의 부모/자식 관계에 대한 케스케이딩 오퍼레이션의 정확한 의미는 다음과 같다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
만일 부모가 <literal>persist()</literal>에 전달될 경우, 모든 자식들이 <literal>persist()</literal>에 전달된다
</para>
</listitem>
<listitem>
<para>
만일 부모가 <literal>merge()</literal>에 전달될 경우, 모든 자식들이 <literal>merge()</literal>에 전달된다
</para>
</listitem>
<listitem>
<para>
만일 부모가 <literal>save()</literal>, <literal>update()</literal> 또는 <literal>saveOrUpdate()</literal>
전달될 경우, 모든 자식들이 <literal>saveOrUpdate()</literal>에 전달된다
</para>
</listitem>
<listitem>
<para>
만일 transient 또는 detached 자식이 영속 부모에 의해 참조될 경우, 그것은 <literal>saveOrUpdate()</literal>에 전달된다
</para>
</listitem>
<listitem>
<para>
만일 부모가 삭제될 경우, 모든 자식들이 <literal>delete()</literal>에 전달된다
</para>
</listitem>
<listitem>
<para>
만일 자식이 영속 부모에 의해 참조 해제 될 경우, <literal>cascade="delete-orphan"</literal>이 아닌 한,
<emphasis>특별한 어떤 것도 발생하지 않는다</emphasis> - 어플리케이션은 필요한 경우에 자식을 명시적으로 삭제해야 한다 -,
<literal>cascade="delete-orphan"</literal>인 경우에 "orphaned(고아)"인 경우 자식이 삭제된다.
</para>
</listitem>
</itemizedlist>
</sect1>
<sect1 id="objectstate-metadata">
<title>메타데이터 사용하기</title>
<para>
Hibernate는 모든 엔티티와 값(value) 타입들을 가진 매우 풍부한 메타-레벨 모형을 필요로 한다. 시간이 지남에 따라, 이 모형은
어플리케이션 그 자체에 매우 유용하다. 예를 들어, 어플리케이션은 어느 객체들이 복사되어야 하는지(예를 들면 가변적인 값(value) 타입들)
그리고 어느 것이 복사되지 말아야 하는지(예를 들면, 불변의 value 타입들과 가능한 연관된 엔티티들)를 인지하는 "스마트" deep-copy
알고리즘을 구현하는데 Hibernate의 메타데이터를 사용할 수도 있다.
</para>
<para>
Hibernate는 <literal>ClassMetadata</literal> 인터페이스와 <literal>CollectionMetadata</literal> 인터페이스
그리고 <literal>Type</literal> 계층구조를 통해 메타데이터를 노출시킨다. 메타데이터 인터페이스들의 인스턴스들은
<literal>SessionFactory</literal>로부터 얻어질 수도 있다.
</para>
<programlisting><![CDATA[Cat fritz = ......;
ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);
Object[] propertyValues = catMeta.getPropertyValues(fritz);
String[] propertyNames = catMeta.getPropertyNames();
Type[] propertyTypes = catMeta.getPropertyTypes();
// get a Map of all properties which are not collections or associations
Map namedValues = new HashMap();
for ( int i=0; i<propertyNames.length; i++ ) {
if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
namedValues.put( propertyNames[i], propertyValues[i] );
}
}]]></programlisting>
</sect1>
</chapter>