JongDae Kim b3f771dbb6 *** empty log message ***
git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@8292 1b8cb986-b30d-0410-93ca-fae66ebed9b2
2005-10-02 06:20:59 +00:00

586 lines
28 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<chapter id="querysql" revision="2">
<title>Native SQL</title>
<para>
당신은 또한 당신의 데이터베이스의 native SQL dialect로 질의들을 표현할 수도 있다. 당신이 오라클의 질의 힌트들 또는
<literal>CONNECT</literal> 키워드와 같은 데이터베이스 지정적인 특징들을 활용하고자 원할 경우에 이것이 유용하다.
그것은 또한 직접적인 SQL/JDBC 기반의 어플리케이션으로부터 Hibernate로의 보다 명료한 이전 경로를 제공한다.
</para>
<para>
Hibernate3은 또한 모든 create, update, delete, load 오퍼레이션들에 대해 (내장 프로시저들을 포함하여) 손으로 작성된 SQL을
지정하는 것을 당신에게 허용해준다.
</para>
<sect1 id="querysql-creating" revision="3">
<title><literal>SQLQuery</literal> 사용하기</title>
<para>
native SQL 질의들의 실행은 <literal>SQLQuery</literal> 인터페이스를 통해 제어되며, 그것은
<literal>Session.createSQLQuery()</literal>을 호출하여 획득된다. 극히 간단한 경우들에서 ,
우리는 다음 형식을 사용할 수 있다:
</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery("select * from cats")
.addEntity(Cat.class)
.list();]]></programlisting>
<para>이 질의는 다음을 지정했다:</para>
<itemizedlist>
<listitem>
<para>
SQL 질의 문자열
</para>
</listitem>
<listitem>
<para>
그 질의에 의해 반환되는 엔티티
</para>
</listitem>
</itemizedlist>
<para>
여기서, 결과 셋 컬럼 이름들은 매핑 문서 내에 지정된 컬럼 이름들과 동일한 것으로 가정된다. 이것은 조인 다중 테이블들을 가진
SQL 질의들에 대해 문제가 될 수 있다. 왜냐하면 동일한 컬럼 이름들이 하나 이상의 테이블 들 내에 나타날 수도 있기 때문이다.
다음 형식은 컬럼 이름 중복에 대해 취약하지 않다:
</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery("select {cat.*} from cats cat")
.addEntity("cat", Cat.class)
.list();]]></programlisting>
<para>이 질의는 다음을 지정했다:</para>
<itemizedlist>
<listitem>
<para>
Hibernate가 컬럼 alias들을 도입하기 위한, placeholder를 가진, SQL 질의 문자열
</para>
</listitem>
<listitem>
<para>
그 질의에 의해 반환된 엔티티와 그것의 SQL 테이블 alias
</para>
</listitem>
</itemizedlist>
<para>
<literal>addEntity()</literal> 메소드는 SQL 테이블 alias를 반환된 엔티티 클래스와 연관지우고,
질의 결과 셋의 형태를 결정한다.
</para>
<para>
<literal>addJoin()</literal> 메소드는 다른 엔티티들과 콜렉션들에 대한 연관들을 로드시키는데 사용될 수 있다.
</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.addEntity("cat", Cat.class)
.addJoin("kitten", "cat.kittens")
.list();]]></programlisting>
<para>
native SQL 질의는 간단한 스칼라 값을 반환하거나 스칼라들과 엔티티들의 조합을 반환할 수도 있다.
</para>
<programlisting><![CDATA[Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
.addScalar("maxWeight", Hibernate.DOUBLE);
.uniqueResult();]]></programlisting>
<para>당신은 당신의 hbm 파일들 내에 결과셋 매핑 정보를 대안저긍로 설명활 수 있고 당신의 질의들을 위해 그것들을
사용할 수 있다.</para>
<programlisting><![CDATA[List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.setResultSetMapping("catAndKitten")
.list();]]></programlisting>
</sect1>
<sect1 id="querysql-aliasreferences">
<title>Alias 참조와 프로퍼티 참조</title>
<para>
위에서 사용된 <literal>{cat.*}</literal> 표기는 "모든 프로퍼티"에 대한 약어이다. 다른 방법으로, 당신은 명시적으로 컬럼들을
리스트할 수 있지만, 심지어 이 경우 조차도 우리는 Hibernate로 하여금 각각의 프로퍼티에 대해 SQL 컬럼 alias들을 끼워넣도록 해야 한다.
컬럼 alias에 대한 placeholder는 테이블 alias 수식어가 붙은 프로퍼티 이름이다. 다음 예제에서, 우리는 매핑 메타데이터에 선언된
것에 대해 다른 테이블 (<literal>cat_log</literal>)로부터 <literal>Cat</literal>들을 검색한다. 우리는 심지어 우리가
좋아할 경우 where 절 속에 프로퍼티 alias들을 사용할 수도 있음을 주목하라.
</para>
<para>
The <literal>{}</literal>-syntax is <emphasis>not</emphasis> required for named queries.
See <xref linkend="querysql-namedqueries"/>
</para>
<programlisting><![CDATA[String sql = "select cat.originalId as {cat.id}, " +
"cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " +
"cat.weight*10 as {cat.weight}, cat.name as {cat.name} " +
"from cat_log cat where {cat.mate} = :catId"
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.setLong("catId", catId)
.list();]]></programlisting>
<para>
<emphasis>노트:</emphasis> 만일 당신이 각각의 프로퍼티를 명시적으로 리스트할 경우, 당신은 그 클래스와
<emphasis>그것의 서브클래스들</emphasis>의 모든 프로퍼티들을 포함해야 한다!
</para>
<para>
다음 테이블은 alias injection을 사용하는 다른 가능성들을 보여준다. 노트 : 결과 내에서 alias 이름들이 예제들이며, 각각의 alias는
사용될 시에 하나의 유일한 그리고 가능한 다른 이름을 가질 것이다.
</para>
<table frame="topbot" id="aliasinjection-summary">
<title>Alias injection 이름들</title>
<tgroup cols="4">
<colspec colwidth="1*"/>
<colspec colwidth="1*"/>
<colspec colwidth="2.5*"/>
<thead>
<row>
<entry>설명</entry>
<entry>구문</entry>
<entry>예제</entry>
</row>
</thead>
<tbody>
<row>
<entry>간단한 프로퍼티</entry>
<entry><literal>{[aliasname].[propertyname]</literal></entry>
<entry><literal>A_NAME as {item.name}</literal></entry>
</row>
<row>
<entry>composite 프로퍼티</entry>
<entry><literal>{[aliasname].[componentname].[propertyname]}</literal></entry>
<entry><literal>CURRENCY as {item.amount.currency}, VALUE as {item.amount.value}</literal></entry>
</row>
<row>
<entry>엔티티의 판별자(Discriminator)</entry>
<entry><literal>{[aliasname].class}</literal></entry>
<entry><literal>DISC as {item.class}</literal></entry>
</row>
<row>
<entry>엔티티의 모든 프로퍼티들</entry>
<entry><literal>{[aliasname].*}</literal></entry>
<entry><literal>{item.*}</literal></entry>
</row>
<row>
<entry>콜렉션 키</entry>
<entry><literal>{[aliasname].key}</literal></entry>
<entry><literal>ORGID as {coll.key}</literal></entry>
</row>
<row>
<entry>콜렉션의 id</entry>
<entry><literal>{[aliasname].id}</literal></entry>
<entry><literal>EMPID as {coll.id}</literal></entry>
</row>
<row>
<entry>콜렉션의 요소</entry>
<entry><literal>{[aliasname].element}</literal></entry>
<entry><literal>XID as {coll.element}</literal></entry>
<entry></entry>
</row>
<row>
<entry>콜렉션 내에 있는 요소의 프로퍼티</entry>
<entry><literal>{[aliasname].element.[propertyname]}</literal></entry>
<entry><literal>NAME as {coll.element.name}</literal></entry>
</row>
<row>
<entry>콜렉션 내에 있는 요소의 모든 프로퍼티들</entry>
<entry><literal>{[aliasname].element.*}</literal></entry>
<entry><literal>{coll.element.*}</literal></entry>
</row>
<row>
<entry>콜렉션의 모든 프로퍼티들</entry>
<entry><literal>{[aliasname].*}</literal></entry>
<entry><literal>{coll.*}</literal></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="querysql-namedqueries" revision="3">
<title>명명된 SQL 질의들</title>
<para>
명명된 SQL 질의들은 HQL 질의와 동일한 방법으로 매핑 문서 속에 정의될 수 있고 정확하게 호출될 수도 있다. 이 경우에, 우리는
<literal>addEntity()</literal> 호출을 필요로 하지 <emphasis>않는다</emphasis>.
</para>
<programlisting><![CDATA[<sql-query name="persons">
<return alias="person" class="eg.Person"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
</sql-query>]]></programlisting>
<programlisting><![CDATA[List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern)
.setMaxResults(50)
.list();]]></programlisting>
<para>
The <literal>&lt;return-join&gt;</literal> 요소와 <literal>&lt;load-collection&gt;</literal>
요소는 연관들을 조인시키고 콜렉션들을 각각 초기화 시키는 질의들을 정의하는데 사용된다.
</para>
<programlisting><![CDATA[<sql-query name="personsWith">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
</sql-query>]]></programlisting>
<para>
명명된 SQL 질의는 스칼라 값을 반환할수도 있다. 당신은 <literal>&lt;return-scalar&gt;</literal> 요소를 사용하여
컬럼 alias와 Hibernate 타입을 선언해야 한다:
</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<return-scalar column="name" type="string"/>
<return-scalar column="age" type="long"/>
SELECT p.NAME AS name,
p.AGE AS age,
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
</sql-query>]]></programlisting>
<para>당신은 여러 개의 명명된 질의들을 가로질러 재사용하거나 <literal>setResultSetMapping()</literal> API를 통해 결과셋
매핑정보들을 재사용하기 위해 <literal>&lt;resultset&gt;</literal> 요소 속에 결과셋 매핑 정보들을 구체화 시킬 수 있다.</para>
<programlisting><![CDATA[<resultset name="personAddress">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>
</resultset>
<sql-query name="personsWith" resultset-ref="personAddress">
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
</sql-query>]]></programlisting>
<sect2 id="propertyresults">
<title>명시적으로 column/alias 이름들을 지정하는데 return-property 사용하기</title>
<para>
Hibernate로 하여금 그것 자신의 alias들을 끼워넣도록 하기 위해 <literal>{}</literal>-구문을 사용하는 것 대신에,
<literal>&lt;return-property&gt;</literal>로서 당신은 사용할 컬럼 alias들이 무엇인지를 Hibernate에게 명시적으로
알려줄 수 있다.
</para>
<programlisting><![CDATA[<sql-query name="mySqlQuery">
<return alias="person" class="eg.Person">
<return-property name="name" column="myName"/>
<return-property name="age" column="myAge"/>
<return-property name="sex" column="mySex"/>
</return>
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
</sql-query>
]]></programlisting>
<para>
<literal>&lt;return-property&gt;</literal>는 또한 다중 컬럼들에 대해 동작한다. 이것은 다중-컬럼 프로퍼티들에
대한 fine grained 제어를 허용할 수 없는 <literal>{}</literal>-구문을 가진 제약을 해결해준다.
</para>
<programlisting><![CDATA[<sql-query name="organizationCurrentEmployments">
<return alias="emp" class="Employment">
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
<return-property name="endDate" column="myEndDate"/>
</return>
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
FROM EMPLOYMENT
WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC
</sql-query>]]></programlisting>
<para>
이 예제에서 우리는 끼워넣기(injection)를 위해 <literal>{}</literal>-구문과 함께 <literal>&lt;return-property&gt;</literal>
사용했음을 주목하라. 사용자들이 컬럼과 프로퍼티들을 참조하고자 원하는 방법을 선택하는 것을 사용자들에게 허용해줌으로써.
</para>
<para>
만일 당신의 매핑이 한 개의 판별자(discriminator )를 가질 경우 당신은 판별자 컬럼을 지정하는데
<literal>&lt;return-discriminator&gt;</literal>를 사용해야 한다.
</para>
</sect2>
<sect2 id="sp_query">
<title>질의를 위한 내장 프로시저 사용하기</title>
<para>
Hibernate 3은 내장 프로시저들을 통한 질의들에 대한 지원을 도입한다. 내장 프로시저들은 Hibernate와 동작하는 것이
가능하도록 첫 번째 출력-파라미터로서 한 개의 결과셋을 반환해야 한다. Oracle9 이상의 버전에서 그런 내장 프로시저에
대한 예제는 다음과 같다:
</para>
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
st_cursor SYS_REFCURSOR;
BEGIN
OPEN st_cursor FOR
SELECT EMPLOYEE, EMPLOYER,
STARTDATE, ENDDATE,
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
RETURN st_cursor;
END;]]></programlisting>
<para>
Hibernate에서 이 질의를 사용하기 위해 당신은 하나의 명명된 질의(a named query)를 통해 그것을 매핑할 필요가 있다.
</para>
<programlisting><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
<return alias="emp" class="Employment">
<return-property name="employee" column="EMPLOYEE"/>
<return-property name="employer" column="EMPLOYER"/>
<return-property name="startDate" column="STARTDATE"/>
<return-property name="endDate" column="ENDDATE"/>
<return-property name="regionCode" column="REGIONCODE"/>
<return-property name="id" column="EID"/>
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
</return>
{ ? = call selectAllEmployments() }
</sql-query>]]></programlisting>
<para>
내장 프로시저들은 현재 스칼라들과 엔티티들 만을 반환함을 주목하라. <literal>&lt;return-join&gt;</literal>
<literal>&lt;load-collection&gt;</literal>은 지원되지 않는다.
</para>
<sect3 id="querysql-limits-storedprocedures">
<title>내장 프로시저들을 사용하는 규칙들/제약들</title>
<para>
Hibernate에서 내장 프로시저들을 사용하기 위해서 프로시저들은 다음 몇몇 규칙들을 따라야 한다. 만일 그것들이 그들 규칙들을
따르지 않을 경우 그것들은 Hibernate와 함께 사용 불가능하다. 만일 당신이 여전히 이들 프로시저들을 사용하고자 원할 경우,
당신은 <literal>session.connection()</literal>을 통해 그것들을 실행시켜야 한다. 데이터베이스 벤더들이 다른 내장
프로시저 의미론/구문을 갖고 있기 때문에, 규칙들은 각각의 데이터베이스에 따라 차이가 난다.
</para>
<para>
내장 프로시저 질의들은 <literal>setFirstResult()/setMaxResults()</literal>로서 쪽매김 될 수 없다.
</para>
<para>
Oracle의 경우 다음 규칙들이 적용된다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
프로시저는 한 개의 결과 셋을 반환해야 한다. 이것은 Oracle 9 또는 10에서 한 개의 <literal>SYS_REFCURSOR</literal>
반환함으로써 행해진다. Oracle에서 당신은 한 개의 <literal>REF CURSOR</literal> 타입을 정의할 필요가 있다.
</para>
</listitem>
<listitem>
<para>
권장되는 형식은 <literal>{ ? = call procName(&lt;parameters&gt;) }</literal> 또는
<literal>{ ? = call procName }</literal>이다.(이것은 Hibernate 규칙이라기 보다는 Oracle 규칙이다.)
</para>
</listitem>
</itemizedlist>
<para>
Sybase 또는 MS SQL server의 경우 다음 규칙들이 적용된다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
프로시저는 한 개의 결과 셋을 반환해야 한다. 이들 서버들이 여러 개의 결과셋들과 업데이트 카운트들을 반환 할수 있다/할 것이이므로,
Hibernate는 결과들을 반복 순환할 것이고 그것의 반환 값으로서 하나의 결과 셋인 첫 번째 결과를 취할 것이다. 그 밖의 모든 것은
폐기될 것이다.
</para>
</listitem>
<listitem>
<para>
만일 당신이 당신의 프로시저 내에 <literal>SET NOCOUNT ON</literal>을 이용 가능하게 할 수 있다면 그것은 아마
보다 효율적이게 될 것이지만 이것은 필요 조건이 아니다.
</para>
</listitem>
</itemizedlist>
</sect3>
</sect2>
</sect1>
<sect1 id="querysql-cud">
<title>create, update 그리고 delete를 위한 맞춤형 SQL</title>
<para>
Hibernate3는 create, update, delete 오퍼레이션들을 위한 맞춤형 문장들을 사용할 수 있다. Hibernate에서 클래스와 콜렉션
영속자들은 구성 시에 생성된 문자열들의 집합(insertsql, deletesql, updatesql 등)을 이미 포함하고 있다.
<literal>&lt;sql-insert&gt;</literal>, <literal>&lt;sql-delete&gt;</literal>,
<literal>&lt;sql-update&gt;</literal> 매핑 태그들은 이들 문자열들을 오버라이드 시킨다:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>]]></programlisting>
<para>
SQL이 당신의 데이터베이스 내에서 직접 실행되어서, 당신이 좋아하는 임의의 dialect를 사용하는 것이 자유롭다. 만일 당신이 데이터베이스
지정적인 SQL을 사용할 경우 이것은 물론 당신의 매핑의 이식성을 감소시킬 것이다.
</para>
<para>
만일 <literal>callable</literal> 속성이 설정되면 내장 프로시저들이 지원된다:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
<sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
<sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
</class>]]></programlisting>
<para>
위치 파라미터들은 Hibernate가 그것들을 기대하는 것과 같은 순서가 되어야 하므로, 위치 파라미터들의 순서는 현재 절대적으로 중요하다.
</para>
<para>
당신은 <literal>org.hiberate.persister.entity</literal> 레벨로 디버그 로깅을 사용 가능하게 함으로써 예상된 순서를 볼 수
있다. 이 레벨을 이용 가능하게 하면 Hibernate는 엔티티들을 생성시키고, 업데이트하고, 삭제하는데 사용되는 정적인 SQL을 출력할 것이다.
(예상되는 결과를 보려면, Hibernate 생성된 정적인 sql을 오버라이드 시키게 매핑 파일들 속에 당신의 맞춤형 SQL을 포함시키지 않도록 염두에
두라.)
</para>
<para>
Hibernate가 문장의 성공을 위해 몇몇 실행 시 체크들을 행하므로, 내장 프로시저들은 대부분의 경우들(읽기:다른 경우들 보다 그것을 더 잘
행한다)에서 insert되고/업데이트되고/삭제된 행들의 개수를 반환하는데 필요하다. Hibernate는 항상 CUD 오퍼레이션들에 대한 숫자
출력 파라미터로서 첫 번째 문장 파라미터를 등록시킨다:
</para>
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS
BEGIN
update PERSON
set
NAME = uname,
where
ID = uid;
return SQL%ROWCOUNT;
END updatePerson;]]></programlisting>
</sect1>
<sect1 id="querysql-load">
<title>로딩을 위한 맞춤형 SQL</title>
<para>
당신은 또한 엔티티 로딩을 위한 당신 자신의 SQL (또는 HQL)을 선언할 수도 있다:
</para>
<programlisting><![CDATA[<sql-query name="person">
<return alias="pers" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {pers.name}, ID AS {pers.id}
FROM PERSON
WHERE ID=?
FOR UPDATE
</sql-query>]]></programlisting>
<para>
이것은 앞서 논의했듯이 단지 명명된 질의 선언이다. 당신은 class 매핑 속에 이 명명된 질의를 참조할 수 있다:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<loader query-ref="person"/>
</class>]]></programlisting>
<para>
이것은 심지어 내장 프로시저들에 동작한다.
</para>
<para>
당신은 콜렉션 로딩을 위한 한 개의 질의를 정의할 수도 있다:
</para>
<programlisting><![CDATA[<set name="employments" inverse="true">
<key/>
<one-to-many class="Employment"/>
<loader query-ref="employments"/>
</set>]]></programlisting>
<programlisting><![CDATA[<sql-query name="employments">
<load-collection alias="emp" role="Person.employments"/>
SELECT {emp.*}
FROM EMPLOYMENT emp
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
</sql-query>]]></programlisting>
<para>
당신은 심지어 조인 페칭에 의해 하나의 콜렉션을 로드시키는 하나의 엔티티를 정의할 수 있다:
</para>
<programlisting><![CDATA[<sql-query name="person">
<return alias="pers" class="Person"/>
<return-join alias="emp" property="pers.employments"/>
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
</sql-query>]]></programlisting>
</sect1>
</chapter>