MS-Sql 서버에서 iBatis 사용 강좌
작성자: Ospace(ospace114@empal.com), 2007.10.26
PDF:
개요
iBatis는 Java에서 DB를 사용하기 위한 대표적인 프레임워크 중에 하나이다. Hibernation이 많이 쓰이기도 하지만, SqlMap형태를 지원하면 나름대로 장점으로 많이 사용되고 있다.
이곳에서는 간단한 환경 구성과 간략한 예제이기 때문에 이곳의 내용만으로는 한계가 있다.
작업환경
기본 환경
- DB: MS-Sql server 2000 (딱히 버전에 상관없지 JDBC만 되면)
- Java: Java2 1.6.x JDK (이것도 적당히 골라서)
- OS: Windows XP sp2 after (별다른 상관은 없지만 ^^;)
JDBC for MS-Sql
MS Sql로 iBatis를 사용하기 전에 먼저 MS Sql용 JDBC 드라이버 설치가 필요하다.
다운로드 위치:
iBatis
그리고 아래에서 iBatis를 다운받아서 적당한 폴더에 압축을 해제한다.
그러면 lib 안에 ibatis-X.X.X.jar (X.X.X은 해당 버전명)이 클래스 경로에 포함되면 된다.
iBatis 환경 설정
작동 흐름
파일간 연결 관계
- SampleTest.Java: 메인 프로그램
- AppSqlConfig.java: iBatis를 사용하기 위한 정보를 불러오는 역할
- SqlMapConfig.xml: iBatis 환경 정보 제공
- SqlMapConfig.properties: DB 연결 정보 제공
- Person.xml: SQL 쿼리 문장
DB 설정하기
예제에 있는 PERSON 테이블 생성 SQL 문은 MS Sql에는 적합하지 않다. 수정이 필요하다. 그리고 너무 많은 데이터 항목이 있어서 일단 몇 개의 대표적인 타입으로 항목을 줄였다.
변경) NUMBER --> NUMERIC
Person.sql(수정됨)
CREATE TABLE PERSON(
PER_ID NUMERIC (5, 0) NOT NULL,
PER_NAME VARCHAR (40) NOT NULL,
PER_BIRTH_DATE DATETIME ,
PER_WEIGHT_KG NUMERIC (4, 2) NOT NULL,
PRIMARY KEY (PER_ID)
)
Person.java
개인 정보를 저장할 클래스이다. 보통 자료 처리하듯이 데이터 다루는데 필요한 클래스를 작성한다고 보면된다. 이는 앞의 DB 테이블의 구조와 동일한 모양을 가진다. 그리고 각 데이터를 액세스할 메소드를 정의해야 한다. IBatis에서는 멤버 변수를 바로 엑세스하지 않고 메소드를 통해서 하기 때문이다. 만약 값을 가져만 온다면 굳이 set메소드를 설정할 필요는 없다. IBatis에서 해당 메소드를 사용할 일이 없기 때문에 문제가 없지만, 만약 저장하게 된다면 문제가 발생한다.
public class Person {
private int id;
private String name;
private Timestamp birthDate;
private double weightInKilograms;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public Timestamp getBirthDate() { return birthDate; }
public void setBirthDate(Timestamp date) { this.birthDate = date; }
public String getName() { return lastName + firstName; }
public void setName(String name) { this.name = name; }
public double getHeightInMeters() { return heightInMeters; }
public void setHeightInMeters(double height) { this.heightInMeters = height; }
}
SqlMapConfig.xml 만들기
이 파일의 역할은 Java 프로그램에서 iBatis에 관련된 모든 정보를 제공하는 역할이다. 예를 들어, DB 연결 정보, 쿼리 문장을 제공한다.
<?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.xml에서 중요한 부분은 <sqlMapConfig>에 있다. 이 태그에 대한 자세한 설명은 iBatis 홈페이지나 인터넷에서 매뉴얼을 검색하면 나온다.
<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="SqlMapConfig.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"
/>
DB 서버에 대한 기본 환경 설정. 연결 방법, 연결 개수, 처리 형식 들을 설정한다.
<!--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}"/>
</dataSource>
</transactionManager>
여기서 DB 접속 위한 정보를 가져와서 설정한다. 프로퍼티 파일에서 불러왔지만, 바로 값을 입력해도 된다. 단지 관리의 편의성을 위해서.
<!--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="Person.xml" />
</sqlMapConfig>
마지막으로 실제 쿼리 문장이 들어 있는 Person.xml을 불러온다.
SqlMapConfig.properties
driver=com.microsoft.jdbc.sqlserver.SQLServerDriver
url=jdbc:microsoft:sqlserver://127.0.0.1:1433;SelectMethod=cursor
username=DB사용자ID
password=암호
오라클 사용자라면 다음과 같이 한다.
Dirver=oracle.jdbc.dirver.OracleDriver
Person.xml
먼저 간단한 테스트를 하기 위해 Select 쿼리만 만들어서 시험해보자.
<?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="Person">
SELECT PER_ID as id,
PER_FIRST_NAME as name,
PER_BIRTH_DATE as birthDate,
PER_WEIGHT_KG as weightInKilograms,
FROM PERSON
WHERE PER_ID = #value#
</select>
</sqlMap>
위 내용에서 주의할 부분이
다음은 좀더 확장된 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" resultClass="Person">
SELECT
PER_ID as id,
PER_FIRST_NAME as name,
PER_BIRTH_DATE as birthDate,
PER_WEIGHT_KG as weightInKilograms,
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="Person">
INSERT INTO
PERSON (PER_ID, PER_NAME, PER_BIRTH_DATE, PER_WEIGHT_KG)
VALUES (#id#, #name#, #birthDate#, #weightInKilograms#)
</insert>
<!--Use Person object (JavaBean) properties as parameters for update. Each of the parameters in the
#hash# symbols is a JavaBeans property. -->
<update id="updatePerson" parameterClass="Person">
UPDATE PERSON
SET PER_FIRST_NAME = #name#,
PER_BIRTH_DATE = #birthDate#,
PER_WEIGHT_KG = #weightInKilograms#,
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="Person">
DELETE PERSON
WHERE PER_ID = #id#
</delete>
</sqlMap>
이 파일에서는
AppSqlConfig.java
다음은 iBatis를 사용하기 위해 SqlMapClient 인스탄스를 얻기 위한 싱글톤 형태의 클래스이다.
import java.io.Reader;
import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
public class AppSqlConfig {
private static final SqlMapClient sqlMap;
static {
try {
String resource ="SqlMapConfig.xml";
Reader reader = Resources.getResourceAsReader(resource);
sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
} catch(Exception e) {
e.printStackTrace();
throw new RuntimeException("Error initializing AppSqlConfig class. Cause: " + e);
}
}
public static SqlMapClient getSqlMapInstance() {
return sqlMap;
}
}
여기서 핵심적인 부분이 SqlMapClient 인스탄스를 정적으로 선언했지만, 이를 초기화하는 부분도 정적으로 되어 있다. 이에 대한 이유가 tutorial 파일에 생성자에서 초기화하는 중에 에러 발생가능성이 있기 때문이라고 한다.
SimpleTest.java
클라이언트로 메인 프로그램이다. 실제 실행되는 파일이다.
import com.ibatis.sqlmap.client.SqlMapClient;
import java.sql.SQLException;
import java.sql.Timestamp;
public class SimpleTest {
public static void main(String[] args)
{
SqlMapClient sqlMap = AppSqlConfig.getSqlMapInstance();
Integer personPk = new Integer(1);
try {
Person person = (Person)sqlMap.queryForObject("getPerson", personPk);
System.out.println("ID: " + person.getId());
System.out.println("Name: " + person.getName());
System.out.println("Birthday: " + person.getBirthDate());
System.out.println("Height: " + person.getHeightInMeters());
// 확장된 Person.xml에서 insert를 사용하기 위한 코드
Person newPerson = new Person();
newPerson.setId(11);
newPerson.setName("Clinton");
newPerson.setBirthDate(new Timestamp(100000));
newPerson.setHeightInMeters(2.83);
sqlMap.insert("insertPerson", newPerson);
}
catch(SQLException e){
e.printStackTrace();
}
}
}
별다른 설명이 없어도 코드만 보면 쉽게 이해할 수 있으리라 생각한다.
실행하기
실행 전에 DB에 기본 데이터 정보를 입력 한다. 반드시 기본키인 ID값은 1로 해서 설정한다. 그리고, newPerson 인스탄스를 사용해서 instert하는 부분은 앞의 확장된 person.xml를 사용하기 때문에 이전 person.xml를 사용한다면 주석처리해서 사용하길 바란다.
Troubleshooting
프로그램 실행 시 오류
에러101
java.sql.SQLException:
[Microsoft][SQLServer 2000 Driver for JDBC]
Can't start a cloned connection while in manual transaction mode.
원인
해결
실행할 때 위와 같은 에러가 발생한다. 이때 앞의 properties 설정에서 url 항목에서 SelectMethod=cursor를 해주면 된다.
프로그램 실행 도중 에러
에러201
[SQLServer 2000 Driver for JDBC][SQLServer]float을(를) 데이터 형식 numeric(으)로 변환하는 중 산술 오버플로 오류가 발생했습니다.
에러202
-- Check the parameter mapping for the 'birthDate' property.
--- Cause: java.sql.SQLException: [Microsoft][SQLServer 2000 Driver for JDBC]The specified SQL type is not supported by this driver.
- 원인 : 아래 에러203과 같음.
에러203
--- Check the parameter mapping for the 'lastName' property.
--- Cause: java.sql.SQLException: [Microsoft][SQLServer 2000 Driver for JDBC]The specified SQL type is not supported by this driver.
- 원인
문자열 멤버 변수에 대해 update 쿼리를 실행하는 경우에 발생. 이 상황은 java에서는 문자열이 초기화되지 않으면 null로 설정되어있다. 예를 들어 lastName = null로 된다. 이 상태에서 IBatis에서 update하면 null값으로 갱신되면서 에러가 발생된다.
이 경우는 다른 멤버 변수에 대해서도 같은 결과를 가진다.
- 해결
적당히 알아서 하도록… 단 반드시 업데이트하려면 반드시 값을 넣어야 한다. 아니면 선택적 update가 되도록 하면 된다.
출처
iBATIS SQL Maps 튜토리얼 for MS Sql(June 17, 2004, 역: 이동국)
History
2008.12.05 ospace 오타 수정.
'3.구현 > Java or Kotlin' 카테고리의 다른 글
Java 쓰레드 상태 (0) | 2008.07.24 |
---|---|
Jar 패키징(Packaging) (0) | 2008.07.21 |
Reactor 패턴의 예제 코드 (0) | 2008.07.17 |
SLF4J simple tutorial (0) | 2007.12.20 |
Java thread에서 IllegalMonitorStateException 예외 발생문제 (0) | 2007.11.28 |