mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-03-01 07:19:15 +00:00
git-svn-id: https://svn.jboss.org/repos/hibernate/core/branches/Branch_3_2@11779 1b8cb986-b30d-0410-93ca-fae66ebed9b2
604 lines
21 KiB
XML
604 lines
21 KiB
XML
<chapter id="associations">
|
|
|
|
<title>연관 매핑들</title>
|
|
|
|
<sect1 id="assoc-intro" revision="1">
|
|
<title>개요</title>
|
|
|
|
<para>
|
|
연관 매핑들은 올바른 것을 얻기가 종종 가장 어려운 것이다. 이 절에서 우리는 단방향 매핑들에서 시작하고, 그런 다음 양방향 경우들을
|
|
검토함으로써, 하나씩 표준적인 경우들을 상세히 논의할 것이다. 우리는 모든 예제들에서 <literal>Person</literal>과
|
|
<literal>Address</literal>를 사용할 것이다.
|
|
</para>
|
|
|
|
<para>
|
|
우리는 연관들을 중재하는 join 테이블로 매핑시킬 것인지 여부에 따라, 그리고 multiplicity(다중성)에 따라 연관들을 분류할 것이다.
|
|
</para>
|
|
|
|
<para>
|
|
null 허용 가능한 foreign 키들은 전통적인 데이터 모델링에서 좋은 실례로 간주되지 않아서, 모든 우리의 예제들은 not null
|
|
foreign 키들을 사용한다. 이것은 Hibernate에서 필수가 아니고, 당신이 null 허용 가능 컨스트레인트들을 드롭시킬 경우 매핑들은
|
|
모두 동작할 것이다.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="assoc-unidirectional" revision="1">
|
|
<title>단방향 연관들</title>
|
|
|
|
<sect2 id="assoc-unidirectional-m21">
|
|
<title>many to one</title>
|
|
|
|
<para>
|
|
<emphasis>단방향 many-to-one 연관</emphasis>은 가장 공통적인 종류의 단방향 연관이다.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<many-to-one name="address"
|
|
column="addressId"
|
|
not-null="true"/>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key, addressId bigint not null )
|
|
create table Address ( addressId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="assoc-unidirectional-121">
|
|
<title>one to one</title>
|
|
|
|
<para>
|
|
<emphasis>foreign 키에 대한 단방향 one-to-one 연관은 대개 아주 동일하다.</emphasis> 유일한 차이점은
|
|
컬럼 유일(unique) 컨스트레인트이다.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<many-to-one name="address"
|
|
column="addressId"
|
|
unique="true"
|
|
not-null="true"/>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
|
|
create table Address ( addressId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
<emphasis>하나의 프라이머리 키에 대한 단방향 one-to-one 연관</emphasis>은 대개 특별한 id 생성기를 사용한다.
|
|
(이 예제에서 연관의 방향이 역전되었음을 주목하라.)
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="personId">
|
|
<generator class="foreign">
|
|
<param name="property">person</param>
|
|
</generator>
|
|
</id>
|
|
<one-to-one name="person" constrained="true"/>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key )
|
|
create table Address ( personId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="assoc-unidirectional-12m">
|
|
<title>one to many</title>
|
|
|
|
<para>
|
|
<emphasis>하나의 foreign 키에 대한 단방향 one-to-many 연관</emphasis>은 매우 색다른 경우이고, 실제로 권장되지 않는다.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<set name="addresses">
|
|
<key column="personId"
|
|
not-null="true"/>
|
|
<one-to-many class="Address"/>
|
|
</set>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key )
|
|
create table Address ( addressId bigint not null primary key, personId bigint not null )
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
우리는 이런 종류의 연관에 대해 하나의 join 테이블을 사용하는 것이 더 좋다고 생각한다.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="assoc-unidirectional-join" revision="1">
|
|
<title>join 테이블들에 대한 단방향 연관들</title>
|
|
|
|
<sect2 id="assoc-unidirectional-join-12m">
|
|
<title>one to many</title>
|
|
|
|
<para>
|
|
<emphasis>하나의 join 테이블에 대한 단방향 one-to-many 연관</emphasis>이 보다 더 선호된다. <literal>unique="true"</literal>를
|
|
지정함으로써 우리는 many-to-many에서 one-to-many로 아중성(multiplicity)를 변경시켰음을 주목하라.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<set name="addresses" table="PersonAddress">
|
|
<key column="personId"/>
|
|
<many-to-many column="addressId"
|
|
unique="true"
|
|
class="Address"/>
|
|
</set>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key )
|
|
create table PersonAddress ( personId not null, addressId bigint not null primary key )
|
|
create table Address ( addressId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="assoc-unidirectional-join-m21">
|
|
<title>many to one</title>
|
|
|
|
<para>
|
|
<emphasis>하나의 join 테이블에 대한 단방향 many-to-one 연관</emphasis>은 그 연관이 선택적일 때 매우 공통적이다.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<join table="PersonAddress"
|
|
optional="true">
|
|
<key column="personId" unique="true"/>
|
|
<many-to-one name="address"
|
|
column="addressId"
|
|
not-null="true"/>
|
|
</join>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key )
|
|
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
|
|
create table Address ( addressId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="assoc-unidirectional-join-121">
|
|
<title>one to one</title>
|
|
|
|
<para>
|
|
<emphasis>하나의 join 테이블에 대한 단방향 one-to-one 연관</emphasis>은 극히 통상적이지 않지만 가능하다.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<join table="PersonAddress"
|
|
optional="true">
|
|
<key column="personId"
|
|
unique="true"/>
|
|
<many-to-one name="address"
|
|
column="addressId"
|
|
not-null="true"
|
|
unique="true"/>
|
|
</join>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key )
|
|
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
|
|
create table Address ( addressId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="assoc-unidirectional-join-m2m">
|
|
<title>many to many</title>
|
|
|
|
<para>
|
|
마지막으로, 우리는 <emphasis>단방향 many-to-many 연관</emphasis>을 갖는다.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<set name="addresses" table="PersonAddress">
|
|
<key column="personId"/>
|
|
<many-to-many column="addressId"
|
|
class="Address"/>
|
|
</set>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key )
|
|
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
|
|
create table Address ( addressId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="assoc-bidirectional" revision="1">
|
|
<title>양방향 연관들</title>
|
|
|
|
<sect2 id="assoc-bidirectional-m21" revision="2">
|
|
<title>one to many / many to one</title>
|
|
|
|
<para>
|
|
<emphasis>양방향 many-to-one 연관</emphasis>은 가장 공통된 종류의 연관이다.(이것은 표준 부모/자식 관계이다. )
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<many-to-one name="address"
|
|
column="addressId"
|
|
not-null="true"/>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<set name="people" inverse="true">
|
|
<key column="addressId"/>
|
|
<one-to-many class="Person"/>
|
|
</set>
|
|
</class>]]></programlisting>
|
|
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key, addressId bigint not null )
|
|
create table Address ( addressId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
만일 당신이 <literal>List</literal>(또는 다른 인덱싱 된 콜렉션)을 사용할 경우 당신은 foreign key의
|
|
<literal>key</literal> 컬럼을 <literal>not null</literal>로 설정하고, Hibernate로 하여금
|
|
각각의 요소의 인덱스를 유지관리하기 위해 (<literal>update="false"</literal>와
|
|
<literal>insert="false"</literal>를 설정함으로써 다른 측을 가상적으로 inverse로 만들어)
|
|
그 콜렉션들 측으로부터 연관을 관리하도록 할 필요가 있다:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id"/>
|
|
...
|
|
<many-to-one name="address"
|
|
column="addressId"
|
|
not-null="true"
|
|
insert="false"
|
|
update="false"/>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id"/>
|
|
...
|
|
<list name="people">
|
|
<key column="addressId" not-null="true"/>
|
|
<list-index column="peopleIdx"/>
|
|
<one-to-many class="Person"/>
|
|
</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">
|
|
<title>one to one</title>
|
|
|
|
<para>
|
|
<emphasis>foreign에 대한 양방향 one-to-one 연관</emphasis>은 꽤 공통적이다.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<many-to-one name="address"
|
|
column="addressId"
|
|
unique="true"
|
|
not-null="true"/>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<one-to-one name="person"
|
|
property-ref="address"/>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
|
|
create table Address ( addressId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
<para>
|
|
<emphasis>하나의 프라이머리 키에 대한 양방향 one-to-one 연관</emphasis>은 특별한 id 생성기를 사용한다.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<one-to-one name="address"/>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="personId">
|
|
<generator class="foreign">
|
|
<param name="property">person</param>
|
|
</generator>
|
|
</id>
|
|
<one-to-one name="person"
|
|
constrained="true"/>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key )
|
|
create table Address ( personId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="assoc-bidirectional-join" revision="1">
|
|
<title>join 테이블들에 대한 양방향 연관들</title>
|
|
|
|
<sect2 id="assoc-bidirectional-join-12m">
|
|
<title>one to many / many to one</title>
|
|
|
|
<para>
|
|
<emphasis>하나의 join 테이블에 대한 양방향 one-to-many 연관</emphasis>. <literal>inverse="true"</literal>는
|
|
연관의 어느 쪽 끝이든 콜렉션 측으로 또는 join 측으로 갈 수 있다.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<set name="addresses"
|
|
table="PersonAddress">
|
|
<key column="personId"/>
|
|
<many-to-many column="addressId"
|
|
unique="true"
|
|
class="Address"/>
|
|
</set>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<join table="PersonAddress"
|
|
inverse="true"
|
|
optional="true">
|
|
<key column="addressId"/>
|
|
<many-to-one name="person"
|
|
column="personId"
|
|
not-null="true"/>
|
|
</join>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key )
|
|
create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
|
|
create table Address ( addressId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="assoc-bidirectional-join-121">
|
|
<title>one to one</title>
|
|
|
|
<para>
|
|
<emphasis>하나의 join 테이블에 대한 양방향 one-to-one 연관</emphasis>은 극히 통상적이지 않지만, 가능하다.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<join table="PersonAddress"
|
|
optional="true">
|
|
<key column="personId"
|
|
unique="true"/>
|
|
<many-to-one name="address"
|
|
column="addressId"
|
|
not-null="true"
|
|
unique="true"/>
|
|
</join>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<join table="PersonAddress"
|
|
optional="true"
|
|
inverse="true">
|
|
<key column="addressId"
|
|
unique="true"/>
|
|
<many-to-one name="person"
|
|
column="personId"
|
|
not-null="true"
|
|
unique="true"/>
|
|
</join>
|
|
</class>]]></programlisting>
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key )
|
|
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
|
|
create table Address ( addressId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="assoc-bidirectional-join-m2m" revision="1">
|
|
<title>many to many</title>
|
|
|
|
<para>
|
|
마지막으로, 우리는 하나의 <emphasis>양방향 many-to-many 연관</emphasis>을 갖는다.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Person">
|
|
<id name="id" column="personId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<set name="addresses" table="PersonAddress">
|
|
<key column="personId"/>
|
|
<many-to-many column="addressId"
|
|
class="Address"/>
|
|
</set>
|
|
</class>
|
|
|
|
<class name="Address">
|
|
<id name="id" column="addressId">
|
|
<generator class="native"/>
|
|
</id>
|
|
<set name="people" inverse="true" table="PersonAddress">
|
|
<key column="addressId"/>
|
|
<many-to-many column="personId"
|
|
class="Person"/>
|
|
</set>
|
|
</class>]]></programlisting>
|
|
|
|
<programlisting><![CDATA[
|
|
create table Person ( personId bigint not null primary key )
|
|
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
|
|
create table Address ( addressId bigint not null primary key )
|
|
]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="assoc-complex">
|
|
<title>보다 복잡한 연관 매핑들</title>
|
|
|
|
<para>
|
|
보다 복잡한 연관 조인들은 <emphasis>극기</emphasis> 드물다.
|
|
Hibernate는 매핑 문서들 내에 삽입된 SQL 조각들을 사용하여 보다 복잡한 상황을 처리하는 것을
|
|
가능하도록 해준다. 예를 들어, 만일 계좌 내역 정보 데이터를 가진 하나이 테이블이
|
|
<literal>accountNumber</literal>, <literal>effectiveEndDate</literal>
|
|
그리고 <literal>effectiveStartDate</literal> 컬럼들을 정의할 경우, 다음과 같이 매핑된다:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<properties name="currentAccountKey">
|
|
<property name="accountNumber" type="string" not-null="true"/>
|
|
<property name="currentAccount" type="boolean">
|
|
<formula>case when effectiveEndDate is null then 1 else 0 end</formula>
|
|
</property>
|
|
</properties>
|
|
<property name="effectiveEndDate" type="date"/>
|
|
<property name="effectiveStateDate" type="date" not-null="true"/>]]></programlisting>
|
|
|
|
<para>
|
|
그때 우리는 다음을 사용하여 하나의 연관을 <emphasis>현재</emphasis> 인스턴스
|
|
(null <literal>effectiveEndDate</literal>을 가진 인스턴스)로 매핑시킬 수 있다:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<many-to-one name="currentAccountInfo"
|
|
property-ref="currentAccountKey"
|
|
class="AccountInfo">
|
|
<column name="accountNumber"/>
|
|
<formula>'1'</formula>
|
|
</many-to-one>]]></programlisting>
|
|
|
|
<para>
|
|
보다 복잡한 예제에서, <literal>Employee</literal>와 <literal>Organization</literal> 사이의
|
|
연관이 전체 고용 내역 데이터를 가진 <literal>Employment</literal> 테이블 내에 유지된다고 가정하자.
|
|
그때 종업원의 <emphasis>가장 최근의</emphasis> 고용주에 대한 하나의 연관(가장 최근의
|
|
<literal>startDate</literal>를 갖고 있는 것)이 다음 방법으로 매핑될 수 있다:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<join>
|
|
<key column="employeeId"/>
|
|
<subselect>
|
|
select employeeId, orgId
|
|
from Employments
|
|
group by orgId
|
|
having startDate = max(startDate)
|
|
</subselect>
|
|
<many-to-one name="mostRecentEmployer"
|
|
class="Organization"
|
|
column="orgId"/>
|
|
</join>]]></programlisting>
|
|
|
|
<para>
|
|
당신은 이 기능으로 아주 생산성을 얻을 수 있지만, 그것은 대개 HQL 또는 criteria 질의를 사용하여 이들 종류의 경우들을
|
|
처리하는 것이 보다 실용적이다.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
|
|
</chapter>
|
|
|