MyBatis 3 사용자가이드 번역자 : 이동국 (fromm0@gmail.com, http://openframework.or.kr) 최신버전은 http://kldp.net/projects/fwko/
Contents MyBatis 는무엇인가?... 5 시작하기... 5 XML 에서 SqlSessionFactory 빌드하기... 5 XML 을사용하지않고 SqlSessionFactory 빌드하기... 6 SqlSessionFactory 에서 SqlSession 만들기... 6 매핑된 SQL 구문살펴보기... 7 명명공간 (Namespaces) 에대한설명... 8 스코프 (Scope) 와생명주기 (Lifecycle)... 8 매퍼설정 XML... 10 properties... 10 settings... 11 typealiases... 12 typehandlers... 13 objectfactory... 14 plugins... 15 environments... 16 transactionmanager... 17 datasource... 18 mappers... 20 SQL Map XML 파일... 20 select... 21 insert, update, delete... 22 sql... 24 Parameters... 25
resultmap... 26 Advanced Result Mapping... 28 id, result... 30 지원되는 JDBC 타입... 31 constructor... 31 association... 32 collection... 35 discriminator... 38 cache... 39 사용자지정캐시사용하기... 40 cache-ref... 42 동적 SQL... 42 if... 42 choose, when, otherwise... 43 trim, where, set... 43 foreach... 45 자바 API... 46 디렉터리구조... 46 SqlSessions... 47 SqlSessionFactoryBuilder... 47 SqlSessionFactory... 49 SqlSession... 50 SelectBuilder... 57 SqlBuilder... 60 Logging... 61 2011-08-05 3
2011-08-05 4
MyBatis 는무엇인가? MyBatis 는개발자가지정한 SQL, 저장프로시저그리고몇가지고급매핑을지원하는퍼시스턴스프레임워크이다. MyBatis 는 JDBC 코드와수동으로셋팅하는파라미터와결과매핑을제거한다. MyBatis 는데이터베이스레코드에원시타입과 Map 인터페이스그리고자바 POJO 를설정하고매핑하기위해 XML 과애노테이션을사용할수있다. 시작하기 모든 MyBatis 애플리케이션은 SqlSessionFactory 인스턴스를사용한다. SqlSessionFactory 인스턴스는 SqlSessionFactoryBuilder 를사용하여만들수있다. SqlSessionFactoryBuilder 는 XML 설정파일에서 SqlSessionFactory 인스턴스를빌드할수있다. XML 에서 SqlSessionFactory 빌드하기 XML 파일에서 SqlSessionFactory 인스턴스를빌드하는것은매우간단한다. 설정을위해클래스패스자원을사용하는것을추천하나, 파일경로나 file:// URL 로부터만들어진 Reader 인스턴스를사용할수도있다. MyBatis 는클래스패스와다른위치에서자원을로드하는것으로좀더쉽게해주는 Resources 라는유틸성클래스를가지고있다. String resource = "org/mybatis/example/configuration.xml"; Reader reader = Resources.getResourceAsReader(resource); sqlmapper = new SqlSessionFactoryBuilder().build(reader); XML 설정파일에서지정하는 MyBatis 의핵심이되는설정은트랜잭션을제어하기위한 TransactionManager 과함께 데이터베이스 Connection 인스턴스를가져오기위한 DataSource 를포함한다. 세부적인설정은조금뒤에보고간단한 예제를먼저보자. <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//dtd Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionmanager type="jdbc"/> <datasource type="pooled"> <property name="driver" value="${driver"/> <property name="url" value="${url"/> <property name="username" value="${username"/> <property name="password" value="${password"/> </datasource> </environment> </environments> <mappers> <mapper resource="org/mybatis/example/blogmapper.xml"/> </mappers> </configuration> 2011-08-05 5
좀더많은 XML 설정방법이있지만, 위예제에서는가장핵심적인부분만을보여주고있다. XML 가장위부분에서는 XML 문서의유효성체크를위해필요하다. environment 요소는트랜잭션관리와커넥션풀링을위한환경적인설정을 나타낸다. mappers 요소는 SQL 코드와매핑정의를가지는 XML 파일인 mapper 의목록을지정한다. XML 을사용하지않고 SqlSessionFactory 빌드하기 XML 보다자바를사용해서직접설정하길원한다면, XML 파일과같은모든설정을제공하는 Configuration 클래스를 사용하면된다. DataSource datasource = BlogDataSourceFactory.getBlogDataSource(); TransactionFactory transactionfactory = new JdbcTransactionFactory(); Environment environment = new Environment("development", transactionfactory, datasource); Configuration configuration = new Configuration(environment); configuration.addmapper(blogmapper.class); SqlSessionFactory sqlsessionfactory = new SqlSessionFactoryBuilder().build(configuration); 이설정에서추가로해야할일은 Mapper 클래스를추가하는것이다. Mapper 클래스는 SQL 매핑애노테이션을가진자바 클래스이다. 어쨌든자바애노테이션의몇가지제약과몇가지설정방법의목잡함에도불구하고, XML 매핑은세부적인 매핑을위해언제든필요하다. 좀더세부적인내용은조금뒤다시보도록하자. SqlSessionFactory 에서 SqlSession 만들기 SqlSessionFactory 이름에서보듯이, SqlSession 인스턴스를만들수있다. SqlSession 은데이터베이스에대해 SQL 명령어를실행하기위해필요한모든메서드를가지고있다. 그래서 SqlSession 인스턴스를통해직접 SQL 구문을실행할수있다. 예를들면 : SqlSession session = sqlmapper.opensession(); try { Blog blog = (Blog) session.selectone( "org.mybatis.example.blogmapper.selectblog", 101); finally { session.close(); 이방법이 MyBatis 의이전버전을사용한사람이라면굉장히친숙할것이다. 하지만좀더좋은방법이생겼다. 주어진 SQL 구문의파라미터와리턴값을설명하는인터페이스 ( 예를들면, BlogMapper.class ) 를사용하여, 문자열처리오류나 타입캐스팅오류없이좀더타입에안전하고깔끔하게실행할수있다. 예를들면 : SqlSession session = sqlsessionfactory.opensession(); try { 2011-08-05 6
BlogMapper mapper = session.getmapper(blogmapper.class); Blog blog = mapper.selectblog(101); finally { session.close(); 그럼좀더자세히살펴보자. 매핑된 SQL 구문살펴보기 이시점에 SqlSession 이나 Mapper 클래스가정확히어떻게실행되는지궁금할것이다. 매핑된 SQL 구문에대한내용이가장중요하다. 그래서이문서전반에서가장자주다루어진다. 하지만다음의두가지예제를통해정확히어떻게작동하는지에대해서는충분히이해하게될것이다. 위예제처럼, 구문은 XML 이나애노테이션을사용해서정의할수있다. 그럼먼저 XML 을보자. MyBatis 가제공하는 대부분의기능은 XML 을통해매핑기법을사용한다. 이전에 MyBatis 를사용했었다면쉽게이해되겠지만, XML 매핑 문서에이전보다많은기능이추가되었다. SqlSession 을호출하는 XML 기반의매핑구문이다. <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//dtd Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="org.mybatis.example.blogmapper"> <select id="selectblog" parametertype="int" resulttype="blog"> select * from Blog where id = #{id </select> </mapper> 간단한예제치고는조금복잡해보이지만실제로는굉장히간단하다. 한개의매퍼 XML 파일에는많은수의매핑구문을정의할수있다. XML 도입무의헤더와 doctype 을제외하면, 나머지는쉽게이해되는구문의형태이다. 여기선 org.mybatis.example.blogmapper 명명공간에서 selectblog 라는매핑구문을정의했고, 이는결과적으로 org.mybatis.example.blogmapper.selectblog 형태로실제명시되게된다. 그래서다음처럼사용하게되는셈이다. Blog blog = (Blog) session.selectone( "org.mybatis.example.blogmapper.selectblog", 101); 이건마치패키지를포함한전체경로의클래스내메서드를호출하는것과비슷한형태이다. 이이름은매핑된 select 구문의 이름과파라미터그리고리턴타입을가진명명공간과같은이름의 Mapper 클래스와직접매핑될수있다. 이건위에서 본것과같은 Mapper 인터페이스의메서드를간단히호출하도록허용한다. 위예제에대응되는형태는아래와같다. BlogMapper mapper = session.getmapper(blogmapper.class); Blog blog = mapper.selectblog(101); 2011-08-05 7
두번째방법은많은장점을가진다. 먼저문자열에의존하지않는다는것이다. 이는애플리케이션을좀더안전하게만든다. 두번째는개발자가 IDE 를사용할때, 매핑된 SQL 구문을사용할때의수고를덜어준다. 세번째는리턴타입에대해타입캐스팅을하지않아도된다. 그래서 BlogMapper 인터페이스는깔끔하고리턴타입에대해타입에안전하며이는파라미터에도그대로적용된다. 명명공간 (Namespaces) 에대한설명 명명공간 (Namespaces) 이이전버전에서는사실선택사항이었다. 하지만이제는패키지경로를포함한전체이름을가진 구문을구분하기위해필수로사용해야한다. 명명공간은인터페이스바인딩을가능하게한다. 명명공간을사용하고자바패키지의명명공간을두면코드가깔끔해지고 MyBatis 의사용성이크게향상될것이다. 이름분석 (Name Resolution): 타이핑을줄이기위해, MyBatis 는구문과결과맵, 캐시등의모든설정요소를위한이름 분석규칙을사용한다. com.mypackage.mymapper.selectallthings 과같은패키지를포함한전체경로명 (Fully qualified names) 은 같은형태의경로가있다면그경로내에서직접찾는다. selectallthings 과같은짧은형태의이름은모호하지않은엔트리를참고하기위해사용될수있다. 그래서짧은 이름은모호해서에러를자주보게되니되도록이면전체경로를사용해야할것이다. BlogMapper 와같은 Mapper 클래스에는몇가지트릭이있다. 매핑된구문은 XML 에전혀매핑될필요가없다. 대신자바 애노테이션을사용할수있다. 예를들어, 위 XML 예제는다음과같은형태로대체될수있다. package org.mybatis.example; public interface BlogMapper { @Select("SELECT * FROM blog WHERE id = #{id") Blog selectblog(int id); 간단한구문에서는애노테이션이좀더깔끔하다. 어쨌든자바애노테이션은좀더복잡한구문에서는제한적이고코드를 엉망으로만들수있다. 그러므로, 복잡하게사용하고자한다면, XML 매핑구문을사용하는것이좀더나을것이다. 스코프 (Scope) 와생명주기 (Lifecycle) 이제부터다룰스코프와생명주기에대해서이해하는것은매우중요하다. 스코프와생명주기를잘못사용하는것은다양한 동시성문제를야기할수있다.. SqlSessionFactoryBuilder 이클래스는인스턴스회되어사용되고던져질수있다. SqlSessionFactory 를생성한후유지할필요는없다. 그러므로 SqlSessionFactoryBuilder 인스턴스의가장좋은스코프는메서드스코프 ( 예를들면, 메서드지역변수 ) 이다. 여러개의 2011-08-05 8
SqlSessionFactory 인스턴스를빌드하기위해 SqlSessionFactoryBuilder 를재사용할수도있지만유지하지않는것이가장 좋다.. SqlSessionFactory 한번만든뒤, SqlSessionFactory 는애플리케이션을실행하는동안존재해야만한다. 그래서삭제하거나재생성할필요가없다. 애플리케이션이실행되는동안여러차례 SqlSessionFactory 를다시빌드하지않는것이가장좋은형태이다. 재빌드하는형태는결과적으로 나쁜냄새 가나도록한다. 그러므로 SqlSessionFactory 의가장좋은스코프는애플리케이션스코프이다. 애플리케이션스코프로유지하기위한다양한방법이존재한다. 가장간단한방법은싱글턴패턴이나 static 싱글턴패턴을사용하는것이다. 또는구글 Guice 나 Spring 과같은의존성삽입컨테이너를선호할수도있다. 이러한프레임워크는 SqlSessionFactory 의생명주기를싱글턴으로관리할것이다. SqlSession 각각의쓰레드는자체적으로 SqlSession 인스턴스를가져야한다. SqlSession 인스턴스는공유되지않고쓰레드에안전하지도않다. 그러므로가장좋은스코프는요청또는메서드스코프이다. SqlSession 을 static 필드나클래스의인스턴스필드로지정해서는안된다. 그리고서블릿프레임워크의 HttpSession 과같은관리스코프에둬서도안된다. 어떠한종류의웹프레임워크를사용한다면, HTTP 요청과유사한스코프에두는것으로고려해야한다. 달리말해서, HTTP 요청을받을? 때마다만들고, 응답을리턴할때마다 SqlSession 을닫을수있다. SqlSession 을닫는것은중요하다. 언제나 finally 블록에서닫아야만한다. 다음은 SqlSession 을닫는것을확인하는표준적인형태다. SqlSession session = sqlsessionfactory.opensession(); try { // do work finally { session.close(); Mapper 인스턴스 Mapper 는매핑된구문을바인딩하기위해만들어야할인터페이스이다. mapper 인터페이스의인스턴스는 SqlSession 에서생성한다. 그래서 mapper 인스턴스의가장좋은스코프는 SqlSession 과동일하다. 어쨌든 mapper 인스턴스의가장좋은스코프는메서드스코프이다. 사용할메서드가호출되면생성되고끝난다. 명시적으로닫을필요는없다. SqlSession session = sqlsessionfactory.opensession(); try { BlogMapper mapper = session.getmapper(blogmapper.class); // do work finally { session.close(); 2011-08-05 9
매퍼설정 XML MyBatis XML 설정파일은다양한셋팅과프로퍼티를가진다. 문서의구조는다음과같다.: configuration o properties o settings o typealiases o typehandlers o objectfactory o plugins o environments environment transactionmanager datasource o mappers properties 이설정은외부에옮길수있다. 자바프로퍼티파일인스턴스에설정할수도있고, properties 요소의하위요소에둘수도 있다. 예를들면 : <properties resource="org/mybatis/example/config.properties"> <property name="username" value="dev_user"/> <property name="password" value="f2fa3!33tyyg"/> </properties> 속성들은파일도처에둘수도있다. 예를들면 : <datasource type="pooled"> <property name="driver" value="${driver"/> <property name="url" value="${url"/> <property name="username" value="${username"/> <property name="password" value="${password"/> </datasource> 이에제에서 username 과 password 는 properties 요소의설정된값으로대체될수있다. driver 와 url 속성은 config.properties 파일에포함된값으로대체될수도있다. 이것은설정에대한다양한옵션을제공하는셈이다. 속성은 SqlSessionBuilder.build() 메서드에전달될수있다. 예를들면 : SqlSessionFactory factory = sqlsessionfactorybuilder.build(reader, props); //... or... SqlSessionFactory factory = 2011-08-05 10
sqlsessionfactorybuilder.build(reader, environment, props); 속성이한개이상존재한다면, MyBatis 는일정한순서로로드한다.: properties 요소에명시된속성을가장먼저읽는다. properties 요소의클래스패스자원이나 url 속성으로부터로드된속성을두번재로읽는다. 그래서이미읽은값이 있다면덮어쓴다. 마지막으로메서드파라미터로전달된속성을읽는다. 앞서로드된값을덮어쓴다. 그래서가장우선순위가높은속성은메서드의파라미터로전달된값이고그다음은자원및 url 속성이고마지막은 properties 요소에명시된값이다. settings 런타임시 MyBatis 의행위를조정하기위한중요한값들이다. 다음표는셋팅과그의미그리고디폴트값을설명한다. 셋팅 설명 사용가능한값들 cacheenabled 설정에서각 mapper 에설정된캐시를전역적으로 true false 사용할지말지에대한여부 lazyloadingenabled 늦은로딩을사용할지에대한여부. 사용하지않는다면 true false 모두즉시로딩할것이다. aggressivelazyloading 활성화상태로두게되면늦은 (lazy) 로딩프로퍼티를 true false 가진객체는호출에따라로드될것이다. 반면에개별 프로퍼티는요청할때로드된다. multipleresultsetsenabled 한개의구문에서여러개의 ResultSet 을허용할지의 true false 여부 ( 드라이버가해당기능을지원해야함 ) usecolumnlabel 칼럼명대신에칼럼라벨을사용. 드라이버마다조금 true false 다르게작동한다. 문서와간단한테스트를통해실제 기대하는것처럼작동하는지확인해야한다. usegeneratedkeys 생성키에대한 JDBC 지원을허용. 지원하는 true false 드라이버가필요하다. true 로설정하면생성키를 강제로생성한다. 일부드라이버 ( 예를들면, Derby) 에서는이설정을무시한다. automappingbehavior MyBatis 가칼럼을필드 / 프로퍼티에자동으로 NONE, 매핑할지와방법에대해명시. PARTIAL 은간단한 PARTIAL, 자동매핑만할뿐, 내포된결과에대해서는처리하지 FULL 않는다. FULL 은처리가능한모든자동매핑을 처리한다. defaultexecutortype 디폴트실행자 (executor) 설정. SIMPLE 실행자는 SIMPLE 특별히하는것이없다. REUSE 실행자는 REUSE PreparedStatement 를재사용한다. BATCH 실행자는 BATCH 구문을재사용하고수정을배치처리한다. defaultstatementtimeout 데이터베이스로의응답을얼마나오래기다릴지를 양수 판단하는타임아웃을셋팅 디폴트 true true true true true False PARTIAL SIMPLE 셋팅되지않음 (null) 2011-08-05 11
위설정을모두사용한 setting 요소의예제이다 : typealiases <settings> <setting name="cacheenabled" value="true"/> <setting name="lazyloadingenabled" value="true"/> <setting name="multipleresultsetsenabled" value="true"/> <setting name="usecolumnlabel" value="true"/> <setting name="usegeneratedkeys" value="false"/> <setting name="enhancementenabled" value="false"/> <setting name="defaultexecutortype" value="simple"/> <setting name="defaultstatementtimeout" value="25000"/> </settings> 타입별칭은자바타입에대한좀더짧은이름이다. 오직 XML 설정에서만사용되며, 타이핑을줄이기위해존재한다. 예를들면 : <typealiases> <typealias alias="author" type="domain.blog.author"/> <typealias alias="blog" type="domain.blog.blog"/> <typealias alias="comment" type="domain.blog.comment"/> <typealias alias="post" type="domain.blog.post"/> <typealias alias="section" type="domain.blog.section"/> <typealias alias="tag" type="domain.blog.tag"/> </typealiases> 이설정에서, Blog 는도처에서 domain.blog.blog 대신사용될수있다. 공통의자바타입에대해서는내장된타입별칭이있다. 이모두대소문자를가린다.. 별칭 _byte _long _short _int _integer _double _float _boolean string byte long short int integer double 매핑된타입 Byte Long Short Int int double float boolean String Byte Long Short Integer Integer Double 2011-08-05 12
별칭 float boolean date decimal bigdecimal object map hashmap list arraylist collection iterator 매핑된타입 Float Boolean Date BigDecimal BigDecimal Object Map HashMap List ArrayList Collection Iterator typehandlers MyBatis 가 PreparedStatement 에파라미터를셋팅하고 ResultSet 에서값을가져올때마다, TypeHandler 는적절한자바 타입의값을가져오기위해사용된다. 다음의표는디폴트 TypeHandlers 를설명한다.. 타입핸들러 자바타입 JDBC 타입 BooleanTypeHandler Boolean, boolean 어떤호환가능한 BOOLEAN ByteTypeHandler Byte, byte 어떤호환가능한 NUMERIC 또는 BYTE ShortTypeHandler Short, short 어떤호환가능한 NUMERIC 또는 SHORT INTEGER IntegerTypeHandler Integer, int 어떤호환가능한 NUMERIC 또는 INTEGER LongTypeHandler Long, long 어떤호환가능한 NUMERIC 또는 LONG INTEGER FloatTypeHandler Float, float 어떤호환가능한 NUMERIC 또는 FLOAT DoubleTypeHandler Double, double 어떤호환가능한 NUMERIC 또는 DOUBLE BigDecimalTypeHandler BigDecimal 어떤호환가능한 NUMERIC 또는 DECIMAL StringTypeHandler String CHAR, VARCHAR ClobTypeHandler String CLOB, LONGVARCHAR NStringTypeHandler String NVARCHAR, NCHAR NClobTypeHandler String NCLOB ByteArrayTypeHandler byte[] 어떤호환가능한 byte 스트림타입 BlobTypeHandler byte[] BLOB, LONGVARBINARY DateTypeHandler Date (java.util) TIMESTAMP DateOnlyTypeHandler Date (java.util) DATE TimeOnlyTypeHandler Date (java.util) TIME SqlTimestampTypeHandler Timestamp (java.sql) TIMESTAMP SqlDateTypeHadler Date (java.sql) DATE SqlTimeTypeHandler Time (java.sql) TIME ObjectTypeHandler Any OTHER, 또는명시하지않는 EnumTypeHandler Enumeration Type VARCHAR 문자열호환타입. 지원하지않거나비표준인타입에대해서는당신스스로만들어서타입핸들러를오버라이드할수있다. 그러기위해서는 TypeHandler 인터페이스를구현하고자바타입에 TypeHandler 를매핑하면된다. 예를들면 : 2011-08-05 13
// ExampleTypeHandler.java public class ExampleTypeHandler implements TypeHandler { public void setparameter( PreparedStatement ps, int i, Object parameter,jdbctype jdbctype) throws SQLException { ps.setstring(i, (String) parameter); public Object getresult( ResultSet rs, String columnname) throws SQLException { return rs.getstring(columnname); public Object getresult( CallableStatement cs, int columnindex) throws SQLException { return cs.getstring(columnindex); // MapperConfig.xml <typehandlers> <typehandler javatype="string" jdbctype="varchar" handler="org.mybatis.example.exampletypehandler"/> </typehandlers> 이러한 TypeHandler 를사용하는것은자바 String 프로퍼티와 VARCHAR 파라미터및결과를위해이미존재하는핸들러를오버라이드하게될것이다. MyBatis 는타입을판단하기위해데이터베이스의메타데이터를보지않는다. 그래서파라미터와결과에정확한타입핸들러를매핑해야한다. MyBatis 가구문이실행될때까지는데이터타입에대해모르기때문이다.. objectfactory 매번 MyBatis 는결과객체의인스턴스를만들기위해 ObjectFactory 를사용한다. 디폴트 ObjectFactory 는디폴트생성자를가진대상클래스를인스턴스화하는것보다조금더많은작업을한다. ObjectFactory 의디폴트행위를오버라이드하고자한다면, 만들수있다. 예를들면 : // ExampleObjectFactory.java public class ExampleObjectFactory extends DefaultObjectFactory { public Object create(class type) { return super.create(type); public Object create( Class type, List<Class> constructorargtypes, List<Object> constructorargs) { return super.create(type, constructorargtypes, constructorargs); 2011-08-05 14
public void setproperties(properties properties) { super.setproperties(properties); // MapperConfig.xml <objectfactory type="org.mybatis.example.exampleobjectfactory"> <property name="someproperty" value="100"/> </objectfactory> ObjectFactory 인터페이스는매우간단한다. 두개의 create 메서드를가지고있으며, 하나는디폴트생성자를처리하고다른하나는파라미터를가진생성자를처리한다. 마지막으로 setproperties 메서드는 ObjectFactory 를설정하기위해사용될수있다. objectfactory 요소에정의된프로퍼티는 ObjectFactory 인스턴스가초기화된후 setproperties 에전달될것이다. plugins MyBatis 는매핑구문을실행하는어떤시점에호출을가로챈다. 기본적으로 MyBatis 는메서드호출을가로채기위한 플러그인을허용한다. Executor (update, query, flushstatements, commit, rollback, gettransaction, close, isclosed) ParameterHandler (getparameterobject, setparameters) ResultSetHandler (handleresultsets, handleoutputparameters) StatementHandler (prepare, parameterize, batch, update, query) 이클래스들의메서드는각각메서드시그니처를통해찾을수있고소스코드는 MyBatis 릴리즈파일에서찾을수있다. 오버라이드할메서드의행위를이해해야만한다. 주어진메서드의행위를변경하거나오버라이드하고자한다면, MyBatis 의 핵심기능에악영향을줄수도있다. 이러한로우레벨클래스와메서드들은주의를해서사용해야한다. 플러그인을사용하는것을제공하는기능에다소간단하다. Interceptor 인터페이스를간단히구현해서, 가로채고 (intercept) 싶은시그니처를명시해야한다. // ExamplePlugin.java @Intercepts({@Signature( type= Executor.class, method = "update", args = {MappedStatement.class,Object.class)) public class ExamplePlugin implements Interceptor { 2011-08-05 15
public Object intercept(invocation invocation) throws Throwable { return invocation.proceed(); public Object plugin(object target) { return Plugin.wrap(target, this); public void setproperties(properties properties) { // MapperConfig.xml <plugins> <plugin interceptor="org.mybatis.example.exampleplugin"> <property name="someproperty" value="100"/> </plugin> </plugins> 위플러그인은매핑된구문의로우레벨실행을책임지는내부객체인 Executor 인스턴스의 update 메서드호출을모두 가로챌것이다. 설정파일오버라이드하기 플러그인을사용해서 MyBatis 핵심행위를변경하기위해, Configuration 클래스전체를오버라이드할수있다. 이클래스를확장하고내부메서드를오버라이드하고, sqlsessionfactorybuilder.build(myconfig) 메서드에그객체를넣어주면된다. 다시얘기하지만이작업은 MyBatis 에큰영향을줄수있으니주의해서해야한다. environments MyBatis 는여러개의환경으로설정될수있다. 여러가지이유로여러개의데이터베이스에 SQL Map 을적용하는데도움이된다. 예를들어, 개발, 테스트, 리얼환경을위해별도의설정을가지거나, 같은스키마를여러개의 DBMS 제품을사용할경우들이다. 그외에도많은경우가있을수있다. 중요하게기억해야할것은, 다중환경을설정할수는있지만, SqlSessionFactory 인스턴스마다한개만사용할수있다는 것이다. 두개의데이터베이스에연결하고싶다면, SqlSessionFactory 인스턴스를두개만들필요가있다. 세개의데이터베이스를 사용한다면, 역시 3 개의인스턴스를필요로한다. 기억하기쉽게 데이터베이스별로하나의 SqlSessionFactory 환경을명시하기위해, SqlSessionFactoryBuilder 에옵션으로추가파라미터를주면된다. 환경을선택하는두가지 시그니처는 SqlSessionFactory factory = sqlsessionfactorybuilder.build(reader, environment); SqlSessionFactory factory = sqlsessionfactorybuilder.build(reader, environment,properties); environment 파라미터가없으면, 디폴트환경이로드된다. 2011-08-05 16
SqlSessionFactory factory = sqlsessionfactorybuilder.build(reader); SqlSessionFactory factory = sqlsessionfactorybuilder.build(reader,properties); environments 요소는환경을설정하는방법을정의한다. <environments default="development"> <environment id="development"> <transactionmanager type="jdbc"> <property name="..." value="..."/> </transactionmanager> <datasource type="pooled"> <property name="driver" value="${driver"/> <property name="url" value="${url"/> <property name="username" value="${username"/> <property name="password" value="${password"/> </datasource> </environment> </environments> 중요한부분을살펴보면 디폴트환경 (Environment) ID ( 예를들면. default= development ). 각각의환경을정의한환경 (Environment) ID ( 예를들면. id= development ). TransactionManager 설정 ( 예를들면. type= JDBC ) DataSource 설정 ( 예를들면. type= POOLED ) 디폴트환경 (environment) 과환경 (environment) ID 는용어자체가역할을설명한다. transactionmanager MyBatis 는두가지타입의 TransactionManager 를제공한다. JDBC 이설정은간단하게 JDBC 커밋과롤백을처리하기위해사용된다. 트랜잭션의스코프를관리하기위해 datasource 로부터커넥션을가져온다. MANAGED 이설정은어떤것도하지않는다. 결코커밋이나롤백을하지않는다. 대신컨테이너가트랜잭션의 모든생명주기를관리한다. 디플트로커넥션을닫긴한다. 하지만몇몇컨테이너는커넥션을닫는것또한기대하지 않기때문에, 커넥션닫는것으로멈추고자한다면, closeconnection 프로퍼티를 false 로설정하라. 예를들면 : <transactionmanager type="managed"> <property name="closeconnection" value="false"/> </transactionmanager> 2011-08-05 17
TransactionManager 타입은어떠한프로퍼티도필요하지않다. 어쨌든둘다타입별칭이있다. 즉 TransactionFactory 를 위한클래스명이나타입별칭중하나를사용할수있다. public interface TransactionFactory { void setproperties(properties props); Transaction newtransaction(connection conn, boolean autocommit); XML 에설정된프로퍼티는인스턴스를만든뒤 setproperties() 메서드에전달할것이다. 당신의구현체가 Transaction 구현체를만들필요가있을것이다.: public interface Transaction { Connection getconnection(); void commit() throws SQLException; void rollback() throws SQLException; void close() throws SQLException; 이두개의인터페이스를사용하여, MyBatis 가트랜잭션을처리하는방법을완벽하게정의할수있다.. datasource datasource 요소는표준 JDBC DataSource 인터페이스를사용하여 JDBC Connection 객체의소스를설정한다. 대부분의 MyBatis 애플리케이션은예제처럼 datasource 를설정할것이다. 여기엔 3 가지의내장된 datasource 타입이있다. UNPOOLED 이구현체는매번요청에대해커넥션을열고닫는간단한 DataSource 이다. 조금늦긴하지만성능을크게 필요로하지않는간단한애플리케이션을위해서는괜찮은선택이다. UNPOOLED DataSource 는 5 개의프로퍼티만으로 설정된다. driver JDBC 드라이버의패키지경로를포함한결제자바클래스명 url 데이터베이스인스턴스에대한 JDBC URL. username 데이터베이스에로그인할때사용할사용자명 password - 데이터베이스에로그인할때사용할패스워드 defaulttransactionisolationlevel 커넥션에대한디폴트트랜잭션격리레벨 필수는아니지만선택적으로데이터베이스드라이버에프로퍼티를전달할수도있다. 그러기위해서는다음예제처럼 driver. 로시작하는접두어로프로퍼티를명시하면된다. 2011-08-05 18
driver.encoding=utf8 이설정은 encoding 프로퍼티를 UTF8 로설정하게된다. 이방법외에도 DriverManager.getConnection(url, driverproperties) 메서드를통해서도프로퍼티를설정할수있다. POOLED DataSource 에풀링이적용된 JDBC 커넥션을위한구현체이다. 이는새로운 Connection 인스턴스를생성하기위해매번초기화하는것을피하게해준다. 그래서빠른응답을요구하는웹애플리케이션에서는가장흔히사용되고있다. UNPOOLED DataSource 에비해, 많은프로퍼티를설정할수있다. poolmaximumactiveconnections 주어진시간에존재할수있는활성화된 ( 사용중인 ) 커넥션의수. 디폴트는 10 이다. poolmaximumidleconnections 주어진시간에존재할수있는유휴커넥션의수 poolmaximumcheckouttime 강제로리턴되기전에풀에서 체크아웃 될수있는커넥션의시간. 디폴트는 20000ms(20 초 ) pooltimetowait 풀이로그상태를출력하고비정상적으로긴경우커넥션을다시얻을려고시도하는로우레벨셋팅. 디폴트는 20000ms(20 초 ) poolpingquery 커넥션이작업하기좋은상태이고요청을받아서처리할준비가되었는지체크하기위해데이터베이스에던지는핑쿼리 (Ping Query). 디폴트는 핑쿼리가없음 이다. 이설정은대부분의데이터베이스로하여금에러메시지를보게할수도있다. poolpingenabled 핑쿼리를사용할지말지를결정. 사용한다면, 오류가없는 ( 그리고빠른 ) SQL 을사용하여 poolpingquery 프로퍼티를셋팅해야한다. 디폴트는 false 이다. poolpingconnectionsnotusedfor poolpingquery 가얼마나자주사용될지설정한다. 필요이상의핑을피하기위해데이터베이스의타임아웃값과같을수있다. 디폴트는 0 이다. 디폴트값은 poolpingenabled 가 true 일경우에만, 모든커넥션이매번핑을던지는값이다. JNDI 이 DataSource 구현체는컨테이너에따라설정이변경되며, JNDI 컨텍스트를참조한다. 이 DataSource 는오직두개의프로퍼티만을요구한다. initial_context 이프로퍼티는 InitialContext 에서컨텍스트를찾기 ( 예를들어, initialcontext.lookup(initial_context)) 위해사용된다. 이프로퍼티는선택적인값이다. 이설정을생략하면, data_source 프로퍼티가 InitialContext 에서직접찾을것이다. data_source DataSource 인스턴스의참조를찾을수있는컨텍스트경로이다. initial_context 룩업을통해 리턴된컨텍스트에서찾을것이다. initial_context 가지원되지않는다면, InitialContext 에서직접찾을것이다. 다른 DataSource 설정과유사하게, 다음처럼 env. 를접두어로프로퍼티를전달할수있다. env.encoding=utf8 이설정은인스턴스화할때 InitialContext 생성자에 encoding 프로퍼티를 UTF8 로전달한다. 2011-08-05 19
mappers 이제우리는매핑된 SQL 구문을정의할시간이다. 하지만먼저, 설정을어디에둘지결정해야한다. 자바는자동으로리소스를찾기위한좋은방법을제공하지않는다. 그래서가장좋은건어디서찾으라고지정하는것이다. 클래스패스에상대적으로리소스를지정할수도있고, url 을통해서지정할수도있다. 예를들면 // Using classpath relative resources <mappers> <mapper resource="org/mybatis/builder/authormapper.xml"/> <mapper resource="org/mybatis/builder/blogmapper.xml"/> <mapper resource="org/mybatis/builder/postmapper.xml"/> </mappers> // Using url fully qualified paths <mappers> <mapper url="file:///var/sqlmaps/authormapper.xml"/> <mapper url="file:///var/sqlmaps/blogmapper.xml"/> <mapper url="file:///var/sqlmaps/postmapper.xml"/> </mappers> SQL 매핑파일에대해서좀더자세히알아보자. SQL Map XML 파일 MyBatis 의가장큰장점은매핑된구문이다. 이건간혹마법을부리는것처럼보일수있다. SQL Map XML 파일은상대적으로간단하다. 더군다나동일한기능의 JDBC 코드와비교하면아마도 95% 이상코드수가감소하기도한다. MyBatis 는 SQL 을작성하는데집중하도록만들어졌다. SQL Map XML 파일은첫번째 (first class) 요소만을가진다. cache 해당명명공간을위한캐시설정 cache-ref 다른명명공간의캐시설정에대한참조 resultmap 데이터베이스결과데이터를객체에로드하는방법을정의하는요소 parametermap 비권장됨! 예전에파라미터를매핑하기위해사용되었으나현재는사용하지않음 sql 다른구문에서재사용하기위한 SQL 조각 insert 매핑된 INSERT 구문 update 매핑된 UPDATE 구문. delete 매핑된 DELEETE 구문. select 매핑된 SELECT 구문. 2011-08-05 20
다음섹션에서는각각에대해좀더세부적으로살펴볼것이다. select select 구문은 MyBatis 에서가장흔히사용할요소이다. 데이터베이스에서데이터를가져온다. 아마도대부분의애플리케이션은데이터를수정하기보다는조회하는기능을많이가진다. 그래서 MyBatis 는데이터를조회하고그결과를매핑하는데집중하고있다. Select 는다음예처럼단순한경우에는단순하게설정된다. <select id= selectperson parametertype= int resulttype= hashmap > SELECT * FROM PERSON WHERE ID = #{id </select> 이구문의이름은 selectperson 이고 int 타입의파라미터를가진다. 그리고결과데이터는 HashMap 에저장된다. 파라미터 표기법을보자. #{id 이표기법은 MyBatis 에게 PreparedStatement 파라미터를만들도록지시한다. JDBC 를사용할때 PreparedStatement 에는? 형태로파라미터가전달된다. 즉결과적으로위설정은아래와같이작동하게되는셈이다. // Similar JDBC code, NOT MyBatis String selectperson = SELECT * FROM PERSON WHERE ID=? ; PreparedStatement ps = conn.preparestatement(selectperson); ps.setint(1,id); 물론 JDBC 를사용하면, 결과를가져와서객체의인스턴스에매핑하기위한많은코드가필요하겠지만, MyBatis 는그 코드들을작성하지않아도되게해준다. select 요소는각각의구문이처리하는방식에대해좀더세부적으로설정하도록많은속성을설정할수있다. <select id= selectperson parametertype= int parametermap= deprecated resulttype= hashmap resultmap= personresultmap flushcache= false usecache= true timeout= 10000 fetchsize= 256 statementtype= PREPARED resultsettype= FORWARD_ONLY > 속성 id parametertype parametermap 설명구문을찾기위해사용될수있는명명공간내유일한구분자구문에전달될파라미터의패키지경로를포함한전체클래스명이나별칭외부 parametermap 을찾기위한비권장된접근방법. 인라인파라미터매핑과 parametertype 을대신사용하라. 2011-08-05 21
속성설명 resulttype 이구문에의해리턴되는기대타입의패키지경로를포함한전체클래스명이나별칭. collection 이경우, collection 타입자체가아닌 collection 이포함된타입이될수있다. resulttype 이나 resultmap 을사용하라. resultmap 외부 resultmap 의참조명. 결과맵은 MyBatis 의가장강력한기능이다. resulttype 이나 resultmap 을사용하라. flushcache 이값을 true 로셋팅하면, 구문이호출될때마다캐시가지원질것이다 (flush). 디폴트는 false 이다. usecache 이값을 true 로셋팅하면, 구문의결과가캐시될것이다. 디폴트는 true 이다. timeout 예외가던져지기전에데이터베이스의요청결과를기다리는최대시간을설정한다. 디폴트는셋팅하지않는것이고드라이버에따라다소지원되지않을수있다. fetchsize 지정된수만큼의결과를리턴하도록하는드라이버힌트형태의값이다. 디폴트는셋팅하지않는것이고드라이버에따라다소지원되지않을수있다. statementtype STATEMENT, PREPARED 또는 CALLABLE 중하나를선택할수있다. MyBatis 에게 Statement, PreparedStatement 또는 CallableStatement 를사용하게한다. 디폴트는 PREPARED 이다. resultsettype FORWARD_ONLY SCROLL_SENSITIVE SCROLL_INSENSITIVE 중하나를선택할수있다. 디폴트는셋팅하지않는것이고드라이버에다라다소지원되지않을수있다. insert, update, delete 데이터를변경하는구문인 insert, update, delete 는매우간단하다. <insert id="insertauthor" parametertype="domain.blog.author" flushcache="true" statementtype="prepared" keyproperty="" keycolumn="" usegeneratedkeys="" timeout="20000"> <update id="insertauthor" parametertype="domain.blog.author" flushcache="true" statementtype="prepared" timeout="20000"> <delete id="insertauthor" parametertype="domain.blog.author" flushcache="true" statementtype="prepared" timeout="20000"> 속성 Id parametertype 설명 구문을찾기위해사용될수있는명명공간내유일한구분자 구문에전달될파라미터의패키지경로를포함한전체클래스명이나별칭 2011-08-05 22
속성설명 parametermap 외부 parametermap 을찾기위한비권장된접근방법. 인라인파라미터매핑과 parametertype 을대신사용하라. flushcache 이값을 true 로셋팅하면, 구문이호출될때마다캐시가지원질것이다 (flush). 디폴트는 false 이다. Timeout 예외가던져지기전에데이터베이스의요청결과를기다리는최대시간을설정한다. 디폴트는셋팅하지않는것이고드라이버에따라다소지원되지않을수있다. statementtype STATEMENT, PREPARED 또는 CALLABLE 중하나를선택할수있다. MyBatis 에게 Statement, PreparedStatement 또는 CallableStatement 를사용하게한다. 디폴트는 PREPARED 이다. usegeneratedkeys ( 입력 (insert) 에만적용 ) 데이터베이스에서내부적으로생성한키 ( 예를들어, MySQL 또는 SQL Server 와같은 RDBMS 의자동증가필드 ) 를받는 JDBC getgeneratedkeys 메서드를사용하도록설정하다. 디폴트는 false 이다. keyproperty ( 입력 (insert) 에만적용 ) getgeneratedkeys 메서드나 insert 구문의 selectkey 하위요소에의해리턴된키를셋팅할프로퍼티를지정. 디폴트는셋팅하지않는것이다. keycolumn ( 입력 (insert) 에만적용 ) 생성키를가진테이블의칼럼명을셋팅. 키칼럼이테이블이첫번째칼럼이아닌데이터베이스 (PostgreSQL 처럼 ) 에서만필요하다. Insert, update, delete 구문의예제이다. <insert id="insertauthor" parametertype="domain.blog.author"> insert into Author (id,username,password,email,bio) values (#{id,#{username,#{password,#{email,#{bio) </insert> <update id="updateauthor" parametertype="domain.blog.author"> update Author set username = #{username, password = #{password, email = #{email, bio = #{bio where id = #{id </update> <delete id="deleteauthor parametertype="int"> delete from Author where id = #{id </delete> 앞서설명했지만, insert 는 key 생성과같은기능을위해몇가지추가속성과하위요소를가진다. 먼저, 사용하는데이터베이스가자동생성키 ( 예를들면, MySQL 과 SQL 서버 ) 를지원한다면, usegeneratedkeys= true 로설정하고대상프로퍼티에 keyproperty 를셋팅할수있다. 예를들어, Author 테이블이 id 칼럼에자동생성키를적용했다고하면, 구문은아래와같은형태일것이다. <insert id="insertauthor" parametertype="domain.blog.author" usegeneratedkeys= true keyproperty= id > insert into Author (username,password,email,bio) values (#{username,#{password,#{email,#{bio) </insert> 2011-08-05 23
MyBatis 는자동생성키칼럼을지원하지않는다른데이터베이스를위해다른방법또한제공한다. 이예제는랜덤 ID 를생성하고있다. <insert id="insertauthor" parametertype="domain.blog.author"> <selectkey keyproperty="id" resulttype="int" order="before"> select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1 </selectkey> insert into Author (id, username, password, email,bio, favourite_section) values (#{id, #{username, #{password, #{email, #{bio, #{favouritesection,jdbctype=varchar ) </insert> 위예제에서, selectkey 구문이먼저실행되고, Author id 프로퍼티에셋팅된다. 그리고나서 insert 구문이실행된다. 이건 복잡한자바코드없이도데이터베이스에자동생성키의행위와비슷한효과를가지도록해준다. selectkey 요소는다음처럼설정가능하다. <selectkey keyproperty="id" resulttype="int" order="before" statementtype="prepared"> 속성설명 keyproperty selectkey 구문의결과가셋팅될대상프로퍼티 resulttype 결과의타입. MyBatis 는이기능을제거할수있지만추가하는게문제가되지는않을것이다. MyBatis 는 String 을포함하여키로사용될수있는간단한타입을허용한다. order BEFORE 또는 AFTER 를셋팅할수있다. BEFORE 로셋팅하면, 키를먼저조회하고그값을 keyproperty 에셋팅한뒤 insert 구문을실행한다. AFTER 로셋팅하면, insert 구문을실행한뒤 selectkey 구문을실행한다. Oracle 과같은데이터베이스에서는 insert 구문내부에서일관된호출형태로처리한다. statementtype 위내용과같다. MyBatis 는 Statement, PreparedStatement 그리고 CallableStatement 을매핑하기위해 STATEMENT, PREPARED 그리고 CALLABLE 구문타입을지원한다. sql 이요소는다른구문에서재사용가능한 SQL 구문을정의할때사용된다. <sql id= usercolumns > id,username,password </sql> SQL 조각은다른구문에포함시킬수있다. <select id= selectusers parametertype= int resulttype= hashmap > select <include refid= usercolumns /> from some_table where id = #{id 2011-08-05 24
</select> Parameters 앞서본구문들에서, 간단한파라미터들의예를보았을것이다. Parameters 는 MyBatis 에서매우중요한요소이다. 대략 90% 정도의간단한경우, 이러한형태로설정할것이다. <select id= selectusers parametertype= int resulttype= User > select id, username, password from users where id = #{id </select> 위예제는매우간단한명명된파라미터매핑을보여준다. parametertype 은 int 로설정되어있다. Integer 과 String 과같은원시타입이나간단한데이터타입은프로퍼티를가지지않는다. 그래서파라미터전체가값을대체하게된다. 하지만복잡한객체를전달하고자한다면, 다음의예제처럼상황은조금다르게된다. <insert id= insertuser parametertype= User > insert into users (id, username, password) values (#{id, #{username, #{password) </insert> User 타입의파라미터객체가구문으로전달되면, id, username, password 프로퍼티는찾아서 PreparedStatement 파라미터로전달된다. 비록파라미터들을구문에전달하는괜찮은예제이지만, 파라미터매핑을위한다른기능또한더있다. 먼저, 파라미터에데이터타입을명시할수있다. #{property,javatype=int,jdbctype=numeric javatype 은파라미터객체의타입을판단하는기준이된다. javatype 은 TypeHandler 를사용하여정의할수도있다. Note: 만약특정칼럼에 null 이전달되면, JDBC Type 은 null 가능한칼럼을위해필요하다. 처리방법에대해서는 PreparedStatement.setNull() 메서드의 JavaDoc 을보라. 좀더다양하게타입핸들링하기위해서는, TypeHandler 클래스를명시할수있다. #{age,javatype=int,jdbctype=numeric,typehandler=mytypehandler 숫자타입을위해서, 크기를판단하기위한 numericscale 속성도있다. #{height,javatype=double,jdbctype=numeric,numericscale=2 마지막으로, mode 속성은 IN, OUT 또는 INOUT 파라미터를명시하기위해사용한다. 파라미터가 OUT 또는 INOUT 이라면, 파라미터의실제값은변경될것이다. mode=out( 또는 INOUT) 이고 jdbctype=cursor( 예를들어, Oracle REFCURSOR) 라면, 파라미터의타입에 ResultSet 를매핑하기위해 resultmap 을명시해야만한다. #{department, 2011-08-05 25
mode=out, jdbctype=cursor, javatype=resultset, resultmap=departmentresultmap MyBatis 는 structs 와같은좀더향상된데이터타입을지원하지만, 파라미터를등록할때, 타입명을구문에전달해야한다. 예를들면, #{middleinitial, mode=out, jdbctype=struct, jdbctypename=my_type, resultmap=departmentresultmap 이런강력한옵션들에도불구하고, 대부분은프로퍼티명만명시하거나 null 가능한칼럼을위해 jdbctype 정도만명시할 것이다. #{firstname #{middleinitial,jdbctype=varchar #{lastname 문자열대체 (String Substitution) #{ 문법은 MyBatis 로하여금 PreparedStatement 프로퍼티를만들어서 PreparedStatement 파라미터 ( 예를들면,?) 에 값을셋팅하도록할것이다. 이방법이안전하기는하지만, 좀더빠른방법이선호되기도한다. 가끔은 SQL 구문에변하지 않는값으로삽입하길원하기도한다. 예를들면, ORDER BY 와같은구문들이다. ORDER BY ${columnname 여기서 MyBatis 는문자열을변경하거나이스케이프처리하지않는다. 중요 : 사용자로부터받은값을이방법으로변경하지않고구문에전달하는건안전하지않다. 이건잠재적으로 SQL 주입공격에노출된다. 그러므로사용자입력값에대해서는이방법을사용하면안된다. 사용자입력값에대해서는언제나자체적으로이스케이프처리하고체크해야한다. resultmap resultmap 요소는 MyBatis 에서가장중요하고강력한요소이다. ResultSet 에서데이터를가져올때작성되는 JDBC 코드를대부분줄여주는역할을담당한다. 사실 join 매핑과같은복잡한코드는굉장히많은코드가필요하다. ResultMap 은간단한구문에서는매핑이필요하지않고, 좀더복잡한구문에서관계를서술하기위해필요하다. 이미앞에서명시적인 resultmap 을가지지않는간단한매핑구문은봤을것이다. <select id= selectusers parametertype= int resulttype= hashmap > select id, username, hashedpassword from some_table where id = #{id 2011-08-05 26
</sql> 모든칼럼의값이결과가되는간단한구문에서는 HashMap 에서키형태로자동으로매핑된다. 하지만대부분의경우, HashMap 은매우좋은도메인모델이되지는못한다. 그래서대부분도메인모델로는자바빈이나 POJO 를사용할것이다. MyBatis 는둘다지원한다. 자바빈의경우를보자. package com.someapp.model; public class User { private int id; private String username; private String hashedpassword; public int getid() { return id; public void setid(int id) { this.id = id; public String getusername() { return username; public void setusername(string username) { this.username = username; public String gethashedpassword() { return hashedpassword; public void sethashedpassword(string hashedpassword) { this.hashedpassword = hashedpassword; 자바빈스펙에기반하여, 위클래스는 3 개의프로퍼티 (id, username, hashedpassword) 를가진다. 이프로퍼티는 select 구문에서칼럼명과정확히일치한다. 그래서자바빈은 HashMap 과마찬가지로매우쉽게 ResultSet 에매핑될수있다. <select id= selectusers parametertype= int resulttype= com.someapp.model.user > select id, username, hashedpassword from some_table where id = #{id </sql> 그리고 TypeAliases 가편리한기능임을기억해두는게좋다. TypeAliases 를사용하면타이핑수를줄일수있다. 예를들면, <!-- In Config XML file --> <typealias type= com.someapp.model.user alias= User /> 2011-08-05 27
<!-- In SQL Mapping XML file --> <select id= selectusers parametertype= int resulttype= User > select id, username, hashedpassword from some_table where id = #{id </sql> 이경우 MyBatis 는칼럼을자바빈에이름기준으로매핑하여 ResultMap 을자동으로생성할것이다. 만약칼럼명이 프로퍼티명과다르다면, SQL 구문에별칭을지정할수있다. 예를들면. <select id= selectusers parametertype= int resulttype= User > select user_id as id, user_name as username, hashed_password as hashedpassword from some_table where id = #{id </sql> ResultMap 에대한중요한내용은다보았다. 하지만다본건아니다. 칼럼명과프로퍼티명이다른경우에대해 데이터베이스별칭을사용하는것과다른방법으로명시적인 resultmap 을선언하는방법이있다. <resultmap id="userresultmap" type="user"> <id property="id" column="user_id" /> <result property="username" column="username"/> <result property="password" column="password"/> </resultmap> 구문에서는 resultmap 속성에이를지정하여참조한다. 예를들면. <select id= selectusers parametertype= int resultmap= userresultmap > select user_id, user_name, hashed_password from some_table where id = #{id </sql> Advanced Result Mapping 이런복잡한구문은어떻게매핑할까.? <!-- Very Complex Statement --> <select id="selectblogdetails" parametertype="int" resultmap="detailedblogresultmap"> select B.id as blog_id, B.title as blog_title, 2011-08-05 28
B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio, A.favourite_section as author_favourite_section, P.id as post_id, P.blog_id as post_blog_id, P.author_id as post_author_id, P.created_on as post_created_on, P.section as post_section, P.subject as post_subject, P.draft as draft, P.body as post_body, C.id as comment_id, C.post_id as comment_post_id, C.name as comment_name, C.comment as comment_text, T.id as tag_id, T.name as tag_name from Blog B left outer join Author A on B.author_id = A.id left outer join Post P on B.id = P.blog_id left outer join Comment C on P.id = C.post_id left outer join Post_Tag PT on PT.post_id = P.id left outer join Tag T on PT.tag_id = T.id where B.id = #{id </select> 아마 Author 에의해작성되고 Comments 이나태그를가지는많은포스트를가진 Blog 를구성하는괜찮은객체모델에 매핑하고싶을것이다. 이건복잡한 ResultMap 으로충분한예제이다. 복잡해보이지만단계별로살펴보면지극히간단하다. <!-- Very Complex Result Map --> <resultmap id="detailedblogresultmap" type="blog"> <constructor> <idarg column="blog_id" javatype="int"/> </constructor> <result property="title" column="blog_title"/> <association property="author" column="blog_author_id" javatype=" Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> <result property="favouritesection" column="author_favourite_section"/> </association> <collection property="posts" oftype="post"> 2011-08-05 29
<id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <association property="author" column="post_author_id" javatype="author"/> <collection property="comments" column="post_id" oftype=" Comment"> <id property="id" column="comment_id"/> </collection> <collection property="tags" column="post_id" oftype=" Tag" > <id property="id" column="tag_id"/> </collection> <discriminator javatype="int" column="draft"> <case value="1" resulttype="draftpost"/> </discriminator> </collection> </resultmap> resultmap 요소는많은하위요소를가진다. 다음은 resultmap 요소의개념적인뷰 (conceptual view) 이다. resultmap constructor 인스턴스화되는클래스의생성자에결과를삽입하기위해사용됨 o idarg ID 인자 ; ID 와같은결과는전반적으로성능을향상시킨다. o arg 생성자에삽입되는일반적인결과 id ID 결과 ; ID 와같은결과는전반적으로성능을향상시킨다. result 필드나자바빈프로퍼티에삽입되는일반적인결과 association 복잡한타입의연관관계 ; 많은결과는타입으로나타난다. o nested result mappings resultmap 스스로의연관관계 collection 복잡한타입의컬렉션 o nested result mappings resultmap 스스로의연관관계 discriminator 사용할 resultmap 을판단하기위한결과값을사용 o case 몇가지값에기초한결과매핑 nested result mappings a case is also a result map itself, and thus can contain many of these same elements, or it can refer to an external resultmap. 가장좋은형태 : 매번 ResultMap 을추가해서빌드한다. 이경우단위테스트가도움이될수있다. 한번에모든 resultmap 을빌드하면, 작업하기어려울것이다. 간단히시작해서단계별로처리하는것이좋다. 프레임워크를사용하는것은종종블랙박스와같다. 가장좋은방법은단위테스트를통해기대하는행위를달성하는것이다. 이건버그가발견되었을때디버깅을위해서도좋은방법이다. 다음섹션은각각의요소에대해좀더상세하게살펴볼것이다. id, result <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> 이건결과매핑의가장기본적인형태이다. id 와 result 모두한개의칼럼을한개의프로퍼티나간단한데이터타입의필드에 매핑한다. 2011-08-05 30
둘사이의차이점은 id 값은객체인스턴스를비교할때사용되는구분자프로퍼티로처리되는점이다. 이점은일반적으로 성능을향상시키지만특히캐시와내포된 (nested) 결과매핑 ( 조인매핑 ) 의경우에더그렇다. 둘다다수의속성을가진다. 속성 property column javatype jdbctype typehandler 설명결과칼럼에매핑하기위한필드나프로퍼티. 자바빈프로퍼티가해당이름과일치한다면, 그프로퍼티가사용될것이다. 반면에 MyBatis 는해당이름이필드를찾을것이다. 점표기를사용하여복잡한프로퍼티검색을사용할수있다. 예를들어, username 과같이간단하게매핑될수있거나 address.street.number 처럼좀더복잡하게매핑될수도있다. 데이터베이스의칼럼명이나별칭된칼럼라벨. resultset.getstring(columnname) 에전달되는같은문자열이다. 패키지경로를포함한클래스전체명이거나타입별칭. 자바빈을사용한다면 MyBatis 는타입을찾아낼수있다. 반면에 HashMap 으로매핑한다면, 기대하는처리를명확히하기위해 javatype 을명시해야한다. 지원되는타입목록에서설명하는 JDBC 타입. JDBC 타입은 insert, update 또는 delete 하는 null 입력이가능한칼럼에서만필요하다. JDBC 의요구사항이지 MyBatis 의요구사항이아니다. JDBC 로직접코딩을하다보면, null 이가능한값에이타입을지정할필요가있을것이다. 이문서앞에서이미타입핸들러에대해설명했다. 이프로퍼티를사용하면, 디폴트타입핸들러를오버라이드할수있다. 이값은 TypeHandler 구현체의패키지를포함한전체클래스명이나타입별칭이다. 지원되는 JDBC 타입 좀더상세한설명전에, MyBatis 는 jdbctype 열거를통해다음의 JDBC 타입들을지원한다. BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED TINYINT REAL VARCHAR BINARY BLOB NVARCHAR SMALLINT DOUBLE LONGVARCHAR VARBINARY CLOB NCHAR INTEGER NUMERIC DATE LONGVARBINARY BOOLEAN NCLOB BIGINT DECIMAL TIME NULL CURSOR constructor <constructor> <idarg column="id" javatype="int"/> <arg column= username javatype= String /> </constructor> 프로퍼티가데이터전송객체 (DTO) 타입클래스로작동한다. 변하지않는클래스를사용하고자하는경우가있다. 거의변하지않는데이터를가진테이블은종종이변하지않는클래스에적합하다. 생성자주입은 public 메서드가없어도인스턴스화할때값을셋팅하도록해준다. MyBatis 는 private 프로퍼티와 private 자바빈프로퍼티를지원하지만많은사람들은생성자주입을선호한다. constructor 요소는이러한처리를가능하게한다. 다음의생성자를보자. public class User { // 2011-08-05 31
public User(int id, String username) { // // 결과를생성자에주입하기위해, MyBatis 는파라미터타입에일치하는생성자를찾을필요가있다. 자바는파라미터 이름에서타입을체크할방법이없다. 그래서 constructor 요소를생성할때, 인자의순서에주의하고데이터타입을 명시해야한다. <constructor> <idarg column="id" javatype="int"/> <arg column= username javatype= String /> </constructor> 나머지속성과규칙은 id 와 result 요소와동일하다. 속성설명 column 데이터베이스의칼럼명이나별칭된칼럼라벨. resultset.getstring(columnname) 에전달되는같은문자열이다. javatype 패키지경로를포함한클래스전체명이거나타입별칭. 자바빈을사용한다면 MyBatis 는타입을찾아낼수있다. 반면에 HashMap 으로매핑한다면, 기대하는처리를명확히하기위해 javatype 을명시해야한다. jdbctype 지원되는타입목록에서설명하는 JDBC 타입. JDBC 타입은 insert, update 또는 delete 하는 null 입력이가능한칼럼에서만필요하다. JDBC 의요구사항이지 MyBatis 의요구사항이아니다. JDBC 로직접코딩을하다보면, null 이가능한값에이타입을지정할필요가있을것이다. typehandler 이문서앞에서이미타입핸들러에대해설명했다. 이프로퍼티를사용하면, 디폴트타입핸들러를오버라이드할수있다. 이값은 TypeHandler 구현체의패키지를포함한전체클래스명이나타입별칭이다. select 다른매핑된구문의 ID 는이프로퍼티매핑이필요로하는복잡한타입을로드할것이다. column 속성의칼럼으로부터가져온값은대상 select 구문에파라미터로전달될것이다. 좀더세부적인설명은 association 요소를보라. resultmap 이인자의내포된결과를적절한객체로매핑할수있는 ResultMap 의 ID 이다. 다른 select 구문을호출하기위한대체방법이다. 여러개의테이블을조인하는것을하나의 ResultSet 으로매핑하도록해준다. ResultSet 은사본을포함할수있고, 데이터를반복할수도있다. 가능하게하기위해서, 내포된결과를다루도록결과맵을 연결 하자. 좀더자세히알기위해서는 association 요소를보라. association <association property="author" column="blog_author_id" javatype=" Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> </association> association 요소는 has-one 타입의관계를다룬다. 예를들어, Blog 는하나의 Author 를가진다. association 매핑은 다른결과와작동한다. 값을가져오기위해대상프로퍼티를명시한다. 2011-08-05 32
MyBatis 는관계를정의하는두가지방법을제공한다. 내포된 (Nested) Select: 복잡한타입을리턴하는다른매핑된 SQL 구문을실행하는방법. 내포된 (Nested) Results: 조인된결과물을반복적으로사용하여내포된결과매핑을사용하는방법. 먼저요소내프로퍼티들을보자. 보이는것처럼, select 와 resultmap 속성만을사용하는간단한결과매핑과는다르다. 속성 property column javatype jdbctype typehandler 설명결과칼럼에매핑하기위한필드나프로퍼티. 자바빈프로퍼티가해당이름과일치한다면, 그프로퍼티가사용될것이다. 반면에 MyBatis 는해당이름이필드를찾을것이다. 점표기를사용하여복잡한프로퍼티검색을사용할수있다. 예를들어, username 과같이간단하게매핑될수있거나 address.street.number 처럼좀더복잡하게매핑될수도있다. 데이터베이스의칼럼명이나별칭된칼럼라벨. resultset.getstring(columnname) 에전달되는같은문자열이다. Note: 복합키를다루기위해서, column= {prop1=col1,prop2=col2 문법을사용해서여러개의칼럼명을내포된 select 구문에명시할수있다. 이것은대상의내포된 select 구문의파라미터객체에 prop1, prop2 형태로셋팅하게될것이다. 패키지경로를포함한클래스전체명이거나타입별칭. 자바빈을사용한다면 MyBatis 는타입을찾아낼수있다. 반면에 HashMap 으로매핑한다면, 기대하는처리를명확히하기위해 javatype 을명시해야한다. 지원되는타입목록에서설명하는 JDBC 타입. JDBC 타입은 insert, update 또는 delete 하는 null 입력이가능한칼럼에서만필요하다. JDBC 의요구사항이지 MyBatis 의요구사항이아니다. JDBC 로직접코딩을하다보면, null 이가능한값에이타입을지정할필요가있을것이다. 이문서앞에서이미타입핸들러에대해설명했다. 이프로퍼티를사용하면, 디폴트타입핸들러를오버라이드할수있다. 이값은 TypeHandler 구현체의패키지를포함한전체클래스명이나타입별칭이다. 관계를위한내포된 select select 다른매핑된구문의 ID 는이프로퍼티매핑이필요로하는복잡한타입을로드할것이다. column 속성의칼럼으로부터가져온값은대상 select 구문에파라미터로전달될것이다. Note: 복합키를다루기위해서, column= {prop1=col1,prop2=col2 문법을사용해서여러개의칼럼명을내포된 select 구문에명시할수있다. 이것은대상의내포된 select 구문의파라미터객체에 prop1, prop2 형태로셋팅하게될것이다. 예를들면. <resultmap id= blogresult type= Blog > <association property="author" column="blog_author_id" javatype="author" select= selectauthor /> </resultmap> <select id= selectblog parametertype= int resultmap= blogresult > SELECT * FROM BLOG WHERE ID = #{id </select> <select id= selectauthor parametertype= int resulttype="author"> SELECT * FROM AUTHOR WHERE ID = #{id 2011-08-05 33
</select> 여기엔두개의 select 구문이있다. 하나는 Blog 를로드하고다른하나는 Author 를로드한다. 그리고 Blog 의 resultmap 은 author 프로퍼티를로드하기위해 selectauthor 구문을사용한다. 다른프로퍼티들은칼럼과프로퍼티명에일치하는것들로자동으로로드될것이다. 이방법은간단한반면에, 큰데이터나목록에는제대로작동하지않을것이다. 이방법은 N+1 Selects 문제 으로알려진 문제점을가진다. N+1 Selects 문제는처리과정의특이성으로인해야기된다. 레코드의목록을가져오기위해하나의 SQL 구문을실행한다. ( +1 에해당 ). 리턴된레코드별로, 각각의상세데이터를로드하기위해 select 구문을실행한다. ( N 에해당 ). 이문제는수백또는수천의 SQL 구문실행이라는결과를야기할수있다. 아마도언제나바라는형태의처리가아닐것이다. 목록을로드하고내포된데이터에접근하기위해즉시반복적으로처리한다면, 늦은로딩으로호출하고게다가성능은많이 나빠질것이다. 그래서다른방법이있다. 관계를위한내포된결과 (Nested Results) resultmap 이인자의내포된결과를적절한객체로매핑할수있는 ResultMap 의 ID 이다. 다른 select 구문을호출하기위한대체방법이다. 여러개의테이블을조인하는것을하나의 ResultSet 으로매핑하도록해준다. ResultSet 은사본을포함할수있고, 데이터를반복할수도있다. 가능하게하기위해서, 내포된결과를다루도록결과맵을 연결 하자. 좀더자세히알기위해서는 association 요소를보라. 위에서내포된관계의매우복잡한예제를보았을것이다. 다음은작동하는것을보기위한좀더간단한예제이다. 개별 구문을실행하는것대신에, Blog 와 Author 테이블을함께조인했다. <select id="selectblog" parametertype="int" resultmap="blogresult"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio from Blog B left outer join Author A on B.author_id = A.id where B.id = #{id </select> 조인을사용할때, 결과의값들이유일하거나좀더명확한이름이되도록별칭을사용하는것이좋다. 이제결과를매핑할수 있다. <resultmap id="blogresult" type="blog"> 2011-08-05 34
<id property= blog_id column="id" /> <result property="title" column="blog_title"/> <association property="author" column="blog_author_id" javatype="author" resultmap= authorresult /> </resultmap> <resultmap id="authorresult" type="author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> </resultmap> 위예제에서, Author 인스턴스를로드하기위한 authorresult resultmap 으로위임된 Blog 의 author 관계를볼수 있을것이다. 매우중요 : id 요소는내포된결과매핑에서매우중요한역할을담당한다. 결과중유일한것을찾아내기위한한개이상의 프로퍼티를명시해야만한다. 가능하면결과중유일한것을찾아낼수있는프로퍼티들을선택하라. 기본키가가장좋은 선택이될수있다. 이제, 위예제는관계를매핑하기위해외부의 resultmap 요소를사용했다. 이외부 resultmap 은 Author resultmap 을 재사용가능하도록해준다. 어쨌든, 재사용할필요가있거나, 한개의 resultmap 에결과매핑을함께위치시키고자한다면, association 결과매핑을내포시킬수있다. 다음은이방법을사용한예제이다. <resultmap id="blogresult" type="blog"> <id property= blog_id column="id" /> <result property="title" column="blog_title"/> <association property="author" column="blog_author_id" javatype="author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> </association> </resultmap> 지금까지 has one 관계를다루는방법을보았다. 하지만 has many 는어떻게처리할까? 그건다음섹션에서다루어보자. collection <collection property="posts" oftype="domain.blog.post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </collection> 2011-08-05 35
collection 요소는관계를파악하기위해작동한다. 사실이내용이중복되는내용으로, 차이점에대해서만주로살펴보자. 위예제를계속진행하기위해, Blog 는오직하나의 Author 를가진다. 하지만 Blog 는많은 Post 를가진다. Blog 클래스에 다음처럼처리될것이다. private List<Post> posts; List 에내포된결과를매핑하기위해, collection 요소를사용한다. association 요소와는달리, 조인에서내포된 select 나 내포된결과를사용할수있다. Collection 을위한내포된 (Nested) Select 먼저, Blog 의 Post 를로드하기위한내포된 select 를사용해보자. <resultmap id= blogresult type= Blog > <collection property="posts" javatype= ArrayList column="blog_id" oftype="post" select= selectpostsforblog /> </resultmap> <select id= selectblog parametertype= int resultmap= blogresult > SELECT * FROM BLOG WHERE ID = #{id </select> <select id= selectpostsforblog parametertype= int resulttype="blog"> SELECT * FROM POST WHERE BLOG_ID = #{id </select> 바로눈치챌수있는몇가지가있지만, 대부분앞서배운 association 요소와매우유사하다. 먼저, collection 요소를사용한 것이보일것이다. 그리고나서새로운 oftype 속성을사용한것을알아차렸을것이다. 이속성은자바빈프로퍼티타입과 collection 의타입을구분하기위해필요하다. <collection property="posts" javatype= ArrayList column="blog_id" oftype="post" select= selectpostsforblog /> 독자왈 : Post 의 ArrayList 타입의글목록 javatype 속성은그다지필요하지않다. MyBatis 는대부분의경우이속성을사용하지않을것이다. 그래서좀더간단하게 설정할수있다. <collection property="posts" column="blog_id" oftype="post" select= selectpostsforblog /> Collection 을위한내포된 (Nested) Results 2011-08-05 36
이시점에, collection 을위한내포된결과가어떻게작동하는지짐작할수있을것이다. 왜냐하면 association 와정확히 일치하기때문이다. 하지만 oftype 속성이추가로적용되었다. 먼저, SQL 을보자. <select id="selectblog" parametertype="int" resultmap="blogresult"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, P.id as post_id, P.subject as post_subject, P.body as post_body, from Blog B left outer join Post P on B.id = P.blog_id where B.id = #{id </select> 다시보면, Blog 와 Post 테이블을조인했고간단한매핑을위해칼럼명에적절한별칭을주었다. 이제 Post 의 Collection 을가진 Blog 의매핑은다음처럼간단해졌다. <resultmap id="blogresult" type="blog"> <id property= id column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" oftype="post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </collection> </resultmap> 다시, 여기서 id 요소의중요성을기억해두거나기억이나지않으면 association 섹션에서다시읽어둬라. 혹시, 결과매핑의재사용성을위해좀더긴형태를선호한다면, 다음과같은형태로도가능하다. <resultmap id="blogresult" type="blog"> <id property= id column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" oftype="post" resultmap= blogpostresult /> </resultmap> <resultmap id="blogpostresult" type="post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </resultmap> 2011-08-05 37
Note: associations 과 collections 에서내포의단계혹은조합에는제한이없다. 매핑할때는성능을생각해야한다. 단위 테스트와성능테스트는애플리케이션에서가장좋은방법을찾도록지속해야한다. MyBatis 는이에수정비용을최대한 줄이도록해줄것이다. discriminator <discriminator javatype="int" column="draft"> <case value="1" resulttype="draftpost"/> </discriminator> 종종하나의데이터베이스쿼리는많고다양한데이터타입의결과를리턴한다. discriminator 요소는클래스상속관계를 포함하여이러한상황을위해고안되었다. discriminator 는자바의 switch 와같이작동하기때문에이해하기쉽다. discriminator 정의는 colume 과 javatype 속성을명시한다. colume 은 MyBatis 로하여금비교할값을찾을것이다. javatype 은동일성테스트와같은것을실행하기위해필요하다. 예를들어 <resultmap id="vehicleresult" type="vehicle"> <id property= id column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javatype="int" column="vehicle_type"> <case value="1" resultmap="carresult"/> <case value="2" resultmap="truckresult"/> <case value="3" resultmap="vanresult"/> <case value="4" resultmap="suvresult"/> </discriminator> </resultmap> 이예제에서, MyBatis 는결과데이터에서각각의레코드를가져와서 vehicle_type 값과비교한다. 만약 discriminator 비교값과같은경우가생기면, 이경우에명시된 resultmap 을사용할것이다. 해당되는경우가없다면무시된다. 만약일치하는경우가하나도없다면, MyBatis 는 discriminator 블럭밖에정의된 resultmap 을사용한다. carresult 가다음처럼정의된다면, <resultmap id="carresult" type="car"> </resultmap> <result property= doorcount column="door_count" /> doorcount 프로퍼티만이로드될것이다. discriminator 경우들의독립적인결과를만들어준다. 이경우우리는물론 car 와 vehicle 간의관계를알수있다. 그러므로, 나머지프로퍼티들도로드하길원하게된다. 그러기위해서는간단하게하나만 변경하면된다. <resultmap id="carresult" type="car" extends= vehicleresult > </resultmap> <result property= doorcount column="door_count" /> 2011-08-05 38
vehicleresult 와 carresult 의모든프로퍼티들이로드될것이다. 한가지더, 도처에설정된외부정의를찾게될지도모른다. 그러므로좀더간결한매핑스타일의문법이있다. 예를들면 <resultmap id="vehicleresult" type="vehicle"> <id property= id column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javatype="int" column="vehicle_type"> <case value="1" resulttype="carresult"> <result property= doorcount column="door_count" /> </case> <case value="2" resulttype="truckresult"> <result property= boxsize column="box_size" /> <result property= extendedcab column="extended_cab" /> </case> <case value="3" resulttype="vanresult"> <result property= powerslidingdoor column="power_sliding_door" /> </case> <case value="4" resulttype="suvresult"> <result property= allwheeldrive column="all_wheel_drive" /> </case> </discriminator> </resultmap> 모든결과매핑이있고, 모두명시하고싶지않다면, MyBatis 는칼럼과프로퍼티명으로자동으로매핑할것이다. 이예제는실제로필요한내용보다좀더많이서술되어있다. cache MyBatis 는쉽게설정가능하고변경가능한쿼리캐싱기능을가지고있다. MyBatis 3 캐시구현체는좀더강력하고쉽게설정할수있도록많은부분이수정되었다. 성능을개선하고순환하는의존성을해결하기위해필요한로컬세션캐싱을제외하고기본적으로캐시가작동하지않는다. 캐싱을활성화하기위해서, SQL 매핑파일에한줄을추가하면된다. <cache/> 하나의간단한구문에다음과같은순서로영향을준다. 매핑구문파일내 select 구문의모든결과가캐시될것이다. 매핑구문파일내 insert, update 그리고 delete 구문은캐시를지울 (flush) 것이다. 2011-08-05 39
캐시는 Least Recently Used (LRU) 알고리즘을사용할것이다. 캐시는스케줄링기반으로시간순서대로지워지지는않는다. ( 예를들면. no Flush Interval) 캐시는리스트나객체에대해 1024 개의참조를저장할것이다. ( 쿼리메서드가실행될때마다 ) 캐시는읽기 / 쓰기캐시처럼처리될것이다. 이것은가져올객체는공유되지않고호출자에의해안전하게 변경된다는것을의미한다. 모든프로퍼티는 cache 요소의속성을통해변경가능하다. 예를들면 : <cache eviction="fifo" flushinterval="60000" size="512" readonly="true"/> 좀더많은프로퍼티가셋팅된이설정은 60 초마다캐시를지우는 FIFO 캐시를생성한다. 이캐시는결과객체또는결과 리스트를 512 개까지저장하고각객체는읽기전용이다. 캐시데이터를변경하는것은개별쓰레드에서호출자간의충돌을 야기할수있다. 사용가능한캐시전략은 4 가지이다. LRU Least Recently Used: 가장오랜시간사용하지않는객체를제거 FIFO First In First Out: 캐시에들어온순서대로객체를제거 SOFT Soft Reference: 가비지컬렉터의상태와강하지않은참조 (Soft References ) 의규칙에기초하여객체를 제거 WEAK Weak Reference: 가비지컬렉터의상태와약한참조 (Weak References) 의규칙에기초하여 점진적으로객체제거 디폴트값은 LRU 이다. flushinterval 은양수로셋팅할수있고, 밀리세컨드로명시되어야한다. 디폴트는셋팅되지않으나, 플러시 (flush) 주기를 사용하지않으면, 캐시는오직구문이호출될때마다캐시를지운다. size 는양수로셋팅할수있고캐시에객체의크기를유지하지만메모리자원이충분해야한다. 디폴트값은 1024 이다. readonly 속성은 true 또는 false 로설정할수있다. 읽기전용캐시는모든호출자에게캐시된객체의같은인스턴스를 리턴할것이다. 게다가그객체는변경할수없다. 이건종종성능에잇점을준다. 읽고쓰는캐시는캐시된객체의복사본을 리턴할것이다. 이건조금더늦긴하지만안전하다. 디폴트는 false 이다.. 사용자지정캐시사용하기 앞서본다양한설정방법에더해, 자체적으로개발하거나써드파티캐시솔루션을사용하여캐시처리를할수있다. 2011-08-05 40
<cache type= com.domain.something.mycustomcache /> 이예제는사용자지정캐시구현체를사용하는방법을보여준다. type 속성에명시된클래스는 org.mybatis.cache.cache 인터페이스를반드시구현해야한다. 이인터페이스는 MyBatis 프레임워크의가장복잡한구성요소중하나이다. 하지만 하는일은간단하다. public interface Cache { String getid(); int getsize(); void putobject(object key, Object value); Object getobject(object key); boolean haskey(object key); Object removeobject(object key); void clear(); ReadWriteLock getreadwritelock(); 캐시를설정하기위해, 캐시구현체에 public 자바빈프로퍼티를추가하고 cache 요소를통해프로퍼티를전달한다. 예를 들어, 다음예제는캐시구현체에서 setcachefile(string file) 를호출하여메서드를호출할것이다. <cache type= com.domain.something.mycustomcache > <property name= cachefile value= /tmp/my-custom-cache.tmp /> </cache> 모든간단한타입의자바빈프로퍼티를사용할수있다. MyBatis 는변환할것이다. 캐시설정과캐시인스턴스가 SQL Map 파일의명명공간에묶여지는 (bound) 것을기억하는게중요하다. 게다가, 같은 명명공간내모든구문은묶여진다. 구문별로캐시와상호작동하는방법을변경할수있거나두개의간단한속성을통해 완전히배제될수도있다. 디폴트로구문은아래와같이설정된다. <select... flushcache= false usecache= true /> <insert... flushcache= true /> <update... flushcache= true /> <delete... flushcache= true /> 이러한방법으로구문을설정하는하는방법은굉장히좋지않다. 대신디폴트행위를변경해야할경우에만 flushcache 와 usecache 속성을변경하는것이좋다. 예를들어, 캐시에서특정 select 구문의결과를제외하고싶거나캐시를지우기위한 select 구문을원할것이다. 유사하게실행할때마다캐시를지울필요가없는 update 구문도있을것이다. 2011-08-05 41