hibernate-orm/doc/reference/ko/modules/collection_mapping.xml
2007-06-29 19:55:14 +00:00

1159 lines
54 KiB
XML

<chapter id="collections">
<title>콜렉션 매핑</title>
<sect1 id="collections-persistent" revision="3">
<title>영속 콜렉션들</title>
<para>
예를 들어 Hibernate는 영속 콜렉션-값을 가진 필드들이 인터페이스 타입으로서 선언될 것을 필요로 한다:
</para>
<programlisting><![CDATA[public class Product {
private String serialNumber;
private Set parts = new HashSet();
public Set getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
}]]></programlisting>
<para>
실제 인터페이스는 <literal>java.util.Set</literal>, <literal>java.util.Collection</literal>,
<literal>java.util.List</literal>, <literal>java.util.Map</literal>, <literal>java.util.SortedSet</literal>,
<literal>java.util.SortedMap</literal> 또는 당신이 좋아하는 어떤 것일 수 있다!(여기서 "당신이 좋아하는 어떤 것"이란
당신이 <literal>org.hibernate.usertype.UserCollectionType</literal>에 대한 구현을 작성해야 함을 의미한다.)
</para>
<para>
우리가 <literal>HashSet</literal>의 인스턴스를 가진 인스턴스 변수를 초기화 시켰던 방법을 주목하라. 이것은 새로이
초기화 된(비-영속) 인스턴스들을 가진 콜렉션 값 프로퍼티들을 초기화 시키는 최선의 방법이다. 당신이 -예를 들어
<literal>persist()</literal>를 호출하여- 인스턴스를 영속화 시킬 때 Hibernate는 실제로 <literal>HashSet</literal>
<literal>Set</literal>에 대한 Hibernate 자신의 구현의 인스턴스로 대체시킬 것이다. 다음과 같은 오류들을 관찰하라:
</para>
<programlisting><![CDATA[Cat cat = new DomesticCat();
Cat kitten = new DomesticCat();
....
Set kittens = new HashSet();
kittens.add(kitten);
cat.setKittens(kittens);
session.persist(cat);
kittens = cat.getKittens(); // Okay, kittens collection is a Set
(HashSet) cat.getKittens(); // Error!]]></programlisting>
<para>
Hibernate에 의해 도입된 영속 콜렉션들은 인터페이스 타입에 따라 <literal>HashMap</literal>, <literal>HashSet</literal>,
<literal>TreeMap</literal>, <literal>TreeSet</literal> 또는 <literal>ArrayList</literal>와 같이 행위한다.
</para>
<para>
콜렉션 인스턴스들은 value 타입들을 가진 통상의 특징을 갖는다. 그것들은 영속 객체에 의해 참조될 때 자동적으로 영속화 되고
참조 해제될 때 자동적으로 삭제된다. 만일 하나의 콜렉션이 하나의 영속 객체로부터 또 다른 영속 객체로 전달될 때, 그것의 요소들은
하나의 테이블로부터 다른 테이블로 이동될 수 있다. 두 개의 엔티티들은 동일한 콜렉션 인스턴스에 대한 참조를 공유하지 않는다.
기본 관계형 모형 때문에 콜렉션 값 프로퍼티들은 null 값 의미들을 지원하지 않는다; Hibernate는 null 콜렉션 참조와 공백의
콜렉션 사이를 구별 짓지 않는다.
</para>
<para>
당신은 이것의 어떤 것에 대해 너무 많이 걱정하지 않아도 될 것이다. 당신이 통상의 자바 콜렉션들을 사용하는 것과 동일한 방법으로
영속 콜렉션들을 사용하라. 단지 당신이 양방향 연관관계들에 대한 의미를 확실히 이해하도록 하라(나중에 논의됨).
</para>
</sect1>
<sect1 id="collections-mapping" revision="4">
<title>콜렉션 매핑들</title>
<para>
콜렉션을 매핑하는데 사용되는 Hiberante 매핑 요소는 인터페이스의 타입에 의존한다. 예를 들어<literal>&lt;set&gt;</literal>
요소는 <literal>Set</literal> 타입의 매핑 프로퍼티들에 사용된다
</para>
<programlisting><![CDATA[<class name="Product">
<id name="serialNumber" column="productSerialNumber"/>
<set name="parts">
<key column="productSerialNumber" not-null="true"/>
<one-to-many class="Part"/>
</set>
</class>]]></programlisting>
<para>
<literal>&lt;set&gt;</literal>과는 별도로, 또한 <literal>&lt;list&gt;</literal>, <literal>&lt;map&gt;</literal>,
<literal>&lt;bag&gt;</literal>, <literal>&lt;array&gt;</literal>, 그리고 <literal>&lt;map&gt;</literal>
매핑 요소들이 존재한다. <literal>&lt;map&gt;</literal> 요소가 대표적이다:
</para>
<programlistingco>
<areaspec>
<area id="mappingcollection1" coords="2 65"/>
<area id="mappingcollection2" coords="3 65"/>
<area id="mappingcollection3" coords="4 65"/>
<area id="mappingcollection4" coords="5 65"/>
<area id="mappingcollection5" coords="6 65"/>
<area id="mappingcollection6" coords="7 65"/>
<area id="mappingcollection7" coords="8 65"/>
<area id="mappingcollection8" coords="9 65"/>
<area id="mappingcollection9" coords="10 65"/>
<area id="mappingcollection10" coords="11 65"/>
<area id="mappingcollection11" coords="12 65"/>
<area id="mappingcollection12" coords="13 65"/>
<area id="mappingcollection13" coords="14 65"/>
<area id="mappingcollection14" coords="15 65"/>
</areaspec>
<programlisting><![CDATA[<map
name="propertyName"
table="table_name"
schema="schema_name"
lazy="true|extra|false"
inverse="true|false"
cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan"
sort="unsorted|natural|comparatorClass"
order-by="column_name asc|desc"
where="arbitrary sql where condition"
fetch="join|select|subselect"
batch-size="N"
access="field|property|ClassName"
optimistic-lock="true|false"
mutable="true|false"
node="element-name|."
embed-xml="true|false"
>
<key .... />
<map-key .... />
<element .... />
</map>]]></programlisting>
<calloutlist>
<callout arearefs="mappingcollection1">
<para>
<literal>name</literal> 콜렉션 프로퍼티 이름
</para>
</callout>
<callout arearefs="mappingcollection2">
<para>
<literal>table</literal> (옵션 - 디폴트는 프로퍼티 이름)
콜렉션 테이블의 이름(one-to-many 연관관계들에 대해서는 사용되지 않음)
</para>
</callout>
<callout arearefs="mappingcollection3">
<para>
<literal>schema</literal> (옵션)
루트 요소 상에 선언된 스키마를 오버라이드 시키는 테이블 스키마의 이름
</para>
</callout>
<callout arearefs="mappingcollection4">
<para>
<literal>lazy</literal> (옵션 - 디폴트는 <literal>true</literal>)는
lazy 페칭을 사용 불가능하도록 하고 그 연관이 항상 eagerly 페치됨을 지정하는데 , 또는 대부분의
연산들이 콜렉션을 초기화시키지 않는 곳에서 "extra-lazy" 페칭을 이용 가능하도록 하는데(매우 큰 콜렉션들에
적당함) 사용될 수 있다
</para>
</callout>
<callout arearefs="mappingcollection5">
<para>
<literal>inverse</literal> (옵션 - 디폴트는 <literal>false</literal>)
이 콜렉션을 양방향 연관관계의 "inverse" 끝(end)으로 표시한다
</para>
</callout>
<callout arearefs="mappingcollection6">
<para>
<literal>cascade</literal> (옵션 - 디폴트는 <literal>none</literal>)
오퍼레이션들이 자식 엔티티들에 대해 케스케이드하는 것을 이용 가능하게 한다
</para>
</callout>
<callout arearefs="mappingcollection7">
<para>
<literal>sort</literal> (옵션)
<literal>natural</literal> 정렬 순서로 정렬된(sorted) 콜렉션 또는 주어진 comparator 클래스를 지정한다
</para>
</callout>
<callout arearefs="mappingcollection8">
<para>
<literal>order-by</literal> (옵션, JDK1.4에서만)
<literal>asc</literal> 또는 <literal>desc</literal> 옵션과 함께 <literal>Map</literal>,
<literal>Set</literal> 또는 bag의 반복 순서를 정의하는 테이블 컬럼(또는 컬럼들)을 지정한다
</para>
</callout>
<callout arearefs="mappingcollection9">
<para>
<literal>where</literal> (옵션)
콜렉션을 검색하거나 제거할 때 사용될 임의적인 SQL <literal>WHERE</literal> 조건을 지정한다
(콜렉션이 오직 이용 가능한 데이터의 부분집합 만을 포함할 경우에 유용하다)
</para>
</callout>
<callout arearefs="mappingcollection10">
<para>
<literal>fetch</literal> (옵션, 디폴트는 <literal>select</literal>)
outer-join 페칭, sequential select 페칭, 그리고 sequential subselect 페칭 사이에서 선택하라.
</para>
</callout>
<callout arearefs="mappingcollection11">
<para>
<literal>batch-size</literal> (옵션, 디폴트는 <literal>1</literal>)
이 콜렉션의 lazily fetching 인스턴스에 대해 "배치 사이즈"를 지정하라.
</para>
</callout>
<callout arearefs="mappingcollection12">
<para>
<literal>access</literal> (옵션 - 디폴트는 <literal>property</literal>):
Hibernate가 콜렉션 프로퍼티 값에 접근하는데 사용할 방도.
</para>
</callout>
<callout arearefs="mappingcollection13">
<para>
<literal>optimistic-lock</literal> (옵션 - 디폴트는 <literal>true</literal>):
콜렉션의 상태에 대한 변경들이 소유하는 엔티티의 버전의 증가로 귀결될 것인지를 지정한다.
(one to many 연관들에 대해, 이 설정을 사용 불가능하게 하는 것이 종종 합당하다.)
</para>
</callout>
<callout arearefs="mappingcollection14">
<para>
<literal>mutable</literal> (옵션 - 디폴트는 <literal>true</literal>):
<literal>false</literal> 값은 콜렉션의 요소들이 결코 변경되지 않음을 지정한다.
(몇몇 경우들에서 마이너 퍼포먼스 최적화).
</para>
</callout>
</calloutlist>
</programlistingco>
<sect2 id="collections-foreignkeys" >
<title>콜렉션 foreign 키들</title>
<para>
콜렉션 인스턴스들은 그 콜렉션을 소유하는 엔티티의 foreign 키에 의해 데이터베이스 내에서 구별지워진다. 이 foreign 키는
그 콜렉션 테이블의 <emphasis>콜렉션 키 컬럼</emphasis> (또는 컬럼들)로서 참조된다. 그 콜렉션 키 컬럼은
<literal>&lt;key&gt;</literal> 요소에 의해 매핑된다.
</para>
<para>
foreign 키 컬럼에 대한 null 허용 가능 컨스트레인트가 존재할 수 있다. 대부분의 콜렉션들에 대해, 이것이 당연히 수반된다.
단방향 one to many 연관들의 경우, foreign 키는 디폴트로 null 허용 가능하여서, 당신은 <literal>not-null="true"</literal>
지정할 필요가 있을 수 있다.
</para>
<programlisting><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
<para>
foreign 키 컨스트레인트는 <literal>ON DELETE CASCADE</literal>를 사용할 수도 있다.
</para>
<programlisting><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
<para>
<literal>&lt;key&gt;</literal> 요소에 대한 전체 정의는 앞 장을 보라.
</para>
</sect2>
<sect2 id="collections-elements" >
<title>콜렉션 요소들</title>
<para>
콜렉션들은 모든 기본 타입들, 컴포넌트들, 그리고 물론 다른 엔티티들에 대한 참조들을 포함하여 거의 대부분의
어떤 다른 Hibernate 타입을 포함할 수도 있다. 이것은 중요한 구분이다: 콜렉션 내에 있는 객체는 "값(value)"
의미로 처리될 수도 있거나(그것의 생명주기는 콜렉션 소유자에 의존한다) 그것은 그것 자신의 생명주기를 가진
또 다른 엔티티에 대한 참조일 수 있다. 후자의 경우, 두 개의 객체들 사이의 "링크" 만이 그 콜렉션에 의해 소유된
상태로 간주된다.
</para>
<para>
포함된 타입은 콜렉션 요소 타입으로서 불려진다. 콜렉션 요소들은 <literal>&lt;element&gt;</literal>
또는 <literal>&lt;composite-element&gt;</literal>에 의해 매핑되거나, 엔티티 참조들의 경우에
<literal>&lt;one-to-many&gt;</literal> 또는 <literal>&lt;many-to-many&gt;</literal>로서
매핑된다. 앞의 두 개는 value 의미를 가진 요소들을 매핑시키고, 뒤의 두개는 엔티티 연관들을 매핑하는데 사용된다.
</para>
</sect2>
<sect2 id="collections-indexed">
<title>인덱싱 된 콜렉션들</title>
<para>
set 과 bag 의미들을 가진 것들을 제외하면, 모든 콜렉션 매핑들은 콜렉션 테이블 내에 <emphasis>인덱스 컬럼</emphasis>-
배열 인덱스, 또는 <literal>List</literal> 인덱스 또는 <literal>Map</literal> 키로 매핑되는 컬럼-을 필요로 한다.
<literal>Map</literal>의 인덱스는 <literal>&lt;map-key&gt;</literal>로 매핑된, 어떤 기본 타입일 수 있고,
그것은 <literal>&lt;map-key-many-to-many&gt;</literal>로 매핑된 엔티티 참조일 수 있거나, 그것은
<literal>&lt;composite-map-key&gt;</literal>로 매핑된 composite 타입일 수 있다. 배열 또는 리스트의 인덱스는
항상 <literal>integer</literal> 타입이고 <literal>&lt;list-index&gt;</literal> 요소를 사용하여 매핑된다.
매핑된 컬럼은 순차적인 정수들을 포함한다(디폴트로 0에서 시작하는 번호가 붙여짐).
</para>
<programlistingco>
<areaspec>
<area id="index1" coords="2 45"/>
<area id="index2" coords="3 45"/>
</areaspec>
<programlisting><![CDATA[<list-index
column="column_name"
base="0|1|..."/>]]></programlisting>
<calloutlist>
<callout arearefs="index1">
<para>
<literal>column_name</literal> (필수):
콜렉션 인덱스 값들을 보관하는 컬럼의 이름.
</para>
</callout>
<callout arearefs="index1">
<para>
<literal>base</literal> (옵션, 디폴트는 <literal>0</literal>):
리스트 또는 배열의 첫 번째 요소에 대응하는 인덱스 컬럼의 값.
</para>
</callout>
</calloutlist>
</programlistingco>
<programlistingco>
<areaspec>
<area id="mapkey1" coords="2 45"/>
<area id="mapkey2" coords="3 45"/>
<area id="mapkey3" coords="4 45"/>
</areaspec>
<programlisting><![CDATA[<map-key
column="column_name"
formula="any SQL expression"
type="type_name"
node="@attribute-name"
length="N"/>]]></programlisting>
<calloutlist>
<callout arearefs="mapkey1">
<para>
<literal>column</literal> (옵션):
콜렉션 인덱스 값들을 보관하는 컬럼의 이름.
</para>
</callout>
<callout arearefs="mapkey2">
<para>
<literal>formula</literal> (옵션):
map의 키를 평가하는데 사용되는 SQL formula.
</para>
</callout>
<callout arearefs="mapkey3">
<para>
<literal>type</literal> (필수): The type of the map 키들의 타입
</para>
</callout>
</calloutlist>
</programlistingco>
<programlistingco>
<areaspec>
<area id="indexmanytomany1" coords="2 45"/>
<area id="indexmanytomany2" coords="3 45"/>
<area id="indexmanytomany3" coords="3 45"/>
</areaspec>
<programlisting><![CDATA[<map-key-many-to-many
column="column_name"
formula="any SQL expression"
class="ClassName"
/>]]></programlisting>
<calloutlist>
<callout arearefs="indexmanytomany1">
<para>
<literal>column</literal> (옵션):
콜렉션 인덱스 값들에 대한 foreign 키 컬럼의 이름.
</para>
</callout>
<callout arearefs="indexmanytomany2">
<para>
<literal>formula</literal> (옵션):
map의 foreign 키를 평가하는데 사용되는 SQL formula.
</para>
</callout>
<callout arearefs="indexmanytomany3">
<para>
<literal>class</literal> (필수): map 키로서 사용되는 엔티티 클래스.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
만일 당신의 테이블이 인덱스 컬럼을 가지 않고, 당신이 여전히 프로퍼티 타입으로서 <literal>List</literal>를 사용하고자
원할 경우, 당신은 그 프로퍼티를 Hibernate <emphasis>&lt;bag&gt;</emphasis>으로서 매핑해야 한다. bag이
데이터베이스로부터 검색될 때 그것은 그것의 순서를 보유하지 않지만, 그것은 선택적으로 정렬(sorting)되거나 ordering될
수도 있다.
</para>
</sect2>
<para>
많은 공통된 관계형 모형들을 다루는, 콜렉션들에 대해 생성될 수 있는 매핑들의 영역이 꽤 존재한다. 여러가지 매핑 선언들이 데이터베이스
테이블들로 변환되는 방법을 당신이 느끼려면 스키마 생성 도구로 실험할 것을 우리는 제안한다.
</para>
<sect2 id="collections-ofvalues" revision="2">
<title>값들을 가진 콜렉션들과 many-to-many 연관들</title>
<para>
어떤 값들을 가진 콜렉션 또는 many-to-many 연관은 foreign 키 컬럼이나 컬럼들, <emphasis>콜렉션 요소 컬럼</emphasis>이나
컬럼들 그리고 가능하면 인덱스 컬럼들이나 컬럼들을 가진 전용 <emphasis>콜렉션 테이블</emphasis>을 필요로 한다.
</para>
<para>
값들을 가진 콜렉션의 경우, 우리는 <literal>&lt;element&gt;</literal> 태그를 사용한다.
</para>
<programlistingco>
<areaspec>
<area id="element1b" coords="2 50"/>
<area id="element2b" coords="3 50"/>
<area id="element3b" coords="4 50"/>
</areaspec>
<programlisting><![CDATA[<element
column="column_name"
formula="any SQL expression"
type="typename"
length="L"
precision="P"
scale="S"
not-null="true|false"
unique="true|false"
node="element-name"
/>]]></programlisting>
<calloutlist>
<callout arearefs="element1b">
<para>
<literal>column</literal> (옵션): 콜렉션 요소 값들을 소유하는 컬럼의 이름.
</para>
</callout>
<callout arearefs="element2b">
<para>
<literal>formula</literal> (옵션): 요소를 평가하는데 사용되는 SQL formula.
</para>
</callout>
<callout arearefs="element3b">
<para>
<literal>type</literal> (필수): 콜렉션 요소의 타입.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
<emphasis>many-to-many association</emphasis> 연관은 <literal>&lt;many-to-many&gt;</literal> 요소를
사용하여 지정된다.
</para>
<programlistingco>
<areaspec>
<area id="manytomany1" coords="2 60"/>
<area id="manytomany2" coords="3 60"/>
<area id="manytomany3" coords="4 60"/>
<area id="manytomany4" coords="5 60"/>
<area id="manytomany5" coords="6 60"/>
<area id="manytomany6" coords="7 60"/>
<area id="manytomany7" coords="8 60"/>
<area id="manytomany8" coords="9 60"/>
</areaspec>
<programlisting><![CDATA[<many-to-many
column="column_name"
formula="any SQL expression"
class="ClassName"
fetch="select|join"
unique="true|false"
not-found="ignore|exception"
entity-name="EntityName"
property-ref="propertyNameFromAssociatedClass"
node="element-name"
embed-xml="true|false"
/>]]></programlisting>
<calloutlist>
<callout arearefs="manytomany1">
<para>
<literal>column</literal> (옵션): 요소 foreign 키 컬럼의 이름.
</para>
</callout>
<callout arearefs="manytomany2">
<para>
<literal>formula</literal> (옵션): 요소 foreign 키 값을 평가하는데 사용되는 SQL formula.
</para>
</callout>
<callout arearefs="manytomany3">
<para>
<literal>class</literal> (필수): 연관된 클래스의 이름.
</para>
</callout>
<callout arearefs="manytomany4">
<para>
<literal>fetch</literal> (옵션 - 디폴트는 <literal>join</literal>):
이 연관에 대해 outer-join 페칭 또는 sequential select 페칭을 이용 가능하게 만든다. 이것은 특별한 경우이다;
엔티티 그리고 다른 엔티티들과 그것의 many-to-many 관계들에 대한 (하나의 <literal>SELECT</literal>
내에서) 전체 eager 페칭의 경우, 당신은 콜렉션 그 자체에 대해서 뿐만 아니라 내포된 요소 <literal>&lt;many-to-many&gt;</literal>
상의 이 속성에 대해 <literal>join</literal> 페칭을 이용 가능하게 할 것이다.
</para>
</callout>
<callout arearefs="manytomany5">
<para>
<literal>unique</literal> (옵션):
foreign-key 컬럼에 대한 유일 컨스트레인트의 DDL 생성을 가능하도록 한다. 이것은 연관 다중성
(association multiplicity)을 효율적으로 one to many로 만든다.
</para>
</callout>
<callout arearefs="manytomany6">
<para>
<literal>not-found</literal> (옵션 - 디폴트는 <literal>exception</literal>):
누락된 행들을 참조하는 foreign 키들이 어떻게 처리될 것인지를 지정한다:
<literal>ignore</literal>는 누락된 한 행을 한 개의 연관으로 다룰 것이다.
</para>
</callout>
<callout arearefs="manytomany7">
<para>
<literal>entity-name</literal> (옵션): <literal>class</literal>에 대한 하나의 대안으로서,
연관된 클래스의 엔티티 이름.
</para>
</callout>
<callout arearefs="manytomany8">
<para>
<literal>property-ref</literal>: (옵션) 이 foreign 키에 조인된 연관 클래스의 프로퍼티의 이름.
지정되지 않을 경우, 연관 클래스의 프라이머리 키가 사용된다.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
몇몇 예제들, 먼저 문자열들을 가진 set:
</para>
<programlisting><![CDATA[<set name="names" table="person_names">
<key column="person_id"/>
<element column="person_name" type="string"/>
</set>]]></programlisting>
<para>
(<literal>order-by</literal> 속성에 의해 결정되는 반복 순서를 가진) 정수들을 포함하는 bag :
</para>
<programlisting><![CDATA[<bag name="sizes"
table="item_sizes"
order-by="size asc">
<key column="item_id"/>
<element column="size" type="integer"/>
</bag>]]></programlisting>
<para>
엔티티들을 가진 배열 - 이 경우에, many to many 연관 :
</para>
<programlisting><![CDATA[<array name="addresses"
table="PersonAddress"
cascade="persist">
<key column="personId"/>
<list-index column="sortOrder"/>
<many-to-many column="addressId" class="Address"/>
</array>]]></programlisting>
<para>
날짜들에 대한 문자열 인덱스들을 가진 map :
</para>
<programlisting><![CDATA[<map name="holidays"
table="holidays"
schema="dbo"
order-by="hol_name asc">
<key column="id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map>]]></programlisting>
<para>
컴포넌트들의 리스트(다음 장에서 논의됨):
</para>
<programlisting><![CDATA[<list name="carComponents"
table="CarComponents">
<key column="carId"/>
<list-index column="sortOrder"/>
<composite-element class="CarComponent">
<property name="price"/>
<property name="type"/>
<property name="serialNumber" column="serialNum"/>
</composite-element>
</list>]]></programlisting>
</sect2>
<sect2 id="collections-onetomany">
<title>One-to-many 연관들</title>
<para>
<emphasis>one to many 연관</emphasis>은 중재하는 콜렉션 테이블 없이 foreign 키를 통해 두 개의 클래스들의 테이블들을
연결시킨다. 이 매핑은 통상의 자바 콜렉션들의 어떤 의미를 상실한다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
포함된 엔티티 클래스의 인스턴스는 그 콜렉션의 하나 이상의 인스턴스에 속하지 않을 수 있다
</para>
</listitem>
<listitem>
<para>
포함된 엔티티 클래스의 인스턴스는 콜렉션 인덱스의 하나 이상의 값에서 나타나지 않을 수 있다
</para>
</listitem>
</itemizedlist>
<para>
<literal>Product</literal>로부터 <literal>Part</literal>로의 연관은 foreign 키 컬럼과 <literal>Part</literal>
테이블에 대한 인덱스 컬럼의 존재를 필요로 한다. <literal>&lt;one-to-many&gt;</literal> 태그는 이것이 one to many
연관임을 나타낸다.
</para>
<programlistingco>
<areaspec>
<area id="onetomany1" coords="2 60"/>
<area id="onetomany2" coords="3 60"/>
<area id="onetomany3" coords="4 60"/>
</areaspec>
<programlisting><![CDATA[<one-to-many
class="ClassName"
not-found="ignore|exception"
entity-name="EntityName"
node="element-name"
embed-xml="true|false"
/>]]></programlisting>
<calloutlist>
<callout arearefs="onetomany1">
<para>
<literal>class</literal> (필수): 연관된 클래스의 이름.
</para>
</callout>
<callout arearefs="onetomany2">
<para>
<literal>not-found</literal> (옵션 - 디폴트는 <literal>exception</literal>):
누락된 행들을 참조하는 캐시된 식별자들이 어떻게 처리될 것인지를 지정한다:
<literal>ignore</literal>는 누락된 한 행을 한 개의 연관으로 다룰 것이다.
</para>
</callout>
<callout arearefs="onetomany3">
<para>
<literal>entity-name</literal> (옵션): <literal>class</literal>에 대한 대안으로서,
연관된 클래스의 엔티티 이름.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
<literal>&lt;one-to-many&gt;</literal> 요소는 어떤 컬럼들을 선언하는데 필요하지 않음을 주목하라. 어딘가에
<literal>table</literal> 이름을 지정하는 것도 필수적이지 않다.
</para>
<para>
<emphasis>매우 중요한 노트</emphasis>: 만일 <literal>&lt;one-to-many&gt;</literal> 연관의 foreign 키 컬럼이
<literal>NOT NULL</literal>로 선언될 경우, 당신은 <literal>&lt;key&gt;</literal> 매핑을 <literal>not-null="true"</literal>
선언해야 하거나 <literal>inverse="true"</literal>로 마크된 콜렉션 매핑을 가진 <emphasis>양방향 연관을 사용해야 한다</emphasis>.
양방향 연관들에 대한 논의는 이 장의 뒷부분을 보라.
</para>
<para>
이 예제는 name으로 <literal>Part</literal> 엔티티들을 가진 map을 보여준다(여기서 <literal>partName</literal>
<literal>Part</literal>의 영속 프로퍼티이다). formula-기반 index의 사용을 주목하라.
</para>
<programlisting><![CDATA[<map name="parts"
cascade="all">
<key column="productId" not-null="true"/>
<map-key formula="partName"/>
<one-to-many class="Part"/>
</map>]]></programlisting>
</sect2>
</sect1>
<sect1 id="collections-advancedmappings">
<title>개선된 콜렉션 매핑들</title>
<sect2 id="collections-sorted" revision="2">
<title>Sorted 콜렉션들</title>
<para>
Hibernate는 <literal>java.util.SortedMap</literal><literal>java.util.SortedSet</literal>를 구현하는
콜렉션들을 지원한다. 당신은 매핑 파일 속에 하나의 comparator를 지정해야 한다:
</para>
<programlisting><![CDATA[<set name="aliases"
table="person_aliases"
sort="natural">
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" sort="my.custom.HolidayComparator">
<key column="year_id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map>]]></programlisting>
<para>
<literal>sort</literal> 속성에 허용되는 값들은 <literal>unsorted</literal>, <literal>natural</literal>,
그리고 <literal>java.util.Comparator</literal>를 구현하는 클래스의 이름이다.
</para>
<para>
Sorted 콜렉션들은 <literal>java.util.TreeSet</literal> 또는 <literal>java.util.TreeMap</literal>처럼 행동한다.
</para>
<para>
만일 당신이 데이터베이스 그 자체가 콜렉션 요소들을 순서지우도록(order)원할 경우 <literal>set</literal>,
<literal>bag</literal> 또는<literal>map</literal> 매핑들에 대해 <literal>order-by</literal> 속성을 사용하라.
이 해결책은 JDK 1.4 이상의 버전에서만 이용 가능하다(그것은 <literal>LinkedHashSet</literal> 또는 <literal>LinkedHashMap</literal>
사용하여 구현된다). 이것은 메모리 내가 아닌, SQL 질의 내에서 순서지움(ordering)을 수행한다.
</para>
<programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" order-by="hol_date, hol_name">
<key column="year_id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date type="date"/>
</map>]]></programlisting>
<para>
<literal>order-by</literal> 속성의 값은 HQL 순서지움(ordering)이 아니라 SQL 순서지움(ordering)임을 노트하라!
</para>
<para>
연관들은 콜렉션 <literal>filter()</literal>를 사용하여 실행 시에 어떤 임의의 criteria(기준)을 사용하여 정렬(sort)될
수도 있다.
</para>
<programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
</sect2>
<sect2 id="collections-bidirectional" revision="1">
<title>양방향 연관들</title>
<para>
<emphasis>양방향 연관</emphasis>은 연관의 양 "끝(end)들"로부터 네비게이션을 허용한다.
두 가지 종류의 양방향 연관들이 지원된다:
<variablelist>
<varlistentry>
<term>one-to-many</term>
<listitem>
<para>
한쪽 끝에 set 또는 bag 값을 갖고, 다른 쪽 긑에 단일 값을 가진 연관
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>many-to-many</term>
<listitem>
<para>
양 끝에서 set 또는 bag 값을 가진 연관
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
당신은 동일한 두 개의 many-to-many 연관들을 동일한 데이터베이스 테이블로 간단하게 매핑하고 한 쪽 끝을
<emphasis>inverse</emphasis>(당신의 선택은 하나이지만, 그것은 인덱싱된 콜렉션일 수 없다)로 선언함으로써 하나의 양방향
many-to-many 연관을 지정할 수도 있다.
</para>
<para>
다음은 양방향 many-to-many 연관에 관한 예제이다; 각각의 카테고리는 많은 아이템들을 가질 수 있고 각각의 아이템은 많은
카테고리들 속에 있을 수 있다:
</para>
<programlisting><![CDATA[<class name="Category">
<id name="id" column="CATEGORY_ID"/>
...
<bag name="items" table="CATEGORY_ITEM">
<key column="CATEGORY_ID"/>
<many-to-many class="Item" column="ITEM_ID"/>
</bag>
</class>
<class name="Item">
<id name="id" column="CATEGORY_ID"/>
...
<!-- inverse end -->
<bag name="categories" table="CATEGORY_ITEM" inverse="true">
<key column="ITEM_ID"/>
<many-to-many class="Category" column="CATEGORY_ID"/>
</bag>
</class>]]></programlisting>
<para>
연관의 inverse 끝(end)에 대해서만 행해진 변경들은 영속화 되지 <emphasis>않는다</emphasis>. 이것은 Hibernate가
모든 양방향 연관에 대해 메모리 내에 두 개의 표상들을 갖는다는 점을 의미한다: A로부터 B로의 하나의 링크와 B로부터 A로의 또 다른 링크.
만일 당신이 자바 객체 모형에 대해 그리고 자바에서 many-to-many 관계를 생성시키는 방법에 대해 생각하면 이것은 이해하기가 더 쉽다:
</para>
<programlisting><![CDATA[
category.getItems().add(item); // The category now "knows" about the relationship
item.getCategories().add(category); // The item now "knows" about the relationship
session.persist(item); // The relationship won't be saved!
session.persist(category); // The relationship will be saved]]></programlisting>
<para>
non-inverse 측은 메모리 내 표상을 데이터베이스로 저장하는데 사용된다.
</para>
<para>
당신은 하나의 one-to-many 연관을 하나의 many-to-one 연관으로서 동일한 테이블 컬럼(들)로 매핑하고 many-값을 가진 끝(end)을
<literal>inverse="true"</literal>로 선언함으로써 하나의 양방향 연관을 정의할 수도 있다.
</para>
<programlisting><![CDATA[<class name="Parent">
<id name="id" column="parent_id"/>
....
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</class>]]></programlisting>
<para>
연관의 한쪽 끝을 <literal>inverse="true"</literal>로 매핑하는 것은 cascade들을 가진 오퍼레이션에 영향을 주지 않으며,
이것들은 orthogonal(직교) 개념들이다!
</para>
</sect2>
<sect2 id="collections-indexedbidirectional">
<title>인덱싱된 콜렉션들을 가진 양방향 연관들</title>
<para>
한쪽 끝이 하나의 <literal>&lt;list&gt;</literal> 또는 <literal>&lt;map&gt;</literal>으로서 표현되는 양방향
연관은 특별한 검토를 필요로 한다. 만일 인덱스 컬럼으로 매핑되는 child 클래스의 프로퍼티가 하나 존재한다면 문제가 없고,
우리는 콜렉션 매핑 상에 <literal>inverse="true"</literal>를 사용하여 계속할 수 있다:
</para>
<programlisting><![CDATA[<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children" inverse="true">
<key column="parent_id"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<property name="name"
not-null="true"/>
<many-to-one name="parent"
class="Parent"
column="parent_id"
not-null="true"/>
</class>]]></programlisting>
<para>
그러나 만일 child 클래스 상에 그런 프로퍼티가 존재하지 않을 경우, 우리는 그 연관을 진정하게 양방향으로 간주할 수 없다
(다른 쪽 끝에서 이용가능하지 않은 그 연관의 끝에서 이용 가능한 정보가 존재한다). 이 경우에 우리는 그 콜렉션을
<literal>inverse="true"</literal>로 매핑시킬 수 없다. 대신에 우리는 다음 매핑을 사용할 수 있다:
</para>
<programlisting><![CDATA[<class name="Parent">
<id name="id" column="parent_id"/>
....
<map name="children">
<key column="parent_id"
not-null="true"/>
<map-key column="name"
type="string"/>
<one-to-many class="Child"/>
</map>
</class>
<class name="Child">
<id name="id" column="child_id"/>
....
<many-to-one name="parent"
class="Parent"
column="parent_id"
insert="false"
update="false"
not-null="true"/>
</class>]]></programlisting>
<para>
이 매핑에서 그 연관의 콜렉션 값을 가진 끝이 foreign 키에 대한 업데이트 책임이 있음을 노트하라.
</para>
</sect2>
<sect2 id="collections-ternary">
<title>Ternary associations(세겹 연관들)</title>
<para>
세 겹의 연관을 매핑하는 세 가지 가능한 접근법들이 존재한다. 하나의 접근법은 그것의 인덱스로서 연관관계를 가진
<literal>Map</literal>을 사용하는 것이다:
</para>
<programlisting><![CDATA[<map name="contracts">
<key column="employer_id" not-null="true"/>
<map-key-many-to-many column="employee_id" class="Employee"/>
<one-to-many class="Contract"/>
</map>]]></programlisting>
<programlisting><![CDATA[<map name="connections">
<key column="incoming_node_id"/>
<map-key-many-to-many column="outgoing_node_id" class="Node"/>
<many-to-many column="connection_id" class="Connection"/>
</map>]]></programlisting>
<para>
두 번째 접근법은 그 연관을 엔티티 클래스로서 단순하게 리모델링 하는 것이다. 이것은 우리가 가장 공통적으로 사용하는 접근법이다.
</para>
<para>
마지막 대안은 우리가 나중에 논의하게 될 composite 요소들을 사용하는 것이다.
</para>
</sect2>
<sect2 id="collections-idbag" revision="1">
<title><literal>&lt;idbag&gt;</literal> 사용하기</title>
<para>
만일 당신이 composite 키들이 나쁜 것이고 엔티티들이 합성 식별자들(대용 키들, surrogate keys)을 가져야 한다는
우리의 견해를 전적으로 수용할 경우, 당신은 우리가 지금까지 보여주었던 값들을 가진 콜렉션들과 many to many 연관들이 모두
composite 키들을 가진 테이블들로 매핑된다는 약간 이상한 점을 발견할 수도 있다! 이제 이 점은 꽤 논의의 여지가 있다; 순수한
연관 테이블은 (비록 composite 값들을 가진 콜렉션<emphasis>일 수도</emphasis> 있을지라도) 대용 키로부터 많은 이점을
취하지 않는 것처럼 보인다. 그럼에도 불구하고 Hibernate는 당신이 값들을 가진 콜렉션들과 many to many 연관들을 대용 키를 가진
테이블로 매핑시키는 것을 당신에게 허용해주는 특징을 제공한다.
</para>
<para>
<literal>&lt;idbag&gt;</literal> 요소는 bag 의미를 가진 <literal>List</literal>(또는 <literal>Collection</literal>)을
매핑하도록 당신에게 허용해준다.
</para>
<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
<collection-id column="ID" type="long">
<generator class="sequence"/>
</collection-id>
<key column="PERSON1"/>
<many-to-many column="PERSON2" class="Person" fetch="join"/>
</idbag>]]></programlisting>
<para>
당신이 볼 수 있듯이, <literal>&lt;idbag&gt;</literal>은 마치 엔티티 클래스인양 synthetic id generator(합성 id 생성기)를
갖는다! 다른 대용 키는 각각의 콜렉션 행에 할당된다. 하지만 Hibernate는 특정 행의 대용 키 값을 발견하는 메커니즘을 제공하지 않는다.
</para>
<para>
<literal>&lt;idbag&gt;</literal>의 업데이트 퍼포먼스는 정규 <literal>&lt;bag&gt;</literal> 보다 훨씬 좋다는 점을
노트하라! Hibernate는 마치 list, map, 또는 set인양, 개별 행들을 효율적으로 위치지울 수 있고 그것들을 개별적으로 업데이트
하거나 삭제시킬 수 있다.
</para>
<para>
현재 구현에서, <literal>native</literal> 식별자 생성 방도는 <literal>&lt;idbag&gt;</literal> 콜렉션 식별자들에 대해
지원되지 않는다.
</para>
</sect2>
</sect1>
<!--다음 요소를 문서화하지 않음 -->
<!--sect1 id="collections-heterogeneous">
<title>Heterogeneous Associations</title>
<para>
<literal>&lt;many-to-any&gt;</literal>와 <literal>&lt;index-many-to-any&gt;</literal> 요소들은
진정한 이종의(heterogeneous) 연관들을 제공한다. 이들 매핑 요소들은 <literal>&lt;any&gt;</literal> 요소와
동일한 방법으로 동작한다 - 그리고 또한 아마 드물게 사용될 수 있을 것이다.
</para>
</sect1-->
<sect1 id="collections-example" revision="1">
<title>콜렉션 예제들</title>
<para>
앞의 절들은 꽤 혼동스럽다. 따라서 예제를 살펴보자. 다음 클래스:
</para>
<programlisting><![CDATA[package eg;
import java.util.Set;
public class Parent {
private long id;
private Set children;
public long getId() { return id; }
private void setId(long id) { this.id=id; }
private Set getChildren() { return children; }
private void setChildren(Set children) { this.children=children; }
....
....
}]]></programlisting>
<para>
<literal>Child</literal> 인스턴스들을 가진 하나의 콜렉션을 갖고 있다. 만일 각각의 자식이 최소한 한 개의 부모를 가질 경우,
대부분의 고유한 매핑은 one-to-many 연관이다:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
이것은 다음 테이블 정의들로 매핑된다:
</para>
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
<para>
만일 부모가 <emphasis>필수적</emphasis>이라면, 양방향 one-to-many 연관관계를 사용하라:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
<many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
<literal>NOT NULL</literal> 컨스트레인트를 주목하라:
</para>
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
create table child ( id bigint not null
primary key,
name varchar(255),
parent_id bigint not null )
alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
<para>
대안적으로, 만일 당신이 이 연관관계가 단방향이어야 함을 절대적으로 역설할 경우, 당신은 <literal>&lt;key&gt;</literal>
매핑 상에 <literal>NOT NULL</literal> 컨스트레인트를 선언할 수 있다:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children">
<key column="parent_id" not-null="true"/>
<one-to-many class="Child"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
반면에, 만일 자식이 여러 부모들을 가질 수 있을 경우, many-to-many 연관이 적절하다:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<class name="Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" table="childset">
<key column="parent_id"/>
<many-to-many class="Child" column="child_id"/>
</set>
</class>
<class name="Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>]]></programlisting>
<para>
테이블 정의들:
</para>
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
create table child ( id bigint not null primary key, name varchar(255) )
create table childset ( parent_id bigint not null,
child_id bigint not null,
primary key ( parent_id, child_id ) )
alter table childset add constraint childsetfk0 (parent_id) references parent
alter table childset add constraint childsetfk1 (child_id) references child]]></programlisting>
<para>
부모/자식 관계 매핑을 연습하는 더 많은 예제들과 전체 리허설은 <xref linkend="example-parentchild"/>를 보라.
</para>
<para>
비록 더 많은 신종 연관 매핑들이 가능할지라도, 우리는 다음 장에서 모든 가능성들을 분류할 것이다.
</para>
</sect1>
</chapter>