JongDae Kim b16340b975 doc subselects better
git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@7522 1b8cb986-b30d-0410-93ca-fae66ebed9b2
2005-07-17 05:02:12 +00:00

1087 lines
50 KiB
XML

<chapter id="queryhql">
<title>HQL: Hibernate Query Language</title>
<para>
Hibernate는 (아주 의도적으로) SQL과 매우 흡사하게 보이는 극히 강력한 질의 언어를 구비하고 있다. 그러나 그 구문에 의해 우롱당하지 말라;
HQL은 상속, 다형성 그리고 연관과 같은 개념들을 이해하여서, 전체적으로 객체 지향적이다.
</para>
<sect1 id="queryhql-casesensitivity">
<title>대소문자 구분</title>
<para>
질의들은 Java 클래스들과 프로퍼티들의 이름들을 제외하면 대소문자를 구분하지 않는다. 따라서 <literal>SeLeCT</literal>
<literal>SELECT</literal>와 같고 <literal>SELECT</literal>와도 같지만 <literal>org.hibernate.eg.FOO</literal>
<literal>org.hibernate.eg.Foo</literal>과 같지 않고 <literal>foo.barSet</literal>
<literal>foo.BARSET</literal>과 같지 않다.
</para>
<para>
이 매뉴얼은 소문자 HQL 키워드를 사용한다. 몇몇 사용자들은 보다 나은 가독성을 위해 대문자 키워드들을 가진 질의들을 찾지만, 우리는
자바 코드 속에 삽입될 때 이 컨벤션이 추하다는 점을 발견한다.
</para>
</sect1>
<sect1 id="queryhql-from">
<title>from 절</title>
<para>
가장 간단한 가능한 Hibernate 질의는 다음 형식이다:
</para>
<programlisting><![CDATA[from eg.Cat]]></programlisting>
<para>
이것은 <literal>eg.Cat</literal> 클래스의 모든 인스턴스들을 간단하게 반환한다. 우리는 대개 클래스 이름을 수식할 필요가 없다.
왜냐하면, <literal>auto-import</literal>가 디폴트이기 때문이다. 따라서 우리는 대개 항상 단지 다음과 같이 작성한다:
</para>
<programlisting><![CDATA[from Cat]]></programlisting>
<para>
대개 당신은 한 개의 <emphasis>alias</emphasis>를 할당할 필요가 있을 것이다. 왜냐하면 당신은 질의의 다른 부분들에서
<literal>Cat</literal>을 참조하고자 원할 것이기 때문이다.
</para>
<programlisting><![CDATA[from Cat as cat]]></programlisting>
<para>
이 질의는 alias <literal>cat</literal><literal>Cat</literal> 인스턴스들에 할당하여서, 우리는 나중에 질의 속에서
그 alias를 사용할 수 있을 것이다. <literal>as</literal> 키워드는 옵션이다; 우리는 또한 다음과 같이 작성할 수 있다:
</para>
<programlisting><![CDATA[from Cat cat]]></programlisting>
<para>
여러 개의 클래스들은 cartesian product(카티젼 곱) 또는 "크로스" 조인으로 귀결되어 나타날 수도 있다.
</para>
<programlisting><![CDATA[from Formula, Parameter]]></programlisting>
<programlisting><![CDATA[from Formula as form, Parameter as param]]></programlisting>
<para>
로컬 변수들에 대한 Java 네이밍 표준들과 일치되게, 첫 소문자를 사용하여 질의 alias들을 명명하는 것은 좋은 습관으로 간주된다
(예를 들면 <literal>domesticCat</literal>).
</para>
</sect1>
<sect1 id="queryhql-joins" revision="1">
<title>연관들과 조인들</title>
<para>
우리는 또한 <literal>join</literal>을 사용하여 , 연관된 엔티티들에 또는 값들을 가진 콜렉션의 요소들에도 alias들을 할당할 수도
있다.
</para>
<programlisting><![CDATA[from Cat as cat
inner join cat.mate as mate
left outer join cat.kittens as kitten]]></programlisting>
<programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
<programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
<para>
지원되는 join 타입들은 ANSI SQL로부터 빌려왔다
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>inner join</literal>
</para>
</listitem>
<listitem>
<para>
<literal>left outer join</literal>
</para>
</listitem>
<listitem>
<para>
<literal>right outer join</literal>
</para>
</listitem>
<listitem>
<para>
<literal>full join</literal> (대개 유용하지 않음)
</para>
</listitem>
</itemizedlist>
<para>
<literal>inner join</literal>, <literal>left outer join</literal>, 그리고 <literal>right outer join</literal>
구조체들이 약칭될 수 있다.
</para>
<programlisting><![CDATA[from Cat as cat
join cat.mate as mate
left join cat.kittens as kitten]]></programlisting>
<para>
당신은 HQL <literal>with</literal> 키워드를 사용하여 특별한 조인 조건들을 제공할 수 있다.
</para>
<programlisting><![CDATA[from Cat as cat
left join cat.kittens as kitten
with kitten.bodyWeight > 10.0]]></programlisting>
<para>
게다가, "fetch" join은 값들을 가진 콜렉션들이나 연관관계들이 한 개의 select를 사용하여, 그것들의 부모 객체들에 따라 초기화 되는
것을 허용해준다. 이것은 콜렉션의 경우에 특히 유용하다. 그것은 연관관계들과 콜렉션들에 대한 매핑 파일의 outer join과 lazy 선언들을
효율적으로 오버라이드 시킨다. 추가 정보는 <xref linkend="performance-fetching"/>을 보라.
</para>
<programlisting><![CDATA[from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens]]></programlisting>
<para>
fetch join은 대개 alias를 할당할 필요가 없다. 왜냐하면, 연관된 객체들이 <literal>where</literal> 절(또는 어떤 다른 절)
속에 사용되지 않을 것이기 때문이다. 또한 연관된 객체들은 질의 결과들 속에 직접 반환되지 않는다. 대신 그것들은 부모 객체를 통해
접근될 수 있다. 우리가 alias를 필요로 할 수 있는 유일한 이유는 더 많은 콜렉션들을 재귀적으로 조인 페칭시키는 경우이다:
</para>
<programlisting><![CDATA[from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens child
left join fetch child.kittens]]></programlisting>
<para>
<literal>fetch</literal> 구조체는 <literal>scroll()</literal>
또는 <literal>iterate()</literal>를 사용하여 호출된 질의들 내에 사용될 수 없음을 노트하라. <literal>fetch</literal>
<literal>setMaxResults()</literal> 또는 <literal>setFirstResult()</literal>와 함께 사용될 수도 없을 것이다.
<literal>fetch</literal>는 특별한 용도의 <literal>with</literal> 조건과도 함께 사용될 수 없다.한 개의 질의 내에 하나
이상의 콜렉션을 조인 페칭시켜 카티젼 곱을 생성시키는 것이 가능한데, 이 경우에 주의하라. 다중 콜렉션 role들을 조인 페칭시키는 것은
또한 때때로 bag 매핑들에 대해 예기치 않은 결과들을 가져다주기 때문에, 당신이 이 경우에 당신의 질의들을 처방하는 방법에 대해 주의하라.
마지막으로 <literal>full join fetch</literal><literal>right join fetch</literal>는 의미가 없다.
</para>
<para>
만일 당신이 (바이트코드 방편으로) property-레벨 lazy 페칭을 사용할 경우, Hibernate로 하여금 <literal>fetch all properties</literal>
사용하여 (첫 번째 질의에서) lazy 프로퍼티들을 즉시 페치하도록 강제시키는 것이 가능하다.
</para>
<programlisting><![CDATA[from Document fetch all properties order by name]]></programlisting>
<programlisting><![CDATA[from Document doc fetch all properties where lower(doc.name) like '%cats%']]></programlisting>
</sect1>
<sect1 id="queryhql-select">
<title>select 절</title>
<para>
<literal>select</literal> 절은 질의 결과 셋 속에 반환할 객체들과 프로퍼티들이 어느 것인지를 골라 내도록 강제한다. 다음을
검토하자:
</para>
<programlisting><![CDATA[select mate
from Cat as cat
inner join cat.mate as mate]]></programlisting>
<para>
질의는 다른 <literal>Cat</literal>들의 <literal>mate</literal>들을 select 할 것이다. 실제로 당신은 이 질의들을 다음과
같이 보다 축약형으로 표현할수도 있다:
</para>
<programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
<para>
질의들은 컴포넌트 타입의 프로퍼티들을 포함하는 임의의 값 타입의 프로퍼티들을 반환할 수도 있다:
</para>
<programlisting><![CDATA[select cat.name from DomesticCat cat
where cat.name like 'fri%']]></programlisting>
<programlisting><![CDATA[select cust.name.firstName from Customer as cust]]></programlisting>
<para>
<literal>Family</literal> 클래스가 적당한 생성자를 갖고 있음을 가정하면,
질의들은 여러 객체들 그리고/또는 프로퍼티들을 <literal>Object[]</literal> 타입의 배열로서,
</para>
<programlisting><![CDATA[select mother, offspr, mate.name
from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr]]></programlisting>
<para>
또는 <literal>List</literal>로서,
</para>
<programlisting><![CDATA[select new list(mother, offspr, mate.name)
from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr]]></programlisting>
<para>
또는 실제 typesafe 자바 객체로서,
</para>
<programlisting><![CDATA[select new Family(mother, mate, offspr)
from DomesticCat as mother
join mother.mate as mate
left join mother.kittens as offspr]]></programlisting>
<para>
반환할 수도 있다.
</para>
<para>
당신은 <literal>as</literal>를 사용하여 select되는 표현식들에 alias들을 할당할 수 있다:
</para>
<programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
from Cat cat]]></programlisting>
<para>
다음은 <literal>select new map</literal>과 함께 사용될 때 가장 유용하다:
</para>
<programlisting><![CDATA[select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n )
from Cat cat]]></programlisting>
<para>
이 질의는 select된 값들에 대한 alias로부터 한 개의 <literal>Map</literal>을 반환한다.
</para>
</sect1>
<sect1 id="queryhql-aggregation">
<title>집계 함수들</title>
<para>
HQL 질의들은 프로퍼티들에 대한 집계(aggregate) 함수들의 결과들을 반환할수도 있다:
</para>
<programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
from Cat cat]]></programlisting>
<!-- NO LONGER SUPPORTED
<para>
Collections may also appear inside aggregate functions in the <literal>select</literal>
clause.
</para>
<programlisting><![CDATA[select cat, count( elements(cat.kittens) )
from Cat cat group by cat]]></programlisting>
-->
<para>
지원되는 집계 함수들은 다음과 같다
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
<literal>avg(...), sum(...), min(...), max(...)</literal>
</para>
</listitem>
<listitem>
<para>
<literal>count(*)</literal>
</para>
</listitem>
<listitem>
<para>
<literal>count(...), count(distinct ...), count(all...)</literal>
</para>
</listitem>
</itemizedlist>
<para>
당신은 select 절 속에 산술 연산자들, 연결 연산자, 그리고 인지된 SQL 함수들을 사용할 수 있다:
</para>
<programlisting><![CDATA[select cat.weight + sum(kitten.weight)
from Cat cat
join cat.kittens kitten
group by cat.id, cat.weight]]></programlisting>
<programlisting><![CDATA[select firstName||' '||initial||' '||upper(lastName) from Person]]></programlisting>
<para>
<literal>distinct</literal> 키워드와 all <literal>all</literal>가 사용될 수 있고 SQL의 경우와 동일한 의미를 갖는다.
</para>
<programlisting><![CDATA[select distinct cat.name from Cat cat
select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
</sect1>
<sect1 id="queryhql-polymorphism">
<title>Polymorphic(다형성) 질의들</title>
<para>
다음과 같은 질의:
</para>
<programlisting><![CDATA[from Cat as cat]]></programlisting>
<para>
<literal>Cat</literal>의 인스턴스들 뿐만 아니라, 또한 <literal>DomesticCat</literal>과 같은 서브클래스들 또한
반환한다. Hibernate 질의들은 <literal>from</literal> 절 내에 <emphasis>임의의</emphasis> 자바 클래스나 인터페이스를
명명할 수 있다. 질의는 그 클래스를 확장하거나 그 인터페이스를 구현하는 모든 영속 클래스들의 인스턴스들을 반환할 것이다. 다음 질의는
모든 영속 객체들을 반환할 것이다:
</para>
<programlisting><![CDATA[from java.lang.Object o]]></programlisting>
<para>
인터페이스 <literal>Named</literal>는 여러 가지 영속 클래스들에 의해 구현될 수도 있다:
</para>
<programlisting><![CDATA[from Named n, Named m where n.name = m.name]]></programlisting>
<para>
이들 마지막 두 개의 질의들은 하나 이상의 SQL <literal>SELECT</literal>를 필요로 할 것임을 노트하라. 이것은 <literal>order by</literal>
절이 정확하게 전체 결과 셋을 순서지우지 않음을 의미한다.(그것은 또한 당신이 <literal>Query.scroll()</literal>을 사용하여
이들 질의들을 호출할 수 없음을 의미한다).
</para>
</sect1>
<sect1 id="queryhql-where">
<title>where 절</title>
<para>
<literal>where</literal> 절은 반환된 인스턴스들의 목록을 제한시키는 것을 당신에게 허용해준다. 만일 alias가 존재하지 않을 경우,
당신은 이름에 의해 프로퍼티들을 참조할 수도 있다:
</para>
<programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
<para>
만일 한 개의 alias가 존재할 경우, 하나의 수식어가 붙은 프로퍼티 이름을 사용하라:
</para>
<programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
<para>
는 'Fritz'로 명명된 <literal>Cat</literal>의 인스턴스들을 반환한다.
</para>
<programlisting><![CDATA[select foo
from Foo foo, Bar bar
where foo.startDate = bar.date]]></programlisting>
<para>
<literal>Foo</literal><literal>startDate</literal> 프로퍼티와 동일한 <literal>date</literal> 프로퍼티를
가진 <literal>bar</literal>의 인스턴스가 존재하는 <literal>Foo</literal>의 모든 인스턴스를 반환할 것이다. 합성 경로
표현식들은 <literal>where</literal> 절을 매우 강력하게 만들어준다. 다음을 검토하자:
</para>
<programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
<para>
이 질의는 테이블 (inner) join을 가진 SQL 질의로 번역된다. 만일 당신이 다음과 같은 어떤 것을 작성했다면
</para>
<programlisting><![CDATA[from Foo foo
where foo.bar.baz.customer.address.city is not null]]></programlisting>
<para>
당신은 SQL에서 네 개의 테이블 join들을 필요로 하는 하나의 질의로 끝낼 것이다.
</para>
<para>
<literal>=</literal> 연산자는 프로퍼티들 뿐만 아니라 또한 인스턴스들을 비교하는데 사용될 수 있다:
</para>
<programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
<programlisting><![CDATA[select cat, mate
from Cat cat, Cat mate
where cat.mate = mate]]></programlisting>
<para>
특별한 프로퍼티(소문자) <literal>id</literal>는 객체의 유일 식별자를 참조하는데 사용될 수 있다.(당신은 또한 그것의 프로퍼티
이름을 사용할 수도 있다.)
</para>
<programlisting><![CDATA[from Cat as cat where cat.id = 123
from Cat as cat where cat.mate.id = 69]]></programlisting>
<para>
두 번째 질의가 효율적이다. 테이블 join이 필요 없다!
</para>
<para>
composite identifier(합성 식별자)들의 프로퍼티들이 또한 사용될 수 있다. <literal>Person</literal>
<literal>country</literal><literal>medicareNumber</literal>로 구성된 composite identifier를 갖는다고
가정하자.
</para>
<programlisting><![CDATA[from bank.Person person
where person.id.country = 'AU'
and person.id.medicareNumber = 123456]]></programlisting>
<programlisting><![CDATA[from bank.Account account
where account.owner.id.country = 'AU'
and account.owner.id.medicareNumber = 123456]]></programlisting>
<para>
다시 한번, 두 번째 질의는 테이블 join을 필요로 하지 않는다.
</para>
<para>
마찬가지로, 특별한 프로퍼티 <literal>class</literal>는 다형적인 영속성(polymorphic persistence)의 경우에 인스턴스의
판별자(discriminator) 값에 액세스한다. where 절 속에 삽입된 Java 클래스 이름은 그것의 판별자(discriminator) 값으로
변환될 것이다.
</para>
<programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
<para>
당신은 또한 컴포넌트들 또는 composite 사용자 정의 타입들의 (그리고 컴포넌트들의 컴포넌트들, 기타의) 프로퍼티들을
지정할 수도 있다. (컴포넌트의 프로퍼티와은 반대로) 컴포넌트 타입의 프로퍼티로 끝나는 경로-표현식을 사용하려고 결코 시도하지 말라.
예를 들어, 만일 <literal>store.owner</literal>가 컴포넌트 <literal>address</literal>를 가진 엔티티일 경우
</para>
<programlisting><![CDATA[store.owner.address.city // okay
store.owner.address // error!]]></programlisting>
<para>
"임의의" 타입은 다음 방법으로 join을 표현하는 것을 우리에게 허용해주는, 특별한 프로퍼티들 <literal>id</literal>
<literal>class</literal>를 갖는다(여기서 <literal>AuditLog.item</literal><literal>&lt;any&gt;</literal>
매핑된 프로퍼티이다).
</para>
<programlisting><![CDATA[from AuditLog log, Payment payment
where log.item.class = 'Payment' and log.item.id = payment.id]]></programlisting>
<para>
<literal>log.item.class</literal><literal>payment.class</literal>는 위의 질의 내에서 완전히 다른 데이터베이스
컬럼들의 값들을 참조할 것임을 노트하라.
</para>
</sect1>
<sect1 id="queryhql-expressions">
<title>표현식들</title>
<para>
<literal>where</literal> 절 속에 허용되는 표현식들은 당신이 SQL로 작성할 수 있는 대부분의 종류의 것들을 포함한다:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
산술 연산자들 <literal>+, -, *, /</literal>
</para>
</listitem>
<listitem>
<para>
바이너리 비교 연산자들 <literal>=, &gt;=, &lt;=, &lt;&gt;, !=, like</literal>
</para>
</listitem>
<listitem>
<para>
논리 연산들 <literal>and, or, not</literal>
</para>
</listitem>
<listitem>
<para>
그룹핑을 나타내는 괄호들 <literal>( )</literal>, indicating grouping
</para>
</listitem>
<listitem>
<para>
<literal>in</literal>,
<literal>not in</literal>,
<literal>between</literal>,
<literal>is null</literal>,
<literal>is not null</literal>,
<literal>is empty</literal>,
<literal>is not empty</literal>,
<literal>member of</literal> and
<literal>not member of</literal>
</para>
</listitem>
<listitem>
<para>
"간단한" 경우, <literal>case ... when ... then ... else ... end</literal>, 그리고
"검색인" 경우, <literal>case when ... then ... else ... end</literal>
</para>
</listitem>
<listitem>
<para>
문자열 연결 <literal>...||...</literal> or <literal>concat(...,...)</literal>
</para>
</listitem>
<listitem>
<para>
<literal>current_date()</literal>, <literal>current_time()</literal>,
<literal>current_timestamp()</literal>
</para>
</listitem>
<listitem>
<para>
<literal>second(...)</literal>, <literal>minute(...)</literal>,
<literal>hour(...)</literal>, <literal>day(...)</literal>,
<literal>month(...)</literal>, <literal>year(...)</literal>,
</para>
</listitem>
<listitem>
<para>
EJB-QL 3.0에 의해 정의된 임의의 함수 또는 오퍼레이터: <literal>substring(), trim(),
lower(), upper(), length(), locate(), abs(), sqrt(), bit_length(), mod()</literal>
</para>
</listitem>
<listitem>
<para>
<literal>coalesce()</literal> 그리고 <literal>nullif()</literal>
</para>
</listitem>
<listitem>
<para>
numeric 값들이나 temporal 값들을 가독성 있는 문자열로 변환시키는 <literal>str()</literal>
</para>
</listitem>
<listitem>
<para>
조인된 인덱싱된 콜렉션의 alias들에 적용되는, HQL <literal>index()</literal> 함수
</para>
</listitem>
<listitem>
<para>
콜렉션 값을 가진 경로 표현식들을 취하는 HQL 함수들 : <literal>some, all, exists, any, in</literal>
사용하여 정량화 될 수 있는 특별한 <literal>elements()</literal><literal>indices</literal>
함수들과 함께 <literal>size(), minelement(), maxelement(), minindex(), maxindex()</literal>.
</para>
</listitem>
<listitem>
<para>
<literal>cast(... as ...)</literal>, 여기서 두번 째 아규먼트는 Hibernate 타입의 이름이고,
ANSI <literal>cast()</literal><literal>extract()</literal>가 기본 데이터베이스에 의해 지원될 경우에는
<literal>extract(... from ...)</literal>
</para>
</listitem>
<listitem>
<para>
조인된 인덱싱된 콜렉션의 alias들을 적용하는 HQL <literal>index()</literal> 함수
</para>
</listitem>
<listitem>
<para>
콜렉션 값 경로 표현식들을 취하는 HQL 함수들: <literal>some, all, exists, any, in</literal>을 사용하여
양이 한정될 수 있는 특별한 <literal>elements()</literal><literal>indices</literal> 함수들과 함께
<literal>size(), minelement(), maxelement(), minindex(), maxindex()</literal> 함수들.
</para>
</listitem>
<listitem>
<para>
<literal>sign()</literal>, <literal>trunc()</literal>, <literal>rtrim()</literal>,
<literal>sin()</literal>과 같이 임의의 데이터베이스-지원 SQL 스칼라 함수
</para>
</listitem>
<listitem>
<para>
JDBC-스타일 위치 파라미터들 <literal>?</literal>
</para>
</listitem>
<listitem>
<para>
명명된 파라미터들 <literal>:name</literal>, <literal>:start_date</literal>, <literal>:x1</literal>
</para>
</listitem>
<listitem>
<para>
SQL 리터럴들 <literal>'foo'</literal>, <literal>69</literal>, <literal>6.66E+2</literal>,
<literal>'1970-01-01 10:00:01.0'</literal>
</para>
</listitem>
<listitem>
<para>
Java <literal>public static final</literal> 상수들. <literal>eg.Color.TABBY</literal>
</para>
</listitem>
</itemizedlist>
<para>
<literal>in</literal><literal>between</literal>은 다음과 같이 사용될 수 있다:
</para>
<programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
<programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
<para>
그리고 부정형들은 다음과 같이 작성될 수 있다
</para>
<programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
<programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
<para>
마찬가지로, <literal>is null</literal><literal>is not null</literal>은 null 값들을 테스트하는데 사용될 수 있다.
</para>
<para>
Boolean들은 Hibernate 구성 내에 HQL 질의 치환들을 선언함으로써 표현식들 내에 쉽게 사용될 수 있다:
</para>
<programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
<para>
이것은 키워드 <literal>true</literal><literal>false</literal> 키워드들을 이 HQL로부터 번역된 SQL에서
리터럴 <literal>1</literal><literal>0</literal>으로 대체될 것이다:
</para>
<programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
<para>
당신은 특별한 프로퍼티 <literal>size</literal>로서 또는 특별한 <literal>size()</literal> 함수로서 콜렉션의 사이즈를
테스트할 수 있다.
</para>
<programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
<programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
<para>
인덱싱된 콜렉션들에 대해, 당신은<literal>minindex</literal><literal>maxindex</literal> 함수들을 사용하여
최소 인덱스과 최대 인덱스를 참조할 수 있다. 유사하게 당신은 <literal>minelement</literal>
<literal>maxelement</literal> 함수를 사용하여 기본 타입을 가진 콜렉션의 최소 요소 및 최대 요소를 참조할 수 있다.
</para>
<programlisting><![CDATA[from Calendar cal where maxelement(cal.holidays) > current date]]></programlisting>
<programlisting><![CDATA[from Order order where maxindex(order.items) > 100]]></programlisting>
<programlisting><![CDATA[from Order order where minelement(order.items) > 10000]]></programlisting>
<para>
SQL 함수들 <literal>any, some, all, exists, in</literal>은 콜렉션의 요소 또는 인덱스 세트(<literal>elements</literal>
함수와 <literal>indices</literal> 함수), 또는 서브질의의 결과를 전달했을 때 지원된다(아래를 보라).
</para>
<programlisting><![CDATA[select mother from Cat as mother, Cat as kit
where kit in elements(foo.kittens)]]></programlisting>
<programlisting><![CDATA[select p from NameList list, Person p
where p.name = some elements(list.names)]]></programlisting>
<programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
<programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
<programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
<para>
이들 구조체들-<literal>size</literal>, <literal>elements</literal>, <literal>indices</literal>,
<literal>minindex</literal>, <literal>maxindex</literal>, <literal>minelement</literal>,
<literal>maxelement</literal>-는 Hibernate3에서 where 절 내에서만 사용될 것임을 노트하라.
</para>
<para>
인덱싱 된 콜렉션들의 요소들(배열들, 리스트들, map들)은 인덱스에 의해 참조될 수 있다(where 절 안에서만):
</para>
<programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
<programlisting><![CDATA[select person from Person person, Calendar calendar
where calendar.holidays['national day'] = person.birthDay
and person.nationality.calendar = calendar]]></programlisting>
<programlisting><![CDATA[select item from Item item, Order order
where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
<programlisting><![CDATA[select item from Item item, Order order
where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
<para>
<literal>[]</literal> 내부의 표현식은 산술 표현실일 수 있다.
</para>
<programlisting><![CDATA[select item from Item item, Order order
where order.items[ size(order.items) - 1 ] = item]]></programlisting>
<para>
HQL은 또한 one-to-many 연관 또는 값들을 가진 콜렉션의 요소들에 대해 미리 만들어진 <literal>index()</literal> 함수를
제공한다.
</para>
<programlisting><![CDATA[select item, index(item) from Order order
join order.items item
where index(item) < 5]]></programlisting>
<para>
기본 데이터베이스에 의해 제공되는 Scalar SQL 함수들이 사용될 수도 있다
</para>
<programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
<para>
당신이 아직 이 모든 것을 납득하지 못한다면, SQL 내에서 다음 질의가 가독성이 얼마나 많고 적은지를 생각해보라:
</para>
<programlisting><![CDATA[select cust
from Product prod,
Store store
inner join store.customers cust
where prod.name = 'widget'
and store.location.name in ( 'Melbourne', 'Sydney' )
and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
<para>
<emphasis>힌트</emphasis> : 다음과 같은 어떤 것
</para>
<programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
FROM customers cust,
stores store,
locations loc,
store_customers sc,
product prod
WHERE prod.name = 'widget'
AND store.loc_id = loc.id
AND loc.name IN ( 'Melbourne', 'Sydney' )
AND sc.store_id = store.id
AND sc.cust_id = cust.id
AND prod.id = ALL(
SELECT item.prod_id
FROM line_items item, orders o
WHERE item.order_id = o.id
AND cust.current_order = o.id
)]]></programlisting>
</sect1>
<sect1 id="queryhql-ordering">
<title>order by 절</title>
<para>
질의에 의해 반환된 리스트는 반환된 클래스 또는 컴포넌트들의 프로퍼티에 의해 순서(ordering)지워질 수 있다:
</para>
<programlisting><![CDATA[from DomesticCat cat
order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
<para>
<literal>asc</literal> 옵션 또는 <literal>desc</literal> 옵션은 각각 오름차순 또는 내림차순 정렬을 나타낸다.
</para>
</sect1>
<sect1 id="queryhql-grouping">
<title>group by 절</title>
<para>
aggregate 값들을 반환하는 질의는 반환된 클래스나 컴포넌트들의 프로퍼티에 의해 그룹지워질 수 있다:
</para>
<programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color]]></programlisting>
<programlisting><![CDATA[select foo.id, avg(name), max(name)
from Foo foo join foo.names name
group by foo.id]]></programlisting>
<para>
또한 <literal>having</literal> 절이 허용된다.
</para>
<programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color
having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
<para>
SQL 함수들과 집계 함수들이 기본 데이터베이스에 의해 지원될 경우(예를 들어 MySQL은 지원되지 않는다)
<literal>having</literal> 절과 <literal>order by</literal> 절 속에 허용된다.
</para>
<programlisting><![CDATA[select cat
from Cat cat
join cat.kittens kitten
group by cat
having avg(kitten.weight) > 100
order by count(kitten) asc, sum(kitten.weight) desc]]></programlisting>
<para>
<literal>group by</literal> 절도 <literal>order by</literal> 절 어느 것도 산술 표현식들을 포함할 수 없다는 점을
노트하라.
</para>
</sect1>
<sect1 id="queryhql-subqueries" revision="2">
<title>서브질의들</title>
<para>
subselect들을 지원하는 데이터베이스들의 경우, Hibernate는 질의들 내에 서브질의들을 지원한다. 서브질의는 괄호로 묶여져야
한다(자주 SQL 집계함수 호출에 의해). 심지어 서로 상관된 서브질의들(외부 질의 내에서 alias를 참조하는 서브질의들)이 허용된다.
</para>
<programlisting><![CDATA[from Cat as fatcat
where fatcat.weight > (
select avg(cat.weight) from DomesticCat cat
)]]></programlisting>
<programlisting><![CDATA[from DomesticCat as cat
where cat.name = some (
select name.nickName from Name as name
)]]></programlisting>
<programlisting><![CDATA[from Cat as cat
where not exists (
from Cat as mate where mate.mate = cat
)]]></programlisting>
<programlisting><![CDATA[from DomesticCat as cat
where cat.name not in (
select name.nickName from Name as name
)]]></programlisting>
<programlisting><![CDATA[select cat.id, (select max(kit.weight) from cat.kitten kit)
from Cat as cat]]></programlisting>
<para>
select 리스트 내에 있는 하나 이상의 표현식을 가진 서브질의들의 경우에 당신은 tuple 생성자를 사용할 수 있다:
</para>
<programlisting><![CDATA[from Cat as cat
where not ( cat.name, cat.color ) in (
select cat.name, cat.color from DomesticCat cat
)]]></programlisting>
<para>
HQL 서브질의들이 select 절 또는 where 절 내에서만 일어날 수 있음을 노트하라.
</para>
<para>
(Oracle 또는 HSQL이 아닌) 몇몇 데이터베이스들 상에서, 당신은 다른 컨텍스트들 내에서, 예를 들면 component들이나 composite
사용자 타입들을 질의할 때 tuple 생성자들을 사용할 수 있음을 노트하라:
</para>
<programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
<para>
이것을 더 풀어쓰면 다음과 동일하다:
</para>
<programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
<para>
당신이 이런 종류의 것을 행하는 것을 원하지 않을 수 있는 두 가지 좋은 이유들이 존재한다: 첫 번째로 데이터베이스 플랫폼들 사이에 완전하게
이식성이 없다; 두 번째로 그 질의는 이제 매핑 문서 속에 있는 프로퍼티들의 순서에 의존한다.
</para>
</sect1>
<sect1 id="queryhql-examples">
<title>HQL 예제들</title>
<para>
Hibernate 질의들은 매우 강력하고 복잡할 수 있다. 사실, 질의 언어의 힘은 Hibernate의 주요 판매 포인트들 중 하나이다. 다음은
내가 최근의 프로젝트에서 사용했던 질의들과 매우 유사한 몇몇 예제 질의들이다. 당신이 작성하게 될 대부분의 질의들은 이것들보다 훨씬
간단하다는 점을 노트하라!
</para>
<para>
다음 질의는 특정 고객에 대한 모든 지불되지 않은 주문들의 주문 id, 항목들의 개수, 그리고 주문의 전체 합계값 그리고 주어진
최소 전체 합계를 전체 합계값에 따라 결과들을 순서지워서 반환한다. 가격 결정에 있어, 그것은 현재의 카타록을 사용한다.
귀결되는 SQL 질의는 <literal>ORDER</literal>, <literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>,
<literal>CATALOG</literal>, <literal>PRICE</literal> 테이블들에 대한 네 개의 inner 조인들과 한 개의(상관지워지지
않은) subselect를 갖고 있다.
</para>
<programlisting><![CDATA[select order.id, sum(price.amount), count(item)
from Order as order
join order.lineItems as item
join item.product as product,
Catalog as catalog
join catalog.prices as price
where order.paid = false
and order.customer = :customer
and price.product = product
and catalog.effectiveDate < sysdate
and catalog.effectiveDate >= all (
select cat.effectiveDate
from Catalog as cat
where cat.effectiveDate < sysdate
)
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc]]></programlisting>
<para>
괴물 같은 것! 실제로 실 생활에서, 나는 서브질의들을 매우 좋아하지 않아서, 나의 질의는 실제로 다음과 같았다:
</para>
<programlisting><![CDATA[select order.id, sum(price.amount), count(item)
from Order as order
join order.lineItems as item
join item.product as product,
Catalog as catalog
join catalog.prices as price
where order.paid = false
and order.customer = :customer
and price.product = product
and catalog = :currentCatalog
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc]]></programlisting>
<para>
다음 질의는 현재 사용자에 의해 가장 최근의 상태 변경이 행해졌던 <literal>AWAITING_APPROVAL</literal> 상태에 있는 모든
지불들을 제외한, 각각의 상태에 있는 지불들의 개수를 카운트 한다. 그것은 <literal>PAYMENT</literal>,
<literal>PAYMENT_STATUS</literal>, <literal>PAYMENT_STATUS_CHANGE</literal> 테이블들에 대한 두 개의 inner
조인들과 하나의 상관관계 지워진 subselect를 가진 SQL 질의로 변환된다.
</para>
<programlisting><![CDATA[select count(payment), status.name
from Payment as payment
join payment.currentStatus as status
join payment.statusChanges as statusChange
where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
or (
statusChange.timeStamp = (
select max(change.timeStamp)
from PaymentStatusChange change
where change.payment = payment
)
and statusChange.user <> :currentUser
)
group by status.name, status.sortOrder
order by status.sortOrder]]></programlisting>
<para>
만일 내가 <literal>statusChanges</literal> 콜렉션을 set가 아닌 list로 매핑했다면, 그 질의는 작성하기가 훨씬 더
간단했을 것이다.
</para>
<programlisting><![CDATA[select count(payment), status.name
from Payment as payment
join payment.currentStatus as status
where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
group by status.name, status.sortOrder
order by status.sortOrder]]></programlisting>
<para>
다음 질의는 현재의 사용자가 속해 있는 조직의 모든 계정들과 지불되지 않은 지불들을 반환하는데 MS SQL Server
<literal>isNull()</literal> 함수를 사용한다. 그것은 <literal>ACCOUNT</literal>, <literal>PAYMENT</literal>,
<literal>PAYMENT_STATUS</literal>, <literal>ACCOUNT_TYPE</literal>, <literal>ORGANIZATION</literal>,
<literal>ORG_USER</literal> 테이블들에 대한 세 개의 inner 조인들, 하나의 outer 조인, 그리고 하나의 subselect를 가진
한 개의 SQL 질의로 번역된다.
</para>
<programlisting><![CDATA[select account, payment
from Account as account
left outer join account.payments as payment
where :currentUser in elements(account.holder.users)
and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
<para>
몇몇 데이터베이스들의 경우, 우리는 (상관관계 지워진) subselect를 없앨 필요가 있을 것이다.
</para>
<programlisting><![CDATA[select account, payment
from Account as account
join account.holder.users as user
left outer join account.payments as payment
where :currentUser = user
and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
</sect1>
<sect1 id="queryhql-bulk" revision="2">
<title>대량 update와 delete</title>
<para>
HQL은 이제 <literal>update</literal>, <literal>delete</literal> 그리고
<literal>insert ... select ...</literal> 문장들을 지원한다.
상세한 것은 <xref linkend="batch-direct"/>를 보라.
</para>
</sect1>
<sect1 id="queryhql-tipstricks">
<title>팁들 &amp; 트릭들</title>
<para>
당신은 실제로 질의 결과들을 반환하지 않고서 그것들(질의 결과들)의 개수를 카운트할 수 있다:
</para>
<programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue()]]></programlisting>
<para>
콜렉션의 크기에 따라 결과를 순서(ordering)지우려면, 다음 질의를 사용하라:
</para>
<programlisting><![CDATA[select usr.id, usr.name
from User as usr
left join usr.messages as msg
group by usr.id, usr.name
order by count(msg)]]></programlisting>
<para>
만일 당신의 데이터베이스가 subselect들을 지원할 경우, 당신은 당신의 질의의 where 절 내에 selection 사이즈에 대한 조건을
위치지울 수 있다:
</para>
<programlisting><![CDATA[from User usr where size(usr.messages) >= 1]]></programlisting>
<para>
만일 당신의 데이터베이스가 subselect를 지원하지 않을 경우, 다음 질의를 사용하라:
</para>
<programlisting><![CDATA[select usr.id, usr.name
from User usr.name
join usr.messages msg
group by usr.id, usr.name
having count(msg) >= 1]]></programlisting>
<para>
이 해결책이 inner 조인 때문에 0개의 메시지를 가진 <literal>User</literal>를 반환할 수 없으므로, 다음 형식이 또한 유용하다:
</para>
<programlisting><![CDATA[select usr.id, usr.name
from User as usr
left join usr.messages as msg
group by usr.id, usr.name
having count(msg) = 0]]></programlisting>
<para>
하나의 JavaBean의 프로퍼티들은 명명된 질의 파라미터들에 바인드될 수 있다:
</para>
<programlisting><![CDATA[Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
q.setProperties(fooBean); // fooBean has getName() and getSize()
List foos = q.list();]]></programlisting>
<para>
콜렉션들은 필터를 가진 <literal>Query</literal> 인터페이스를 사용하여 쪼매김하는 것이 가능하다:
</para>
<programlisting><![CDATA[Query q = s.createFilter( collection, "" ); // the trivial filter
q.setMaxResults(PAGE_SIZE);
q.setFirstResult(PAGE_SIZE * pageNumber);
List page = q.list();]]></programlisting>
<para>
콜렉션 요소들은 질의 필터를 사용하여 순서(ordering)지워지거나 그룹지워질 수 도 있다:
</para>
<programlisting><![CDATA[Collection orderedCollection = s.filter( collection, "order by this.amount" );
Collection counts = s.filter( collection, "select this.type, count(this) group by this.type" );]]></programlisting>
<para>
당신은 콜렉션을 초기화 하지 않고서 그것(콜렉션)의 크기를 찾을 수 있다:
</para>
<programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
</sect1>
</chapter>