Where The Streets Have No Name

iBATIS - SQL Map XML 파일 본문

Developement/Java

iBATIS - SQL Map XML 파일

highheat 2008. 7. 15. 21:39

출처 : http://blog.naver.com/zhaong/120047184215

mapped statement의 예제

 

ex)

<sqlMap id = "Product">
    <cacheModel id = "productCathe" type = "LRU">
        <flushInterval hours = "24"/>
        <property name = "size" value = "1000"/>
    </cacheModel>

 

    <typeAlias alias = "product" type = "com.ibatis.example.Product"/>

 

    <parameterMap id = "productParam" class = "product">

        <parameter property = "id"/>

    </parameterMap>

 

    <resultMap id = "productResult" class = "product">

        <result property = "id" column = "PRD_ID"/>

        <result property = "description" column = "PRD_DESCRIPTION"/>

    </resultMap>

 

    <select id = "getProduct" prameterMap = "productParam" resultMap = "productResult" cacheModel = "product-cache">

        select * from PRODUCT where PRD_ID = ?

    </select>

</sqlMap>

 

 

복잡한가? 비록 프로임웍이 당신을 위해 많은 것을 하더라도 간단한 select statement를 위해 너무 많은 추가적인 작업을 하는것 처럼 보인다. 하지만 아래와 같은 축소버전으로도 가능하다.

 

 

ex)

<sqlMap id = "Product">

    <select id = "getProduct" parameterClass = "com.ibatis.example.Product" resultClass = "com.ibatis.example.Product">

        select

            PRD_ID as id,

            PRD_DESCRIPTION as description

        from PRODUCT

        where PRD_ID = #id#

    </select>

</sqlMap>

 

 

지금 SQL Map을 행위적인 측면에서 보면 이 statement는 정확하게 같지는 않다. 즉 몇가지 다른점을 가진다. 먼저 후자의 statement는 캐쉬를 명시하지 않아서 매번의 요청시 데이터베이스에 직접  요청한다. 두번째 후자의 statement는 약간의 부하를 야기할수 있는 프레임워크의 자동맵핑기능을 사용한다. 어찌됐든 두가지 statement는 모두 자바코드로부터 정확하게 같은 방법으로 작동하지 않을 것이다. 그리고 당신의 첫번째 좀더 간단한 솔루션으로 시작할 것이고 나중에는 필요하면 좀더 향상된 맵핑으로 옮겨 갈것이다. 가장 간단한 솔루션이 많은 경우에 가장 좋은 연습이다.

 

하나의 SQL Map XML 파일은 만흔 캐쉬 모델, 파라이터 맵핑, result맵핑 그리고 statement를 포함할 수 없다. 당신의 애플리케이션을 위해 statement와 maps를 신중하게 구성하라.

 

 

 

* 맵핑된(Mapped) Statements

 

SQL Maps 개념은 맵핑된 statement에 집중한다. 맵핑된 statement는 어떠한 SQL문을 사용할수도 있고 파라미터 maps(input)과 result maps(output)를 가질수 있다. 만약 간단한 경우라면 맵핑된 statement는 파라미터와 result를 위한 클래스로 직접 설정할수 있다. 맵핑된 statement는 메모리내에 생산된 results를 캐슁하기 위해 캐슁모델을 사용하도록 설정할 수도 있다.

 

 

<statement id = "statementName"

                [parameterClass="some.class.Name"]

                [resultClass="some.class.Name"]

                [parameterMap="nameOfParameterMap"]

                [resultMap="nameOfResultMap"]

                [cacheModel="nameOfCache"]

                >

    select * from PRODUCT where PRD_ID = [?|#propertyName#]

        order by [$simpleDynamic$]

</statement>

 

 

위 statement에서 [괄호]부분은 옵션이고 몇몇의 경우에만 혼합할 필요가 있다.

 

 

<statement id = "insertTestProduct">

    insert into PRODUCT(PRD_ID, PRD_DESCRIPTION) value (1, "Shih Tzu")

</statement>

 

 

위 예제는 명확하게 실행될꺼 같지는 않지만 어쨋든 이것은 당신의 임의의 SQL 문을 실행하기 위해 SQL Map프레임워크를 사용한다면 유용할수 있다. 어찌됐든 이것은 파라미터 Maps와 Result Maps를 사용하는 자바빈즈 맵핑기능을 공통적으로 사용할 것이다. 다음의 다양한 센션은 구조와 속성, 그들이 어떻게 맵핑된 statement에 영향을 끼치는 지를 서술한다.

  

* Statement 타입

 

<statement> 요소는 어떤 타입의 SQL 문을 사용할 수 있는 일반적인 "catch all" statement이다. 일반적으로 이것은 좀더 다양한 특성의 statement요소중 하나를 사용하기 위한 좋은 생각이다. 좀더 다양한 특성의 요소는 좀더 직관적인 XML DTD를 제공하고 때때로 일반적인 <statement>요소가 제공하지 않는 추가적인 기능을 제공한다. 아래의 내용은 statement요소와 그들이 지원하는 속성과 기능을 목록화 한다.

 

Statement Element : <statement>

Attributes : id

                parameterClass

                resultClass

                parameterMap

                resultMap

                cacheModel

                xmlResultName

Child Elements : All dynamic elements

Methods : insert update delete All query methods

 

 

Statement Element : <insert>

Attributes : id

                parameterClass

                parameterMap

Child Elements : All dynamic elements <select Key>

Methods : insert update delete

 

 

Statement Element : <update>

Attributes : id

                parameterClass

                parameterMap

Child Elements : All dynamic elements

Methods : insert update delete

 

 

Statement Element : <delete>

Attributes : id

                parameterClass

                parameterMap

Child Elements : All dynamic elements

Mehtods : insert update delete

 

 

Statement Element : <select>

Attirbutes : id

                parameterClass

                resultClass

                parameterMap

                resultMap

                cacheModel

Child Elements : All dynamic elements

Methods : All query methods

 

 

Statement Element : <procedure>

Attributes : id

                parameterClass

                resultClass

                parameterMap

                resultMap

                xmlResultName

Child Elements : All dynamic elements

Methods : insert update delete

               All query methods

  

* The SQL

 

SQL 은 map의 가장 중요한 부분을 차지한다. 이것은 당신의 DB와 JDBC드리이버에 적합한 어떤 SQL이 될수 있다. 당신은 가능한 어떤 기능을 사용할 수 있고 당신의 드라이버가 지원하는 한 다중 statement에 전달할 수도 있다. 당신이 하나의 문서에서 SQL과 XML을 혼합하기 때문에 특수문자의 충돌이 잠재적으로 존재한다. 대부분의 공통적인 것은 greater-than(>)과 less-than(<)문자들이다. 이것들은 SQL문에서 공통적으로 요구되고 XML에서는 예약어이다. 당신의 SQL 문에 들어갈 필요가 있는 특수 XML문자를 처리하기 위한 간단한 해결법이 있다. 표준적인 XML CDATA 섹션을 사용함으로써 특수문자의 어떤것도 파싱되지 않고 문제는 해결된다.

 

에를 들면,

 

<statement id = "getPersonsByAge" parameterClass = "int" resultClass = "examples.domain.Person">

    <![CDATA[

        select * from PERSON

            where AGE < #value#

    ]]>

</statement>

  

* 자동 생성 키

 

많은 관계형 데이터베이스 시스템은 기본키(primary key)필드에 자동생성을 지원한다. 이 RDBMS의 기능은 종종 특정업체에 종속된다. SQL Map은 <insert>요소의 <selectKey>를 통해 자동생성키를 지원한다. 선생성키(pre-generated - 이를 테면 오라클)과 후생성키(post-generated - 이를 테면 MS-SQL서버) 모두 지원한다.

 

아래 그 예제가 있다.

 

<!-- Oracle SEQUENCE Example -->

<insert id = "insertProduct-ORACLE" parameterClass = "com.domain.Product">

    <selectKey resultClass = "int" keyProperty = "id">

        SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL

    </selectKey>

    insert into PRODUCT(PRD_ID, PRD_DESCRIPTION)

        values(#id#, #description#)

</insert>

 

<!-- Microsoft SQL Server IDENTITY Column Example -->

<insert ind = "insertProduct-MS-SQL"  parameterClass = "com.domain.Product">

    insert into PRODUCT(PRD_DESCRIPTION) values(#description#)

 

    <selectKey resultClass = "int" keyProperty = "id">

        select @@IDENTITY as ID

    </selectKey

</insert>

 

  

* 저장 프로시저

 

저장 프로시저는 <procedure> statement요소를 통해 지원된다. 저장 프로시저를 출력물 파라미터와 함께 어떻게 사용하는지 아래에서 보여준다.

 

<parameterMap id = "swapParameters" class = "map">

    <parameter property = "email1" jdbcType = "VARCHAR" javaType = "java.lang.String" mode = "INOUT"/>

    <paraemter property = "email2" jdbcType = "VARCHAR" javaType = "java.lang.String" mode = "INOUT"/>

</parameterMap>

 

<procedure id = "swapEmailAddress" parameterMap = "swapParameters">

    {call swap_email_address(?, ?){

</procedure>

 

 

위처럼 프로시저를 호출하는 것은 파라미터 객체(map)내에서 두개의 컬럼사이에 두개의 이메일 주소를 교체하는 것이다. 파라미터 객체는 파라미터 맵핑의 mode 속성값이 "INOUT" 또는 "OUT"일 경우에만 변경된다. 다른 경우라면 변경되지 않고 남는다. 명백한 불변의 파라미터 객체(이를 테면 String)는 변경할 수 없다.

 

 

주의! : 언제나 표준적인 JDBC저장프로시저를 사용하도록 하라. 좀더 다양한 정보를 보기 위해서는 JDBC CallableStatement문서를 보라.

   

* parameterClass

 

parameterClass 속성값은 자바 클래스의 전체경로를 포함(예를 들면 패키지를 포함한)한 이름이다. parameterClass 속성은 옵션이지만 사용이 굉장히 추천되는 것이다. 이것은 프레임웍 성능을 향상시키는 만큼 statement에 전달하는 파라미터를 제한하는데 사용된다. 만약 당신이 parameterMap을 사용한다면 parameterClass 속성을 사용할 필요가 없다. 예를 들면 당신이 파라미터로 전달하기 위한 "examples.domain.Product" 타입의 객체를 허락하길 원한다면 당신은 다음처럼 할 수 있다.

 

<statement id = "statementName" parameterClass = "examples.domain.Product">

    insert into PRODUCT values(#id#, #description#, #price#)

</statement>

 

 

중요 : 비록 이전버전과의 호환성을 위한 옵션이지만, 언제나 파라미터 클래스를 제공하는 것은 매우 추천되는 사항이다.

        (물론 요구 파라미터가 없더라도) 프레임웍이 먼저 타입을 안다면 스스로 최적화 능력을 가지기 때문에 당신은 클래스를

        제공함으로써 좀더 낳은 성능을 달성할 수 있다.

 

명시된 parameterClass 없이 선호하는 속성(get/set 메소드)을 가지는 자바빈즈는 파라미터를 받을 것이고 어느위치에서 매우 유용하다.

  

* parameterMap

 

parameterMap 속성값은 명시된(밑의 경우처럼) parameterMap요소의 이름이다. parameterMap속성은 parameterClass 속성과 인라인 파라미터의 이익이 되도록 사용된다. XML의 깔끔함과 일관성이 당신의 걱정이거나 당신이 좀더 상세한 parameterMap(이를 테면 저장프로시저)이 필요하다면 이것은 좋은 접근법이다.

 

주의! : 동적으로 맵핑된 statement는 단지 인라인 파라미터만 지원하고 파라미터 map과는 작동하지 않는다.

 

parameterMap의 생각은 JDBC PreparedStatement의 값 토큰과 매치되는 정렬된 파라미터 목록을 명시한다.

 

예를 들면 :

 

<parameterMap id = "insert-product-param" class = "com.domain.Product">

    <parameter property = "id"/>

    <parameter property = "description"/>

</parameterMap>

 

<statement id = "insertProduct" parameterMap = "insert-product-param">

    insert into PRODUCT(PRD_ID, PRD_DESCRIPTION) values(?, ?)

</statement>

 

 

위의 예제에서, 파라미터 map은 SQL문에서 값토큰("?")에 매치되고 정렬되는 두개의 파라미터를 서술한다. 그래서 첫번째 "?"는 "id"속성값에 대체되고 두번째는 "description"속성값에 대체된다.

  

* 인라인 파라미터 맛보기

 

인라인 파라미터는 맵핑된 statement내부에서 사용될수 있다.

 

예를 들면 :

 

<statement id = "insertProduct">

    insert into PRODUCT(PRD_ID, PRD_DESCRIPTION)

        values(#id#, #description#)

</statement>

 

 

위 예제에서 인라인 파라미터는 #id#와 #description#이다. 각각은 statement 파라미터에 대체하는 자바빈즈 속성을 표현한다. Product클래스는 포함된 프로퍼티 토큰이 위치해 있는 statement내에 위치하는 값을 위해 읽게되는 id와 description프로퍼티를 가진다. id = 5와 description="dog"를 가지는 Product를 넘겨받은 statement는 다음처럼 수행된다.

 

insert into PRODUCT(PRD_ID, PRD_DESCRIPTION)

    values(5, "dog");

  

* resultClass

 

resultClass 속성값은 자바클래스의 전체경로를 포함(예를 들면 패키지를 포함한)한 이름이다. resultClass속성은 우리에게 ResultSetMetaData에 기반한 JDBC ResultSet에 자동맵핑되는 클래스를 명시하도록 한다. 자바빈즈의 프로퍼티와 ResultSet의 컬럼에 매치될때마다 프로퍼티는 컬럼값과 함께 생성된다. 이것은 매우 짧고 달콤하게 맵핑된 statement를 쿼리한다.

 

예를 들면 :

 

<statement id = "getPerson" prameterClass = "int" resultClass = "examples.domain.Person">

    SELECT

        PER_ID as id,

        PER_FIRST_NAME as firstName,

        PER_LAST_NAME as lastName,

        PER_BIRTH_DATE as birthDate,

        PER_WEIGHT_KG as weightInKilograms,

        PER_HEIGHT_M as heightInMeters

    FROM PERSON

    WHERE PER_ID = #value#

</statement>

 

 

위의 예제에서 Person클래스는 id, firstName, lastName, birthDate, weightInKilograms, heightInMeters를 포함하는 프로퍼티를 가진다. 컬럼 별칭과 함께 대응되는 각각은 SQL select문에 의해 서술된다. 컬럼별칭은 데이터베이스 컬럼이름에 매치되지 않을때만 요구된다. 일반적으로 요구되지 않는다. 실행되었을때 Person 객체는 프로퍼티이름과 컬럼명에 기반해서 초기화되기 위해 맵핑되는 result set으로부터 초기화되고 결과를 반환한다.

 

resultClass로 자동맵핑하는데는 몇가지 제한점이 있다. 출력컬럼의 타입을 명시하는 방법은 없다. 관련된 데이터를 자동적으로 로드하는 방법이 없고 ResultSetMetaData에 접근하는데 필요한 접근법내에서 하찮은 성능결과가 있다. 이 제한점 모드는 명시적인 resultMap을 사용함으로써 극복할 수 있다.

  

* resultMap

 

resultMap프로퍼티는 좀더 공통적으로 사용되고 이해하기 위해 가장 중요한 속성중에 하나이다. 이 resultMap속성값은 명시된 resultMap요소의 이름이다. resultMap속성을 사용하는 것은 당신에게 result set으로부터 데이터와 컬럼에 맵핑되는 프로퍼티를 어떻게 꺼내는지 제어하도록 한다. resultClass속성을 사용하는 자동맵핑접근법과는 달리 resultMap은 당신에게 컬럼타입을 명시하고 null값을 대체 그리고 복합 프로퍼티맵핑(다른 자바빈즈, Collections 그리고 원시타입래퍼)을 허락한다.

  

아래의 예제는 resultMap이 어떻게 statement에 관련되었는지 보여준다.

 

<resultMap id = "get-product-result" class = "com.ibatis.example.Product">

    <result property = "id" column = "PRD_ID"/>

    <result property = "description" column = "PRD+DESCRIPTION"/>

</resultMap>

 

<statement id = "getProduct" resultMap = "get-product-result">

    select * f rom PRODUCT

</statement>

 

 

위 예제에서 SQL쿼리로부터 ResultSet은 resultMap정의를 사용해서 Product인스턴스에 맵핑할 것이다. resultMap은 "id"프로퍼티가 "PRD_ID" 컬럼과 "PRD_DESCRIPTION"컬럼에 의해 생성되는 "description"프로퍼티에 의해 생성될 것이다. "select * "을 사용하는것이 지원된다는것에 주의하라. ResultSet내 반환컬럼 모두에 맵핑할 필요는 없다.

  

* cacheModel

 

cacheModel속성값은 정의된 cacheModel요소의 이름이다. cacheModel은 쿼리가 맵핑된 statement를 사용하기 위한 캐쉬를 서술하는데 사용된다. 각각의 쿼리맵핑 statement는 다른 cacheModel이나 같은것을 사용할 수 있다. cacheModel요소와 그것의 속성에 대한 모든 상세설명은 나중에 언급되며, 다음 예제는 어떻게 statement와 관련되는지 보여준다.

 

<cacheModel id = "product-cache" imlementation = "LRU">

    <flushInterval hours = "24">

    <flushOnExecute statement = "insertProduct"/>

    <flushOnExecute statement = "updateProduct"/>

    <flushOnExecute statement = "deleteProduct"/>

    <property name = "size" value = "1000">

</cacheModel>

 

<statement id = "getProductList" parameterClass = "int" cacheModel = "product-cache">

    select * from PRODUCT where PRD_CAT_ID = #value#

</statement>

 

 

위 예제에서는 캐쉬는 WEAK참조타입을 사용한다. products를 위해 정의되고 24시간마다 또는 관련된 update문이 수행될때마다 지워진다.(flush)

  

* xmlResultName

 

mapping result를  XML문서로 직접적으로 만들 때 xmlResultName의 값은 XML문서의 가장 상위 요소의 이름이 될것이다.

 

예를 들면 :

 

<select id = "getPerson" parameterClass = "int" resultClass = "xml" xmlResultName = "person">

    SELECT

        PER_ID as id,

        PER_FIRST_NAME as firstName,

        PER_LAST_NAME as lastName,

        PER_BIRTH_DATE as birthDate,

        PER_WEIGHT_KG as weightInKilograms,

        PER_HEIGHT_M as heightInMeters

    FROM PERSON

        WHERE PER_ID = #value#

</select>

  

위 select statement는 다음 구조의 XML객체를 생성할 것이다.

 

<person>

    <id>1</id>

    <firstName>Clinton</firstName>

    <lastName>Begin</lastName>

    <birthDate>1990-01-01</birthDate>

    <weightInKilograms>89</weightInKilograms>

    <heightInMeters>1.77</heightInMeters>

</person>

  

* 파라미터 Maps와 인라인 파라미터

 

당신이 위에서 본것처럼 parameterMap는 자바빈즈 프로퍼티를 statement의 프로퍼티에 맵핑시키는 작업을 수행한다. 비록 parameterMaps가 외부형태내에 드물게 발생하더라도 그것들이 당신에게 인라인 파라미터를 이해하도록 도와준다는 것을 이해하라.

 

<parameterMap id = "parameterMapName" [class = "com.domain.Product"]>

    <parameter property = "propertyName" [jdbcType = "VARCHAR"][javaType = "string"][nullValue="NUMERIC"][null="-9999999"]/>

    <parameter .../>

    <parameter .../>

</parameterMap>

 

 

[괄호]내의 부분은 옵션이다. parameterMap는 스스로는 statement가 그것을 참조할 때 사용하는 구분자로써 단지 id속성만 필요하다. Class속성은 옵션이지만 크게 사용이 추천되는 것이다. Statement의 parameterClass속성과 유사하게 class속성은 프레임워크가 성능을 위해 엔진을 최적화 하는 것만큼 들어오는 파라미터를 체크하도록 한다.

 

- <parameter>요소

 

parameterMap은 statement의 파라미터에 직접 맵핑하는 파라미터 맵핑의 어떤 숫자를 포함한다. 다음의 일부 섹션은 property 요소의 속성을 서술한다.

 

1. property

파라미터 map의 property속성은 맵핑된 statement에 전달되는 파라미터 객체의 자바빈즈 프로퍼티(get메소드)의 이름이다. 그 이름은 statement내에 필요한 횟수에 의존하는 것보다 좀더 사용될수 있다.

 

 

2. jdbcType

jdbcType속성은 이 프로퍼티에 의해 셋팅되는 파라미터의 컬럼타입을 명시적으로 정의하는데 사용된다. 몇몇 JDBC드라이버는 명시적은 드라이버 컬럼타입을 부르는 거시 없이 어떤 작동을 위해 컬럼의 타입을 확인할 수 없다. 이것의 완벽한 예제는 PreparedStatement.setNull(int parameterIndex, int sqlType) 메소드이다. 이 메소드는 정의하기 위한 타입을 요구한다. 몇몇 드라이버는 간단하게 Types.OTHER또는 Types.NULL을 보냄으로써 함축되는 타입을 허락한다. 어쨌든 행위는 비일관적이고 몇몇 드라이버는 정의되기 위한 정확한 타입을 필요로 한다. 어찌됐든 행위는 비일관적이고 몇몇 드라이버는 정의되기 위한 정확한 타입을 필요로 한다. 그런경우를 위해서 SQL Maps API는 parameterMap프로퍼티 요소의 jdbcType속성을 사용하여 정의되기 위한 타입을 허락한다.

 

이 속성은 컬럼이 null이 가능할 때(nullable)만 요구된다. Type속성을 사용하는 다른 이유는 명시적으로 date타입을 정의하는 것이다.자바는 단지 하나의 Date값타입(java.util.Date)을 가지는데 반해 대개의 SQL데이터베이스는 많은, 대개 최소3가지이상의 타입을 가진다. 당신의 컬럼타입이 DATE나 DATETIME중에 하나로 명시적으로 정의하길 바랄지도 모르기 때문이다.

 

jdbcType 속성은 JDBC타입 클래스내 변수와 매치되는 어떤 문자열값에 셋팅될수 있다. 비록 이것은 그것들중에 어떤것에 셋팅될수 있지만 몇몇 타입은 지원되지 않는다(이를테면 blobs). 프레임웍에 의해 지원되는 타입은 차후에 기술한다.

 

주의! : 대부분의 드라이버는 단지 null이 가능한 컬럼을 위해 정의되는 타입을 필요로 한다. 그러므로 그런 드라이버를 위해 당신은 null이 가능한 컬럼을 위해 타입을 정의할 필요가 있다.

 

주의! : 오라클 드라이버를 사용할 때 당신은 이것의 타입을 정의하지 않고서는 컬럼에 null값을 넣을 때 "Invalid column type"에러를 보게될 것이다.

 

 

3. javaType

javaTYpe속성은 셋팅되기 위한 파라미터의 자바 프로퍼티를 명시적으로 정의하기 위해 사용된다. 대게 이것은 리플렉션(reflection)을 통해 자바빈즈 프라퍼티로부터 파생된다. 하지만 Map과 XML맵핑 같은 특정 맵핑은 프레임웍을 위한 타입을 제공하지 않는다. 만약 javaType가 셋팅되지 않고 프레임웍도 어떤 타입인지 구별할 수 없다면 타입은 객체로 간주될 것이다.

 

 

4. nullValue

nullValue속성은 어떤 유효한 값(프로퍼티 타입에 기초로 해서)에 셋팅할 수 있다. null속성은 null값 대체를 정의하기 위해 사용된다. 이것이 의미하는 것은 자바빈즈 프로퍼티내에서 검색되는 값인 NULL이 데이터베이스에 쓰여질 것이라는 것이다.(들어오는 null값 대체의 상반된 행위). 이것은 당신에게 null값을 지원하지 않는 타입(이를 테면 int, double, float등등)을 위해 당신의 애플리케이션내에 "magic" null숫자를 사용하도록 허락한다. 프로퍼티의 그런타입은 적합한 null값을 포함할때 NULL은 값대신에 데이터베이스에 쓰여질 것이다.

 

 

ex)<parameterMap> - 모든 구조를 사용하는 parameterMap의 예제가 다음과 같다.

 

<parameterMap id = "insert-product-param" class = "com.domain.Product">

    <parameter property = "id" jdbcType = "NUMERIC" javaType = "int" nullValue = "-9999999"/>

    <parameter property = "description" jdbcType = "VARCHAR" nullValue = "NO_ENTRY"/>

</parameterMap>

 

<statement id = "insertProduct" parameterMap = "insert-product-param">

    insert into PROUCT(PRD_ID, PRD_DESCRIPTION) values(?, ?);

</statement>

 

 

위 예제에서 자바빈즈 프로퍼티인 id와 description은 목록화되는 순서대로 맵핑된 Statement인 insertProduct의 파라미터에 적용될 것이다. 그래서 id는 첫번째 파라미터(?)에 적용되고 description는 두번째 파라미터에 적용된다. 만약에 순서가 반대라면 XML은 다음처럼 보일 것이다.

 

<parameterMap id = "insert-product-param" class = "com.domain.Product">

    <parameter property = "description" jdbcType = "VARCHAR" nullValue = "NO_ENTRY"/>

    <parameter property = "id" jdbcType = "NUMERIC" javaType = "int" nullValue = "-9999999"/>

</parameterMap>

 

<statement id = "insertProduct" parameterMap = "insert-product-param">

    insert into PROUCT(PRD_DESCRIPTION, PRD_ID) values(?, ?);

</statement>

 

 

주의! : parameter Map 이름은 정의된 SQL Map XML파일에 위치한다. 당신은 SQL Map(<sqlMap> root태그에 셋팅된)의 id와 함께 파라미터 Map의 id를 앞에 붙임으로써 다른 SQL Map XML파일내에 파라미터 Map을 참조할 수 있다. 예를 들면 다른 파일로부터 위의 파라미터 map를 참조하기 위해 참조하기 위한 전체이름은 "Product.insert-product-param"이 될것이다.

  

* 인라인 파라미터 Maps

 

매우 상세한 설명에도 불구하고 parameterMaps을 선언하기 위해 위의 문법은 매우 장황하다. 파라미터 Maps을 위한 정의(definition)을 간단하게 하고 코드를 줄일 수 있는 좀더 다양한 문법이 있다. 그 대안인 문법은 자바빈즈 프로퍼티이름을 맵핑된 statement에 인라인 시키는 것이다. 초기 설정에 의해 명시적으로 정의된 parameterMap이 없는 어떤 맵핑된 statement는 인라인 파라미터를 위해 파싱될것이다. 이전의 인라인 파라미터를 구현한 예제(이를 테면 Product)는 다음처럼 보일것이다.

 

<statement id = "insertProduct" parameterClass = "com.domain.Product">

    insert into PRODUCT(PRD_ID, PRD_DESCRIPTION)

        values(#id#, #description#);
</statement>

 

 

타입을 선언하는 것은 다음의 문법을 사용함으로써 인라인 파라미터로 할 수 있다.

 

<statement id = "insertProduct" parameterClass = "com.domain.Product">

    insert into PRODUCT(PRD_ID, PRD_DESCRIPTION)

        values(#id:NUMERIC#, #description:VARCHAR#);

</statement>

 

 

타입을 선언하는 것과 null값을 대체하는 문법은 다음을 사용함으로써 인라인 파라미터로 할 수 있다.

 

<statement id = "insertProduct" parameterClass = "com.domain.Product">

    insert into PRODUCT(PRD_ID, PRD_DESCRIPTION)

        values(#id:NUMERIC:-9999999#, #description:VARCHAR:NO_ENTRY#);

</statement>

 

 

주의! : 인라인 파라미터를 사용할 때 당신은 타입정의없이 null값 대체를 명시할 수 없다. 당신은 순서대로 파싱하기 위해 둘다 명시해야 한다.

 

주의! : Null값의 완전한 투명성을 원한다면 당신은 이 문서의 나중에 설명되는 것처럼 당신의 result maps내에 null값 대체를 반드시 명시해야 한다.

 

주의! : 많은 수의 타입 서술자와  null값 대체가 필요하다면 외부파일을 이용해 코드를 정리해서 사용할 수 있다.

  

* 원시타입 파라미터

 

파라미터처럼 사용하기 위해 자바빈을 쓰는 것은 언제나 필요하고 편리한 것은 아니다. 이런 경우에 당신은 직접적으로 파라미터를 사용하는 것처럼 원시타입 래퍼객체(String, Integer, Date등등)를 사용하는 것이 편리할 것이다.

 

예를 들면 :

 

<statement id = "insertProduct" parameter = "java.lang.Integer">

    select * from PRODUCT where PRD_ID = #value#

</statement>

 

 

PRD_ID가 숫자 타입이라고 가정하면, 호출이 되었을 때 java.lang.Integer객체를 전달할수 있는 맵핑된 statement를 만들것이다. #value# 파라미터는 Integer인스턴스의 값으로 대체될것이다. "value"라는 이름은 간단한 문법(이를 테면 괄호)안의 요소이고 별명이 될수 있다. Reustl Map은 result처럼 원시타입을 잘 지원한다. 파라미터로 원시타입을 사용하는 방법에 대해서 좀더 다양한 정보를 위해서는 Result Map섹션과 프로그래밍 SQL Maps(API)를 보라.

 

원시 타입은 좀더 간결한 코드를 위해서 별칭된다. 예를 들면 "int"는 "java.lang.Integer"대신에 사용될수 있다. 별칭은 아래의 "파라미터 Map과 result Map을 위해 지원되는 타입" 이라는 제목의 테이블에서 이야기 되었다.

  

* Map 타입 파라미터

 

당신이 자바빈즈 클래스를 쓰는 것이 필요하지 않거나 편리하지 않은 위치에 있고 하나의 원시타입 파라미터를 쓰지는 않는다면 파라미터객체로 Map(이를 테면 HashMap, TreeMap)을 사용할 수 있다.

 

예를 들면 :

 

<statement id = "insertProduct" prarmeterClass = "java.util.Map">

    select * from PRODUCT

        where PRD_CAT_ID = #catId#

            and PRD_CODE = #code#

</statement>

 

 

맵핑된 statement구현내에서는 차이점이 없다는 것을 알수 있다. 위의 예제에서 만약 Map인스턴스가 statement를 위한 호출로 전달되었다면 Map은 "catId" 와 "code"라는 이름의 키를 포함해야만 한다. 이 값은 Integer와 String과 같은 선호되는 타입이 되는 그런 키에 의해 참조된다. Result Map은 result처럼 Map타입을 아주 잘 지원한다. 파라미터처럼 Map타입을 사용하는것에 대한 좀더 상세한 정보를 위해서는 result Map섹션과 프로그래밍 SQL Map(API)를 보라.

 

Map 타입 역시 좀더 간결한 코드를 위해서 별칭된다. 예를 들면 "map"은 "java.util.Map"을 대신할 수 있다. 별칭은 아래의 "파리미터 Map과 resultMap을 위해 지원되는 타입" 이라는 제목의 테이블에서 이야기된다.

  

* Result Maps

 

Result Maps는 SQL Maps의 가장 중요한 컴포넌트이다. resultMap은 자바빈즈 프로퍼티를 맵핑된 커리 statement를 실행함으로써 생산된 ResultSet의 컬럼에 맵핑시키는 책임ㅇ르 진다. resultMap의 구조는 다음과 같이 보인다.

 

<resultMap id = "resultMapName" class = "some.domain.Class" [extends = "parent-resultMap"]>

    <result property = "propertyName" column = "COLUMN_NAME"

                [columnIndex = "1"][javaType = "int"][jdbcType = "NUMERIC"]

                [nullValue = "-9999999"][select = "someOtherStatement"]

    />

    <result .../>

    <result .../>

    <result .../>

</resultMap>

 

 

[괄호] 부분은 옵션이다. resultMap은 스스로 statement가 그것을 참조하기 위해 사용할 id속성을 가진다. resultMap는 클래스나 타입별칭의 전체경로를 포함한 이름인 class속성을 가진다. 이 클래스는 이것을 포함하는 result맵핑에 기반하여 초기화되고 생성될 것이다. Extends속성은 resultMap에 기초한 다른 resultMap의 이름을 옵션적으로 셋팅할 수 있다. 이것은 상위 resultMap의 모든 프로퍼티가 하위 resultMap의 부분을 포함하는 것처럼 자바내에서 클래스를 확장하는 것과 유사하다. 상위 resultMap의 프로퍼티는 하위 resultMap프로퍼티와 부모 resultMap가 자식 앞에서 정의되기 전에 언제나 추가된다. 상위/하위 resultMap을 위한 클래스는 같은 것을 필요로 하지 않을뿐 아니라 모든 것이 관련될 필요도 없다.

 

resultMap은 자바빈즈를 ResultSet의 컬럼에 맵핑시키는 어느정도의 프로퍼티 맵핑을 포함할수 있다. 그런 프로퍼티 맵핑은 문서내에서 정의하기 위해 적용될것이다. 관련 클래스는 각각의 프로퍼티, Map또는 XML을 위한 get/set메소드를 가진 자바 빈즈와 호환되는 클래스여야만 한다.

 

주의! : 컬럼은 Result Map내에서 정의되기 위해서 명시적으로 읽을 것이다.

 

- property

result map의 property속성은 맵핑 statement에 의해 반환되는 result객체의 자바빈즈 프로퍼티(get 메소드)이름이다. 이름은 results를 생성할 때 필요한 횟수에 의존적인 값보다 더 크게 사용될 수 있다.

 

- column

column속성값은 프로퍼티를 생성하기 위해 사용될 값들로부터 ResultSet내의 컬럼의 이름이다.

 

- columnIndex

옵션적인(최소한의) 성능향상을 위해서 columnIndex 속성값은 자바빈즈 프로퍼티를 생성하기 위해 사용될 값으로부터의 ResultSet내의 컬럼의 인덱스 이다. 이것은 애플리케이션의 99%정도엔 필요하지 않을것이고 유지를 위한 노력과 속도를 위해 가독성을 의생한다. 몇몇 JDBC드라이버는 다른것들이 동적으로 속도를 올려주는 동안 어떤 성능의 이득도 구체화하지 않는다.

 

- jdbcType

jdbcType 속성은 자바빈즈 프로퍼티를 생성하는데 사용되는 ResultSet 컬럼의 데이터베이스 컬럼타입을 명시적으로 정의하는데 사용된다. 비록 result maps이 null값과 함께 같은 어려움을 가지지 않는다고 하더라도 Date프로퍼티처럼 어떤 맵핑타입을 위해 유용할수 있는 타입을 정의한다. 자바는 오직 하나의 Date값 타입을 가지고 SQL데이터베이스는 여러가지를 가지기 때문에 dates(또는 다른 타입)타입을 정확하게 셋팅하는 것을 확신하는 몇몇경우에 필요하게 될것이다. 유사하게도 String타입은 VARCHAR, CHAR또는 CLOB에 의해 생성될것이다. 그래서 그런 경우에 필요한 타입을 정의한다.

 

- javaType

javaType 속성은 셋팅되는 프로퍼티의 자바 프로퍼티타입을 명시적으로 정의하기 위해 사용된다. 대개 이것은 리플렉션을 통해 자바빈즈 프로퍼티로부터 끌어낼 수 있다. 하지만 Map과 XML맵핑과 같은 맵핑은 프레임워크를 위한 타입을 제공할 수 없다. 만약 javaType가 셋팅되지 않고 프레임워크가 그 타입을 구분할 수 없다면 타입은 객체로 가정되어 처리될 것이다.

 

- nullValue

nullValue속성은 데이터베이스내에서 NULL값을 대신해서 사용되기 위한 값을 정의한다. 그래서 만약 ResultSet으로부터 NULL이 읽어졌다면 자바빈 프로퍼티는 NULL대신에 nullValue속성에 의해 정의된 값을 셋팅할 것이다. null속성값은 어떠한 값이라도 될수 있지만 프로퍼티타입을 위해서는 적절해야만 한다.

 

만약 당신의 데이터베이스가 NULL이 가능한 컬럼을 가진다면 당신은 당신의 애플리케이션이 다음처럼 result map내에서 그것을 정의할 수 있는 변수값과 함께 NULL을 표시하기를 원한다.

 

<resultMap id = "get-product-result" class = "com.ibatis.example.Product">

    <result property = "id" column = "PRD_ID"/>

    <result property = "description" column = "PRD_DESCRIPTION"/>

    <result property = "subCode" cloumn = "PRD_SUB_CODE" nullValue = "-999"/>

</resultMap>

 

 

위 예제에서 만약 PRD_SUB_CODE이 NULL로 읽혀진다면 subCode 프로퍼티는 -999라는 값으로 세팅될것이다. 이것은 당신에게 데이터베이스내에서 NULL이 가능한 컬럼을 표현하기 위해 당신의 자바클래스내에 원시타입을 사용하도록 허락할 것이다. 만약 당신이 updates/inserts 같은 쿼리를 위한 작업을 수행하기를 원할 때 당신은 파라미터 map내에 nullValue를 정의해야만 한다는 것을 기억해야한다.

  

* select

 

select 속성은 객체사이의 관계를 서술하고 자동적으로 복합 프로퍼티 타입을 로드하는데 사용된다. statement프로퍼티값은 다른 맵핑된 statement의 이름이 되어야만 한다. 데이터베이스 컬럼값은 statement속성이 파라미터처럼 관계된 맵핑 statement로 전달하는 것은 같은 property요소내에 정의된다. 그러므로 컬럼은 원시타입으로 지원이 되어야 한다. 지원되는 원시타입과 복합 프로퍼티 맵핑/관계에 대해서는 나중에 이야기 된다.

 

 

* 내포하는 Result Maps

 

만약 당신이 명시적으로 정의된 resultMap의 재사용을 요구하지 않는다는 매우 간단한 요구사항을 가진다면 맵핑된 statement의 resultClass속성을 셋팅함으로써 result map을 함축적으로 정의하는 빠른 방법이 있다. 이 묘기는 당신이 반환되는 result set이 당신의 자바빈의 쓰기 가능한 프로퍼티 이름에 매치되는 컬럼이름(또는 라벨/별칭)을 가지는 것을 확실히 해야만 한다는 것이다. 예를 들면 만약 우리가 위에서 서술된 Product 클래스를 생각할 때 우리는 다음처럼 내포하는 result map으로 맵핑된 statement를 생성할 수 있다.

 

<statement id = "getProduct" resultClass = "com.ibatis.example.Product">

    select

        PRD_ID as id,

        PRD_DESCRIPTION as description

    from PRODUCT

        where PRD_ID = #value#

</statement>

 

 

위의 맵핑된 statement는 resultClass를 표기하고 Product클래스의 자바빈즈 프로퍼티에 매치되는 각각의 컬럼을 위한 별칭을 명시한다. 이것은 모두 필수(required)이다. Result map은 필요하지 않다. 여기서 교환(tradeoff)은 당신이 컬럼타입(대개 필수가 아닌)과 null값(또는 다른 어떤 프로퍼티 속성)을 정의하는 기회를 가지지 않는것이다. 많은 데이터베이스가 대소문자를 가리지 않기 때문에 내포된 result map 또한 가리지 않는다. 만약 당신의 자바빈이 두개의 프로퍼티를 가진다면 하나의 이름은 firstName이고 다른 것은 firstname이다.(두개의 값은 대소문자의 차이이다.) 그것들은 동일하고 당신은 내포된 result map을 사용할 수 없을 것이다.(이것은 자바빈 클래스의 디자인에서 잠재적인 문제점을 파악하게 될 것이다.) 게다가 resultClass를 통해 자동맵핑을 하면 몇몇 성능에 관련된 부하가 발생할 것이다. ResultSetMetaData에 접근하는 것은 몇몇 쓰여진 JDBC드라이버는 느리게 만들수 있다.

 

 

 

* 원시타입의 Results(이를 테면 String, Integer, Boolean)

 

자바빈 호환 클래스를 지원하기 위해 추가적으로 result Map은 String, Integer, Boolean 등등과 같은 간단한 자바타입 래퍼를 편리하게 생성할 수 있다. 원시타입객체의 collection은 밑에서 이야기 되는 API(executeQueryForList()를 보라)들을 사용해서 가져올 수 있다. 원시타입은 자바빈처럼 같은 방법으로 정확하게 맵핑된다. 원시타입은 당신이 선호하는 (대개 "value" 또는 "va") 이름형식의 어떤것처럼 될수 있는 하나의 프로퍼티만을 가질수 있다. 예를 들면 우리가 전체 Product클래스 대신에 모든 product서술자(description)의 목록만을 로드하길 원한다면 map은 다음처럼 보여질 것이다.

 

<resultMap id = "get-product-result" class = "java.lang.String">

    <result property = "value" column = "PRD_DESCRIPTION"/>

</resultMap>

 

 

좀더 간단한 접근법은 맵핑된 statement안에서 간단하게 result class를 사용하는 것이다.("as" 키워드를 사용해서 "values"라는 컬럼별칭을 사용하는 것을 주의깊게 보라)

 

<statement id - "getProductCount" resultClass = "java.lang.Integer">

    select count(1) as value

        from PRODUCT

</statement>

  

* Map Results

 

Result Maps는 HashMap또는  TreeMap 처럼 Map인스턴스를 편리하게 생성할 수 있다. 그런 객체(Map의 List)의 collection은 아래에서 이야기되는 API(executeQueryForList()를 보라)들을 사용해서 가져올 수 있다. Map타입은 자바빈과 같은 방법으로 정확하게 맵핑된다. 하지만 자바빈 프로퍼티셋팅 대신에 Map의 key들은 대응되는 맵핑컬럼을 위한 값을 참조하도록 셋팅한다.

 

예를 들면 만약 우리가 product의 값을 Map으로 빨리 로드시키길 원한다면 우리는 다음과 같이 할 것이다.

 

<resultMap id = "get-product-result" class = "java.util.HashMap">

    <result property = "id" column = "PRD_ID"/>

    <result property = "code" column = "PRD_CODE"/>

    <result property = "description" cloumn = "PRD_DESCRIPTION"/>

    <result property = "suggestedPrice" column = "PRD_SUGGESTED_PRICE"/>

</resultMap>

 

 

위 예제에서 HashMap인스턴스는 Product데이터를 생성할 것이다. 프로퍼티 이름 속성(이를 테면 "id")은 HashMap의 키가 될 것이다. 맵핑컬럼의 값은 HashMap의 값이 될것이다. 물론 당신의 Map타입을 가지고 내포된 result map을 사용할 수도 있다.

 

예를 들면 :

 

<statement id = "getProductCount" resultClass = "java.util.HashMap">

    select * from PRODUCT

</statement>

 

 

위의 것은 반환된 ResultSet의 Map표현을 당신에게 줄것이다.

 

* 복합(Complex) Properties(이를 테면 사용자에 의해 정의된 클래스의 프로퍼티)

 

이것을 선호하는 데이터와 클래스를 로드하는 방법을 알고 있는 맵핑된 statement와 함께 관련된 resultMap프로퍼티에 의해 복합타입의 프로퍼티(사용자에 의해 생성된 클래스)를 자동적으로 생성하는 것은 가능하다. 데이터베이스내 데이터는 언제나 복합프로퍼티는 관계의 "many side"로부터 이고 프로퍼티 자신은 관계의 "one side"로부터이다라는 것을 고정하는 클래스에서 1:1관계 또는 1:M관계를 통해 표현된다.

 

예를 들면 :

 

<resultMap id = "get-product-result" class = "com.ibatis.example.Product">

    <result property = "id" column = "PRD_ID"/>

    <result property = "description" column = "PRD_DESCRIPTION"/>

    <result property = "category" column = "PRD_CAT_ID" select = "getCategory/>

</resultMap>

 

<resultMap id = "get-category-result" class = "com.ibatis.example.Category">

    <result property = "id" column = "CAT_ID"/>

    <result property = "description" column = "CAT_DESCRIPTION"/>

</resultMap>

 

<statement id = "getProduct" parameterClass = "int" resultMap = "get-product-result">

    select * from PRODUCT where PRD_ID = #value#

</statement>

 

<statement id = "getCategory" parameterClass = "int" resultMap = "get-category-sult">

    select * from CATEGORY where CAT_ID = #value#

</statement>

 

 

위 예제에서 Product의 인스턴스는 Category타입의 category를 호출하는 프로퍼티를 가진다. Category는 복합사용자 타입이기 때문에 JDBC는 그것을 생성하는 방법을 가지지 않는다. 프로퍼티 맵핑과 함께 다른 맵핑된 statement를 관련시킴으로써 우리는 그것을 생성하기 위한 SQL Map엔진을 위해 충분한 정보를 제공한다. getProduct를 수행하면 get-product-result result map이 PRD_CAT_ID컬럼내 반환되는 값을 사용해서 getCategory를 호출할 것이다. get-category-result result map은 Category를 초기화 할 것이고 그것을 생성한다. 전체 Category인스턴스는 Product의 category프로퍼티로 셋팅한다.

  

 

* N + 1 Select(1:1) 피하기

 

위 솔루션을 사용할 때 문제점은 당신이 Product 를 로드할 때마다 두개(Product를 위해 하나 그리고 Category를 위해서 하나 총2개)의 SQL문이 실제적으로 구동된다는 것이다. 이 문제는 하나의 Product를 로드할 때는 큰 문제가 아닌것처럼 보이지만 만약 10개의 Product를 로드하는 쿼리를 한다면 각각의 쿼리는 관련된 category를 로드하기 위한 Product를 위해서도 실행될것이다. 결과적으로 11번의 쿼리를 하게된다. Product의 목록을 위해 하나, 관련된 Category를 로드하기 위해 반환되는 Product를 위해 하나씩(N + 1 또는 이 경우엔 10 + 1 = 11)쿼리를 하게된다.

 

해결법은 분리된 select문 대신에 조인과 내포된(nested)프로퍼티 맵핑을 사용하는 것이다. 여기에 그와 같은 상황을 사용한 예제가 있다.

 

<resultMap id = "get-product-result" class = "com.ibatis.example.Product">

    <result property = "id" column = "PRD_ID"/>

    <result property = "description" column = "PRD_DESCRIPTION"/>

    <result property = "category.id" column = "CAT_ID"/>

    <result property = "category.description" column = "CAT_DESCRIPTION"/>

</resultMap>

 

<statement id = "getProduct" parameterClass = "int" resultMap = "get-product-result">

    select * from PRODUCT, CATEGORY where PRD_CAT_ID = CAT_ID and PROD_ID = #value#

</statement>

 

  

*  늦은(Lazy) 로딩 대 조인 (1:1)

 

조인을 사용하는 것이 언제나 더 좋은 결과를 내지는 않는다는 것에 주의하는 것은 매우 중요하다. 만약 당신이 관계 객체에 접근하는 것이 거의 없는 상황이라면 조인을 피하는 것이 더 빠르고 모든 category프로퍼티의 로딩이 불필요하다. 이것은 outer조인을 포함하는 데이터베이스 디자인이나 null값이 가능하거나 인덱스가 없는 컬럼에는 사실이다. 이런 상황에서 늦은 로딩과 bytecode향상 옵션으로 sub-select솔루션을 사용하는 것은 좀더 향상된 결과를 보여준다. 일반적인 규칙은 연관된 프로퍼티에 접근하는 것을 좀더 하고자 할때만 조인을 사용하라. 반면에 늦은 로딩이 옵션이 아닐때에만 그것을 사용하라.

 

만약 당신이 사용할 방법을 결정하는데 문제가 있다면 걱정하지 마라. 그것은 문제도 아니다. 당신은 자바코드 충돌없이 이것을 항상 변경할 수 있다. 위의 두 예제는 같은 객체형태의 결과를 보이고 정확하게 같은 메소드 호출을 사용해서 로드된다. 만약 당신이 캐쉬를 가능하게 하면 단지 하나의 고려사항은 separate select(조인이 아닌) 솔루션을 사용하는 것이 반환되는 캐쉬된 인스턴스내에 결과를 보이게 된다.

  

* 복합 Collection 프로퍼티

 

복합 객체의 목록을 표현하는 프로퍼티를 로드하는 것은 가능하다. 데이터베이스내의 데이터는 M:M 관계나 1:M 관계에 의해 표현될 것이다. 객체 목록을 로드하는 것은 statement에 어떤 변경사항도 주지 않는다. SQL Map프레임워크가 비즈니스 객체내에서 리스트처럼 프로퍼티를 로드하기위해 요구되는 단 하나의 차이점은 java.util.List 또는 java.util.Collection 타입이 되어야 한다는 것이다. 예를 들면 Category 가 Product인스턴스 목록을 가진다면 맵핑은 다음처럼 보일것이다.(Category가 java.util.List타입의 "productList"라고 불리는 프로퍼티를 가진다고 가정하자)

 

<resultMap id = "get-category-result" class = "com.ibatis.example.Category">

    <result property = "id" column = "CAT_ID"/>

    <result property = "description" column = "CAT_DESCRIPTION"/>

    <result property = "productList" column = "CAT_ID" select = "getProductsByCatId"/>

</resultMap>

 

<resultMap id = "get-product-result" class = "com.ibatis.example.Product">

    <result property = "id" column = "PRD_ID"/>

    <result property = "description" column = "PRD_DESCRIPTION"/>

</resultMap>

 

<statement id = "getCategory" parameterClass = "int" resultMap = "get-category-result">

    select * from CATEGORY where CAT_ID = #value#

</statement>

 

<statement id = "getProductByCatId" parameterClass = "int" resultMap = "get-product-result">

    select * from PRODUCT where PRD_CAT_ID = #value#

</statement>

  

* N + 1 Selects(1:M과 M:N) 피하기

 

이것은 위의 1:1 상황고 유사하다. 하지만 굉장히 많은 데이터를 포함할 때 좀더 큰 걱정거리가 될것이다. 위 해결법과 함께 문제는 당신이 Category를 로드할때마다 두개의 SQL문(하나는 Category를 위한 하나이고 하나는 Product에 대한 목록을 위한 것)은 실질적으로 수행된다. 이 문제는 하나의 Category를 로드할 때 평범한 것처럼 보이지만 10개의 Category를 로드하는 쿼리문을 실행할 때는 각각의 쿼리가 Product의 목록을 로드하기 위한 각각의 Category를 위해서 수행될 것이다. 결과적으로 11개의 쿼리가 수행된다. 하나는 Category목록을 위한 것이고 각각의 Product관련 목록을 반환하는 각각의 Category를 위한 것이다. (N + 1 또는 이경우엔 10 + 1 = 11) 이 상황을 더욱 나쁘게 만들려면 우리는 굉장히 많은 데이터를 다루면 된다.

 

1:N과 M:N해별법? : 이 이슈를 해결하는 기능은 구현되지 않았다. 이것은 조만간 새로운 릴리즈에 포함될것이다.

(역자주 : 이 문제는 SQL Maps 2.0.9에서 해결이 된걸로 알고 있다.)

  

* 늦은(Lazy) 로딩 대 조인(1:M and M:N)

 

먼저 이야기된 1:1상황처럼 조인을 사용하는 것이 언제나 더 좋다는 것이 아니라는 것을 아는 것은 중요하다. 이것은 대량의 데이터로 인하여 개별적인 값 프로퍼티를 위한 것보다 collection 프로퍼티에서 좀더 사실적이다. 만약 당신이 관련된 객체에 접근하는 것이 드문 상황(이를 테면 Category 클래스의 ProductList 프로퍼티)이라면 이것은 조인과 product목록의 필요없는 로딩을 피한다면 정말 빠르게 될것이다. 이것은 outer조인과 null이 가능하고 아니면 또는 인덱스가 없는 컬럼을 포함한 데이터베이스 디자인에는 특별히 사실이다. 이런 상황에서 늦은(lazy)로딩과 bytecode향상옵션으로 sub-select솔루션을 사용하여 성능을 좀더 향상시켜준다. 일반적인 규칙은 연관된 프로퍼티에 접근하는 것을 좀더 하고자 할때만 조인을 사용하라. 반면에 늦은 로딩이 옵션이 아닐때에만 그것을 사용하라.

 

먼저 언급했던 것처럼 만약 당신이 어떤 방법을 사용해야 하는지 결정하는데 문제가 있다면 걱정하지 마라. 어떤 방법을 사용할지에 대해서 걱정하는 것은 필요없는 일이다. 당신은 당신의 자바코드에 충돌없이 그것을 변화시킬수 있다. 위의 두 예제는 같은 객체형태의 결과를 보이고 정확하게 같은 메소드 호출을 사용해서 로드된다. 만약 당신이 캐쉬를 가능하게 하면 단지 하나의 고려사항 separate select(조인이 아니) 솔루션을 사용하는 것이 반환되는 캐쉬된 인스턴스내에 결과를 보이게 된다.

  

* 복합 키또는 다중 복합 파라미터 프로퍼티

 

당신은 위 예제에서 column 속성에 의해 resultMap내에 정의된 것처럼 사용되어지는 것은 하나의 키라는 것이 언급되었다. 이것은 단지 하나의 키만이 관계된 맵핑 statement에 관련 될 수 있다는 것을 제안했다. 어쨌든 관계된 맵핑 statement에 전달할 다중 컬럼을 허락하는 대안적인 문법이 있다. 이것은 복합키 관계가 존재하는 상황이나 당신이 간단하게 #value#와 다른 이름의 파라미터를 사용하고자 할 때 편리하다. Column 속성이 간단{param=1=column1, param2=column2,..., paramN=columnN} 할 때 대안적인 문법이다. PAYMENT테이블이 Cstomer ID와 Order ID를 둘다 키로 할때 다음의 예제를 보고 생각해 보자.

 

<resultMap id = "get-order-result" class = "com.ibatis.example.Order">

    <result property = "id" column = "ORD_ID"/>

    <result property = "customerId" column = "ORD_CAT_ID"/>

    ...

    <result property = "payments" column = "{itemId=ORD_ID, custId=ORD_CST_ID}" select = "getOrderPayments"/>

</resultMap>

 

<statement id = "getOrderPayments" resultMap = "get-order-result">

    select * from PAYMENT

        where PAY_ORD_ID = #itemId#

            and PAY_CST_ID = #custId#

</statement>

 

 

옵션적으로 당신은 그것들이 파라미터처럼 같은 순서로 정렬되는 것처럼 컬럼이름을 정의할 수 있다.

 

{ORD_ID, ORD_CST_ID}

 

언제나처럼 이것은 읽기와 유지라는 것의 영향과 함께 미세한 성능획득이 있다.

 

중요! : 현재의 SQL Map프레임웍은 순환하는 관계를 자동으로 해석하지 않는다. 부모/자식 관계(트리)를 구현할 때 이것을 알고 있어라. 쉬운 대안은 간단하게 부모객체를 로드하지 않는 경우를 위한 하나 또는 "N+1 avoidance" 해결법에서 서술된 조인을 사용하는 경우를 위한 두번째 result map을 정의하는 것이다.

 

주의! : 몇몇 JDBC드라이버(이를 테면 내장된 PointBase)는 동시에 다중 ResultSet(connection 마다)을 지원하지 않는다. 그런 드라이버는 SQL Map엔진이 다중 ResultSet connection을 요구하지 않기 때문에 복잡한 객체 맵핑과는 작동하지 않을 것이다. 다시 말해 조인을 사용하는거 대신에 이것을 해석할 수 있다.

 

주의! : Result Map 이름은 언제나 그것들을 정의된 SQL Map XML 파일에 위치한다. 당신은 SQL Map의 이름을 Result map의 이름앞에 위치시킴으로써 다른 SQL Map XML파일내의 Result Map을 참조할 수 있다.

 

만약 당신이 JDBC를 위해 MS의 SQL Server2000드라이버를 사용한다면 당신은 수동 트랜잭션 모드인 동안 다중 statement를 수행하기 위해 connection url에 SelectMethod = Cursor를 추가할 필요가 있을지도 모른다.