ibatis SQL Maps 튜토리얼 For SQL Maps Version 2.0 June 17, 2004 번역 : 이동국 (fromm0@gmail.com) 오타및오역은위메일주소로보내주시기바랍니다. 1
소개 이튜토리얼은 SQL Maps 의전형적인사용법을설명한다. 각각의주제에대해서좀더상세한정보는 http://www.ibatis.com 에 서얻을수있는 SQL Maps 개발자가이드를통해서찾아볼수있다. SQL Maps 을사용하기위해준비하기 SQL Maps 프레임워크는나쁜데이터베이스모델과객체모델에매우관대하다. 이것에도불구하고당신의데이터베이스 ( 또는 정규화 ) 와객체를디자인할때가장좋은예제를사용하도록추천한다. 다음과같이함으로써당신은좋은성능과깔끔한디 자인을얻을수있을것이다. 시작하기위해가장쉬운방법은당신이수행하고자하는것을분석하는것이다. 당신의비즈니스객체는무엇인가.? 당신의 데이터베이스테이블은무엇인가.? 그들은각각어떻게관계되는가.? 첫예제를위해전형적인자바빈즈플랫폼을위해다음 의간단한 Person 클래스를보자. Person.java package examples.domain; //imports implied. public class Person { private int id; private String firstname; private String lastname; private Date birthdate; private double weightinkilograms; private double heightinmeters; public int getid () { return id; public void setid (int id) { this.id = id; //let s assume we have the other getters and setters to save space 우리의데이터베이스에이 Person 클래스를어떻게맵핑시킬까.? SQL Maps 는테이블대클래스, 다중테이블대클래스, 다중 클래스대테이블등과같은관계로부터어떠한제약도가지지않는다. 당신이유효한 SQL 의모든기능을가지기때문에제약 은거의없다. 이예제를위해테이블대클래스관계를나타내기에적합한다음의간단한테이블을사용하자. Person.sql CREATE TABLE PERSON( PER_ID NUMBER (5, 0) NOT NULL, PER_FIRST_NAME VARCHAR (40) NOT NULL, PER_LAST_NAME VARCHAR (40) NOT NULL, PER_BIRTH_DATE DATETIME, PER_WEIGHT_KG NUMBER (4, 2) NOT NULL, 2
) PER_HEIGHT_M NUMBER (4, 2) NOT NULL, PRIMARY KEY (PER_ID) SQL Map 설정파일 클래스와테이블로작업하기편안한시점에시작하기가장좋은방법은 SQL Map 설정파일이다. 이파일은우리의 SQL Map 구현물을위한가장상위설정으로작동할것이다. 이설정파일은 XML 파일이다. 이것은프라퍼티, JDBC 데이터소스그리고 SQL Maps 를설정할것이다. 이것은당신에게많은수 의다른형태로구현될수있는데이터소스를중앙집중적으로설정할수있는편리한위치를준다. 프레임워크는 ibatis SimpleDataSource, Jakarta DBCP, JNDI 를검색을통해찾을수있는어떤데이터소스를포함해서많은수의데이터소스구현 물을다룰수있다. 개발자가이드에서좀더상세하게다루어진다. 구조는아래예제처럼간단하다. SqlMapConfigExample.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 usually relative to the classpath and is optional. --> <properties resource="examples/sqlmap/maps/sqlmapconfigexample.properties" /> <!--These settings control SqlMap configuration details, primarily to do with transaction management. They are all optional (see the Developer Guide for more). --> <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"/> 3
<property name="jdbc.password" value="${password"/> </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> SqlMapConfigExample.properties # This is just a simple properties file that simplifies automated configuration # of the SQL Maps configuration file (e.g. by Ant builds or continuous # integration tools for different environments etc.) # These values can be used in any property value in the file above (e.g. ${driver ) # Using a properties file such as this is completely optional. driver=oracle.jdbc.driver.oracledriver url=jdbc:oracle:thin:@localhost:1521:oracle1 username=jsmith password=test SQL Map 파일들 지금우리는설정된데이터소스를가지고있고중앙집중적설정파일은수행할준비가되었다. 우리는 SQL 코드와파라미터객 체를위한맵핑그리고 result 객체를포함하는 SQL Map 파일을제공할필요가있을것이다. 아래의예제를가지고계속적으로 Person 클래스와 PERSON 테이블을위해 SQL Map 파일을빌드해보자. 우리는 SQL 문서를 일반적인구조와간단한 select 문을가지고시작할것이다. Person.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE sqlmap PUBLIC "-//ibatis.com//dtd SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map- 2.dtd"> <sqlmap namespace="person"> <select id="getperson" 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# </select> </sqlmap> 4
위의예제는 SQL Map의가장간단한형태를보여준다. 이것은이름매치 (name matching) 에기초로하여 ResultSet의칼럼을자바빈즈프라퍼티 ( 또는 map키등등 ) 에자동적으로맵핑하는 SQL Maps프레임워크의기능을사용한다. #value# 토큰은입력파라미터이다. 좀더상세하게말하면 value 의사용은우리가간단한원시래퍼타입 ( 이를테면, Integer 하지만우리는이것에어떠한제한도하지않는다.) 을사용하는것을적용한다. 매우간단함에도불구하고자동결과맵핑 (auto-result mapping) 접근법을사용하는데에는몇가지제한이있다. 출력칼럼의타입을정의하거나관계된데이터 ( 복잡한프라퍼티 ) 를자동적으로로드하는방법이없다. 그리고이접근법은 ResultSetMetaData을접근하는것을요구하기에미세한성능영향이있다. resultmap을사용함으로써우리는이런제한점모두를극복할수있다. 하지만지금단순함은우리의목표이다. 그리고우리는나중에다른접근법으로변경할수도 ( 자바소스코드에어떠한변경도없이 ) 있다. 대개의데이터베이스애플리케이션은데이터베이스로부터간단하게읽지않는다. 그들은데이터베이스내데이터를변경할수도있습니다. 우리는맵핑된 statement내 SELECT를간단히찾을수있는예제를이미보았습니다. 하지만 INSERT, UPDATE 그리고 DELETE에대해서는.? 좋은뉴스는전혀다르지않다는것이다. 아래에서우리는데이터에접근하고변경하기위한완벽한 statement을제공하기위해좀더많은 statement으로우리의 Person SQL Map를완성할것이다. Person.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE sqlmap PUBLIC "-//ibatis.com//dtd SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map- 2.dtd"> <sqlmap namespace="person"> <!--Use primitive wrapper type (e.g. Integer) as parameter and allow results to be auto-mapped results to Person object (JavaBean) properties --> <select id="getperson" parameterclass= int resultclass="examples.domain.person"> SELECT PER_ID as id, PER_FIRST_NAME as firstname, PER_LAST_NAME as lastname, PER_BIRTH_DATE as birthdate, PER_WEIGHT_KG as weightinkilograms, PER_HEIGHT_M as heightinmeters FROM PERSON WHERE PER_ID = #value# </select> <!--Use Person object (JavaBean) properties as parameters for insert. Each of the parameters in the #hash# symbols is a JavaBeans property. --> <insert id="insertperson" parameterclass="examples.domain.person"> INSERT INTO PERSON (PER_ID, PER_FIRST_NAME, PER_LAST_NAME, PER_BIRTH_DATE, PER_WEIGHT_KG, PER_HEIGHT_M) VALUES (#id#, #firstname#, #lastname#, #birthdate#, #weightinkilograms#, #heightinmeters#) </insert> <!--Use Person object (JavaBean) properties as parameters for update. Each of the parameters in the #hash# symbols is a JavaBeans property. --> 5
<update id="updateperson" parameterclass="examples.domain.person"> UPDATE PERSON SET PER_FIRST_NAME = #firstname#, PER_LAST_NAME = #lastname#, PER_BIRTH_DATE = #birthdate#, PER_WEIGHT_KG = #weightinkilograms#, PER_HEIGHT_M = #heightinmeters# WHERE PER_ID = #id# </update> <!--Use Person object (JavaBean) id properties as parameters for delete. Each of the parameters in the #hash# symbols is a JavaBeans property. --> <delete id="deleteperson" parameterclass="examples.domain.person"> DELETE PERSON WHERE PER_ID = #id# </delete> </sqlmap> SQL Map 프레임워크로프로그래밍하기. 지금그것은우리가모두설정하고맵핑했다. 우리가해야할필요가있는모든것은자바애플리케이션내의코드이다. 첫번 째단계는 SQL Map 를설정하는것이다. 이것은우리가이전에생성한 SQL Map 설정 XML 파일을로드하는간단한방법이다. XML 파일로딩을간단히하기위해서우리는프레임워크와함께포함된 Resource 클래스를사용할수있다. String resource = com/ibatis/example/sqlmap-config.xml ; Reader reader = Resources.getResourceAsReader (resource); SqlMapClient sqlmap = SqlMapClientBuilder.buildSqlMapClient(reader); SqlMapClient 객체는오랜기간지속되고쓰레드에안전한 (thread safe) 서비스객체이다. 주어진애플리케이션실행을위해 당신은오직한번그것을초기화하고설정할필요가있다. 이것은정적멤버와기본클래스를위해좋은지원자를만들어주 거나당신이좀더중앙집중적으로설정하고전역적으로유용하게만들기를선호한다면당신은당신자신의편리한클래스를 만들수있다. 여기에당신이쓸수있는편리한클래스예제가있다. public MyAppSqlConfig { private static final SqlMapClient sqlmap; static { try { String resource = com/ibatis/example/sqlmap-config.xml ; Reader reader = Resources.getResourceAsReader (resource); sqlmap = SqlMapClientBuilder.buildSqlMapClient(reader); catch (Exception e) { // If you get an error at this point, it doesn t matter what it was. It is going to be // unrecoverable and we will want the app to blow up hard so we are aware of the // problem. You should always log such errors and re-throw them in such a way that // you can be made immediately aware of the problem. e.printstacktrace(); throw new RuntimeException ( Error initializing MyAppSqlConfig class. Cause: + e); 6
public static getsqlmapinstance () { return sqlmap; 데이터베이스로부터객체읽기 지금 SqlMap 인스턴스는초기화되었고쉽게접근가능하다. 우리는이것을사용할수있다. 데이터베이스로부터 Person 객체를 얻기위해이것을사용해보자. 데이터베이스로부터 Person 객체를얻기위해우리는간단하게 SqlMap 인스턴스, 맵핑된 statement 의이름그리고 Person ID 가필요하다. Person #5 를로드해보자. SqlMapClient sqlmap = MyAppSqlMapConfig.getSqlMapInstance(); // as coded above Integer personpk = new Integer(5); Person person = (Person) sqlmap.queryforobject ( getperson, personpk); 데이터베이스에객체쓰기 우리는지금데이터베이스로부터 Person 객체를가진다. 몇몇데이터를변경해보자. 우리는 person 의 height 와 weight 를변경 할것이다. person.setheightinmeters(1.83); // person as read above person.setweightinkilograms(86.36); sqlmap.update( updateperson, person); 만약우리가이 Person 을삭제하기를원한다면다음처럼쉽다. sqlmap.delete ( deleteperson, person); 새로운 Person 을추가하는것은유사하다. Person newperson = new Person(); newperson.setid(11); // you would normally get the ID from a sequence or custom table newperson.setfirstname( Clinton ); newperson.setlastname( Begin ); newperson.setbirthdate (null); newperson.setheightinmeters(1.83); newperson.setweightinkilograms(86.36); sqlmap.insert ( insertperson, newperson); 7
모든것을수행했다.!! 다음단계 이것은짧은튜토리얼의끝이다. 완벽한 SQL Maps 2.0 개발자가이드를위해서는 http://www.ibatis.com 를방문하길바란다. Jakarta Struts, ibatis DAO 2.0 그리고 SQL Maps 2.0 을기반으로하는완벽한웹애플리케이션예지인 JPetStore 4 또한제공한 다. CLINTON BEGIN MAKES NO WARRANTIES, EXPRESS OR IMPLIED, AS TO THE INFORMATION IN THIS DOCUMENT. 2004 Clinton Begin. All rights reserved. ibatis and ibatis logos are trademarks of Clinton Begin. The names of actual companies and products mentioned herein may be the trademarks of their respective owners. 8