당신은 또한 당신의 데이터베이스의 native SQL dialect로 질의들을 표현할 수도 있다. 당신이 오라클의 질의 힌트들 또는 CONNECT 키워드와 같은 데이터베이스 지정적인 특징들을 활용하고자 원할 경우에 이것이 유용하다. 그것은 또한 직접적인 SQL/JDBC 기반의 어플리케이션으로부터 Hibernate로의 보다 명료한 이전 경로를 제공한다.
Hibernate3은 또한 모든 create, update, delete, load 오퍼레이션들에 대해 (내장 프로시저들을 포함하여) 손으로 작성된 SQL을 지정하는 것을 당신에게 허용해준다.
native SQL 질의들의 실행은 SQLQuery 인터페이스를 통해 제어되며, 그것은 Session.createSQLQuery()을 호출하여 획득된다. 다음은 이 API를 질의에 사용하는 방법을 설명한다.
가장 기본적인 SQL 질의는 스칼라들(값들)의 목록을 얻는 것이다.
sess.createSQLQuery("SELECT * FROM CATS").list(); sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
이것들은 둘다 CATS 테이블 내에 있는 각각의 컬럼에 대한 스칼라 값들을 가진 Object 배열들의 List를 반환할 것이다. Hibernate는 반환되는 스칼라 값들의 실제 순서와 타입들을 도출하는데 ResultSetMetadata를 사용할 것이다.
ResultSetMetadata 사용의 오버헤드를 피하거나 간단하게는 반환되는 것을 보다 명시적이게끔 하기 위해 우리는 addScalar()를 사용할 수 있다.
sess.createSQLQuery("SELECT * FROM CATS") .addScalar("ID", Hibernate.LONG) .addScalar("NAME", Hibernate.STRING) .addScalar("BIRTHDATE", Hibernate.DATE)
이 질의는 다음을 지정했다:
SQL 질의 문자열
반환할 컬럼들과 타입들
이것은 여전히 Object 배열들을 반환할 것이지만, 이제 그것은 ResultSetMetdata를 사용하지 않을 것이고 대신에 기반 결과셋으로부터 ID, NAME 그리고 BIRTHDATE 컬럼을 각각 Long, String 그리고 Short 타입으로 반환할 것이다. 심지어 그 질의가 *를 사용하고 세 개의 열거된 컬럼들 보다 더 많은 것을 반환할 수 있을지라도, 이것은 또한 오직 이들 세 개의 컬럼들 만이 반환될 것임을 의미한다.
스칼라들 중 몇몇 또는 전부에 대한 타입 정보를 남겨두는 것이 가능하다.
sess.createSQLQuery("SELECT * FROM CATS") .addScalar("ID", Hibernate.LONG) .addScalar("NAME") .addScalar("BIRTHDATE")
이것은 본질적으로 앞의 것과 동일한 질의이지만, 이제 ResultSetMetaData는 ID의 타입이 명시적으로 지정되어 있으므로 NAME과 BIRTHDATE의 타입을 결정하는데 사용된다.
java.sql.Types returned from ResultSetMetaData이 Hibernate 타입들로 매핑되는 방법은 Dialect에 의해 제어된다. 만일 특정 타입이 매핑되지 않거나 예상되는 타입으로 귀결되지 않을 경우에 Dialect 내에 있는 registerHibernateType에 대한 호출들을 통해 그것을 맞춤화 시키는 것이 가능하다.
위의 질의들은 스칼라 값들을 반환하는 것, 결과셋들로부터 "원래의" 값들을 기본적으로 반환하는 것에 대한 전부였다. 다음은 addEntity()를 통해 native sql 질의로부터 엔티티 객체들을 얻는 방법을 보여준다.
sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class); sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
이 질의는 다음을 지정했다:
SQL 질의 문자열
그 질의에 의해 반환되는 엔티티
Cat이 컬럼 ID, NAME 그리고 BIRTHDATE로서 매핑된다고 가정하면, 위의 질의들은 둘다 각각의 요소가 하나의 Cat 엔티티인 하나의 List를 반환할 것이다.
만일 그 엔티티가 또 다른 엔티티에 대해 many-to-one로 매핑되어 있다면 또한 native 질의를 실행할 때 이것을 반환하는 것이 필수적고, 그 밖의 경우 데이터베이스 지정적인 "컬럼이 발견되지 않았습니다" 오류가 일어날 것이다. 추가적인 컬럼은 * 표기를 사용할 자동적으로 반환될 것이지만, 우리는 다음 Dog에 대한 many-to-one 예제에서처럼 명시적인 것을 더 선호한다:
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
이것은 cat.getDog()이 고유하게 기능하는 것을 허용한다.
프락시를 초기화 시킴에 있어 가능한 특별한 라운드트립을 피하기 위해서 Dog에서 eagerly join시키는 것이 간으하다. 이것은 addJoin() 메소드를 통해 행해지는데, 그것은 연관이나 콜렉션 내에서 조인시키는 것을 당신에게 허용해준다.
sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID") .addEntity("cat", Cat.class) .addJoin("cat.dog");
이 예제에서 반환되는 Cat들은 데이터베이스에 대한 임의의 특별한 라운드크립 없이 전체적으로 초기화된 그것들의 dog 프로퍼티를 갖는다. 우리가 join의 대상 프로퍼티 경로를 지정하는 것을 가능하도록 하기 위해 하나의 alias 이름("cat")을 추가했음을 주지하라. 대신에 예를 들어 Cat이 Dog에 대해 one-to-many를 가질 경우, 콜렉션들에 대해 동일한 eager joining을 행하는 것이 가능하다.
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID") .addEntity("cat", Cat.class) .addJoin("cat.dogs");
이 단계에서 우리는 Hibernate에서 native 질의들을 사용가능하도록 만들기 위해 sql 질의들을 강화시키지는 것을 시작하지 않고서도 native 질의들로서 가능한 것의 한계에 도달하고 있다; 문제점들은 동일한 타입의 여러 엔티티들을 반환할 때 또는 디폴트 alias/column 이름들이 충분하지 않을 때 발생하기 시작한다.
지금까지 결과 셋 컬럼 이름들은 매핑 문서 내에 지정된 컬럼 이름들과 동일하다고 가정되어 있다. 동일한 컬럼이 하나 이상의 테이블 내에서 나타날 수 있기 때문에, 이것은 여러 개의 테이블들을 조인시키는 SQL 질의들에 대해 문제가 될 수 있다.
컬럼 alias 주입은 다음 질의(아마 실패할 것이다)에서 필요하다:
sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID") .addEntity("cat", Cat.class) .addEntity("mother", Cat.class)
이 질의의 목적은 단위 행 당 두 개의 Cat 인스턴스들, 하나의 cat 그리고 그것의 mother를 반환하는 것이다. 왜냐하면 그것들이 동일한 컬럼 이름들로 매핑되어 있기 때문에 이것은 실패할 것이고 데이베이스 상에서 반환된 컬럼 alias들은 아마 매핑들 내에 지정된 컬럼들("ID" 와 "NAME")과 같지 않은 "c.ID", "c.NAME" 등의 형식일 것이다.
다음 형식은 컬럼 이름 중복 취약점을 갖지 않는다:
sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID") .addEntity("cat", Cat.class) .addEntity("mother", Cat.class)
이 질의는 다음을 지정했다:
컬럼 alias들을 주입하기 위한 Hibernate용 placeholder들을 가진 SQL 질의 문자열
그 질의에 의해 반환되는 엔티티들
위에 사용된 {cat.*} 과 {mother.*} 표기는 "모든 프로퍼티들"에 대한 생략이다. 다른 방법으로 당신은 컬럼들을 명시적으로 열거할 수도 있지만, 이 경우에 우리는 Hibernate로 하여금 SQL 컬럼 alias들을 각각의 컬럼에 주입시키도록 강제한다. 컬럼 alias를 위한 placeholder는 단지 그 테이블 alias에 의해 수식된 프로퍼티 이름이다. 다음 예제에서, 우리는 다른 테이블(cat_log)로부터 매핑 메타데이터 내에 선언된 것으로의 Cat들과 그것들의 mother들을 검색한다. 우리는 우리가 좋다면 심지어 where 절 내에 프로퍼티 alias를 사용할 수도 있음을 주지하라.
String sql = "SELECT ID as {c.id}, NAME as {c.name}, " + "BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " + "FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID"; List loggedCats = sess.createSQLQuery(sql) .addEntity("cat", Cat.class) .addEntity("mother", Cat.class).list()
대부분의 경우에 위의 alias 주입이 필요하지만, composite 프로퍼티들, 상속 판별자들 (inheritance discriminators), 콜렉션 등과 같은 보다 복잡한 매핑들과 관련된 질의들의 경우에는 고유한 alias들을 주입시키는 것을 Hibernate에게 허용하는데 사용될 몇몇 특별한 alias들이 존재한다.
다음 테이블은 alias 주입을 사용하는 다른 가능성들을 보여준다. 노트: 결과 내에 있는 alias 이름들이 예제이며, 각각의 alias는 사용될 때 하나의 유일한 이름과 아마 다른 이름을 가질 것이다.
표 16.1. alias 주입 이름들
설명 | 구문 | 예제 | |
---|---|---|---|
간단한 프로퍼티 | {[aliasname].[propertyname] | A_NAME as {item.name} | |
composite 프로퍼티 | {[aliasname].[componentname].[propertyname]} | CURRENCY as {item.amount.currency}, VALUE as {item.amount.value} | |
엔티티의 판별자(Discriminator) | {[aliasname].class} | DISC as {item.class} | |
엔티티의 모든 프로퍼티들 | {[aliasname].*} | {item.*} | |
콜렉션 키 | {[aliasname].key} | ORGID as {coll.key} | |
콜렉션의 id | {[aliasname].id} | EMPID as {coll.id} | |
콜렉션의 요소 | {[aliasname].element} | XID as {coll.element} | |
콜렉션 내에 있는 요소의 프로퍼티 | {[aliasname].element.[propertyname]} | NAME as {coll.element.name} | |
콜렉션 내에 있는 요소의 모든 프로퍼티들 | {[aliasname].element.*} | {coll.element.*} | |
콜렉션의 모든 프로퍼티들 | {[aliasname].*} | {coll.*} |
native sql 질의에 ResultTransformer를 적용하는 것이 가능하다. 예를 들어 non-managed 엔티티들을 반환하기 위해 ResultTransformer를 허용하는 것.
sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS") .setResultTransformer(Transformers.aliasToBean(CatDTO.class))
이 질의는 다음을 지정했다:
SQL 질의 문자열
결과 변환자(transformer)
위의 질의는 초기화되어 있고 NAME과 BIRTHNAME의 값들을 CatDTO의 대응하는 프로퍼티들과 필드들 속으로 주입시킨 CatDTO의 리스트를 반환할 것이다.
상속의 부분으로서 매핑되는 엔티티들을 질의하는 native sql 질의들은 baseclass의 모든 프로퍼티들을 포함해야 하고 그 모든 것이 서브클래스화 되어야 한다.
Native sql 질의들은 위치 파라미터들 뿐만 아니라 명명된 파라미터들을 지원한다:
Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class); List pusList = query.setString(0, "Pus%").list(); query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class); List pusList = query.setString("name", "Pus%").list();
명명된 SQL 질의들은 HQL 질의와 동일한 방법으로 매핑 문서 속에 정의될 수 있고 정확하게 호출될 수도 있다. 이 경우에, 우리는 addEntity() 호출을 필요로 하지 않는다.
<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>
List people = sess.getNamedQuery("persons") .setString("namePattern", namePattern) .setMaxResults(50) .list();
<return-join> 요소와 <load-collection> 요소는 연관들을 조인시키고 콜렉션들을 각각 초기화 시키는 질의들을 정의하는데 사용된다.
<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>
명명된 SQL 질의는 스칼라 값을 반환할수도 있다. 당신은 <return-scalar> 요소를 사용하여 컬럼 alias와 Hibernate 타입을 선언해야 한다:
<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>
당신은 여러 개의 명명된 질의들을 가로질러 재사용하거나 setResultSetMapping() API를 통해 결과셋 매핑정보들을 재사용하기 위해 <resultset> 요소 속에 결과셋 매핑 정보들을 구체화 시킬 수 있다.
<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>
다른방법으로 당신은 당신의 hbm 파일들 내에 직접 자바 코드로 된 결과셋 매핑 정보를 사용할 수 있다.
List cats = sess.createSQLQuery( "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id" ) .setResultSetMapping("catAndKitten") .list();
Hibernate로 하여금 그것 자신의 alias들을 끼워넣도록 하기 위해 {}-구문을 사용하는 것 대신에, <return-property>로서 당신은 사용할 컬럼 alias들이 무엇인지를 Hibernate에게 명시적으로 알려줄 수 있다.
<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>
<return-property>는 또한 다중 컬럼들에 대해 동작한다. 이것은 다중-컬럼 프로퍼티들에 대한 fine grained 제어를 허용할 수 없는 {}-구문을 가진 제약을 해결해준다.
<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>
이 예제에서 우리는 끼워넣기(injection)를 위해 {}-구문과 함께 <return-property>를 사용했음을 주목하라. 사용자들이 컬럼과 프로퍼티들을 참조하고자 원하는 방법을 선택하는 것을 사용자들에게 허용해줌으로써.
만일 당신의 매핑이 한 개의 판별자(discriminator )를 가질 경우 당신은 판별자 컬럼을 지정하는데 <return-discriminator>를 사용해야 한다.
Hibernate 3은 내장 프로시저들과 함수들을 통한 질의 지원을 도입한다. 대부분의 다음 문서는 양자 모두에 동일하게 적용된다. 내장 프로시저/함수는 Hibernate와 동작하는 것이 가능하도록 첫 번째 out-파라미터로서 한 개의 결과셋을 반환해야 한다. Oracle9 이상의 버전에서 그런 내장 프로시저에 대한 예제는 다음과 같다:
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;
Hibernate에서 이 질의를 사용하기 위해 당신은 하나의 명명된 질의(a named query)를 통해 그것을 매핑할 필요가 있다.
<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>
내장 프로시저들은 현재 스칼라들과 엔티티들 만을 반환함을 주목하라. <return-join>과 <load-collection>은 지원되지 않는다.
Hibernate에서 내장 프로시저들을 사용하기 위해서 프로시저들/함수들은 다음 몇몇 규칙들을 따라야 한다. 만일 그것들이 그들 규칙들을 따르지 않을 경우 그것들은 Hibernate와 함께 사용 불가능하다. 만일 당신이 여전히 이들 프로시저들을 사용하고자 원할 경우, 당신은 session.connection()을 통해 그것들을 실행시켜야 한다. 데이터베이스 벤더들이 다른 내장 프로시저 의미론/구문을 갖고 있기 때문에, 규칙들은 각각의 데이터베이스에 따라 차이가 난다.
내장 프로시저 질의들은 setFirstResult()/setMaxResults()로서 쪽매김 될 수 없다.
권장되는 호출 형식은 표준 SQL92이다: { ? = call functionName(<parameters>) } 또는 { ? = call procedureName(<parameters>}. Native 호출 구문은 지원되지 않는다.
Oracle의 경우 다음 규칙들이 적용된다:
하나의 함수는 하나의 결과 셋을 반환해야 한다. 프로시저의 첫 번째 파라미터는 하나의 결과 셋을 반환하는 하나의 OUT이어야 한다. 이것은 Oracle 9 또는 10에서 하나의 SYS_REFCURSOR를 사용하여 행해진다. Oracle에서 당신은 REF CURSOR 타입을 정의할 필요가 있는데, Oracle 보고서를 보라.
Sybase 또는 MS SQL server의 경우 다음 규칙들이 적용된다:
프로시저는 한 개의 결과 셋을 반환해야 한다. 이들 서버들이 여러 개의 결과셋들과 업데이트 카운트들을 반환 할수 있다/할 것이이므로, Hibernate는 결과들을 반복 순환할 것이고 그것의 반환 값으로서 하나의 결과 셋인 첫 번째 결과를 취할 것이다. 그 밖의 모든 것은 폐기될 것이다.
만일 당신이 당신의 프로시저 내에 SET NOCOUNT ON을 이용 가능하게 할 수 있다면 그것은 아마 보다 효율적이게 될 것이지만 이것은 필요 조건이 아니다.
Hibernate3는 create, update, delete 오퍼레이션들을 위한 맞춤형 문장들을 사용할 수 있다. Hibernate에서 클래스와 콜렉션 영속자들은 구성 시에 생성된 문자열들의 집합(insertsql, deletesql, updatesql 등)을 이미 포함하고 있다. <sql-insert>, <sql-delete>, <sql-update> 매핑 태그들은 이들 문자열들을 오버라이드 시킨다:
<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>
SQL이 당신의 데이터베이스 내에서 직접 실행되어서, 당신이 좋아하는 임의의 dialect를 사용하는 것이 자유롭다. 만일 당신이 데이터베이스 지정적인 SQL을 사용할 경우 이것은 물론 당신의 매핑의 이식성을 감소시킬 것이다.
만일 callable 속성이 설정되면 내장 프로시저들이 지원된다:
<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>
위치 파라미터들은 Hibernate가 그것들을 기대하는 것과 같은 순서가 되어야 하므로, 위치 파라미터들의 순서는 현재 절대적으로 중요하다.
당신은 org.hiberate.persister.entity 레벨로 디버그 로깅을 사용 가능하게 함으로써 예상된 순서를 볼 수 있다. 이 레벨을 이용 가능하게 하면 Hibernate는 엔티티들을 생성시키고, 업데이트하고, 삭제하는데 사용되는 정적인 SQL을 출력할 것이다. (예상되는 결과를 보려면, Hibernate 생성된 정적인 sql을 오버라이드 시키게 매핑 파일들 속에 당신의 맞춤형 SQL을 포함시키지 않도록 염두에 두라.)
Hibernate가 문장의 성공을 위해 몇몇 실행 시 체크들을 행하므로, 내장 프로시저들은 대부분의 경우들(읽기:다른 경우들 보다 그것을 더 잘 행한다)에서 insert되고/업데이트되고/삭제된 행들의 개수를 반환하는데 필요하다. Hibernate는 항상 CUD 오퍼레이션들에 대한 숫자 출력 파라미터로서 첫 번째 문장 파라미터를 등록시킨다:
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;
당신은 또한 엔티티 로딩을 위한 당신 자신의 SQL (또는 HQL)을 선언할 수도 있다:
<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>
이것은 앞서 논의했듯이 단지 명명된 질의 선언이다. 당신은 class 매핑 속에 이 명명된 질의를 참조할 수 있다:
<class name="Person"> <id name="id"> <generator class="increment"/> </id> <property name="name" not-null="true"/> <loader query-ref="person"/> </class>
이것은 심지어 내장 프로시저들에 동작한다.
당신은 콜렉션 로딩을 위한 한 개의 질의를 정의할 수도 있다:
<set name="employments" inverse="true"> <key/> <one-to-many class="Employment"/> <loader query-ref="employments"/> </set>
<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>
당신은 심지어 조인 페칭에 의해 하나의 콜렉션을 로드시키는 하나의 엔티티를 정의할 수 있다:
<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>