http://blog.hibernate.org/cgi-bin/blosxom.cgi/2004/08/23/

iBATIS와 같은 간단한 JDBC프레임워크를 둘러싼 최근에 많은 말들이 있다. 스스로는 객체기반 도메인 모델이 필요하지 않고 하나의 트랜잭션내에 속하는 엔터티의 깊은 구조와 함께 작동하지 않는 애플리케이션 사용을 위해 iBATIS의 기본적인 생각을 좋아한다. JDBC프레임워크는 몇몇 "비상식적인" 기존의 데이터베이스와 함께 작업을 수행한다면 좋은 방법이다. ORM솔류션은 관계가 적당한 참조적인 통일성 제약으로 잘 정의된 외부키처럼 표현된다고 가정하는 경향이 있다.(Hibernate3이 Hibernate 2.x보다 조금 덜 하다)

몇몇 사람들조차 JDBC프레임워크가 ORM이 가장 적합한 시스템(명백한 관계 스키마를 가진 객체기반 애플리케이션)에서도 ORM의 적합한 대안이라고 제안한다. 그들은 생성된 SQL보다 손으로 직접 작성한 SQL문이 좀더 나은 상태가 된다고 말한다. 하지만 나는 그게 진실이라고 생각하지 않는다. 대부분의 애플리케이션에 의해 필요한 엄청난 양의 SQL코드가 장황한 종류이고 간단히 사람의 조정을 필요로 하지 않을 뿐 아니라 JDBC프레임워크가 ORM과는 다른 의미의 단계에서 작동하기 때문이다. iBATIS같은 솔루션은 이슈화된 SQL과 결과의 데이터셋의 의미에 대해 많은 것을 알지 않는다. 이것은 효과적인 캐슁같은 성능 최적화를 위한 다소 적은 기회를 가진다는 것을 의미한다. 더군다나 우리가 손으로 작성한 SQL문을 볼때마다 우리는 N+1 조회문제를 보게 된다. 이것은 우리가 함께 가져올 필요가 있는 관계의 각각의 조합을 위한 새로운 SQL쿼리를 쓰는것이 굉장히 장황하다는 것이다. HQL은 SQL보다 덜 장황하기 때문에 여기서 이런점을 명백하게 도와준다. ORM이 만들수 있는 최적화의 종류를 달성할수 있는 JDBC프레임워크를 위해 비슷한 레벨의 정교함을 가져야만 한다. 본질적으로 이것은 SQL생성을 뺀 ORM이 될 필요가 있다. 사실 우리는 존재하는 JDBC프레임워크내에 얻어지는 전개를 벌써 보기 시작했다. 이것은 명백히 진술된 이익(단순함)중 하나를 해치기 시작한다.

이것은 또한 다음의 흥미로운 생각을 떠오르게 한다. 만약 서서히 요건을 추가하면 JDBC프레임워크는 SQL생성을 뺀 ORM처럼 끝나게 될것이다. 왜 SQL생성을 뺀 Hibernate와 같은 존재하는 ORM솔루션을 사용하지 않는가.?

Hibernate팀은 오랜기간동안 때때로 손으로 작성된 쿼리와 함께 생성된 SQL문과 혼합되고 일치할 필요가 있다는 것을 인식하고 있었다. .Hibernate의 예전 버전에서는 우리의 해결법은 Hibernate가 사용하는 JDBC Connection을 간단히 드러내는 것이었다. 그래고 당신은 당신 자신의 prepared statement를 수행할수 있었다. 이것은 얼마전부터 변경되기 시작했다. Max Andersen은 이것에 많은 작업을 했다. 지금의 Hibernate3은 생성된 SQL문 없이 Hibernate의 다른 기능 모두의 잇점을 가진체 전체 애플리케이션을 쓰는것이 가능하게 됐다.

우리는 이러한 방법으로 사람들이 Hibernate를 사용하도록 기대할까..? 나는 많은 사람들이 언제나 장황한 INSERT, UPDATE, DELETE문을 쓰는것을 즐길것이라고 의심한다. 반면에 우리는 때때로 어느정도의 사람들이 쿼리를 커스터마이징할 필요가 있다고 생각한다. 하지만 이것을 증명하기 위해 나는 당신이 어떻게 할수 있는지 보여줄것이다. 만약 당신이 진실로 원한다면.

간단한 Person-Employment-Organization 도메인 모델을 보자.(당신은 org.hibernate.test.sql 패키지에서 이 코드를 볼수 있다. 그래서 나는 여기서 이것을 다시 생성하지 않을것이다.) 가장 간단한 클래스는 Person이다. 여기에 맵핑이 있다.

<class name="Person" lazy="true">
    <id name="id" unsaved-value="0">
        <generator class="increment"/>
    </id>
    
    <property name="name" not-null="true"/>
    
    <loader query-ref="person"/>
    
    <sql-insert>INSERT INTO PERSON (NAME, IDVALUES UPPER(?), ? )</sql-insert>
    <sql-update>UPDATE PERSON SET NAME=UPPER(?WHERE ID=?</sql-update>
    <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>

주의할 첫번째 것은 손으로 작성된 INSERT, UPDATE 그리고 DELETE 문이다. 파라미터의 순서는 위에서 목록화된 프라퍼티의 순서와 일치한다. 나는 여기서 특별히 흥미로운것이 없다고 짐작한다.

좀더 흥미가 가는것은 <loader>태그이다. 이것은 우리가 get(), load() 또는 늦은(lazy) 관계 할당(association fetching)을 사용하는 person을 로드할때 사용되는 명명된 쿼리에 대한 참조를 정의한다. 특히 명명된 쿼리는 순수한 SQL쿼리가 될것이다. 이 경우에는

<sql-query name="person">
    <return alias="p" class="Person" lock-mode="upgrade"/>
    SELECT NAME AS {p.name}, ID AS {p.idFROM PERSON WHERE ID=? FOR UPDATE
</sql-query>

(순수한 SQL 쿼리는 아마 엔터티의 다중 "칼럼"을 반환하게 될것이다. 이것은 단지 하나의 엔터티를 반환하는 매우 간단한 경우이다.)

Employment는 조금 더 복잡하다. 특별히 모든 프라퍼티가 INSERT 그리고 UPDATE 문내에 포함되는것은 아니다.

<class name="Employment" lazy="true">
    <id name="id" unsaved-value="0">
        <generator class="increment"/>
    </id>
    
    <many-to-one name="employee" not-null="true" update="false"/>
    <many-to-one name="employer" not-null="true" update="false"/>
    <property name="startDate" not-null="true" update="false" 
        insert="false"/>
    <property name="endDate" insert="false"/>
    <property name="regionCode" update="false"/>
    
    <loader query-ref="employment"/>
    
    <sql-insert>
        INSERT INTO EMPLOYMENT 
            (EMPLOYEE, EMPLOYER, STARTDATE, REGIONCODE, ID
            VALUES (?, ?, CURRENT_DATE, UPPER(?), ?)
    </sql-insert>
    <sql-update>UPDATE EMPLOYMENT SET ENDDATE=? WHERE ID=?</sql-update>
    <sql-delete>DELETE FROM EMPLOYMENT WHERE ID=?</sql-delete>
</class>

<sql-query name="employment">
    <return alias="emp" class="Employment"/>
    SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer}
        STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
        REGIONCODE as {emp.regionCode}, ID AS {emp.id}
    FROM EMPLOYMENT
    WHERE ID = ?
</sql-query>

Organization의 맵핑은 Employments의 집합을 가진다.:

<class name="Organization" lazy="true">
    <id name="id" unsaved-value="0">
        <generator class="increment"/>
    </id>
    
    <property name="name" not-null="true"/>
    
    <set name="employments" 
        lazy="true" 
        inverse="true">
        
        <key column="employer"/> <!-- only needed for DDL generation -->
        
        <one-to-many class="Employment"/>
        
        <loader query-ref="organizationEmployments"/>
    </set>
    
    <loader query-ref="organization"/>
    
    <sql-insert>
        INSERT INTO ORGANIZATION (NAME, IDVALUES UPPER(?), ? )
    </sql-insert>
    <sql-update>UPDATE ORGANIZATION SET NAME=UPPER(?WHERE ID=?</sql-update>
    <sql-delete>DELETE FROM ORGANIZATION WHERE ID=?</sql-delete>
</class>

Organization를 위한 <loader>쿼리가 있을 뿐아니라 Employments의 집합을 위한 것도 있다.

<sql-query name="organization">
    <return alias="org" class="Organization"/>
    SELECT NAME AS {org.name}, ID AS {org.idFROM ORGANIZATION
    WHERE ID=?
</sql-query>

<sql-query name="organizationEmployments">
    <return alias="empcol" collection="Organization.employments"/>
    <return alias="emp" class="Employment"/>
    SELECT {empcol.*}
        EMPLOYER AS {emp.employer}, EMPLOYEE AS {emp.employee},
        STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
        REGIONCODE as {emp.regionCode}, ID AS {emp.id}
    FROM EMPLOYMENT empcol
    WHERE EMPLOYER = :id AND DELETED_DATETIME IS NULL
</sql-query>    

내가 이 코드를 썼을때 나는 Hibernate가 나를 위해 SQL을 쓰는 잇점을 느끼기 시작했다. 이것은 간단한 예제이다. 나는 내가 나중에 유지해야하는 코드의 35이상의 줄을 삭제할것이다.

마침내 이 쿼리를 위해 우리는 순수한 SQL쿼리(명명된 쿼리 또는 자바코드에 내장된)를 사용할 수 있다. 예를 들면

<sql-query name="allOrganizationsWithEmployees">
    <return alias="org" class="Organization"/>
    SELECT DISTINCT NAME AS {org.name}, ID AS {org.id
    FROM ORGANIZATION org
    INNER JOIN EMPLOYMENT e ON e.EMPLOYER = org.ID
</sql-query>

개인적으로 나는 XML보다 자바내에서 프로그래밍 하는것을 선호한다. 그래서 이 기능 모두는 나의 선호를 위해서 XML을 너무 크게 만든다. 나는 SQL생성에 충실할것이라고 생각한다. 내가 SQL을 좋아하지 않는것은 아니다. 사실 나는 SQL의 굉장한 팬이다. 그리고 내가 Hibernate의 로깅을 사용할때 지난 쿼리를 스크롤 해보는것을 좋아한다. 이것은 Hibernate가 내가 SQL을 쓰는것보다 더 좋다.

Add new attachment

Only authorized users are allowed to upload new attachments.
« This page (revision-1) was last changed on 06-Apr-2006 09:45 by UnknownAuthor