데이터매퍼 (a.k.a SQL Maps) Version 2.0 개발자가이드 2006 년 11 월 30일 번역 : 이동국 (fromm0@gmail.com) 오타및오역은위메일주소로보내주시기바랍니다. 1
목차 소개데이터매퍼설치 1.x 에서업그레이드하기 SQL Map XML 설정파일 <properties> 요소 <settings> 요소 <resultobjectfactory> 요소 <typealias> 요소 <transactionmanager> 요소 <datasource> 요소 <sqlmap> 요소 SQL Map XML 파일매핑구문구문타입 SQL SQL 재사용하기자동생성키저장프로시저파라미터맵과인라인파라미터인라인파라미터맵원시타입파라미터 Map 타입파라미터대체문자열결과맵내포하는결과맵원시타입의결과복합프라퍼티 N+1 조회 (1:1) 피하기복합 Collection 프라퍼티 N+1 조회 (1:M and M:N) 피하기복합키또는다중복합파라미터프라퍼티파라미터맵과결과맵을지원하는타입사용자정의타입핸들러생성하기캐싱된매핑구문결과읽기전용대읽기 / 쓰기직렬화가능한읽기 / 쓰기캐시캐시타입동적인매핑구문 Dynamic 요소이항연산요소단항연산요소다른요소간단한동적 SQL요소데이터매퍼로프로그래밍하기 : The API 설정트랜잭션다중쓰레드프로그래밍 ibatis 클래스로딩일괄처리 SqlMapClient API 를통해구문실행하기 SqlMap 로깅하기한페이지의자바빈즈과정 Resources (com.ibatis.common.resources.*) Resources 국제화 SimpleDataSource (com.ibatis.common.jdbc.*) 2
소개 ibatis 데이터매퍼프레임워크는당신이관계형데이터베이스에접근할때필요한자바코드를현저하게줄일수있도록도와줄것이다. ibatis는간단한 XML서술자를사용해서간단하게자바빈즈를 SQL 구문에매핑시킨다. 간단함 (Simplicity) 이란다른프레임워크와객체관계매핑툴에비해 ibatis의가장큰장점이다. ibatis 데이터매퍼를사용하기위해서당신은자바빈즈와 XML 그리고 SQL에친숙할필요가있다. 여기에서는추가적으로배워야할것이거의없고테이블을조인하거나복잡한쿼리문을수행하기위해필요한복잡한스키마도없다. 데이터매퍼를사용하면당신은실제 SQL문의모든기능을그대로사용할수있다. 데이터매퍼 (com.ibatis.sqlmap.*) 개념 ibatis 데이터매퍼 API 는프로그래머에게자바빈즈객체를 PreparedStatement 파라미터와 ResultSets 으로쉽게매핑할수있도록한다. 데이터매퍼의기본적인생각은간단함 (simple) 이다. 이는자바코드의 20% 를사용하여 JDBC 기능의 80% 를제공하는간단한프레임워크라는뜻이다. 이것은어떻게작동하는가.? 데이터매퍼는자바빈즈, Map 구현체, 원시래퍼타입 (String, Integer ) 그리고 SQL 문을위한 XML 문서를매핑하기위한 XML 서술자를사용하는매우간단한프레임워크를제공한다. 다음은생명주기에대한높은레벨의서술이다. 1) 파라미터 ( 자바빈즈, Map 또는원시래퍼 ) 로써객체를제공한다. 파라미터객체는 update 문에서입력값을셋팅하기위해사용되거나쿼리문의 where 절을셋팅하기위해서사용된다. 2) 매핑된구문을실행한다. 이단계는마법이일어나는곳이다. 데이터매퍼프레임워크는 PreparedStatement 인스턴스를생성할것이고제공된파라미터객체를사용해서파라미터를셋팅한다. 그리고구문를실행하고 ResultSet 으로부터결과객체를생성한다. 3) update의경우에영향을미친 rows의숫자를반환한다. 조회작업인경우에한개 (single) 의객체또는컬렉션객체를반환한다. 파라미터처럼결과객체는자바빈즈, Map 원시타입래퍼또는 XML이될수있다. 밑의다이어그램은그림으로다시표현한것이다. 3
설치 ibatis 데이터매퍼프레임워크를설치는클래스패스에 JAR 파일을넣어주는간단한작업이다. 이클래스패스는 JVM시작시지정된클래스패스 (java -cp 인자로지정된 ) 나웹애플리케이션의 /WEB-INF/lib 디렉토리가될수도있다. 자바클래스패스에대한충분한설명은이문서의범위를넘어선다. 자바와 / 또는클래스패스가처음이라면, 다음의자료를참고하라. http://java.sun.com/j2se/1.4/docs/tooldocs/win32/classpath.html http://java.sun.com/j2se/1.4.2/docs/api/java/lang/classloader.html http://java.sun.com/j2se/1.4.2/docs/ ibatis 는하나의 JAR 파일만을가진다. 파일명은다음과같은형태를가진다. ibatis-version.build.jar 예를들면 ibatis-2.3.0.677.jar 과같은형태이다. 대개애플리케이션의클래스패스에이하나의 JAR 파일을두는것으로충분하다. JAR 파일과의존성 프레임워크가너무많은의존성을가진다면이것은애플리케이션이나다른프레임워크에통합되기힘들게만든다. 2.0 의중요한핵심사항은의존성관리와제거의중점을두었다. 그러므로만약당신이 jdk1.4 를사용한다면실제의존적인것은 Jakarta Commons Logging 프레임워크뿐이다. 이추가적인 JAR 파일은아래의웹사이트에서다운로드받을수있다. 그들은기능에의해분류된다. 다음은추가적인패키지를사용할때필요한것들의목록이다. 설명사용할시점 레거시 JDK 지원 만약에당신이 JDK1.4 보다하위버전을사용하고당신의애플리케이션서버가이런 JAR 파일을제공하지않는다면당신은이런옵션패키지가필요할것이다. 다운로드위치 JDBC 2.0 Extensions http://java.sun.com/products/jdbc/download.ht ml JTA 1.0.1a http://java.sun.com/products/jta/ Xerces 2.4.0 http://xml.apache.org/xerces2-j/ ibatis 의이전버전호환 런타임바이트코드향상 당신이 ibatis 의예전버전 DAO(1.x) 프레임워크를사용하고있거나 SQL Maps(1.x) 의예전버전을사용하고있다면이디렉토리의 JAR 파일을간단하포함시킴으로써계속작업을할수있다. 만약당신이늦은 (lazy) 로딩과성능에대해고려하기위한 CGLIB2.0 bytecode 개선을사용하길원한다면당신이 Jakarta DBCP Connection pool 을사용하길원한다면중앙집중적이거나분산캐싱지원을위한 OSCache 를사용하길원한다면 ibatis DAO 1.3.1 http://sourceforge.net/projects/ibatisdb/ CGLIB 2.0 http://cglib.sf.net DataSource 구현체 DBCP 1.1 http://jakarta.apache.org/commons/dbcp/ 분산캐싱 OSCache 2.0.1 http://www.opensymphony.com/oscache/ 로깅솔루션 Log4J 로깅을사용하길원한다면 Log4J 1.2.8 http://logging.apache.org/log4j/docs/ 로깅솔루션 Jakarta Commons Logging 를사용하고 자한다면 1.x 에서업그레이드하기 당신은업그레이드할것인가.? Jakarta Commons Logging http://jakarta.apache.org/commons/loggi ng 만약당신이업그레이드를시도한다면결정할수있는가장좋은방법이다. 여기에몇가지업그레이드절차가있다. 1. 버전 2.0 은 1.x 릴리즈와거의완벽한호완성을가지도록유지되었다. 그래서몇몇사람들에게는단순히 JAR 파일만교체하는것으로충분할것이다. 이것은최소한의이득을발생시키지만가장간단하다. 당신은당신의 XML 파일이나자바코드를변경할필요가없다. 몇몇모순되는것들이발견될지도모른다. 4
2. 두번째는당신의 XML파일을 2.0스펙에적합하도록변경하는것이다. 하지만이는 1.x 자바 API를그대로사용한다. 적은호환성이슈내안전한해결법은매핑파일사이에발생한다. Ant 작업은당신을위해 XML 파일을변환하기위해서프레임워크에포함된다. 3. 4. 세번째옵션은당신의 XML 파일과자바코드를변환하는것이다. 자바코드를변환하기위한툴은없다. 그래서이것은손으로직접해야한다. 마지막옵션은전체를업그레이드하지않는것이다. 만약에당신이어렵다고느낀다면 1.x 릴리즈에서시스템이작동하는것을두려워하지마라. 당신의오래된애플리케이션을그대로놔두는것은나쁜생각이아니다. 만약에오래된애플리케이션이인식적인면에서제대로리팩토링되어있다면당신은 SQL Maps 를업그레이드잘할수있을것이다. 1.x 에서 2.x 으로 XML 설정파일변환하기 2.0 프레임워크는 Ant 빌드시스템을통해수행되는 XML 문서변환기를포함한다. 당신의 XML 문서를변환하는것은 1.x코드가작동중에자동으로오래된 XML 파일을변환하는것처럼옵션적이다. 여전히당신이업그레이드를함으로써편안하게당신의파일을변환하는것이좋은생각이다. 당신은다소적은호환적인이슈를경험할것이고새로운기능중몇개의장점을얻을수있을것이다 ( 비록당신이 1.x 자바 API 을사용하더라도.). Ant 작업은당신의 build.xml 파일내에다음과비슷하게보일것이다. <taskdef name="convertsqlmaps" classname="com.ibatis.db.sqlmap.upgrade.converttask" classpathref="classpath"/> <target name="convert"> <convertsqlmaps todir="d:/targetdirectory/" overwrite="true"> <fileset dir="d/sourcedirectory/"> <include name="**/maps/*.xml"/> </fileset> </convertsqlmaps> </target> 당신이보는것처럼이것은 Ant 복사작업과거의같고사실이것은 Ant 복사작업을확장한것이다. 그래서당신은복사하는작업을하는어떤것도할수있다. JAR 파일들 : 예전것을빼내고새것을넣자. 업그레이드를할때존재하는 ( 예전의 ) ibatis 파일과의존적인것들을모두지우고새파일을대체하는것이좋은생각이다. 여전히필요한당신의다른컴포넌트또는프레임워크를모두지우지않도록주의해라. JAR 파일의대부분은당신환경에의존적이다. JAR 파일과의존적인것에대해서는위에서서술된것을보아라. 다음의테이블은예전파일과새파일을목록화한다. Old Files ibatis-db.jar 1.2.9b 버전에서부터이파일은다음의 3 개의파일로분리되었다. ibatis-common.jar ibatis-dao.jar ibatis-sqlmap.jar New Files Ibatis-version.build.jar ( 필수 ) 5
commons-logging.jar commons-logging-api.jar commons-collections.jar commons-dbcp.jar commons-pool.jar oscache.jar jta.jar jdbc2_0-stdext.jar xercesimpl.jar xmlparserapis.jar jdom.jar commons-logging-1-0-3.jar ( 필수 ) commons-collections-2-1.jar ( 옵션 ) commons-dbcp-1-1.jar ( 옵션 ) commons-pool-1-1.jar ( 옵션 ) oscache-2-0-1.jar ( 옵션 ) jta-1-0-1a.jar ( 옵션 ) jdbc2_0-stdext.jar ( 옵션 ) xercesimpl-2-4-0.jar ( 옵션 ) xmlparserapis-2-4-0.jar ( 옵션 ) xalan-2-5-2.jar ( 옵션 ) log4j-1.2.8.jar ( 옵션 ) cglib-full-2-0-rc2.jar ( 옵션 ) 이가이드의나머지는당신이 SQL Maps 를사용하는것에대해소개할것이다. SQL Map XML 설정파일 (http://ibatis.apache.org/dtd/sql-map-config-2.dtd) SQL Maps 는데이터소스, 데이터맵퍼에대한설정, 쓰레드관리와같은 SQL Maps 와다른옵션에대한설정을제공하는중앙집중적인 XML 설정파일을사용해서설정된다. 다음은 SQL Maps 설정파일의예이다. SqlMapConfig.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE sqlmapconfig PUBLIC "-//ibatis.apache.org//dtd SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd"> <!-- 다음은정확한 XML 헤더를위한필수값이다. --> <sqlmapconfig> <!-- 여기서명시된파일내프라퍼티 (name=value) 는이설정파일내고정자 (placeholder) 에의해사용될수있다. ( 이를테면. ${driver}. 이파일은클래스패스에상대적이고선택적인사항이다. --> <properties resource=" examples/sqlmap/maps/sqlmapconfigexample.properties " /> <!-- 이셋팅은가장기본적으로는트랜잭션관리를하며 SqlMapClient 설정상세를제어한다. 이것들은모두선택적이다. --> <settings cachemodelsenabled="true" enhancementenabled="true" lazyloadingenabled="true" maxrequests="128" maxsessions="10" maxtransactions="5" usestatementnamespaces="false" defaultstatementtimeout="5" statementcachingenabled="true" classinfocacheenabled="true" /> <!-- 이요소는 ibatis 가결과객체를생성하기위해사용할 factory 클래스를선언한다. 이요소는선택사항이며뒷부분에서상세하게설명한다. --> <resultobjectfactory type="com.mydomain.myresultobjectfactory" > <property name="someproperty" value="somevalue"/> </resultobjectfactory> <!-- 긴전체경로를포함한클래스명을위한좀더짧은이름을사용하기위한별칭을타이핑한다. --> <typealias alias="order" type="testdomain.order"/> <!--SimpleDataSource 를이용한 SQL Map 를사용하기위한데이터소스설정. 6
위자원으로부터프라퍼티사용에주의. --> <transactionmanager type="jdbc" > <datasource type="simple"> <property name="jdbc.driver" value="${driver}"/> <property name="jdbc.connectionurl" value="${url}"/> <property name="jdbc.username" value="${username}"/> <property name="jdbc.password" value="${password}"/> <property name="jdbc.defaultautocommit" value="true" /> <property name="pool.maximumactiveconnections" value="10"/> <property name="pool.maximumidleconnections" value="5"/> <property name="pool.maximumcheckouttime" value="120000"/> <property name="pool.timetowait" value="500"/> <property name="pool.pingquery" value="select 1 from ACCOUNT"/> <property name="pool.pingenabled" value="false"/> <property name="pool.pingconnectionsolderthan" value="1"/> <property name="pool.pingconnectionsnotusedfor" value="1"/> </datasource> </transactionmanager> <!-- 이 SQL map 에의해로드되는모든 SQL Map 파일을인식한다. 경로는클래스패스에상대적이다. --> <sqlmap resource="examples/sqlmap/maps/person.xml" /> </sqlmapconfig> 이문서의다음부분은 SQL Maps 설정파일의다양한부분을논의한다. <properties> 요소 SQL Maps 은 SQL Maps XML 설정파일과함께속하는표준적인자바속성파일 (name=value) 을지정하는하나의 <properties> 요소를가질수있다. 그렇게함으로써속성파일내에각각의이름지어진값들은 SQL Maps 설정파일내에참조될수있는변수가될수있고모든 SQL Maps 는내부에서참조된다. 예를들면속성파일이다음을포함한다면 driver=org.hsqldb.jdbcdriver 그러면 SQL Maps 설정파일또는설정문서에의해참조되는각각의 SQL Maps 는 ${driver} 형태로사용가능하고 org.hsqldb.jdbcdriver 라는값이참조된다. 예를들면 <property name="jdbc.driver" value="${driver}"/> 이것은빌드되거나테스트그리고배치되는동안편리하게된다. 이것은다중환경이나설정파일을위한자동화툴을사용하는당신의애플리케이션을쉽게인식하도록한다. 프라퍼티는클래스패스나어떤유효한 URL 로부터로드될수있다. 예를들면고정된파일경로를위해다음처럼사용한다. <properties url= file:///c:/config/my.properties /> <settings> 요소 <settings> 요소는 XML 파일을빌드하는 SqlMapClient 인스턴스를위해다양한옵션과최적화를설정하도록한다. setting 요소와그것의모든속성값은모두옵션적이다. 제공되는속성값과그것들의다양한행위는다음의테이블에서서술된다. 7
maxrequests maxsessions 이것은한꺼번에 SQL 문을수행할수있는쓰레드의수이다. 셋팅값보다많은쓰레드는다른쓰레드가수행을완료할때까지블록된다. 다른 DBMS 는다른제한을가진다. 이것은최소한 10 개의 maxtransactions 이고언제나 maxsessions 과 maxtransactions 보다크다. 종종동시요청값의최대치를줄이면성능향상을보여준다. 예 : maxrequests= 256 Default: 512 이것은주어진시간동안활성될수있는세션의수이다. 세션은명시적으로주어질수도있고프로그램적으로요청될수도있고쓰레드가 SqlMapClient인스턴스를사용할때마다자동적으로생성될수도있다. 이것은언제나 maxtransaction 보다같거나커야하고 maxrequests 보다작아야한다. 동시세션값의최대치를줄이면전체적인메모리사용량을줄일수있다. 예 : maxsessions= 64 Default: 128 maxtransactions 이것은한꺼번에 SqlMapClient.startTransaction() 에들어갈수있는쓰레드의최대갯수이다. 셋팅값보다많은쓰레드는다른쓰레드가나올때까지블록된다. 다른 DBMS 는다른제한을가진다. 이값은언제나 maxsessions 보다작거나같아야하고 maxrequests 보다작아야한다. 종종동시트랜잭션의최대치를줄이면성능향상을보여준다. 예 : maxtransactions= 16 Default: 32 cachemodelsenabled 이셋팅은 SqlMapClient 를위한모든캐쉬모델을가능하게하거나가능하지않게한다. 이것은디버깅시도움이된다. 예 : cachemodelsenabled= true Default: true (enabled) lazyloadingenabled 이셋팅은 SqlMapClient 를위한모든늦은 (lazy) 로딩을가능하게하거나가능하지않게한다. 이것은디버깅시도움이된다. 예 : lazyloadingenabled= true Default: true (enabled) enhancementenabled 이셋팅은향상된늦은 (lazy) 로딩처럼최적화된자바빈즈속성접근을위해런타임시바이트코드향상을가능하게한다. 예 : enhancementenabled= true Default: false (disabled) usestatementnamespaces 이셋팅을가능하게하면당신은 sqlmap 이름과 statement 이름으로구성된전체적인이름 (fully qualified name) 으로매핑구문를참조해야한다. 예를들면 : queryforobject( sqlmapname.statementname ); 예제 : usestatementnamespaces= false Default: false (disabled) 8
defaultstatementtimeout (ibatis 버전 2.2.0 이나그이후버전 ) classinfocacheenabled 이셋팅은모든구문에대한 JDBC 쿼리타임아웃처럼적용될정수값이다. 이값은매핑구문의 statement 속성으로무시될수있다. 이값이명시되지않으면, 매핑구문의 statement 속성이설정되지않는다면쿼리타임아웃이셋팅되지않을것이다. 드라이버가구문이종료되도록기다리는초단위의값으로설정한다. 하지만모든드라이버가이셋팅을지원하지는않으니반드시확인하고사용해야한다. 이셋팅을사용하도록설정하면, ibatis 는클래스의캐시를관리할것이다. 많은클래스가재사용된다면재빨리시작하도록해줄것이다. 예제 : classinfocacheenabled= true Default: true (enabled) statementcachingenabled (ibatis 버전 2.3.0 이나그이후버전 ) 이셋팅을사용하도록설정하면, ibatis 는 prepared 구문의지역적인캐시를관리할것이다. 이설정은명확한성능향상을보여줄것이다. 예제 : statementcachingenabled= true Default: true (enabled) <resultobjectfactory> 요소 중요 : 이기능은 ibatis 2.2.0 이나그이후버전에서사용가능하다. resultobjectfactory 요소는 SQL 구문의실행으로결과객체를생성하기위한 factory 클래스를명시한다. 이요소의사용하지않을수도있다. 이요소를명시하지않는다면, ibatis 는결과객체를생성 (class.newinstance()) 하기위한내부기법을사용할것이다. ibatis 는다음의경우결과객체를생성한다. 1. ResultSet 에서반환되는레코드를매핑할때 ( 가장공통적인경우 ) 2. resultmap 으로결과요소의내포된 select 구문을사용할때, 내포된 select 구문이 parameterclass 를선언한다면, ibatis 는내포된 select 를실행하기전에클래스의인스턴스를생성하고값을채울것이다. 3. 저장프로시저를실행할때, ibatis 는 OUTPUT 파라미터를위한객체를생성할것이다. 4. 내포된결과맵을처리할때, 내포된결과맵이 N+1 쿼리를피하도록 groupby 지원으로조합되어사용된다면, 객체는대개 Collection, List 또는 Set 타입의구현체이될것이다. 결과객체 factory 를통해사용자정의구현체를제공할수도있다. 내포된결과맵으로 1:1 조인을사용하는것으로, ibatis 는이 factory 를통해명시한도메인객체의인스턴스를생성할것이다. factory 를구현하도록결정했다면, factory클래스는 com.ibatis.sqlmap.engine.mapping.result.resultobjectfactory 인터페이스를반드시구현해야만하고 public 디폴트생성자를반드시가져야한다. ResultObjectFactory 인터페이스는두개의메소드를가진다. 하나는객체를생성하고다른하나는설정에서명시된프라퍼티값을받는다. 예를들어, 다음처럼 resultobjectfactory 설정요소를명시하도록해보자. <resultobjectfactory type="com.mydomain.myresultobjectfactory" > <property name="someproperty" value="somevalue"/> </resultobjectfactory> 그리고나서다음처럼결과객체 factory 클래스를코딩할것이다. package com.mydomain; import com.ibatis.sqlmap.engine.mapping.result.resultobjectfactory; public class MyResultObjectFactory implements ResultObjectFactory { 9
public MyResultObjectFactory() { super(); } public Object createinstance(string statementid, Class clazz) throws InstantiationException, IllegalAccessException { } // create and return instances of clazz here... public void setproperty(string name, String value) { // save property values here... } } ibatis 는설정에서명시된각각의프라퍼티를위해매번 setproperty 메소드를호출할것이다. 모든프라퍼티들은 createinstance 메소드가처리되도록호출하기전에셋팅될것이다. ibatis 는앞서언급한경우처럼객체가생성될필요가있을때마다 createinstance 메소드를호출할것이다. CreateInstance 메소드에서 null 이반한된다면, ibatis 는일반적인방법 (class.newinstance()) 을통해객체를생성하도록시도할것이다. java.util.collection 이나 java.util.list 를생성하기위한요청에서 null 을반환한다면, ibatis는 java.util.arraylist 를생성할것이다. java.util.set 을생성하기위한요청에서 null 을반환한다면, ibatis는 java.util.hashset 을생성할것이다. ibatis 는객체생성을요청하는컨텍스트를알도록현재의구문을전달한다. <typealias> 요소 typealias 요소는긴전체경로를포함한클래스명을참조하기위한짧은이름을명시하도록한다. 예를들면 <typealias alias="shortname" type="com.long.class.path.class"/> SQL Maps 설정파일에서사용되는미리정의된몇몇 alias 가있다. 그것들은 Transaction Manager Aliases JDBC com.ibatis.sqlmap.engine.transaction.jdbc.jdbctransactionconfig JTA com.ibatis.sqlmap.engine.transaction.jta.jtatransactionconfig EXTERNAL com.ibatis.sqlmap.engine.transaction.external.externaltransactionconfig Data Source Factory Aliases SIMPLE DBCP JNDI com.ibatis.sqlmap.engine.datasource.simpledatasourcefactory com.ibatis.sqlmap.engine.datasource.dbcpdatasourcefactory com.ibatis.sqlmap.engine.datasource.jndidatasourcefactory <transactionmanager> 요소 1.0 변환노트 : SQL Maps 1.0 은다중의데이터소스설정을허락했다. 이것은다루기어렵고몇가지나쁜예제를소개했다. 그러므로 2.0 에서는오직하나의데이터소스만을허락한다. 다중의배치 / 설정을위해서는시스템에의해다르게설정되거나 SQL Maps 를빌드할때파라미터처럼전달되는다중속성파일이추천된다. <transactionmanager> 요소는당신이 SQL Maps 를위한트랜잭션관리를설정하도록한다. type 속성값은사용하기위한트랜잭션관리자를표시한다. 그값은클래스명이거나타입 alias 일수있다. 3 개의트랜잭션관리자는 JDBC, JTA 그리고 EXTERNAL 중에하나로표시할수있다. JDBC 커넥션 commit() 과 rollback() 메소드를통해트랜잭션를제어하기위한 JDBC 를사용하게된다. JTA 이트랜잭션관리자는 SQL Maps 가다른데이터베이스나트랜잭션자원을포함하는더욱더넓은범위의트랜잭션을포함하도록하는 JTA 전역트랜잭션를사용한다. 이설정은 JNDI 자원으로부터사용자트랜잭션을위치시키기위한 UserTransaction 속성값을요구한다. JNDI 데이터소스예제는다음의설정예제에서보라. EXTERNAL 이것은당신자신이트랜잭션을관리하도록한다. 당신은여전히데이터소스를설정할수있지만프레임워크생명주기의부분처럼트랜잭션이커밋되거나롤백되지않는다. 이것은당신애플리케이션의부분이 10
외부적으로 SQL Maps 트랜잭션을관리해야한다는것이다. 이셋팅은비-트랜잭션 ( 예를들면읽기전용 ) 데이터베이스에유용하다. <transactionmanager> 요소는 true 나 false 가될수있는 commitrequired 요소를선택할수있다. 대개 ibatis는 insert, update 또는 delete 작업이실행되지않고서는트랜잭션을커밋하지않을것이다. 하지만명시적으로 committransaction() 메소드를호출하면커밋이된다. 이런명시적인호출은종종문제를야기한다. insert, update, delete 작업이실행되지않아도 ibatis 가모든작업에대해커밋하길바란다면, commitrequired 속성을 true 값으로셋팅하라. 다음과같은경우에이속성값이유용하다. 1. 2. 반환되는레코드수만큼데이터를수정하는저장프로시저를호출할경우. 이런경우 queryforlist() 메소드를사용하여프로시저를호출할것이다. 그래서 ibatis 는대개커밋을하지않을것이다. 그렇기때문에실제데이터를수정하는작업이롤백이될것이다. 웹스피어환경에서커넥션풀링과 JNDI <datasource> 그리고 JDBC 와 JTA 트랜잭션관리자를사용할경우. 웹스피어는풀링된커넥션의모든트랜잭션이커밋되거나커넥션이풀에반횐되지않을것이다. commitrequired 속성이 EXTERNAL 트랜잭션관리자를사용할때에는영향을끼치지않는다는것을주의히라. 몇몇트랜잭션관리자는추가적인설정프라퍼티를요구한다. 다음의표는다양한트랜잭션관리자를위해사용가능한추가적인프라퍼티들을보여준다. 트랜잭션관리자 프라퍼티 EXTERNAL 프라퍼티설명 DefaultAutoCommit true 로셋팅하면, 각각의데이터소스가제공하는값이아니라면 setautocommit(true) 메소드가각각의트랜잭션에서호출될것이다. false 로셋팅하거나명시하지않는다면, 각각의데이터소스가제공하는값이아니라면 setautocommit(false) 메소드가각각의트랜잭션에서호출될것이다. 이값은 SetAutoCommitAllowed 프라퍼티에의해무시될수있다. SetAutoCommitAllowed true 로셋팅하거나명시하지않는다면, DefaultAutoCommit 프라퍼티가발생하는것으로명시된다. false 로셋팅한다면, ibatis 는어떤경우에 setautocommit 를호출하지않을것이다. 이값은 setautocommit 메소드가특정환경에서호출이되지않는웹스피어와같은환경에서유용하다. JTA 프라퍼티설명 UserTransaction 이프라퍼티는필수값이다. 사용자트랜잭션의값이다. 대개의경우 java:comp/usertransaction 로셋팅된다. <datasource> 요소트랜잭션관리자설정의포함된부분은 datasource 요소이고 SQL Maps 를사용하기위한데이터소스를설정하기위한속성값의집합이다. 여기엔프레임워크에서제공되는 3 가지데이터소스타입이있지만당신은당신만의데이터소스를사용할수도있다. 포함된 DataSourceFactory 구현은다음에상세하게논의가될것이고각각을위해제공되는설정은아래예제를보라. SimpleDataSourceFactory SimpleDataSource 는데이터소스를제공하는컨테이너가없는경우에커넥션을제공하기위해기본적으로풀링데이터소스구현을제공한다. 이것은 ibatis SimpleDataSource 커넥션풀링을기초로한다. <transactionmanager type="jdbc"> <datasource type="simple"> <property name="jdbc.driver" value="org.postgresql.driver"/> <property name="jdbc.connectionurl" 11
value="jdbc:postgresql://server:5432/dbname"/> <property name="jdbc.username" value="user"/> <property name="jdbc.password" value="password"/> <!-- OPTIONAL PROPERTIES BELOW --> <property name="jdbc.defaultautocommit" value="false"/> <property name="pool.maximumactiveconnections" value="10"/> <property name="pool.maximumidleconnections" value="5"/> <property name="pool.maximumcheckouttime" value="120000"/> <property name="pool.timetowait" value="10000"/> <property name="pool.pingquery" value="select * from dual"/> <property name="pool.pingenabled" value="false"/> <property name="pool.pingconnectionsolderthan" value="0"/> <property name="pool.pingconnectionsnotusedfor" value="0"/> <property name="driver.driverspecificproperty" value="somevalue"/> </datasource> </transactionmanager> Driver. 이라는접두사를가지는프라퍼티는 JDBC 드라이버를참조하는프라퍼티처럼추가될것이다. DbcpDataSourceFactory 이구현체은 DataSource API 를통해커넥션풀링서비스를제공하기위해 Jakarta DBCP (Database Connection Pool) 을사용한다. 이 DataSource 는애플리케이션 / 웹컨테이너가 DataSource 구현체을제공하지못하거나당신이 standalone 애플리케이션을구동할때이상적이다. DbcpDataSourceFactory 를위해명시해야하는설정파라미터의예제는다음과같다. <transactionmanager type="jdbc"> <datasource type="dbcp"> <property name="driverclassname" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> <!-- OPTIONAL PROPERTIES BELOW --> <property name="maxactive" value="10"/> <property name="maxidle" value="5"/> <property name="maxwait" value="60000"/> <!-- Use of the validation query can be problematic. If you have difficulty, try without it. --> <property name="validationquery" value="select * from ACCOUNT"/> <property name="logabandoned" value="false"/> <property name="removeabandoned" value="false"/> <property name="removeabandonedtimeout" value="50000"/> <property name="driver.driverspecificproperty" value="somevalue"/> </datasource> </transactionmanager> 다음의 URL 에서사용가능한모든프라퍼티들을볼수있다. http://jakarta.apache.org/commons/dbcp/configuration.html Driver. 이라는접두사를가지는프라퍼티는위에서보여주는것처럼 JDBC 드라이버를참조하는프라퍼티처럼추가될것이다. ibatis 는아래에서보여주는것처럼다소덜유연한레거시설정옵션또한지원한다. 어쨌든, 우리는위에서보여주는설정옵션을사용하도록추천한다. <transactionmanager type="jdbc"> <!-- Legacy DBCP Configuration --> <datasource type="dbcp"> <property name="jdbc.driver" value="${driver}"/> <property name="jdbc.connectionurl" value="${url}"/> <property name="jdbc.username" value="${username}"/> <property name="jdbc.password" value="${password}"/> <!-- OPTIONAL PROPERTIES BELOW --> 12
<property name="pool.maximumactiveconnections" value="10"/> <property name="pool.maximumidleconnections" value="5"/> <property name="pool.maximumwait" value="60000"/> <!-- Use of the validation query can be problematic. If you have difficulty, try without it. --> <property name="pool.validationquery" value="select * from ACCOUNT"/> <property name="driver.driverspecificproperty" value="somevalue"/> </datasource> </transactionmanager> 앞에서보여준프라퍼티는레거시설정옵션을사용할때 ibatis 가인식하는프라퍼티일뿐이다. Driver. 이라는접두사를가지는프라퍼티는위에서보여주는것처럼 JDBC 드라이버를참조하는프라퍼티처럼추가될것이다. JndiDataSourceFactory 이구현체은애플리케이션컨테이너내 JNDI 컨텍스트로부터 DataSource 구현체을가져와야할것이다. 이것은전형적으로애플리케이션서버를사용중이고컨테이너관리커넥션풀그리고제공되는 DataSource 구현체이있을때사용한다. JDBC DataSource 구현체에접근하기위한표준적인방법은 JNDI 컨텍스트를통하는것이다. JndiDataSourceFactory 는 JNDI 를통해 DataSource 에접근하는기능을제공한다. 데이터소스내에명시되어야하는설정파라미터는다음과같다. <transactionmanager type="jdbc" > <datasource type="jndi"> <property name="datasource" value="java:comp/env/jdbc/jpetstore"/> </datasource> </transactionmanager> 위설정은일반적인 JDBC 트랜잭션관리지만컨테이너가자원을관리한다. 당신은다음처럼전역 (global) 트랜잭션을설정하길원할수도있다. <transactionmanager type="jta" > <property name="usertransaction" value="java:/comp/usertransaction"/> <datasource type="jndi"> <property name="datasource" value="java:comp/env/jdbc/jpetstore"/> </datasource> </transactionmanager> UserTransaction 인스턴스가발견될수있는 JNDI 위치를가지키는 UserTransaction 값에주의하라. 좀더넓은범위의트랜잭션을가지는 SQL Maps 가다른데이터베이스와트랜잭션자원을포함하기위해서는 JTA트랜잭션관리가요구된다. JNDI 컨텍스트프라퍼티는 context. 라는접두사를가진추가적인프라퍼티를명시하기전에추가될수있다. 예를들어다음과같다. <sqlmap> 요소 <property name= context.java.naming.provider.url value= ldap://somehost:389 /> sqlmap 요소는명시적으로 SQL Map 이나다른 SQL Map 설정파일을포함할때사용한다. SqlMapClient인스턴스에의해사용되는각각의 SQL Map XML 파일은반드시선언되어야한다. SQL Map XML 파일은클래스패스나 URL로부터스트림 (stream) 자원처럼로드될것이다. 당신은 SQL Maps 를명시해야한다. 다음은그에대한예이다. <!--CLASSPATH RESOURCES --> <sqlmap resource="com/ibatis/examples/sql/customer.xml" /> <sqlmap resource="com/ibatis/examples/sql/account.xml" /> <sqlmap resource="com/ibatis/examples/sql/product.xml" /> <!--URL RESOURCES --> <sqlmap url="file:///c:/config/customer.xml " /> 13
<sqlmap url="file:///c:/config/account.xml " /> <sqlmap url="file:///c:/config/product.xml" /> 다음의다양한섹견은 SQL Map XML 파일들의구조에대해서서술한다. SQL Map XML 파일 ( http://ibatis.apache.org/dtd/sql-map-config-2.dtd) 위예제에서우리는 SQL Maps 의가장간단한형태를보았다. SQL Map 문서구조내에사용가능한다른옵션이있다. 좀더많은기능을가지는매핑구문의예제이다. <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE sqlmap PUBLIC "-//ibatis.apache.org//dtd SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"> <sqlmap namespace= Product > </sqlmap> <cachemodel id= productcache 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 parametermap= productparam resultmap= productresult cachemodel= product-cache > select * from PRODUCT where PRD_ID =? 너무많은가.? 비록프레임워크가당신을위해많은것을하더라도간단한 select 구문을위해너무많은추가적인작업을하는것처럼보인다. 걱정하지마라다음은위의것의축소버전이다. <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE sqlmap PUBLIC "-//ibatis.apache.org//dtd SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"> <sqlmap namespace= 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# 14
</sqlmap> 지금 SQL Map 을행위적인측면에서보면이구문은정확하게같지는않다. 즉몇가지다른점을가진다. 먼저후자의구문은캐쉬를명시하지않아서매번의요청시데이터베이스에직접요청한다. 두번째후자의구문은약간의부하를야기할수있는프레임워크의자동매핑기능을사용한다. 어쨌든두가지구문모두자바코드로부터정확하게같은방법으로작동하지않을것이다그리고당신은첫번째좀더간단한솔루션으로시작할것이고나중에는필요하면좀더향상된매핑으로옮겨갈것이다. 가장간단한솔루션이많은경우에가장좋은연습이다. 하나의 SQL Map XML 파일은많은캐쉬모델, 파라미터매핑, 결과매핑그리고구문을포함할수없다. 당신의애플리케이션을위해구문과맵을신중하게구성하라. 매핑구문 SQL Maps 개념은매핑구문에집중한다. 매핑구문는어떠한 SQL 문을사용할수도있고파라미터 maps(input) 과결과맵s(output) 를가질수있다. 만약간단한경우라면매핑구문는파라미터와결과를위한클래스로직접설정할수있다. 매핑구문는메모리내에생산된결과를캐싱하기위해캐쉬모델을사용하도록설정할수도있다. <statement id= statementname [parameterclass= some.class.name ] [resultclass= some.class.name ] [parametermap= nameofparametermap ] [resultmap= nameofresultmap ] [cachemodel= nameofcache ] [timeout= 5 ]> select * from PRODUCT where PRD_ID = [? #propertyname#] order by [$simpledynamic$] </statement> 위구문에서 [ 괄호 ] 부분은옵션이고몇몇의경우에만혼합할필요가있다. <insert id= inserttestproduct > insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (1, Shih Tzu ) </insert> 위예제는명백하게발생할꺼같지는않다. 어쨌든이것은당신이임의의 SQL 문을실행하기위해 SQL Map프레임워크를사용한다면유용할수있다. 어쨌든이것은파라미터맵과결과맵을사용하는자바빈즈매핑기능을공통적으로사용할것이다. 다음의다양한섹션은구조와속성, 그들이어떻게매핑구문에영향을끼치는지서술한다. 구문타입 <statement> 요소는어떤타입의 SQL 문을사용할수있는일반적인 catch all 구문이다. 일반적으로이것은좀더다양한특성의 statement 요소중하나를사용하기위한좋은생각이다. 좀더다양한특성의요소는좀더직관적인 XML DTD 를제공하고때때로일반적인 <statement> 요소가제공하지않는추가적인기능을제공한다. 다음의테이블은 statement 요소와그들이지원하는속성과기능을목록화한다. Statement 요소 속성 하위요소 메소드 <statement> id parameterclass resultclass parametermap resultmap cachemodel resultsettype fetchsize 모든동적요소 insert update delete 모든쿼리메소드 15
xmlresultname remapresults timeout <insert> <update> <delete> <select> <procedure> id parameterclass parametermap timeout id parameterclass parametermap timeout id parameterclass parametermap timeout id parameterclass resultclass parametermap resultmap cachemodel resultsettype fetchsize xmlresultname remapresults timeout id parameterclass resultclass 모든동적요소 <selectkey> 모든동적요소 모든동적요소 모든동적요소 모든동적요소 insert update delete insert update delete insert update delete 모든쿼리메소드 insert update delete SQL SQL 은맵의가장중요한부분을차지한다. 이것은당신의데이터베이스와 JDBC 드라이버에적합한어떤 SQL 이될수있다. 당신은가능한어떤기능을사용할수있고당신의드라이버가지원하는한다중구문에전달할수도있다. 당신이하나의문서에서 SQL 과 XML 을혼합하기때문에특수문자의충돌이잠재적으로존재한다. 대부분의공통적인것은 greater-than 과 less-than 문자들이다.(<>). 이것들은 SQL 문에서공통적으로요구되고 XML 에서는예약어이다. 당신의 SQL 문에들어갈필요가있는특수 XML 문자를처리하기위한간단한해결법이있다. 표준적인 XML CDATA 섹션을사용함으로써특수문자의어떤것도파싱되지않고문제는해결된다. 예를들면 <select id="getpersonsbyage" parameterclass= int resultclass="examples.domain.person"> SELECT * FROM PERSON WHERE AGE <![CDATA[ > ]]> #value# SQL 재사용하기 SqlMaps 를사용할때, 종종 SQL 문의일부가중복되는것을보곤한다. 예를들어, FROM 절이나제약조건들이그런경우이다. ibatis 는중복된 SQL 문의일부를재사용하도록하는강력한요소를제공한다. 간단하게몇가지항목을가져와서그항목의개수를센다고가정해보자. 대개다음처럼작성할것이다. <select id="selectitemcount" resultclass="int"> SELECT COUNT(*) AS total FROM items WHERE parentid = 6 16
<select id="selectitems" resultclass="item"> SELECT id, name FROM items WHERE parentid = 6 중복을제거하기위해, <sql> 과 <include> 요소를사용할것이다. <sql> 요소는재사용하기위한일부를저장한다. 구문내그일부를포함하도록 <include> 요소를사용한다. 예를들어 : <sql id="selectitem_fragment"> FROM items WHERE parentid = 6 </sql> <select id="selectitemcount" resultclass="int"> SELECT COUNT(*) AS total <include refid="selectitem_fragment"/> <select id="selectitems" resultclass="item"> SELECT id, name <include refid="selectitem_fragment"/> <include> 요소는다른맵에있는 SQL 을참조할수있도록명명공간을사용한다. ( 어쨌든 ibatis 가 SqlMaps 을로드하는방식때문에맵의정보는구문의일부를가져오기전에로그될것이다. ) SQL 문의일부분은쿼리실행시포함되고처리된다. <sql id="selectitem_fragment"> FROM items WHERE parentid = #value# </sql> <select id="selectitemcount" parameterclass="int" resultclass="int"> SELECT COUNT(*) AS total <include refid="selectitem_fragment"/> <select id="selectitems" parameterclass="int" resultclass="item"> SELECT id, name <include refid="selectitem_fragment"/> 자동생성키 많은관계형데이터베이스시스템은기본키 (primay 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" > SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL </selectkey> insert into PRODUCT (PRD_ID,PRD_DESCRIPTION) values (#id#,#description#) </insert> 17
<! Microsoft SQL Server IDENTITY Column Example --> <insert id="insertproduct-ms-sql" parameterclass="com.domain.product"> insert into PRODUCT (PRD_DESCRIPTION) values (#description#) <selectkey resultclass="int" > SELECT @@IDENTITY AS ID </selectkey> </insert> selectkey 구문이 insert SQL 앞에있다면 insert 구문보다먼저실행된다. 하지만 insert 구문뒤에있다면뒤에실행된다. 앞의예제에서오라클예제는 selectkey 가 insert구문앞에서실행되는것을보여준다 ( 시퀀스에적절한 ). SQL 서버의예제는 selectkey 구문이 insert 구문뒤에서실행된다는것을보여준다 (identity 칼럼에적절한 ). IBATIS 버전 2.2.0 이나그이후버전에서, 원한다면구문의실행순서를명시적으로정할수있다. SelectKey 요소는실행순서를명시적으로셋팅하기위해사용될수있는 type 속성을지원한다. Type 속성의값은 pre 나 post 둘중하나가될수있다. 이값은구문이 insert 구문의앞이나뒤에서실행된다는것을의미한다. Type 속성을명시한다면, 명시한값은 selectkey 요소의위치에따를것이다. 예를들어, 다음구문에서 selectkey 구문은비록요소가 insert 구문뒤에위치하더라도 insert 구문이전에실행될것이다. <insert id="insertproduct-oracle-type-specified" parameterclass="com.domain.product"> insert into PRODUCT (PRD_ID,PRD_DESCRIPTION) values (#id#,#description#) <selectkey resultclass="int" type="pre" > SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL </selectkey> </insert> <selectkey> 속성 : <selectkey> 속성 resultclass <selectkey> 구문의실행결과로생성되는자바클래스 ( 대개는 Integer 나 Long) keyproperty 설명 <selectkey> 구문의실행결과처럼파라미터객체에셋팅될프라퍼티. 명시하지않으면, ibatis 는데이터베이스에서반환되는칼럼명에기초하여프라퍼티를찾도록시도할것이다. 프라퍼티를찾을수없다면, 프라퍼티가셋팅되지는않을것이지만 ibatis 는여전히 <insert> 구문의결과로생성된키를반환할것이다. type pre 나 post 값을가질수있다. 명시한다면, 관련된 insert 구문이앞 (pre) 이나뒤 (post) 에 select key 구문이실행된다는것을나타낸다. 명시하지않는다면순서는 insert 구문내요소의위치에따라좌우될것이다. 즉 SQL 앞에위치한다면 selectkey 는구문이실행되기전에먼저실행될것이다. 이속성은 ibatis 2.2.0 이나그이후의버전에서만사용가능하다. 저장프로시저 저장프로시저는 <procedure> statement 요소를통해지원된다. 저장프로시저를출력물파라미터와함께어떻게사용하는지다음예제에서보여준다. <parametermap id="swapparameters" class="map" > <parameter property="email1" jdbctype="varchar" javatype="java.lang.string" mode="inout"/> <parameter property="email2" jdbctype="varchar" javatype="java.lang.string" mode="inout"/> </parametermap> <procedure id="swapemailaddresses" parametermap="swapparameters" > {call swap_email_address (?,?)} </procedure> 18
위처럼프로시저를호출하는것은파라미터객체 (map) 내에서두개의칼럼사이에두개의이메일주소를교체하는것이다. 파라미터객체는파라미터매핑의 mode 속성값이 INOUT 또는 OUT 일경우에만변경된다. 다른경우라면변경되지않고남는다. 명백한불변의파라미터객체 ( 이를테면 String) 는변경할수없다. 주의! 언제나표준적인 JDBC 저장프로시저를사용하도록하라. 좀더다양한정보를보기위해서는 JDBC CallableStatement 문서를보라. parameterclass parameterclass 속성값은자바클래스의전체경로를포함 ( 예를들면패키지를포함한 ) 한이름이다. parameterclass 속성은옵션이지만사용이굉장히추천되는것이다. 이것은프레임워크성능을향상시키는만큼 statement 에전달하는파라미터를제한하는데사용된다. 만약당신이 parametermap 을사용한다면 parameterclass 속성을사용할필요가없다. 예를들면당신이파라미터로전달하기위한 examples.domain.product 타입의객체를허락하길원한다면당신은다음처럼할수있을것이다. <insert id= statementname parameterclass= examples.domain.product > insert into PRODUCT values (#id#, #description#, #price#) </insert> 중요 : 비록이전버전과의호환성을위한옵션이지만이것은언제나파라미터클래스를제공하는것은매우추천되는사항이다 ( 물론요구되는파라미터가없더라도 ). 프레임워크가먼저타입을안다면스스로최적화능력을가지기때문에당신은클래스를제공함으로써좀더나은성능을달성할수있다. 명시된 parameterclass 없이선호하는속성 (get/set메소드) 을가지는자바빈즈는파라미터를받을것이고어느위치에서매우유용하다. parametermap parametermap 속성값은명시된 ( 밑의경우처럼 ) parametermap 요소의이름이다. parametermap속성은 parameterclass 속성과인라인파라미터의이익이되도록사용된다. XML 의깔끔함과일관성이당신의걱정이거나당신이좀더상세한 parametermap( 이를테면저장프로시저 ) 이필요하다면이것은좋은접근법이다. 주의! 동적으로매핑구문는단지인라인파라미터만지원하고파라미터 map 과는작동하지않는다. parametermap 의생각은 JDBC PreparedStatement 의값토큰과매치되는정렬된파라미터목록을명시한다. 예를들면 : <parametermap id= insert-product-param class= com.domain.product > <parameter property= id /> <parameter property= description /> </parametermap> <insert id= insertproduct parametermap= insert-product-param > insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (?,?) </insert> 위의예제에서, 파라미터 map 은 SQL 문에서값토큰 (? ) 에매치되고정렬되는두개의파라미터를서술한다. 그래서첫번째? 는 id 속성값에대체되고두번째는 description 속성값에대체된다. 파라미터 map 과그들의옵션은이문서나중에좀더다양하게서술될것이다. 인라인파라미터의빠른언급 이문서에나중에제공되는좀더상세화된설명에도불구하고인라인파라미터에대한빠른언급을한다. 인라인파라미터는매핑구문내부에서사용될수있다. 예를들면 : <insert id= insertproduct > 19
insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (#id#, #description#) </insert> 위예제에서인라인파라미터는 #id# 와 #description# 이다. 각각은구문파라미터를대체하는자바빈즈속성을표현한다. Product 클래스는포함된프라퍼티토큰이위치해있는구문내에위치하는값을위해읽게되는 id 와 description 프라퍼티을가진다. id=5 와 description= dog 를가지는 Product 를넘겨받은구문은다음처럼수행된다. insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (5, dog ); resultclass resultclass 속성값은자바클래스의전체경로를포함 ( 예를들면패키지를포함한 ) 한이름이다. resultclass 속성은우리에게 ResultSetMetaData 에기반한 JDBC ResultSet 에자동매핑되는클래스를명시하도록한다. 자바빈즈의프라퍼티와 ResultSet 의칼럼이매치될때마다프라퍼티는칼럼값과함께생성된다. 이것은매우짧고달콤하게매핑구문를쿼리한다. 예를들면 : <select id="getperson" parameterclass= 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# 위의예제에서 Person 클래스는 id, firstname, lastname, birthdate, weightinkilograms, heightinmeters 를포함하는프라퍼티를가진다. 칼럼별칭과함께대응되는각각은 SQL select 문에의해서술된다. 칼럼별칭은데이터베이스칼럼이름이매치되지않을때만요구된다. 일반적으로는요구되지않는다. 실행되었을때 Person 객체는프라퍼티이름과칼럼명에기반해서초기화되기위해매핑되는결과세트로부터초기화되고결과를반환한다. resultclass 으로자동매핑하는데는몇가지제한점이있다. 출력칼럼의타입을명시하는방법은없다. 관련된데이터를자동적으로로드하는방법이없고 ResultSetMetaData 에접근하는데필요한접근법내에서하찮은성능결과가있다. 이제한점모드는명시적인 resultmap 를사용함으로써극복할수있다. 결과맵은이문서나중에좀더상세하게다루어질것이다. resultmap resultmap 프라퍼티는좀더공통적으로사용되고이해하기위해가장중요한속성중에하나이다. 이 resultmap속성값은명시된 resultmap 요소의이름이다. resultmap 속성을사용하는것은당신에게결과세트로부터데이터와칼럼에매핑되는프라퍼티를어떻게꺼내는지제어하도록한다. resultclass 속성을사용하는자동매핑접근법과는달리 resultmap 는당신에게칼럼타입을명시하고 null 값을대체그리고복합프라퍼티매핑 ( 다른자바빈즈, Collections 그리고원시타입래퍼 ) 을허락한다. resultmap 구조의모든상세정보는이문서나중에설명된다. 하지만다음의예제는 resultmap 가어떻게구문에관련되었는지보여준다. <resultmap id= get-product-result class= com.ibatis.example.product > <result property= id column= PRD_ID /> <result property= description column= PRD_DESCRIPTION /> </resultmap> <select id= getproduct resultmap= get-product-result > select * from PRODUCT 20
위예제에서 SQL 쿼리로부터 ResultSet 은 resultmap 정의를사용해서 Product 인스턴스에매핑할것이다. resultmap은 id 프라퍼티가 PRD_ID 칼럼과 PRD_DESCRIPTION 칼럼에의해생성되는 description 프라퍼티에의해생성될것이다. select * 를사용하는것은지원된다는것에주의하라. ResultSet 내반환칼럼모두에매핑할필요는없다. cachemodel cachemodel 속성값은정의된 cachemodel 요소의이름이다. cachemodel 은쿼리가매핑구문를사용하기위한캐쉬를서술하는데사용된다. 각각의쿼리매핑구문는다른 cachemodel 이나같은것을사용할수있다. cachemodel 요소와그것의속성에대한모든상세설명은나중에언급된다. 다음예제는어떻게구문과관련되는지보여준다. <cachemodel id="product-cache" type="lru"> <flushinterval hours="24"/> <flushonexecute statement="insertproduct"/> <flushonexecute statement="updateproduct"/> <flushonexecute statement="deleteproduct"/> <property name= size value= 1000 /> </cachemodel> <select id= getproductlist parameterclass= int cachemodel= product-cache > select * from PRODUCT where PRD_CAT_ID = #value# 위예제에서캐쉬는 WEAK 참조타입을사용하는 products 를위해정의되고 24 시간마다또는관련된 update 문이수행될때마다지워진다 (flush). xmlresultname 매핑결과를 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 구문은다음구조의 XML 객체를생성할것이다. <person> <id>1</id> <firstname>clinton</firstname> <lastname>begin</lastname> <birthdate>1900-01-01</birthdate> <weightinkilograms>89</weightinkilograms> <heightinmeters>1.77</heightinmeters> </person> remapresults remapresults 속성은 <statement>, <select>, 그리고 <procedure> 에서사용가능하다. 이것은선택적인속성이고디폴트는 false 이다. 21
remapresults 속성은쿼리가반환칼럼의다양한세트를가질때 true 셋팅되어야만한다. 다음쿼리를보자. SELECT $fieldlist$ FROM table 이전예제에서칼럼의목록은테이블이언제나같더라도동적이다. SELECT * FROM $sometable$ 이전예제에서테이블은다를수있다. Select 절의 * 사용때문에, 결과적인칼럼이름은다를수있다. 동적요소는하나의쿼리수행에서다음수행까지변경하기위한목록을야기할수있다. resultset 메타데이타를알고 / 판단하기위한오버헤드가명백하지않기때문에, ibatis 는마지막쿼리수행에반환된것만을기억할것이다. 이것은위예제와비슷한상황에서문제를발생시킨다. 만약반환칼럼이변경된다면, remapresults 를 true 로셋팅하라. 그렇지않다면메타데이타검색의오버헤드를제거하기위해 remapresults 를 false 로셋팅하라. resultsettype SQL 구문의 resultsettype 을명시하기위해, 다음을사용할수있다. FORWARD_ONLY: 커서는앞쪽으로만이동한다. SCROLL_INSENSITIVE: 커서는스크롤가능하지만다른것에의한변경에는대개민감하지않다. SCROLL_SENSITIVE: 커서는스크롤가능하고다른것에의한변경에대개민감하다. resultsettype 은대개요구되지않는다. 그리고서로다른 JDBC 드라이버는같은 resultsettype 셋팅을사용하더라도다르게행동할것이다. ( 이를테면. Oracle 은 SCROLL_SENSITIVE 를지원하지않는다.). fetchsize SQL 구문의 fetchsize 를셋팅하는것은수행될것이다. 이것은 JDBC 드라이버에데이터베이스서버로의왕복을줄이기위해 prefetching 힌트를제공한다. timeout (ibatis 버전 2.2.0 나그이후의버전 ) 구문을위한 JDBC 쿼리타임아웃을셋팅한다. 여기서명시된값은 SQLMapConfig.xml 파일의 defaultstatementtimeout 셋팅에명시된값을무시할것이다. 디폴트타임아웃을명시하고특정구문을위한타임아웃을원하지않는다면, 타임아웃값을 0 으로셋팅하라. 명시된값은드라이버가종료되도록구문을기다리는초단위값이다. 이셋팅을지원하지않는드라이버가있다는점에주의하라. 파라미터맵과인라인파라미터 당신이위에서본것처럼 parametermap 는자바빈즈프라퍼티를구문의프라퍼티에매핑시키는작업을수행한다. 비록 parametermaps 가외부형태내에드물게발생하더라도그것들이당신에게인라인파라미터를이해하도록도와준다는것을이해하라. <parametermap id= parametermapname [class= com.domain.product ]> <parameter property = propertyname [jdbctype= VARCHAR ] [javatype= string ] [nullvalue= -9999 ] [typename= {REF or user-defined type} ] [resultmap=someresultmap] [mode=in OUT INOUT] [typehandler=sometypehandler] [numericscale=2]/> <parameter /> <parameter /> </parametermap> 22
[ 괄호 ] 내의부분은옵션이다. parametermap 는구문이참조할때사용하는구분자로써단지 id 속성만필요하다. Class 속성은옵션이지만크게사용이추천되는것이다. 구문의 parameterclass 속성과유사하게 class 속성은프레임워크가성능을위해엔진을최적화하는것만큼들어오는파라미터를체크하도록한다. <parameter> 요소 parametermap 은구문의파라미터에직접매핑하는파라미터매핑의어떤숫자를포함한다. 다음의일부섹션은 property 요소의속성을서술한다. property 파라미터맵의 property 속성은매핑구문에전달되는파라미터객체의자바빈즈프라퍼티 (get메소드) 의이름이다. 그이름은구문에필요한횟수에의존하는것보다좀더사용될수있다. 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 에러를보게될것이다. javatype javatype 속성은셋팅되기위한파라미터의자바프라퍼티를명시적으로정의하기위해사용된다. 대게이것은리플렉션 (reflection) 을통해자바빈즈프라퍼티로부터파생된다. 하지만 Map 과 XML 매핑같은특정매핑은프레임워크를위한타입을제공하지않는다. 만약 javatype 가셋팅되지않고프레임워크도어떤타입인지구별할수없다면타입은객체로간주될것이다. typename typename 속성은 REF 타입이나사용자정의타입을명시하기위해사용된다. javadoc 에보면.. typename 속성은사용자- 정의나 REF 출력파라미터를위해사용된다. 예를들면, 사용자- 정의타입은 STRUCT, DISTINCT, JAVA_OBJECT, 그리고명명된배열타입을포함한다. 사용자- 정의파라미터를위해, 파라미터의전체경로가포함된 SQL 타입명이주어진다. 반면에 REF 파라미터는주어진참조타입의전체경로가포함된타입명을요구한다. JDBC 드라이버는타입코드를필요로하지않으며타입명정보는이것을무시한다. 이식가능하기위해, 애플리케이션은이러한사용자정의와 REF 파라미터를위한값을제공해야만한다. 비록이것이사용자- 정의와 REF 파라미터가되더라도, 이속성은 23
JDBC 타입의파라미터를등록하기위해사용된다. 만약파라미터가사용자- 정의나 REF 타입을가지지않는다면, typename 파라미터를무시된다. nullvalue nullvalue 속성은어떤유효한값 ( 프라퍼티타입에기초로해서 ) 에셋팅할수있다. null 속성은 null 값대체를정의하기위해사용된다. 이것이의미하는것은자바빈즈프라퍼티내에서검색되는값인 NULL 이데이터베이스에쓰여질것이라는것이다 ( 들어오는 null 값대체의상반된행위 ). 이것은당신에게 null 값을지원하지않는타입 ( 이를테면 int, double, float등등 ) 을위해당신의애플리케이션내에 magic null 숫자를사용하도록허락한다. 프라퍼티의그런타입은적합한 null 값을포함할때 NULL 은값대신에데이터베이스에쓰여질것이다. resultmap 저장프로시저의 output 파라미터의값으로 java.sql.resultset 의인스턴스를기대할때 resultmap 요소를명시한다. 이값은 ibatis 로하여금객체매핑을위한일반적인결과세트를가능하게할것이다. mode mode 속성은저장프로시저파라미터의타입을명시한다. 사용가능한값은 IN, OUT, INOUT 이다. typehandler typehandler 속성은디폴트 ibatis 타입대신에이프라퍼티를위해사용될사용자정의타입핸들러를명시하기위해사용된다. 명시한다면, 값은 com.ibatis.sqlmap.engine.type.typehandler 인터페이스나 com.ibatis.sqlmap.client.extensions.typehandlercallback 인터페이스를구현하는패키지경로를포함한클래스명이어야만한다. 이값은전역타입핸들러를무시한다. 이문서의뒷부분에사용자정의타입핸들러에대해상세히다룬다. numericscale (numericscale 은 ibatis 버전 2.2.2 나그이후버전에서사용가능하다.) numericscale 속성은 NUMERIC 이나 DECIMAL 형태의저장프로시서 outout 파라미터를위한크기를지정하기위해사용된다. mode 속성에 OUT, INOUT 를지정하고 jdbctype 를 DECIMAL 이나 NUMERIC 로지정한다면, numericscale 값을지정해야만한다. 이속성을위한값은 0 보다같거나큰정수값이어야만한다. <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> <insert id= insertproduct parametermap= insert-product-param > insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (?,?) </insert> 위예제에서자바빈즈프라퍼티인 id 와 description 는목록화되는순서대로매핑구문인 insertproduct 의파라미터에적용될것이다. 그래서 id 는첫번째파라미터 (?) 에적용되고 description 는두번째파라미터에적용된다. 만약에순서가반대라면 XML 은다음처럼보일것이다. <parametermap id= insert-product-param class= com.domain.product > <parameter property= description /> <parameter property= id /> </parametermap> <insert id= insertproduct parametermap= insert-product-param > 24
insert into PRODUCT (PRD_DESCRIPTION, PRD_ID) values (?,?) </insert> 주의! Parameter Map 이름은정의된 SQL Map XML 파일에위치한다. 당신은 SQL Map(<sqlMap> root 태그에셋팅된 ) 의 id 와함께파라미터 Map 의 id 를앞에붙임으로써다른 SQL Map XML 파일내에파라미터 Map 을참조할수있다. 예를들면다른파일로부터위의파라미터 map 를참조하기위해참조하기위한전체이름은 Product.insert-productparam 이될것이다. 인라인파라미터맵 매우상세한설명에도불구하고 parametermaps 을선언하기위한위의문법은매우장황하다. 파라미터 Maps 을위한정의 (definition) 을간단하게하고코드를줄일수있는좀더다양한문법이있다. 그대안적인문법은자바빈즈프라퍼티이름을매핑구문에인라인시키는것이다. 초기설정에의해명시적으로정의된 parametermap 이없는어떤매핑구문는인라인파라미터를위해파싱될것이다. 이전의인라인파라미터를구현한예제 ( 이를테면 Product) 는다음처럼보일것이다. <insert id= insertproduct parameterclass= com.domain.product > insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (#id#, #description#) </insert> 타입을선언하는것은다음의문법을사용함으로써인라인파라미터로할수있다. <insert id= insertproduct parameterclass= com.domain.product > insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (#id:numeric#, #description:varchar#) </insert> 타입을선언하는것과 null 값대체는다음문법을사용함으로써인라인파라미터로할수있다. <insert id= insertproduct parameterclass= com.domain.product > insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (#id:numeric:-999999#, #description:varchar:no_entry#) </insert> 주의! 인라인파라미터를사용할때당신은타입정의없이 null 값대체를명시할수없다. 당신은순서대로파싱하기위해둘다명시해야한다. 주의! Null 값의완전한투명성을원한다면당신은이문서의나중에설명되는것처럼당신의결과맵s 내에 null 값대체를반드시명시해야한다. 주의! 당신이많은수의타입서술자와 null 값대체가필요하다면당신은외부적인것을사용해서코드를정리할수있어야할것이다. 인라인파라미터맵구문 ibatis 는인라인파라미터맵을위해두가지의문법을지원한다. 하나는간단한문법이고다른하나는좀더상세하고복잡한문법이다. 간단한문법은다음과같다. #propertyname# - OR - #propertyname:jdbctype# - OR - #propertyname:jdbctype:nullvalue# 이문법의예제는위와같다. PropertyName 요소는파라미터객체의프라퍼티명 ( 또는파라미터객체가 String, Integer 처럼간단한값이라면파라미터객체자체의값 ) 이다. JdbcType 요소는파라미터의 JDBC 타입을지정하기위해사용된다. 값은 java.sql.types(varchar, INTEGER, 등등.) 의목록중하나의타입이어야만한다. 대개 jdbctype 요소는값이 NULL 일수있거나 DATE 나 TIME 필드를사용하도록지정할가능성이있다면필요하다. NullValue 요소는위에서언급된것처럼 NULL 대체값을지정하기위해사용된다. JdbcType 을지정하지않는다면 nullvalue 를지정할수없다는것에주 25
의하라. 이문법은일반적인형태의파라미터맵의몇가지고급옵션을사용할필요가없을때 ( 예를들어, 저장프로시저를호출할때 ) 대부분적절한다. 좀더상세한문법은다음과같다. #propertyname,javatype=?,jdbctype=?,mode=?,nullvalue=?,handler=?,numericscale=?#? 는해당속성에대해지정해야하는값이다. 상세한문법은일반적인파라미터맵의대부분의값을사용하도록해준다. PropertyName 요소는필수이고나머지는선택적이다. propertyname 요소가가장먼저나와야한다는것을제외하고는이값들은특별한순서를가지지않으므로임의의순서로지정하면된다. 각각의속성들을위해사용되는값들은일반적인형태의파라미터맵을사용할때사용된값과같다. 이문법에대해유의하라. Handler 속성은별칭이등록되는것처럼타입핸들러를위한별칭형태의이름을사용할것이다. 저장프로시저를호출하기위해사용된이분법의예제는다음과같다. <procedure id= callprocedure parameterclass= com.mydomain.myparameter > {call MyProcedure (#parm1,jdbctype=integer,mode=in#, #parm2,jdbctype=integer,mode=in#, #parm3,jdbctype=decimal,mode=out,numericscale=2#)} </procedure> 원시타입파라미터 파라미터처럼사용하기위해자바빈을쓰는것은언제나필요하고편리한것은아니다. 이런경우에당신은직접적으로파라미터를사용하는것처럼원시타입래퍼객체 (String, Integer, Date등등 ) 를사용하는것을환영할것이다. 예를들면 : <select id= insertproduct parameter= java.lang.integer > select * from PRODUCT where PRD_ID = #value# PRD_ID 가숫자타입이라고가정하자. 호출이되었을때 java.lang.integer 객체를전달할수있는매핑구문를만들것이다. #value# 파라미터는 Integer 인스턴스의값으로대체될것이다. value 라는이름은간단한문법 ( 이를테면괄호 ) 안의요소이고별명이될수있다. 결과맵는 result 처럼원시타입을잘지원한다. 파라미터로원시타입을사용하는방법에대해서좀더다양한정보를위해서는결과맵섹션과프로그래밍 SQL Maps(API) 를보라. 원시타입은좀더간결한코드를위해서별칭된다. 예를들면 int 는 java.lang.integer 대신에사용될수있다. 별칭아래의 파라미터 Map 과결과맵을위해지원되는타입 이라는제목의테이블에서이야기된다. Map 타입파라미터 당신이자바빈즈클래스를쓰는것이필요하지않거나편리하지않은위치에있고하나의원시타입파라미터을쓰지는않는다면파라미터객체로 Map( 이를테면 HashMap, TreeMap) 을사용할수있다. 예를들면 : <select id= insertproduct parameterclass= java.util.map > select * from PRODUCT where PRD_CAT_ID = #catid# and PRD_CODE = #code# 매핑구문구현내에서는차이점이없다는것을알라. 위의예제에서만약 Map 인스턴스가 statement 를위한호출로전달되었다면 Map 은 catid 과 code 라는이름의키를포함해야만한다. 이값은 Integer 과 String 과같은선호되는타입이되는그런키에의해참조된다. 결과맵은 result 처럼 Map 타입을아주잘지원한다. 파라미터처럼 Map 타입을사용하는것에대한좀더상세한정보를위해서는결과맵섹션과프로그래밍 SQL Map(API) 를보라. Map 타입역시좀더간결한코드를위해별칭된다. 예를들면 map 는 java.util.map 을대신할수있다. 별칭은아래의 파라미터 Map 과결과맵을위해지원되는타입 이라는제목의테이블에서이야기된다. 26
대체문자열 ibatis 는 SQL 을실행하기위해언제나 JDBC prepared 구문을사용한다. JDBC prepared 구문은 파라미터제조기 (parameter markers) 를사용해서파라미터를지원한다. 파라미터맵과인라인파라미터모두 ibatis 에지정된파라미터대신에파라미터제조기로 SQL 을생성하도록한다. 예를들어, 구문을다음처럼만든다면 select * from PRODUCT where PRD_ID = #value# ibatis 는이 SQL 문자열로 prepared 구문을생성할것이다. select * from PRODUCT where PRD_ID =? 데이터베이스는대부분의경우파라미터제조기를허용하지만 SQL 구문의일부에대해서만이다. 예를들어, 다음과같은형태의구문은허용되지않는다. select * from? 데이터베이스는이구문은처리할수는없을것이다. 왜냐하면사용할테이블의무엇인지알지못하기때문이다. 그래서다음처럼구문을지정한다면, select * from #tablename# SQLException 을보게될것이다. 이러한몇가지이슈를극복하기위해, ibatis 는구문이준비되기전에문자열을 SQL 로대체하기위한문법을제공한다. 동적인 SQL 구문을생성하기위해대체문자열지원을사용할수있다. 대체문자열의예제는다음과같다. select * from $tablename$ 이러한문법으로, ibatis 는구문이준비되기전에 tablename 프라퍼티의값을 SQL 로대체할것이다. 이러한지원기능으로, 문자열을 SQL 구문의일부로대체할수있다. 중요한노트 1: 이지원기능은 String 타입만을대체한다. 그래서 Date 나 Timestamp 와같은복잡한데이터타입에는적절하지않다. 중요한노트 2: SQL select 구문에서테이블이름, 칼럼목록을변경하기위해이기능을사용한다면, 언제나지정해야만한다. 결과맵 결과맵은 SQL Maps 의가장중요한컴포넌트이다. resultmap 는자바빈즈프라퍼티를매핑된쿼리구문을실행함으로써생산된 ResultSet 의칼럼에매핑시키는책임을진다. resultmap 의구조는다음과같이보인다. <resultmap id= resultmapname class= some.domain.class [extends= parent-resultmap ] [groupby= some property list ]> <result property= propertyname column= COLUMN_NAME [columnindex= 1 ] [javatype= int ] [jdbctype= NUMERIC ] [nullvalue= -999999 ] [select= someotherstatement ] [resultmap= someotherresultmap ] [typehandler= com.mydomain.mytypehandler ] /> <result /> <result /> <result /> </resultmap> [ 괄호 ] 부분은옵션이다. resultmap 는구문을참조하기위해사용할 id 속성을가진다. resultmap 는클래스나타입별칭의전체경로를포함한이름인 class 속성을가진다. 이클래스는이것을포함하는 result 매핑에기반하여초기화되고생성될것이다. Extends속성은 resultmap 에기초한다른 resultmap 의이름을옵션적으로셋팅할수있다. 이것은상위 27
resultmap 의모든프라퍼티가하위 resultmap 의부분을포함하는것처럼자바내에서클래스를확장하는것과유사하다. 상위 resultmap 의프라퍼티는하위 resultmap 프라퍼티와부모 resultmap 가자식앞에서정의되기전에언제나추가된다. 상위 / 하위 resultmap 를위한클래스는같은것을필요로하지않을뿐아니라모든것이관련될필요도없다. resultmap 은자바빈즈를 ResultSet 의칼럼에매핑시키는어느정도의프라퍼티매핑을포함할수있다. 그런프라퍼티매핑은문서내에서정의하기위해적용될것이다. 관련클래스는각각의프라퍼티, Map 또는 XML 을위한 get/set메소드를가진자바빈즈와호환되는클래스여야만한다. 주의! 칼럼은결과맵내에서정의되기위해서명시적으로읽을것이다. 다음의섹션은 property 요소의속성들을서술한다. property 결과맵의 property 속성은매핑구문에의해반환되는결과객체의자바빈즈프라퍼티 (get메소드) 이름이다. 이름은결과를생성할때필요한횟수에의존적인값보다더크게사용될수있다. column column 속성값은프라퍼티를생성하기위해사용될값들로부터의 ResultSet 내의칼럼의이름이다. columnindex 옵션적인 ( 최소한의 ) 성능향상을위해서 columnindex 속성값은자바빈즈프라퍼티를생성하기위해사용될값으로부터의 ResultSet 내의칼럼의인덱스이다. 이것은애플리케이션의 99% 정도엔필요하지않을것이고유지를위한노력과속도를위해가독성을희생한다. 몇몇 JDBC 드라이버는다른것들이동적으로속도를올려주는동안어떤성능이득도구체화하지않을것이다. jdbctype jdbctype 속성은자바빈즈프라퍼티를생성하는데사용되는 ResultSet 칼럼의데이터베이스칼럼타입을명시적으로정의하는데사용된다. 비록결과맵이 null 값과함께같은어려움을가지지않는다고하더라도 Date 프라퍼티처럼어떤매핑타입을위해유용할수있는타입을정의한다. 자바는오직하나의 Date 값타입을가지고 SQL 데이터베이스는여러가지를가지기때문에 dates( 또는다른타입 ) 타입을정확하게셋팅하는것을확신하는몇몇경우에필요하게될것이다. 유사하게도 String 타입은 VARCHAR, CHAR 또는 CLOB 에의해생성될것이다. 그래서그런경우에필요한타입을정의하라. javatype javatype 속성은셋팅되는프라퍼티의자바프라퍼티타입을명시적으로정의하기위해사용된다. 대개이것은리플렉션 (reflection) 을통해자바빈즈프라퍼티로부터끌어낼수있다. 하지만 Map 와 XML 매핑과같은매핑은프레임워크를위한타입을제공할수없다. 만약 javatype 가셋팅되지않고프레임워크가그타입을구분할수없다면타입은객체로가정되어처리될것이다. nullvalue nullvalue 속성은데이터베이스내에서 NULL 값을대신해서사용되기위한값을정의한다. 그래서만약 ResultSet으로부터 NULL 이읽었다면자바빈프라퍼티는 NULL 대신에 nullvalue 속성에의해정의된값을셋팅할것이다. null 속성값은어떠한값을될수있지만프라퍼티타입을위해서는적절해야만한다. 만약당신의데이터베이스가 NULL 이가능한칼럼을가진다면당신은당신의애플리케이션이다음처럼결과맵에서그것을정의할수있는변수값과함께 NULL 을표시하기를원한다. <resultmap id= get-product-result class= com.ibatis.example.product > <result property= id column= PRD_ID /> 28
<result property= description column= PRD_DESCRIPTION /> <result property= subcode column= PRD_SUB_CODE nullvalue= -999 /> </resultmap> 위예제에서만약 PRD_SUB_CODE 이 NULL 로읽혀진다면 subcode 프라퍼티는 -999 라는값으로셋팅될것이다. 이것은당신에게데이터베이스내에서 NULL 이가능한칼럼을표현하기위해당신의자바클래스내에원시타입을사용하도록허락할것이다. 만약당신이 updates/inserts 같은쿼리를위한작업을수행하기를원할때당신은파라미터맵에 nullvalue를정의해야만한다는것을기억해라. select select 속성은객체사이의관계를서술하고자동적으로복합프라퍼티타입을로드하는데사용된다. 구문프라퍼티값은다른매핑구문의이름이되어야만한다. 데이터베이스칼럼값은구문속성이파라미터처럼관계된매핑구문으로전달하는것처럼같은 property 요소내에정의된다. 그러므로칼럼은원시타입으로지원이되어야한다. 지원되는원시타입과복합프라퍼티매핑 / 관계에대한상세정보는이문서나중에이야기된다. resultmap resultmap 속성은결과매핑에서재사용될수있는내포된 resultmap 을언급하기위해사용된다. 이속성은 1:1관계나 1:N 관계에서사용될수있다. 1:N관계라면, 프라퍼티는 Collection(List, Set, Collection 등등 ) 이되어야만한다. ibatis 가레코드를그룹화하는방법을나타내는 resultmap 요소의 groupby 속성을지정해야만한다. 1:1관계에서는, 프라퍼티가어떤타입이어도상관없고 groupby 속성을지정하든하지않든문제가되지않는다. 몇가지조인이 1:N 이고몇가지는 1:1 인경우에 groupby 속성을사용할수있다. typehandler typehandler 속성은디폴트 ibatis 타입대신에이프라퍼티를위해사용될사용자정의타입핸들러를명시하기위해사용된다. 명시한다면, 값은 com.ibatis.sqlmap.engine.type.typehandler 인터페이스나 com.ibatis.sqlmap.client.extensions.typehandlercallback 인터페이스를구현하는패키지경로를포함한클래스명이어야만한다. 이값은전역타입핸들러를무시한다. 이문서의뒷부분에사용자정의타입핸들러에대해상세히다룬다. 내포하는결과맵 만약당신이명시적으로정의된 resultmap 의재사용을요구하지않는다는매우간단한요구사항을가진다면매핑구문의 resultclass 속성을셋팅함으로써결과맵을함축적으로정의하는빠른방법이있다. 이묘기는당신이반환되는결과세트가당신의자바빈의쓰기가능한프라퍼티이름에매치되는칼럼이름 ( 또는라벨 / 별칭 ) 을가지는것을확실해야만한다는것이다. 예를들면만약우리가위에서서술된 Product 클래스를생각할때우리는다음처럼내포하는결과맵으로매핑구문를생성할수있다. <select id= getproduct resultclass= com.ibatis.example.product > select PRD_ID as id, PRD_DESCRIPTION as description from PRODUCT where PRD_ID = #value# 위의매핑구문는 resultclass 를표기하고 Product 클래스의자바빈즈프라퍼티에매치되는각각의칼럼를위한별칭을명시한다. 이것은모두필수 (required) 이다. 결과맵은필요하지않다. 여기서교환 (tradeoff) 은당신이칼럼타입 ( 대개필수가아닌 ) 과 null값 ( 또는다른어떤프라퍼티속성 ) 을정의하는기회를가지지않는것이다. 많은데이터베이스가대소문자를가리지않기때문에내포된결과맵는또한가리지않는다. 만약당신의자바빈이두개의프라퍼티를가진다면하나의이름은 firstname 이고다른것은 firstname이다 ( 두개의값은대소문자의차이이다 ). 그것들은동일하고당신은내포된결과맵를사용할수없을것이다 ( 이것은자바빈클래스의디자인에서잠재적인문제점을파악하게될것이다.). 게다가 resultclass 를통해자동매핑을하면몇몇성능에관련된부하가발생할것이다. ResultSetMetaData 에접근하는것은몇몇쓰여진 JDBC 드라이버로는느리게만들수있다. 29
원시타입의결과 ( 이를테면 String, Integer, Boolean) 자바빈호환클래스를지원하기위해추가적으로결과맵은 String, Integer, Boolean 등등과같은간단한자바타입래퍼를편리하게생성할수있다. 원시타입객체의 collection 은밑에서이야기되는 API(executeQueryForList() 를보라 ) 들을사용해서가져올수있다. 원시타입은자바빈처럼같은방법으로정확하게매핑된다. 원시타입은당신이선호하는 ( 대개 value 또는 val ) 이름형식의어떤것처럼될수있는하나의프라퍼티만을가질수있다. 예를들면우리가전체 Product 클래스대신에모든 product서술자 (description) 의목록만을로드하길원한다면맵은다음처럼보여질것이다. <resultmap id= get-product-result class= java.lang.string > <result property= value column= PRD_DESCRIPTION /> </resultmap> 좀더간단한접근법은매핑구문안에서간단하게결과클래스를사용하는것이다.( as 키워드를사용해서 value 라는칼럼별칭을사용하는것을주의깊게보라.) <select id= getproductcount resultclass= java.lang.integer > select count(1) as value from PRODUCT Map Results 결과맵s 은 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 column= PRD_DESCRIPTION /> <result property= suggestedprice column= PRD_SUGGESTED_PRICE /> </resultmap> 위예제에서 HashMap 인스턴스는 Product 데이터를생성할것이다. 프라퍼티이름속성 ( 이를테면 id ) 은 HashMap 의키가될것이다. 매핑칼럼의값은 HashMap 의값이될것이다. 물론당신은 Map 타입을가지고내포된결과맵을사용할수도있다. 예를들면 : <select id= getproductcount resultclass= java.util.hashmap > select * from PRODUCT 위의것은반환된 ResultSet 의 Map 표현을당신에게줄것이다. 복합 (Complex) 프라퍼티 ( 이를테면사용자에의해정의된클래스의프라퍼티 ) 이것은선호하는데이터와클래스를로드하는방법을알고있는매핑구문와함께관련된 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 /> 30
<result property= description column= CAT_DESCRIPTION /> </resultmap> <select id= getproduct parameterclass= int resultmap= get-product-result > select * from PRODUCT where PRD_ID = #value# <select id= getcategory parameterclass= int resultmap= get-category-result > select * from CATEGORY where CAT_ID = #value# 위예제에서 Product 의인스턴스는 Category 타입의 category 를호출하는프라퍼티를가진다. Category 는복합사용자타입이기때문에 JDBC 는그것을생성하는방법을가지지않는다. 프라퍼티매핑과함께다른매핑구문를관련시킴으로써우리는그것을생성하기위한 SQL Map 엔진을위해충분한정보를제공한다. getproduct 를수행하면 get-product-result 결과맵이 PRD_CAT_ID 칼럼내반환되는값을사용해서 getcategory 을호출할것이다. get-category-result 결과맵은 Category 를초기화할것이고그것을생성한다. 전체 Category 인스턴스는 Product 의 category 프라퍼티로셋팅한다. N+1 조회 (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> <select id= getproduct parameterclass= int resultmap= get-product-result > select * from PRODUCT, CATEGORY where PRD_CAT_ID=CAT_ID and PRD_ID = #value# ibatis 버전 2.2.0 또는그이후의버전에서, 칼럼을반복하는대신에 1:1 쿼리로결과맵을재사용할수있다. 이사용법의예제는다음과같다. <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 resultmap= get-category-result /> </resultmap> <resultmap id= get-category-result class= com.ibatis.example.category > <result property= id column= CAT_ID /> <result property= description column= CAT_DESCRIPTION /> </resultmap> <select id= getproduct parameterclass= int resultmap= get-product-result > select * from PRODUCT, CATEGORY 31