ibatis SQL Maps 개발자가이드 Version 2.0 June 17, 2004 번역 : 이동국 (fromm0@gmail.com) 오타및오역은위메일주소로보내주시기바랍니다. 1
소개 SQL Maps 프레임워크는당신이관계형데이터베이스에접근할때필요한자바코드를현저하게줄일수있도록도와줄것이다. SQL Maps는간단한 XML서술자를사용해서간단하게자바빈즈를 SQL statement에맵핑시킨다. 간단함이란다른프레임워크와객체관계맵핑툴에비해 SQL Maps의가장큰장점이다. SQL Maps를사용하기위해서당신은자바빈즈와 XML 그리고 SQL에친숙할필요가있다. 여기엔배워야할것도거의없고테이블을조인하거나복잡한쿼리문을수행하기위해필요한복잡한스키마도없다. SQL Maps를사용하면당신은실제 SQL문의모든기능을가질수있다. SQL Maps (com.ibatis.sqlmap.*) 개념 SQL Map API 는프로그래머에게자바빈즈를 PreparedStatement 파라미터와 ResultSets 으로쉽게맵핑할수있도록한다. SQL Maps 의기본적인생각은간단함 (simple) 이다. 이는자바코드의 20% 를사용하여 JDBC 기능의 80% 를제공하는간단한프레 임워크라는뜻이다. 이것은어떻게작동하는가.? SQL Maps 는자바빈즈, Map 구현, 원시래퍼타입 (String, Integer ) 그리고 SQL 문을위한 XML 문서를맵핑하기위한 XML 서술자를사용하는매우간단한프레임워크를제공한다. 다음은생명주기에대한높은레벨의서술이다. 1) 파라미터 ( 자바빈즈, Map 또는원시래퍼 ) 로써객체를제공한다. 파라미터객체는 update 문내에입력값을셋팅하 기위해사용되거나쿼리문의 where 절을셋팅하기위해서사용된다. 2) 맵핑된 statement 을실행한다. 이단계는마법이일어나는곳이다. SQL Maps 프레임워크는 PreparedStatement 인스턴스를생성할것이고제공된파라미터객체를사용해서파라미터를셋팅한다. 그리고 statement 를실행하고 ResultSet 으로부터결과객체를생성한다. 3) update 의경우에영향을미친 rows 의숫자를반환한다. 조회문일경우에한개 (single) 의객체또는컬렉션객 체를반환한다. 파라미터처럼결과객체는자바빈즈, Map 원시타입래퍼또는 XML 이될수있다. 밑의다이어그램은서술된것을설명한다. 2
설치 SQL Maps 프레임워크설치는간단하게클래스패스에필요한 JAR 파일을두면된다. 이것은 JVM 시작시정의된클래스패스에 두거나웹애플리케이션의 /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 파일을클래스패스에두면된다. File Name Description Required ibatis-common.jar ibatis Common Utilities YES ibatis-sqlmap.jar ibatis SQL Maps Framework YES ibatis-dao.jar ibatis Data Access Objects Framework. NO JAR 파일과의존성 프레임워크가너무많은의존성을가진다면이것은애플리케이션이나다른프레임워크에통합되기힘들게만든다. 2.0의중요한키포인트는의존성관리와제거의중점을두었다. 그러므로만약당신이 jdk1.4를사용한다면실제의존적인것은 Jakarta Commons Logging 프레임워크뿐이다. 이추가적인 JAR파일은배포판의 /lib/optional디렉토리에서찾을수있다. 그들은기능에의해분류된다. 다음은추가적인패키지를사용할때필요한것들의목록이다. Description When to Use Directories 3
Legacy JDK Support ibatis Backward Compatibility Runtime Bytecode Enhancement DataSource Implementation Distributed Caching Logging Solution 만약에당신이 JDK1.4보다하위버전을사용하고당신의애플리케이션서버가이런 JAR파일을제공하지않는다면당신은이런옵션패키지가필요할것이다. 당신이 ibatis의예전버전 DAO(1.x) 프레임워크를사용하고있거나 SQL Maps(1.x) 의예전버전을사용하고있다면이디렉토리의 JAR파일을간단하포함시킴으로써계속작업을할수있다. 만약당신이늦은 (lazy) 로딩과성능에대해고려하기위한 CGLIB2.0 bytecode 개선을사용하길원한다면당신이 Jakarta DBCP Connection pool을사용하길원한다면중앙집중적이거나분산캐슁지원을위한 OSCache를사용하길원한다면 Log4J 로깅을사용하길원한다면 /lib/optional/jdbc /lib/optional/jta /lib/optional/xml /lib/optional/compatibility /lib/optional/enhancement /lib/optional/dbcp /lib/optional/caching /lib/optional/logging 1.x 에서업그레이드하기 당신은업그레이드할것인가.? 만약당신이업그레이드를시도한다면결정할수있는가장좋은방법이다. 여기에몇가지업그레이드절차가있다. 1. 버전 2.0은 1.x릴리즈와거의완벽한호완성을가지도록유지되었다. 그래서몇몇사람들에게는단순히 JAR파일만교체하는것으로충분할것이다. 이것은최소한의이득을발생시키지만가장간단하다. 당신은당신의 XML파일이나자바코드를변경할필요가없다. 몇몇모순되는것들이발견될지도모른다. 2. 두번째는당신의 XML파일을 2.0스펙에적합하도록변경하는것이다. 하지만이는 1.x 자바 API를그대로사용한다. 적은호환성이슈내안전한해결법은맵핑파일사이에발생한다. Ant작업은당신을위해 XML파일을변환하기위해서프레임워크에포함된다. 3. 세번째옵션은당신의 XML파일과자바코드를변환하는것이다. 자바코드를변환하기위한툴은없다. 그래서이것은손으로직접해야한다. 4. 마지막옵션은전체를업그레이드하지않는것이다. 만약에당신이어렵다고느낀다면 1.x릴리즈에서시스템이작동하는것을두려워하지마라. 당신의오래된애플리케이션을그대로놔두는것은나쁜생각이아니다. 만약에오래된애플리케이션이인식적인면에서제대로리팩토링되어있다면당신은 SQL Maps를업그레이드잘할수있을것이다. 1.x 에서 2.x 으로 XML 설정파일변환하기 2.0프레임워크는 Ant빌드시스템을통해수행되는 XML문서변환기를포함한다. 당신의 XML문서를변환하는것은 1.x코드가작동중에자동으로오래된 XML파일을변환하는것처럼옵션적이다. 여전히당신이업그레이드를함으로써편안하게당신의파일을변환하는것이좋은생각이다. 당신은다소적은호환적인이슈를경험할것이고새로운기능중몇개의장점을얻을수있을것이다 ( 비록당신이 1.x자바 API을사용하더라도.). 4
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 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 New Files ibatis-common.jar ( 필수 ) ibatis-sqlmap.jar ( 필수 ) ibatis-dao.jar (DAO프레임워크를사용하는것에따라옵션 ) 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 를사용하는것에대해소개할것이다. 5
SQL Map XML 설정파일 (http://www.ibatis.com/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.com//dtd SQL Map Config 2.0//EN" "http://www.ibatis.com/dtd/sql-map-config-2.dtd"> <!--Always ensure to use the correct XML header as above! --> <sqlmapconfig> <!--The properties (name=value) in the file specified here can be used placeholders in this config file (e.g. ${driver}. The file is relative to the classpath and is completely optional. --> <properties resource=" examples/sqlmap/maps/sqlmapconfigexample.properties " /> <!--These settings control SqlMapClient configuration details, primarily to do with transaction management. They are all optional (more detail later in this document). --> <settings cachemodelsenabled="true" enhancementenabled="true" lazyloadingenabled="true" maxrequests="32" maxsessions="10" maxtransactions="5" usestatementnamespaces="false" /> <!--Type aliases allow you to use a shorter name for long fully qualified class names. --> <typealias alias="order" type="testdomain.order"/> <!--Configure a datasource to use with this SQL Map using SimpleDataSource. Notice the use of the properties from the above resource --> <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"/> 6
</datasource> </transactionmanager> <!--Identify all SQL Map XML files to be loaded by this SQL map. Notice the paths are relative to the classpath. For now, we only have one --> <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 요 소와그것의모든속성값은모두옵션적이다. 제공되는속성값과그것들의다양한행위는다음의테이블에서서술된다. maxrequests 이것은한꺼번에 SQL문을수행할수있는쓰레드의수이다. 셋팅값보다많은쓰레드는다른쓰레드가수행을완료할때까지블록된다. 다른 DBMS는다른제한을가진다. 이것은최소한 10개의 maxtransactions이고언제나 maxsessions과 maxtransactions보다크다. 종종동시요청값의최대치를줄이면성능향상을보여준다. maxsessions 예 : maxrequests= 256 Default: 512 이것은주어진시간동안활성될수있는세션의수이다. 세션은명시적으로주어질수도있고프로그램적으로요청될수도있고쓰레드가 SqlMapClient 인스턴스를사용할때마다자동적으로생성될수도있다. 이것은언제나 7
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) <typealias> 요소 typealias 요소는길고전체적인클래스명을참조하기위한짧은이름을명시하도록한다. 예를들면 <typealias alias="shortname" type="com.long.class.path.class"/> SQL Maps 설정파일에서사용되는미리정의된몇몇 alias 가있다. 그것들은 Transaction Manager Aliases 8
JDBC JTA EXTERNAL Data Source Factory Aliases SIMPLE DBCP <transactionmanager> 요소 com.ibatis.sqlmap.engine.transaction.jdbc.jdbctransactionconfig com.ibatis.sqlmap.engine.transaction.jta.jtatransactionconfig com.ibatis.sqlmap.engine.transaction.external.externaltransactionconfig com.ibatis.sqlmap.engine.datasource.simpledatasourcefactory com.ibatis.sqlmap.engine.datasource.dbcpdatasourcefactory 1.0 변환노트 : SQL Maps 1.0 은다중의데이터소스설정을허락했다. 이것은다루기어렵고몇가지나쁜예제를소개했다. 그 러므로 2.0 에서는오직하나의데이터소스만을허락한다. 다중의배치 / 설정을위해서는시스템에의해다르게설정되거나 SQL Maps 를빌드할때파라미터처럼전달되는다중속성파일이추천된다. <transactionmanager> 요소는당신이 SQL Maps 를위한트랜잭션관리를설정하도록한다. type 속성값은사용하기위한 트랜잭션관리자는표시한다. 그값은클래스명이거나타입 alias 일수있다. 3 개의트랜잭션관리자는 JDBC, JTA 그리고 EXTERNAL 중에하나로표시할수있다. JDBC Connection commit() 과 rollback() 메소드를통해트랜잭션를제어하기위한 JDBC 를사용하게된다. JTA 이트랜잭션관리자는 SQL Maps 가다른데이터베이스나트랜잭션자원을포함하는더욱더넓은범위의트랜잭 션을포함하도록하는 JTA 전역트랜잭션를사용한다. 이설정은 JNDI 자원으로부터사용자트랜잭션을위치시키기위 한 UserTransaction 속성값을요구한다. JNDI 데이터소스예제는다음의설정예제에서보라. EXTERNAL 이것은당신자신이트랜잭션을관리하도록한다. 당신은여전히데이터소스를설정할수있지만프레임워크생명주기의부분처럼트랜잭션이커밋되거나롤백되지않는다. 이것은당신애플리케이션의부분이외부적으로 SQL Maps 트랜잭션을관리해야한다는것이다. 이셋팅은비-트랜잭션 ( 예를들면읽기전용 ) 데이터베이스에유용하다. <datasource> 요소 트랜잭션관리자설정의포함된부분은 datasource 요소이고 SQL Maps 를사용하기위한데이터소스를설정하기위한속성값 의집합이다. 여기엔프레임워크에서제공되는 3 가지데이터소스타입이있지만당신은당신만의데이터소스를사용할수도 있다. 포함된 DataSourceFactory 구현은다음에상세하게논의가될것이고각각을위해제공되는설정은아래예제를보라. SimpleDataSourceFactory SimpleDataSource 는데이터소스를제공하는컨테이너가없는경우에 connection 을제공하기위해기본적으로풀 링 (pooling) 데이터소스구현을제공한다. 이것은 ibatis SimpleDataSource connection 풀링을기초로한다. <transactionmanager type="jdbc"> <datasource type="simple"> <property name="jdbc.driver" value="org.postgresql.driver"/> <property name="jdbc.connectionurl" value="jdbc:postgresql://server:5432/dbname"/> <property name="jdbc.username" value="user"/> <property name="jdbc.password" value="password"/> <!--OPTIONAL PROPERTIES BELOW --> <property name="pool.maximumactiveconnections" value="10"/> <property name="pool.maximumidleconnections" value="5"/> <property name="pool.maximumcheckouttime" value="120000"/> 9
<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"/> </datasource> </transactionmanager> DbcpDataSourceFactory 이구현물은 DataSource API 를통해 connection 풀링서비스를제공하기위해 Jakarta DBCP (Database Connection Pool) 을사용한다. 이 DataSource 는애플리케이션 / 웹컨테이너가 DataSource 구현물을제공하지못 하거나당신이 standalone 애플리케이션을구동할때이상적이다. DbcpDataSourceFactory 를위해명시해야하 는설정파라미터의예제는다음과같다. <transactionmanager type="jdbc"> <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 --> <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="pool.logabandoned" value="false"/> <property name="pool.removeabandoned" value="false"/> <property name="pool.removeabandonedtimeout" value="50000"/> </datasource> </transactionmanager> JndiDataSourceFactory 이구현물은애플리케이션컨테이너내 JNDI 컨텍스트로부터 DataSource 구현물을가져와야할것이다. 이것은전형적 으로애플리케이션서버를사용중이고컨테이너관리 connection 풀그리고제공되는 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:/ctx/con/usertransaction"/> 10
<datasource type="jndi"> <property name="datasource" value="java:comp/env/jdbc/jpetstore"/> </datasource> </transactionmanager> UserTransaction 인스턴스가발견될수있는 JNDI 위치를가지키는 UserTransaction 값에주의하라. 좀더넓은범위 의트랜잭션을가지는 SQL Maps 가다른데이터베이스와트랜잭션자원을포함하기위해서는 JTA 트랜잭션관리가 요구된다. <sqlmap> 요소 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 " /> <sqlmap url="file:///c:/config/account.xml " /> <sqlmap url="file:///c:/config/product.xml" /> 다음의다양한섹견은 SQL Map XML 파일들의구조에대해서서술한다. SQL Map XML 파일 ( http://www.ibatis.com/dtd/sql-mapconfig-2.dtd) 위예제에서우리는 SQL Maps 의가장간단한형태를보았다. SQL Map 문서구조내에사용가능한다른옵션이있다. 좀더 많은기능을가지는 mapped statement 의예제이다. <sqlmap id= Product > <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> 11
</sqlmap> <select id= getproduct parametermap= productparam resultmap= productresult cachemodel= product-cache > select * from PRODUCT where PRD_ID =? </select> 너무많은가.? 비록프레임워크가당신을위해많은것을하더라도간단한 select statement 를위해너무많은추가적인작 업을하는것처럼보인다. 걱정하지마라다음은위의것의축소버전이다. <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 id= inserttestproduct > 12
insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (1, Shih Tzu ) 위예제는명백하게발생할꺼같지는않다. 어쨌든이것은당신이임의의 SQL 문을실행하기위해 SQL Map 프레임워크를사 용한다면유용할수있다. 어쨌든이것은파라미터 Maps 와 Result Maps 을사용하는자바빈즈맵핑기능을공통적으로사용할 것이다. 다음의다양한섹션은구조와속성, 그들이어떻게맵핑된 statement 에영향을끼치는지서술한다. Statement 타입 <statement> 요소는어떤타입의 SQL 문을사용할수있는일반적인 catch all statement이다. 일반적으로이것은좀더다양한특성의 statement요소중하나를사용하기위한좋은생각이다. 좀더다양한특성의요소는좀더직관적인 XML DTD를제공하고때때로일반적인 <statement> 요소가제공하지않는추가적인기능을제공한다. 다음의테이블은 statement요소와그들이지원하는속성과기능을목록화한다. Statement Element Attributes Child Elements Methods <statement> id parameterclass resultclass parametermap resultmap cachemodel xmlresultname All dynamic elements insert update delete All query methods <insert> <update> <delete> <select> <procedure> id parameterclass parametermap id parameterclass parametermap id parameterclass parametermap id parameterclass resultclass parametermap resultmap cachemodel id parameterclass resultclass parametermap resultmap xmlresultname All dynamic elements <selectkey> All dynamic elements All dynamic elements All dynamic elements All dynamic elements insert update delete insert update delete insert update delete All query methods insert update delete All query methods The SQL SQL 은 map의가장중요한부분을차지한다. 이것은당신의데이터베이스와 JDBC드라이버에적합한어떤 SQL이될수있다. 당신은가능한어떤기능을사용할수있고당신의드라이버가지원하는한다중 statement에전달할수도있다. 당신이하나의문서에서 SQL과 XML을혼합하기때문에특수문자의충돌이잠재적으로존재한다. 대부분의공통적인것은 greater-than 과 less-than 문자들이다.(<>). 이것들은 SQL문에서공통적으로요구되고 XML에서는예약어이다. 당신의 SQL문에들어갈필요가있는특수 XML문자를처리하기위한간단한해결법이있다. 표준적인 XML CDATA 섹션을사용함으로써특수문자의어떤것 13
도파싱되지않고문제는해결된다. 예를들면 <statement id="getpersonsbyage" parameterclass= int resultclass="examples.domain.person"> <![CDATA[ SELECT * FROM PERSON WHERE AGE > #value# ]]> 자동생성키 많은관계형데이터베이스시스템은기본키 (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" 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 id="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"/> <parameter property="email2" jdbctype="varchar" javatype="java.lang.string" mode="inout"/> </parametermap> <procedure id="swapemailaddresses" parametermap="swapparameters" > {call swap_email_address (?,?)} </procedure> 위처럼프로시저를호출하는것은파라미터객체 (map) 내에서두개의칼럼사이에두개의이메일주소를교체하는것이다. 파라 14
미터객체는파라미터맵핑의 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#) 중요 : 비록이전버전과의호환성을위한옵션이지만이것은언제나파라미터클래스를제공하는것은매우추천되는사항 이다 ( 물론요구되는파라미터가없더라도 ). 프레임워크가먼저타입을안다면스스로최적화능력을가지기때문에당신은 클래스를제공함으로써좀더나은성능을달성할수있다. 명시된 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 (?,?); 위의예제에서, 파라미터 map 은 SQL 문에서값토큰 (? ) 에매치되고정렬되는두개의파라미터를서술한다. 그래서첫번째? 는 id 속성값에대체되고두번째는 description 속성값에대체된다. 파라미터 map 과그들의옵션은이문서나중에좀더 다양하게서술될것이다. 15
인라인파라미터의빠른언급 이문서에나중에제공되는좀더상세화된설명에도불구하고인라인파라미터에대한빠른언급을한다. 인라인파라미터는 맵핑된 statement 내부에서사용될수있다. 예를들면 : <statement id= insertproduct > insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (#id#, #description#); 위예제에서인라인파라미터는 #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" 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객체는프라퍼티이름과칼럼명에기반해서초기화되기위해맵핑되는 result set으로부터초기화되고결과를반환한다. resultclass 으로자동맵핑하는데는몇가지제한점이있다. 출력칼럼의타입을명시하는방법은없다. 관련된데이터를자동적 으로로드하는방법이없고 ResultSetMetaData 에접근하는데필요한접근법내에서하찮은성능결과가있다. 이제한점모드는 명시적인 resultmap 를사용함으로써극복할수있다. Result maps 는이문서나중에좀더상세하게다루어질것이다. resultmap resultmap 프라퍼티는좀더공통적으로사용되고이해하기위해가장중요한속성중에하나이다. 이 resultmap 속성값은명 16
시된 resultmap요소의이름이다. resultmap속성을사용하는것은당신에게 result set으로부터데이터와칼럼에맵핑되는프라퍼티를어떻게꺼내는지제어하도록한다. resultclass속성을사용하는자동맵핑접근법과는달리 resultmap는당신에게칼럼타입을명시하고 null값을대체그리고복합프라퍼티맵핑 ( 다른자바빈즈, Collections 그리고원시타입래퍼 ) 을허락한다. resultmap 구조의모든상세정보는이문서나중에설명된다. 하지만다음의예제는 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 * from PRODUCT 위예제에서 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# 위예제에서캐쉬는 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, 17
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>1900-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 요소의속성을서술한다. property 파라미터 map 의 property 속성은맵핑된 statement 에전달되는파라미터객체의자바빈즈프라퍼티 (get 메소드 ) 의이름이다. 그이름은 statement 내에필요한횟수에의존하는것보다좀더사용될수있다. jdbctype jdbctype 속성은이프라퍼티에의해셋팅되는파라미터의칼럼타입을명시적으로정의하는데사용된다. 몇몇 JDBC 드라이 버는명시적인드라이버칼럼타입을부르는것없이어떤작동을위해칼럼의타입을확인할수없다. 이것의완벽한예제는 18
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가셋팅되지않고프레임워크도어떤타입인지구별할수없다면타입은객체로간주될것이다. nullvalue nullvalue 속성은어떤유효한값 ( 프라퍼티타입에기초로해서 ) 에셋팅할수있다. null 속성은 null값대체를정의하기위해사용된다. 이것이의미하는것은자바빈즈프라퍼티내에서검색되는값인 NULL이데이터베이스에쓰여질것이라는것이다 ( 들어오는 null값대체의상반된행위 ). 이것은당신에게 null값을지원하지않는타입 ( 이를테면 int, double, float등등 ) 을위해당신의애플리케이션내에 magic null 숫자를사용하도록허락한다. 프라퍼티의그런타입은적합한 null값을포함할때 NULL은값대신에데이터베이스에쓰여질것이다. <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 PRODUCT (PRD_ID, PRD_DESCRIPTION) values (?,?); 19
위예제에서자바빈즈프라퍼티인 id 와 description 는목록화되는순서대로맵핑된 Statement 인 insertproduct 의파라미터 에적용될것이다. 그래서 id 는첫번째파라미터 (?) 에적용되고 description 는두번째파라미터에적용된다. 만약에순서가반 대라면 XML 은다음처럼보일것이다. <parametermap id= insert-product-param class= com.domain.product > <parameter property= description /> <parameter property= id /> </parametermap> <statement id= insertproduct parametermap= insert-product-param > insert into PRODUCT (PRD_DESCRIPTION, PRD_ID) values (?,?); 주의! 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 id= insertproduct parameterclass= com.domain.product > insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (#id:numeric#, #description:varchar#); 타입을선언하는것과 null 값대체는다음문법을사용함으로써인라인파라미터로할수있다. <statement id= insertproduct parameterclass= com.domain.product > insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (#id:numeric:-999999#, #description:varchar:no_entry#); 주의! 인라인파라미터를사용할때당신은타입정의없이 null 값대체를명시할수없다. 당신은순서대로파싱하기위해둘다 명시해야한다. 주의! Null 값의완전한투명성을원한다면당신은이문서의나중에설명되는것처럼당신의 result maps 내에 null 값대체를 반드시명시해야한다. 20
주의! 당신이많은수의타입서술자와 null 값대체가필요하다면당신은외부적인것을사용해서코드를정리할수있어야 할것이다. 원시타입파라미터 파라미터처럼사용하기위해자바빈을쓰는것은언제나필요하고편리한것은아니다. 이런경우에당신은직접적으로파 라미터를사용하는것처럼원시타입래퍼객체 (String, Integer, Date 등등 ) 를사용하는것을환영할것이다. 예를들면 : <statement id= insertproduct parameter= java.lang.integer > select * from PRODUCT where PRD_ID = #value# PRD_ID가숫자타입이라고가정하자. 호출이되었을때 java.lang.integer객체를전달할수있는맵핑된 statement를만들것이다. #value# 파라미터는 Integer인스턴스의값으로대체될것이다. value 라는이름은간단한문법 ( 이를테면괄호 ) 안의요소이고별명이될수있다. Result Map는 result처럼원시타입을잘지원한다. 파라미터로원시타입을사용하는방법에대해서좀더다양한정보를위해서는 Result Map섹션과프로그래밍 SQL Maps(API) 를보라. 원시타입은좀더간결한코드를위해서별칭된다. 예를들면 int 는 java.lang.integer 대신에사용될수있다. 별칭은아 래의 파라미터 Map 과 result Map 을위해지원되는타입 이라는제목의테이블에서이야기된다. Map 타입파라미터 당신이자바빈즈클래스를쓰는것이필요하지않거나편리하지않은위치에있고하나의원시타입파라미터을쓰지는않는 다면파라미터객체로 Map( 이를테면 HashMap, TreeMap) 을사용할수있다. 예를들면 : <statement id= insertproduct parameterclass= java.util.map > select * from PRODUCT where PRD_CAT_ID = #catid# and PRD_CODE = #code# 맵핑된 statement구현내에서는차이점이없다는것을알라. 위의예제에서만약 Map인스턴스가 statement를위한호출로전달되었다면 Map은 catid 과 code 라는이름의키를포함해야만한다. 이값은 Integer과 String과같은선호되는타입이되는그런키에의해참조된다. Result Map은 result처럼 Map타입을아주잘지원한다. 파라미터처럼 Map타입을사용하는것에대한좀더상세한정보를위해서는 result Map섹션과프로그래밍 SQL Map(API) 를보라. Map 타입역시좀더간결한코드를위해별칭된다. 예를들면 map 는 java.util.map 을대신할수있다. 별칭은아래의 파라 미터 Map 과 result Map 을위해지원되는타입 이라는제목의테이블에서이야기된다. 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= -999999 ] [select= someotherstatement ] 21
</resultmap> /> <result /> <result /> <result /> [ 괄호 ] 부분은옵션이다. resultmap는스스로 statement가그것을참조하기위해사용할 id속성을가진다. resultmap는클래스나타입별칭의전체경로를포함한이름인 class속성을가진다. 이클래스는이것을포함하는 result맵핑에기반하여초기화되고생성될것이다. Extends속성은 resultmap에기초한다른 resultmap의이름을옵션적으로셋팅할수있다. 이것은상위 resultmap의모든프라퍼티가하위 resultmap의부분을포함하는것처럼자바내에서클래스를확장하는것과유사하다. 상위 resultmap의프라퍼티는하위 resultmap프라퍼티와부모 resultmap가자식앞에서정의되기전에언제나추가된다. 상위 / 하위 resultmap를위한클래스는같은것을필요로하지않을뿐아니라모든것이관련될필요도없다. resultmap 은자바빈즈를 ResultSet 의칼럼에맵핑시키는어느정도의프라퍼티맵핑을포함할수있다. 그런프라퍼티맵핑은 문서내에서정의하기위해적용될것이다. 관련클래스는각각의프라퍼티, Map 또는 XML 을위한 get/set 메소드를가진자바 빈즈와호환되는클래스여야만한다. 주의! 칼럼은 Result Map 내에서정의되기위해서명시적으로읽을것이다. 다음의섹션은 property 요소의속성들을서술한다. 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에의해생성될것이다. 그래서그런경우에필요한타입을정의하라. 22
javatype javatype 속성은셋팅되는프라퍼티의자바프라퍼티타입을명시적으로정의하기위해사용된다. 대개이것은리플렉션 (reflection) 을통해자바빈즈프라퍼티로부터끌어낼수있다. 하지만 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 column= 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 23
from PRODUCT where PRD_ID = #value# 위의맵핑된 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 또는 val ) 이름형식의어떤것처럼될수있는하나의프라퍼티만을가질수있다. 예를들면우리가전체 Product 클래스대신에모든 product 서술자 (description) 의목록만을로드하길원한다면 map 은다음처럼보여질것이다. <resultmap id= get-product-result class= java.lang.string > <result property= value column= PRD_DESCRIPTION /> </resultmap> 좀더간단한접근법은맵핑된 statement 안에서간단하게 result class 를사용하는것이다.( as 키워드를사용해서 value 라 는칼럼별칭을사용하는것을주의깊게보라.) <statement id= getproductcount resultclass= java.lang.integer > select count(1) as value from PRODUCT 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 column= PRD_DESCRIPTION /> <result property= suggestedprice column= PRD_SUGGESTED_PRICE /> </resultmap> 위예제에서 HashMap 인스턴스는 Product 데이터를생성할것이다. 프라퍼티이름속성 ( 이를테면 id ) 은 HashMap 의키가될 것이다. 맵핑칼럼의값은 HashMap 의값이될것이다. 물론당신은 Map 타입을가지고내포된 result map 을사용할수도있다. 24
예를들면 : <statement id= getproductcount resultclass= java.util.hashmap > select * from PRODUCT 위의것은반환된 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 id= getcategory parameterclass= int resultmap= get-category-result > select * from CATEGORY where CAT_ID = #value# 위예제에서 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 Selects (1:1) 피하기 위솔루션을사용할때문제점은당신이 Product를로드할때마다두개 (Product를위해하나그리고 Category를위해서하나. 총 2개 ) 의 SQL문이실제적으로구동된다는것이다. 이문제는하나의 Product를로드할때는큰문제가아닌것처럼보이지만만약 10개의 Product를로드하는쿼리를한다면각각의쿼리는관련된 category를로드하기위한 Product를위해서도실행될것이다. 결과적으로 11번의쿼리를하게된다. Product의목록을위해하나, 관련된 Category를로드하기위해반환되는 Product 를위해하나씩 (N+1 또는이경우엔 10+1=11) 해결법은분리된 select 문대신에조인과내포된 (nested) 프라퍼티맵핑을사용하는것이다. 여기에그와같은상황을사용한 25
예제가있다. <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 PRD_ID = #value# 늦은 (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 id= getproductsbycatid parameterclass= int resultmap= get-product-result > select * from PRODUCT where PRD_CAT_ID = #value# 26
N+1 Selects (1:M 과 M:N) 피하기 이것은위의 1:1 상황과유사하다. 하지만굉장히많은데이터를포함할때좀더큰걱정거리가될것이다. 위해결법과함께문제는당신이 Category를로드할때마다두개의 SQL문 ( 하나는 Category를위한하나이고하나는 Products에대한목록을위한것 ) 은실질적으로수행된다. 이문제는하나의 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 속성이간단 {param1=column1, param2=column2,, paramn=columnn} 할때대안적인문법이다. PAYMENT 테이블이 Customer ID 와 Order ID 를둘다키로할때다음의예제를 보고생각해보라. <resultmap id= get-order-result class= com.ibatis.example.order > <result property= id column= ORD_ID /> <result property= customerid column= ORD_CST_ID /> <result property= payments column= {itemid=ord_id, custid=ord_cst_id} select= getorderpayments /> </resultmap> <statement id= getorderpayments 27
resultmap= get-payment-result > select * from PAYMENT where PAY_ORD_ID = #itemid# and PAY_CST_ID = #custid# 옵션적으로당신은그것들이파라미터처럼같은순서로정렬되는것처럼칼럼이름을정의할수있다. 예를들면 {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 을추가할필요가있을지도모른다.(MS 의지식기반기사 313181 을보 라. http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b313181). 파라미터 Maps 와 Result Maps 를위해지원되는타입들 파라미터와 result 를위해 ibatis 프레임워크에의해지원되는자바타입은다음과같다. Java Type JavaBean/Map Property Mapping Result Class / Parameter Class*** Type Alias** boolean YES NO boolean java.lang.boolean YES YES boolean byte YES NO byte java.lang.byte YES YES byte short YES NO short java.lang.short YES YES short int YES NO int/integer java.lang.integer YES YES int/integer long YES NO long java.lang.long YES YES long float YES NO float java.lang.float YES YES float double YES NO double java.lang.double YES YES double java.lang.string YES YES string 28
java.util.date YES YES date java.math.bigdecimal YES YES decimal * java.sql.date YES YES N/A * java.sql.time YES YES N/A * java.sql.timestamp YES YES N/A * java.sql. date 타입사용은좋지않다 (discouraged). 대신에 java.util.date 를사용하는것이제일좋다. **. 파라미터나 result 클래스를정의할때타입별칭은전체경로의클래스명에두는것이좋다. *** int, boolean and float 와같은원시타입은 ibatis 데이터베이스레이어가완전한객체지향접근법을사용하는것처럼직 접적으로원시타입을지원하지는않는다. 그러므로모든파라미터와 result 는그들의상위레벨에서객체가되어야한다. 부수 적으로 JDK1.5 의 autoboxing 기능은잘사용되기위해원시타입을허락한다. 캐쉬상태의맵핑된 Statement Results 맵핑된 statement 쿼리로부터의 result 는 statement 태크내에 cachemodel 파라미터를정의함으로써간단하게캐쉬될수있다. 캐쉬모델은당신의 SQL Map 내에서정의된설정된캐쉬다. 캐쉬모델은다음처럼 cachemodel 요소를사용해서설정된다. <cachemodel id="product-cache" type ="LRU" readonly= true serialize= false > <flushinterval hours="24"/> <flushonexecute statement="insertproduct"/> <flushonexecute statement="updateproduct"/> <flushonexecute statement="deleteproduct"/> <property name= cache-size value= 1000 /> </cachemodel> 위의캐쉬모델은 LRU(Least Recently Used) 방식을사용해서 product-cache 라는이름의캐쉬인스턴스를생성할것이다. type 속성값은전체경로의클래스명이거나아래처럼구현을포함하는것의별칭이다. flush 요소에기초로하여캐쉬모델내에 서정의된다. 이캐쉬는 24 시간마다삭제된다. interval 요소내에서 hours, minutes, seconds 또는 milliseconds 단위로설정이 되어서삭제된다. 캐쉬는추가적으로 insertproduct, updateproduct, 또는 deleteproduct 맵핑 statement 가수행될때마다삭제 된다. 캐쉬를위해 flush on execute 요소의숫자값이정의될수있다. 몇몇캐쉬구현물은위에서보여지는 cache-size 같은 추가적인프라퍼티를필요로한다. LRU 캐쉬의경우에크기는캐쉬내저장되기위한항목의갯수로결정된다. 캐쉬모델이설 정되었을때당신은맵핑된 statement 에의해사용되기위한캐쉬모델을정의할수있다. 예를들면 <statement id= getproductlist cachemodel= product-cache > select * from PRODUCT where PRD_CAT_ID = #value# 읽기전용대읽기 / 쓰기 프레임워크는읽기전용과읽기 / 쓰기캐쉬를모두지원한다. 읽기전용캐쉬는모든유저에의해공유되어서좀더큰성능향상을보여준다. 어쨌든읽기전용캐쉬로부터읽어들인객체는변경할수없다. 대신에새로운객체는업데이트를위해데이터베이스 ( 또는읽기 / 쓰기캐쉬 ) 로부터읽어야만한다. 반면에정정 (retrieval) 및변경을위한객체를사용할경우에는읽기 / 쓰기캐쉬가추천된다 ( 이를테면필수이다 ). 읽기전용캐쉬를사용하기위해서는캐쉬모델요소에 readonly= true 를셋팅하라. 기초설정값은읽기전용 (true) 이다. 직렬화가능한읽기 / 쓰기캐쉬 29
당신이동의한다면서술된것처럼세션당캐쉬는전역애플리케이션성능에자그마한이익을준다. 읽기 / 쓰기캐쉬의다른타입은전체애플리케이션이직렬화가능한읽기 / 쓰기캐쉬라면성능향상을보여준다. 이캐쉬는각각의세션에캐쉬된객체의다른인스턴스를반환할것이다. 그러므로각각의세션은안전하게반환된인스턴스를변경할수있다. 여기서의미론적인차이점을알아보자면당신은언제나캐쉬로부터반환된같은인스턴스를기대하겠지만캐쉬내에서당신은다른것을얻게될것이다. 또한직렬화가능한캐쉬로부터저장된모든객체는직렬화가능해야만한다. 이것은직렬화가능한캐쉬로조합된늦은 (lazy) 로딩기능을사용하기에는어려울것이라는것을의미한다. 왜냐하면늦은 (lazy) 프록시는직렬화가능하지않기때문이다. 캐쉬의조합을해결하는가장좋은방법은늦은 (lazy) 로딩과테이블조인을간단히시도하는것이다. 직렬화가능한캐쉬를사용하기위해서는기초설정캐쉬모델이읽기전용이고직렬화가능하지않기때문에 readonly= false 와 serialize= true 로셋팅하라. 읽기전용캐쉬는직렬화되지않을것이다. 캐쉬타입들 캐쉬모델은다른타입의캐쉬를지원하기위해서플러그인형태의프레임워크를사용한다. 그구현은 cachemodel요소의 type 속성값내에정의된다. 이정의된클래스이름은 CacheController인터페이스의구현이나아래에서논의되는 4가지별칭중에하나가되어야만한다. 게다가설정파라미터는 cachemodel내에포함된 property요소를통해구현체로전달될수있다. 현재배포판에는 4가지구현물을포함하고있다. 그들은다음과같다. MEMORY (com.ibatis.db.sqlmap.cache.memory.memorycachecontroller) MEMORY 캐쉬는캐쉬행위를관리하기위해서참조타입을사용한다. 그것은가비지컬렉터 (garbage collector) 가캐쉬내에 머물러있는지아닌지효과적으로결정한다. MEMORY 캐쉬는객체재사용의일정한패턴이없는애플리케이션또는메모리 가충분하지않은애플리케이션을위한좋은선택이다. MEMORY 구현은다음처럼설정된다. <cachemodel id="product-cache" type="memory"> <flushinterval hours="24"/> <flushonexecute statement="insertproduct"/> <flushonexecute statement="updateproduct"/> <flushonexecute statement="deleteproduct"/> <property name= reference-type value= WEAK /> </cachemodel> 단지하나의프라퍼티가 MEMORY 캐쉬구현에의해인식된다. reference-type 라는이름의프라퍼티는 STRONG, SOFT 또는 WEAK 의값으로셋팅되어야만한다. 그값들은 JVM 내에유효한여러가지메모리참조타입에대응된다. 다음의테이블은 MEMORY 캐쉬를위해사용될수있는다른참조타입을서술한다. 참조타입을핵심을좀더이해하기위해서 는 reachability 에대한정보를위한 java.lang.ref 부분의 JDK 문서를보기를바란다. WEAK (default) SOFT 이참조타입은대부분의경우에가장좋은선택이고참조타입을정의하지않는다면디폴트로설정되는값이다. 이것은대개의결과에성능을향상시킬것이다. 하지만할당된다른객체내에서사용되는메모리를완전히제거할것이다. 그결과는현재사용중이지않다는것을가정한다. 이참조타입은결과물이현재사용중이지않고메모리가다른객체를위해필요한경우에메모리가바닥나는가능성을제거할것이다. 어쨌든이것은할당되고좀더중요한객체에유효하지않는메모리에대해대부분공격적인참조타입이아니다. 30
STRONG 이참조타입은명시적을캐쉬가삭제될때까지메모리내에저장된결과물을보증한다. 이것은 1) 매우작음, 2) 절대적으로정적, and 3) 매우종종사용되는결과에좋다. 장점은특수한쿼리를위해매우좋은성능을보인다. 단점은결과물에의해사용되는메모리가필요할때다른객체를위해메모리를반환하지않는다. LRU (com.ibatis.db.sqlmap.cache.lru.lrucachecontroller) LRU 캐쉬는객체가자동으로캐시로부터어떻게삭제되는지결정하기위해 Least Recently Used( 가장최근에사용된 ) 알고리즘을사용한다. 캐쉬가가득찼을때가장최근에접근된객체는캐쉬로부터삭제된다. 이방법은종종참조되는특수한객체가있을때이것은가장최근에삭제된변경과함께캐쉬내에남을것이다. LRU캐쉬는오랜시간동안하나이상의사용자에게특별한객체가사용되는패턴을가지는애플리케이션을위해서좋은선택이다 ( 이를테면페이지처리된목록사이에앞페이지뒷페이지를탐색하는, 특수한검색키.. 등등 ). LRU 구현은다음처럼설정된다. <cachemodel id="product-cache" type="lru"> <flushinterval hours="24"/> <flushonexecute statement="insertproduct"/> <flushonexecute statement="updateproduct"/> <flushonexecute statement="deleteproduct"/> <property name= size value= 1000 /> </cachemodel> 단지하나의프라퍼티만이 LRU캐쉬에의해인식된다. size 라는이름의프라퍼티는한번에캐쉬내에고정되는객체의최대갯수를표현하는숫자값으로설정해야한다. 여기서기억해야할중요한것은하나의문자열인스턴스로부터자바빈즈의 ArrayList 해당되는어떠한객체로도될수있다는것이다. 그래서메모리포화의위험이있다면당신의캐쉬내너무많이저장하지마라. FIFO (com.ibatis.db.sqlmap.cache.fifo.fifocachecontroller) FIFO 캐쉬는객체가캐쉬로부터자동적으로어떻게삭제될지결정하기위해 First In First Out( 먼저들어온것을먼저보 낸다.) 알로리즘을사용한다. 캐쉬가가득찼을때가장오래된객체는캐쉬로부터삭제될것이다. FIFO 캐쉬는특수한쿼 리가빠른성공내에서적은수로참조되는패턴을사용할때좋다. 하지만나중에몇몇시점에서는가능하지않다. FIFO 구현은다음처럼설정된다. <cachemodel id="product-cache" type="fifo"> <flushinterval hours="24"/> <flushonexecute statement="insertproduct"/> <flushonexecute statement="updateproduct"/> <flushonexecute statement="deleteproduct"/> <property name= size value= 1000 /> </cachemodel> 단지하나의프라퍼티가 FIFO캐쉬에의해인식된다.. size 라는이름의프라퍼티는한번에캐쉬내에고정되는객체의최대갯수를표현하는숫자값으로설정해야한다. 여기서기억해야할중요한것은하나의문자열인스턴스로부터자바빈즈의 ArrayList 해당되는어떠한객체로도될수있다는것이다. 그래서메모리포화의위험이있다면당신의캐쉬내너무많이저장하지마라. 31
OSCACHE (com.ibatis.db.sqlmap.cache.oscache.oscachecontroller) OSCACHE 캐쉬는 OSCache 2.0 캐쉬엔진을위한플러그인이다. 이것은설정가능하고구분가능하고유연성이있다. OSCACHE 구현은다음처럼설정된다. <cachemodel id="product-cache" type="oscache"> <flushinterval hours="24"/> <flushonexecute statement="insertproduct"/> <flushonexecute statement="updateproduct"/> <flushonexecute statement="deleteproduct"/> </cachemodel> OSCACHE 구현은설정을위해어떠한프라퍼티요소도사용하지않는다. 대신에 OSCache 인스턴스는클래스패스가장상위 에위치시켜야하는표준적인 oscache.properties 을사용해서설정된다. 당신은알고리즘, 캐쉬크기, 영속성접근법 ( 메모리, 파일등등 ) 그리고클러스터링을설정할수있다. 좀더많은정보를위해서는 OSCache 문서를참조하라. OSCache 와그문서는다음의 Open Symphony 웹사이트에서찾을수 있다. http://www.opensymphony.com/oscache/ 동적으로맵핑되는 Statements JDBC 를사용해서직접적으로작동할때매우공통적인문제는동적 SQL 이다. 파라미터값뿐만아니라파라미터와칼럼이모 두포함된변경을하는 SQL 문에서작업을하는것은매우어렵다. 전형적인해결법은조건적인 if-else 문과지겨운문자열연결 덩어리를사용하는것이다. 요구되는결과는종종쿼리가예제객체와유사한객체를찾기위해빌드될수있는예제에의해쿼 리된다. SQL Map API 는어떤맵핑된 statement 요소에적용될수있는상대적으로훌륭한해결법을제공한다. 이것은간단한 에제이다. <select id="dynamicgetaccountlist" cachemodel="account-cache" resultmap="account-result" > select * from ACCOUNT <isgreaterthan prepend="and" property="id" comparevalue="0"> where ACC_ID = #id# </isgreaterthan> order by ACC_LAST_NAME </select> 위에제에서파라미터빈의 id 프라퍼티의상태에의존해서생성될수있는두가지가능한 statement 가있다. 만약파라미터가 0 보다크다면 statement 는다음처럼생성될것이다. select * from ACCOUNT where ACC_ID =? 또는파라미터가 0 이거나더작다면 statement 는다음처럼보일것이다. 32
select * from ACCOUNT 이것의즉각적인유용함은좀더복잡한상황이발생하기전까지명백하게되지는않을것이다. 예를들면다음은좀더복잡한 예제이다. <statement id="dynamicgetaccountlist" resultmap="account-result" > select * from ACCOUNT <dynamic prepend="where"> <isnotnull prepend="and" property="firstname"> (ACC_FIRST_NAME = #firstname# <isnotnull prepend="or" property="lastname"> ACC_LAST_NAME = #lastname# </isnotnull> ) </isnotnull> <isnotnull prepend="and" property="emailaddress"> ACC_EMAIL like #emailaddress# </isnotnull> <isgreaterthan prepend="and" property="id" comparevalue="0"> ACC_ID = #id# </isgreaterthan> </dynamic> order by ACC_LAST_NAME 상황에의존적으로위동적 statement 로부터각각다른 16 가지의 SQL 문이생성될수있다. if-slse 구조와문자열연결을코딩 하는것은덩어리지고수백라인의로드를요구할수있다. 동적 statement 를사용하는것은당신의 SQL 문에몇몇조건적인태그를추가하는것처럼간단하다. 예를들면 <statement id="somename" resultmap="account-result" > select * from ACCOUNT <dynamic prepend="where"> <isgreaterthan prepend="and" property="id" comparevalue="0"> ACC_ID = #id# </isgreaterthan> <isnotnull prepend= and" property="lastname"> ACC_LAST_NAME = #lastname# </isnotnull> </dynamic> order by ACC_LAST_NAME 위의 statement에서 <dynamic> 요소는동적인 SQL부분을구분한다. dynamic요소는옵션이고포함된조건이 statement에덧붙여지지않는다면 WHERE 과같은구문이포함되지않을경우에 WHERE 같은구문을관리하는방법을제공한다. statement부분은 statement내에포함될 SQL코드를포함할지안할지결정하는조건적요소를제한없이포함할수있다. 조건적인요소의모든것은쿼리로전달되는파라미터객체의상태에기초하여작동한다. 동적인요소와조건적인요소모두 33
prepend 속성을가진다. prepend속성은필요하다면부모요소의 prepend에의해오버라이딩되기위해자유로운코드의일부이다. 위예제에서 where 구문은첫번째 true조건구문을오버라이딩할것이다. 이것은 SQL문이정확하게빌드되는것을확인하기위해필요하다. 예를들면첫번째 true조건의경우에서 AND문이필요없고사실 statement이끝날것이다. 다음의섹션은다양한종류의요소 ( 바이너리조건을포함, 단일성분의조건과 iterate) 를서술한다. 바이너리조건적인요소 바이너리조건적인요소는정적값또는다른프라퍼티값을위한프라퍼티값과비교한다. 만약결과가 true 라면몸체부분 은 SQL 쿼리에포함된다. 바이너리조건적인속성 : prepend statement에붙을오버라이딩가능한 SQL부분 ( 옵션 ) property 비교되는프라퍼티 ( 필수 ) compareproperty 비교되는다른프라퍼티 ( 필수또는 comparevalue) comparevalue 비교되는값 ( 필수또는 compareproperty) <isequal> 프라퍼티와값또는다른프라퍼티가같은지체크. <isnotequal> 프라퍼티와값또는다른프라퍼티가같지않은지체크. <isgreaterthan> 프라퍼티가값또는다른프라퍼티보다큰지체크. <isgreaterequal> 프라퍼티가값또는다른프라퍼티보다크거나같은지체크. <islessthan> 프라퍼티가값또는다른프라퍼티보다작은지체크. <islessequal> 프라퍼티가값또는다른프라퍼티보다작거나같은지체크. 사용법예제 : <islessequal prepend= AND property= age comparevalue= 18 > ADOLESCENT = TRUE </islessequal> 단일조건적인요소 단일조건적인요소는특수한조건을위해프라퍼티의상태를체크한다. 단일조건적인속성 : prepend statement 에붙을오버라이딩가능한 SQL 부분 ( 옵션 ) property 체크되기위한프라퍼티 ( 필수 ) <ispropertyavailable> 프라퍼티가유효한지체크 ( 이를테면파라미터빈의프라퍼티이다.) <isnotpropertyavailable> 프라퍼티가유효하지않은지체크 ( 이를테면파라미터의프라퍼티가아니다.) <isnull> <isnotnull> <isempty> 프라퍼티가 null 인지체크 프라퍼티가 null 이아닌지체크 Collection, 문자열또는 String.valueOf() 프라퍼티가 null 이거나 empty( or 34