430 likes | 444 Views
iBATIS 2. 이동국 NHN September 28 th , 2008. Overview. 소개 사용하기 iBATIS 3.0 소식 현재 진행중인 이슈. iBATIS..?. 소개. Simple Lightweight. 소개. 소개. 매우 간단한 XML 서술자를 사용해서 클래스와 SQL 구문을 매핑하는 퍼시스턴스 프레임워크. CLASS. SQL. XML. 4. 소개. JDBC 와 iBATIS. JDBC
E N D
iBATIS 2 이동국 NHN September 28th, 2008
Overview • 소개 • 사용하기 • iBATIS 3.0 소식 • 현재 진행중인 이슈
iBATIS..? 소개 SimpleLightweight
소개 소개 • 매우 간단한 XML서술자를 사용해서 클래스와 SQL구문을 매핑하는 퍼시스턴스 프레임워크 CLASS SQL XML 4
소개 JDBC와 iBATIS • JDBC conn = DriverManager.getConnection(url, user, pass); stmt = conn.prepareStatement(); String sql = "SELECT * FROM employees"; rs = stmt.executeQuery(sql); while (rs.next()) { rs.getString("..."); rs.getString("..."); ... } rs.close(); stmt.close(); conn.close(); 5
소개 JDBC와 iBATIS • iBATIS sqlMapClient.queryForList("selectEmployees") <select id="selectEmployees" resultClass="Employee"> SELECT * FROM employees </select> 6
소개 JDBC와 iBATIS • JDBC conn = DriverManager.getConnection(url, user, pass); String sql = "insert into employees values(?,?,?,?)"; stmt = conn.prepareStatement(sql); stmt.setString(1, ".."); stmt.setString(2, ".."); stmt.setString(3, ".."); stmt.setString(4, ".."); int result = stmt.executeUpdate(); stmt.close(); conn.close(); 7
소개 JDBC와 iBATIS • iBATIS sqlMapClient.insert("insertEmployees", employee) <insert id="insertEmployees" parameterClass="Employee"> insert into employees values(#id#,#name#,#val1#,#val2#) </insert> 8
소개 JDBC와 iBATIS • 3.0에서 추가될 코드 컨벤션에 따른 SQL구문 자동생성 Employee getEmployee(int id); → SELECT id, firstName, lastName FROM Employee WHERE id = #id# List<Employee> listAllEmployees(); → SELECT id, firstName, lastName FROM Employee 9
소개 장점과 단점 • 장점 SQL문과 소스 코드 분리로 소스 코드의 간결함을 유지 할 수 있다. 데이터베이스 자원에 대한 필요이상의 제어를 자동으로 해결해 준다. • 단점 SQL문과 소스 코드를 분리한다는 점에서는 잇점이지만 오히려 지나치게 많은 파일을 생성해서 관리상의 어려움을 초래 할 수도 있다. 클래스 파일에 대한 리로드 기능은 WAS 및 기타 제품에 의해 적절히 보장 받을 수 있으나 XML 리로드에 대해서는 별도의 대책이 요구된다. 10
사용하기 데이터 타입 11
사용하기 #과 $ • # SELECT * FROM account WHERE acc_id = #id# → SELECT * FROM account WHERE acc_id = ? • $SELECT * FROM account WHERE acc_id = $id$ → SELECT * FROM account WHERE acc_id = 1 12
사용하기 SQL 재사용하기 <sql id="selectAccount_frag"> SELECT acc_id, acc_first_name, acc_last_name, acc_email FROM account </sql> <select id="selectAllAccounts" resultClass="Account"> <include refid="selectAccount_frag" /> </select> 13
사용하기 Result Map • 일반적인 Result Map <resultMap id="accountMap" class="Account"> <result property="id" column="acc_id" /> <result property="emailAddress" column="acc_email" /> </resultMap> 14
사용하기 Result Map -account • 복합 Collection Result Map에 사용되는 객체 구조 • public class Account { • private int id; • private String firstName; • ..... • private List<AccountFamily> families = null; 15
사용하기 Result Map • 복합 Collection Result Map을 JDBC로 구현한다면 • String accountSql = "SELECT * FROM account WHERE acc_id = ?"; • String familySql = "SELECT * FROM Account_family WHERE acc_id = ?"; • accountStmt = conn.prepareStatement(accountSql); • accountRs = accountStmt.executeQuery(); • while (accountRs.next()){ • accountRs.getString("..."); • accountRs.getString("..."); • familyStmt = conn.prepareStatement(familySql); • familyRs = familyStmt.executeQuery(); • while( familyRs ){ • familyRs.getString(""); • ... • } • account.setFamilies(...); • ... • } ResultSet내에서 하위 ResultSet 처리 16
사용하기 Result Map • 복합 Collection Result Map <resultMap id="get_account_nplus1" class="Account"> <result property="id" column="acc_id" /> <result property="families“column="{id1=acc_id, id2=acc_id}" select="SubList.selectFamilyList" /> </resultMap> • 로그 Connection Executing Statement: SELECT * FROM account WHERE acc_id = ? Executing Statement: SELECT * FROM Account_family WHERE acc_id = ?ResultSet Header: [id, fullName] Result: [1, HongGilDong] Result: [1, SpiderMan] Header: [acc_id, acc_first_name, acc_last_name, acc_email, acc_id] Result: [1, DongGuk, Lee, fromm0@gmail.com, 1] 17
사용하기 Result Map • 복합 Collection Result Map에서 테이블조인을 JDBC로 구현한다면 • String sql = "SELECT * FROM account a LEFT JOIN account_family f ON a.acc_id = f.acc_id WHEREa.acc_id=?"; • stmt = conn.prepareStatement(sql); • rs = stmt.executeQuery(); • int i = 0; • while (rs.next()){ • family = new Family(); • if( i == 0 ){ • account.setId(); • ... • } • family.setId(); • list.add(family); • i++; • } 18
사용하기 Result Map • 복합 Collection Result Map <resultMap id="get_account_family_avoidnplus1" class="Account" groupBy="id"> <result property="id" column="acc_id" /> <result property="families"resultMap="Base.get_family_avoidnplus1" /> </resultMap> • 로그 Connection Executing Statement: SELECT * FROM account a LEFT JOINaccount_family f ON a.acc_id = f.acc_id WHEREa.acc_id=? ResultSet Header: [acc_id, acc_first_name, acc_last_name, acc_email, acc_id, family_fullname] Result: [1, DongGuk, Lee, fromm0@gmail.com, 1, HongGilDong] Result: [1, DongGuk, Lee, fromm0@gmail.com, 1, SpiderMan] Result: [1, DongGuk, Lee, fromm0@gmail.com, 1, BatMan] 19
사용하기 파라미터 처리 • 인라인 파라미터 • 문법 #propertyName# #propertyName:jdbcType# #propertyName:jdbcType:nullValue# • 샘플 <insert id="insertAccount"> INSERT INTO account ( acc_id, acc_first_name, acc_last_name, acc_email ) VALUES ( #id,jdbcType = INTEGER, javaType = int, nullValue =-99999#, #firstName, jdbcType = VARCHAR, javaType = string,nullValue=NULL#, #lastName, jdbcType = VARCHAR, javaType = string, nullValue = Hong#, #emailAddress, jdbcType = VARCHAR, javaType = string, nullValue = NULL# ) </insert> 20
사용하기 파라미터 처리 • 파라미터 맵 • 문법 <parameterMap id=”parameterMapName” [class=”com.domain.Product”]> <parameter property =”propertyName” [jdbcType=”VARCHAR”] [javaType=”string”] [nullValue=“-9999”] [typeName=”{REF or user-defined type}”] [resultMap=someResultMap] [mode=IN|OUT|INOUT] [typeHandler=someTypeHandler] [numericScale=2]/> <parameter …… /> <parameter …… /> </parameterMap> 21
사용하기 파라미터 처리 • 샘플 <parameterMap id="accountParam" class="Account"> <parameter property="id" jdbcType="INTEGER" javaType="int“ nullValue="-9999999" /> <parameter property="lastName" jdbcType="VARCHAR" javaType="string" nullValue="Hong" /> <parameter property="firstName" jdbcType="VARCHAR" javaType="string" nullValue="NULL" /> <parameter property="emailAddress" jdbcType="VARCHAR" javaType="string" nullValue="NULL" /> </parameterMap> <insert id="insertAccount" parameterMap="Base.accountParam"> INSERT INTO account (acc_id, acc_first_name, acc_last_name, acc_email ) VALUES ( ?,?,?,? ) </insert> 22
사용하기 파라미터 처리 • 대개는 인라인 파라미터에서 프로퍼티명만 표기하는 형태로 사용 • 파라미터맵을 명시하는 경우 파라미터 인덱스 값으로 값을 셋팅하기 때문에 쿼리문의 파라미터를 #프로퍼티명# 형태로 처리하지 않고 ? 형태로 처리해야 함 23
사용하기 동적 SQL • <dynamic> • 이항연산 요소 <isEqual><isNotEqual><isGreaterThan> <isGreaterEqual><isLessThan> <isLessEqual> 24
사용하기 동적 SQL • 단항연산 요소 <isPropertyAvailable> <isNotPropertyAvailable> <isNull> <isNotNull> <isEmpty> <isNotEmpty> • 파라미터 요소 <isParameterPresent> <isNotParameterPresent> • <iterate> 25
사용하기 동적 SQL • 단항연산 요소 <isPropertyAvailable> <isNotPropertyAvailable> <isNull> <isNotNull> <isEmpty> <isNotEmpty> • 파라미터 요소 <isParameterPresent> <isNotParameterPresent> • <iterate> 26
사용하기 동적 SQL <select id="getAccountByDynamicSQL" parameterClass="int" resultClass="Account"> SELECT acc_id, acc_first_name, acc_last_name, acc_email FROM account WHERE acc_id = #id# <isNotEqual compareValue="1"> and acc_email = '' </isNotEqual> </select> 27
사용하기 동적 SQL • 3.0 에서추가될 동적 SQL public class GetAccountListSQL extends SQLSource { public String getSQL(Object param) { Account acct = (Account) param; append("select * from ACCOUNT"); prepend("WHERE"); if (exists(acct.getEmailAddress())) { append("AND", "ACC_EMAIL like #EmailAddress#"); } if (greaterThan(0, acct.getID())) { append("AND", "ACC_ID = #ID#"); } } } <Select id="getAccountList“ source="org.apache.GetAccountListSQL" ...> 28
사용하기 트랜잭션 • 자동 트랜잭션 메서드 호출 단위의 트랜잭션 • 로컬 트랜잭션 • startTransaction 트랜잭션 시작 • commitTransaction 트랜잭션 커밋 • endTransaction 커밋이 되지 않은 경우 롤백, JDBC자원 회수 • Spring과 같은 서비스 계층에서의 트랜잭션 관리 29
사용하기 캐시 타입 • GC기반 MEMORY • 시간 기반 LRU FIFO • 외부 캐시 OSCACHE 30
사용하기 캐시 • 캐시되는 데이터 • SQL맵 구문 아이디 • 실제 쿼리문 • com.ibatis.sqlmap.engine.mapping.statement아래 각각의 Statement객체 내 메서드 명 예) executeQueryForList 이외 iBATIS의 캐시 객체에서 지정하는 몇 가지 값들 31
사용하기 Spring에서 iBATIS사용하기 • SqlMapClientDaoSupport 를 통해 SqlMapClientTemplate 사용하기 • mappingLocations <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation“ value="classpath:kr/or/openframework/dao/ibatis/SqlMapConfig.xml"/> <property name="mappingLocations" value="classpath:kr/or/openframework/dao/ibatis/AccountWithFamily*.xml“ /> </bean> Spring 2.5.5에서 추가됨 iBATIS 2.3.2이상의 버전이 필요함 32
사용하기 Spring에서 iBATIS사용하기 • SqlMapConfig.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd"> <sqlMapConfig> <settings cacheModelsEnabled="true" enhancementEnabled="true" lazyLoadingEnabled="false" useStatementNamespaces="true" /> <sqlMap resource="openframework/dao/ibatis/Dummy.xml" /> <!--sqlMap resource="openframework/dao/ibatis/Account1.xml" /> <sqlMap resource="openframework/dao/ibatis/Account2.xml" /> <sqlMap resource="openframework/dao/ibatis/Account3.xml" /> <sqlMap resource="openframework/dao/ibatis/Account4.xml" /--> </sqlMapConfig> 33
사용하기 성능을 향상시키는 방법 • parameterMap이나parameterClass속성을 명시적으로 선언 • <update id="updateAccount" parameterClass="Account"> • </update> • <resultMap>를 명시적으로 정의하고 결과의 재매핑(remapResults = true)옵션은 사용하지 말라. • Map이나XML이 아닌 자바Bean을 사용하라. • 표현이나 배열 또는 혼합된Bean이나Map 프로퍼티가 아닌 간단한 프로터티를 사용하라. • <settings>요소에 바이트코드 향상 기능을 활성화한다. - enhancementEnabled="true" 34
iBATIS 3.0 소식 iBATIS 3.0 소식 • SQL구문을 설정하는 애노테이션 추가 @Select("SELECT #id(EMP_ID:NUMERIC), #name(NAME:VARCHAR) ", "FROM EMPLOYEE") List<Employee> selectAllEmployees(); @Select({"SELECT #id(EMP_ID:NUMERIC), #name(NAME:VARCHAR) ", "FROM EMPLOYEE ", "WHERE EMP_ID = @id"}) Employee selectEmployee(int id); 35
iBATIS 3.0 소식 iBATIS 3.0 소식 • ResultMap을 대체하는 애노테이션 @ResultClass (Employee.class) @PropertyResults({ @Result(property="departments.id", column="D.DEPT_ID"), @Result(property="departments.name", column="D.NAME"), }) @Collections ({ @Collection(type=Department.class, property="departments", groupBy="id"), }) @Select("SELECT #id, #name, " + "#departments.id, #departments.name " + "FROM COMPANY C, DEPARTMENT D " + "WHERE C.COMP_ID = D.COMP_ID") List<Company> selectAllCompaniesWithJoin(); 36
현재 진행중인 이슈 현재 진행중인 이슈 • Support for JDBC 3 Named parameters http://issues.apache.org/jira/browse/IBATIS-96 이를테면 다음과 같은 형태의parameterMap을 사용.. <parameterMap id="insert-product-param" class="com.domain.Product"> <parameter property="description" parameter="desc" /> <parameter property="id" parameter="productId" /> </parameterMap> 37
현재 진행중인 이슈 현재 진행중인 이슈 • Auto Reloading Sql-map configuration.By Observing modifed date (adding datetime in Variables) or making a method reload conf. http://issues.apache.org/jira/browse/IBATIS-208sqlmap설정파일에 대한 Hot Deploy기능 지원 38
현재 진행중인 이슈 현재 진행중인 이슈 • Improving startup time (Dynamically loading SQL maps based on namespace) http://issues.apache.org/jira/browse/IBATIS-425 설정파일이 너무 많은 경우 WAS가 정상적으로 시작되는 시간이 너무 오래 걸려서 운영 시 어려움이 있다.namespace기반으로 초기 일부만 로딩하고 나머지는 해당 namespace를 기준으로 차후 로딩을 하는 형태의 기능 요청. 39
현재 진행중인 이슈 현재 진행중인 이슈 • Ability to build SqlMapClient programmatically without SqlMapConfig.xml file http://issues.apache.org/jira/browse/IBATIS-416 Spring과 사용하는 경우 sql-map-config.xml 파일에는 특별히 많은 내용이 들어가지 않기 때문에 별도로 설정파일을 만들 필요 없이 iBATIS관련 API호출로 설정파일의 갯수를 줄이는 방안에 대한 요청. 이미 Spring에서도 버전을 올라가면서 관련 설정을 줄일 수 있는 기능을 하나씩 추가하고 있음. 40
현재 진행중인 이슈 현재 진행중인 이슈 • Using Java constants in statements http://issues.apache.org/jira/browse/IBATIS-147 parameterClass가 아닌 다른 상수만을 가지는 외부 클래스의 값을 SQL구문에서 그대로 사용하기 위한 기능 요청. • Support generics http://issues.apache.org/jira/browse/IBATIS-273 JDK 1.5이상에서 지원하는 제네릭을 지원해달라는 요청. 41
현재 진행중인 이슈 참고자료 • 관련 사이트 iBATIS- http://ibatis.apache.org openframework 위키 –http://openframework.or.kr/Wiki.jsp?page=PersistentLayer#refpersistentLayer-2 • 관련 서적 iBATIS 인 액션 (이동국, 위키북스) Pro Spring (Rob Harrop, Jan Machac다) 42