2008년 8월 21일 목요일

에고테스트

에고그램 ? : 에고그램은 미국의 심리학자 J.M.듀세이가 고안한 성격분석 표지법이다. 듀세이는 복잡한 사람의 성격을 5가지 영역으로 구분하여 쉽게 분석할 수 있도록 표준화하였다. 그 기초는 미국의 정신분석학자 에릭 반이 개발한 교류분석법(TA)을 바탕으로 하고 있는데 TA는 5가지 마음 중 어느 부분이 자신에게 영향을 끼치는지에 따라 사고방식이나 행동이 달라진다고 규정하고 있다. 5가지 마음은 비판적인 마음 CP, 용서하는 마음 NP, 부모의 마음 A, 자유로운 어린이의 마음 FC, 순응하는 마음 AC이다. 이 다섯가지 마음의 비율이 개인의 성격을 결정한다고 한다.

참조 사이트 : http://byule.com/board/?mid=ego

유용한 즐겨찾기 모음

이 요약은 사용할 수 없습니다. 이 글을 보려면 여기를 클릭하세요.

2008년 8월 20일 수요일

시제, 수동/능동, 의문/부정 정리

현재

* He watches TV every night.
*

TV is watched by him every night.

그는 매일 밤 TV 를 본다.


* Does he watch TV every night?
*

Is TV watched by him every night?

그는 매일 밤 TV 보니?


* He doesn't watch TV every night.
*

TV isn't watched by him every night.

그는 매일밤 TV 를 보지 않는다.



현재진행 - UNIT8

* He is watching TV now.
*

TV is being watched by him now.

그는 지금 TV 를 보고 있다.



* Is He watching TV now?
*

Is TV being watched by him now?

그는 지금 TV 를 보고 있니?



* He is not watching TV now.
*

TV is not being watched by him now.

그는 지금 TV 를 보고 있지 않다.


과거 - UNIT11, 12

* He watched TV last night.
*

TV was watched by him last night.

그는 어젯 밤 TV 를 봤다.



* Did he watch TV last night?
*

Was TV watched by him last night?

그는 어젯 밤 TV 를 봤니?



* He didn't watch TV last night.
*

TV wasn't watched by him last night.

그는 어젯 밤 TV 를 안 봤다.


과거진행 - UNIT13

* He was watching TV last night.
*

TV was being watched by him last night.

그는 어젯밤 TV 를 보고 있었다.


* Was he watching TV last night?
*

Was TV being watched by him last night?

그는 어젯밤 TV 를 보고 있었니?



* He was not watching TV last night.
*

TV was not being watched by him last night.

그는 어젯밤 TV 를 보고 있지 않았다.


현재완료 - UNIT19

* He has just watched TV.
*

TV has just been watched by him.

그는 지금 막 TV 를 봤었다.



* Has he just watched TV?
*

Has TV just been watched by him?

그는 지금 막 TV 를 봤었니?



* He has not just watched TV.
*

TV has not just been watched by him.

그는 지금 막 TV 를 보지 않았다.


기타

*

He used to work in factory. - UNIT15

그는 공장에서 일했었다. (지금은 공장에서 일하지 않는다)


*

Has he ever worked in factory? - UNIT16

그는 공장에서 일한 적 있니? (과거부터 지금까지 통틀어서)



*

How long has he worked in factory? - UNIT17

그는 얼마나 오랫동안 공장에서 일하고 있니?



*

He has been watching TV for 3 hours.

그는 세시간째 TV 를 보고 있다.



*

He is watching TV this night. - UNIT26

그는 TV 를 볼꺼다. (가까운 미래에 계획까지 다 잡아놓은 상태)



*

He is going to watch TV. - UNI27

그는 TV 를 볼꺼다. (이미 본인이 맘 먹은 상태, 계획은 없음)



*

He will watch TV. - UNIT28

그는 TV 를 볼꺼다. (불확실한 미래, 보려고 하겠지만 안 볼 수도 있음)


*

He might watch TV.

그는 아마도 TV 를 볼꺼다. (불확실한 추측, 가능성)


*

He can watch TV.

그는 TV 를 볼 수 있다.


*

He could watch TV.

그는 TV 를 볼 수 있었다.


*

He must watch TV.

그는 TV 를 꼭 봐야하고 볼 수 밖에 없다. (강제성을 띈 의무, 어쩔 수 없는 것)


*

He should watch TV.

그는 TV 를 보는 것이 좋겠다. (강제성이 없음)


*

He has to watch TV.

그는 TV 를 봐야한다. (구어체에서는 must 보다 많이 쓰임)



참조 사이트 : http://hoooriza.springnote.com/pages/1421070

Basic Grammar is Use

UNIT 1 - am/is/are

* Thank you, That's very kind of you.

UNIT 2 - am/is/are

*

7개의 의문문

Where, What, Who, How, Why, Which, When
UNIT 3 - I am doing

* ~ing 형으로 바꿀때 규칙

단어의 마지막 두글자가 [모음][자음] 형태이고 그 바로 앞이 [모음] 이 아니면 무조건 마지막 자음 반복

run -> running

sit ->sitting

swim -> swimming

* die(죽다) -> dying, dye(염색하다) -> dying 이니까 바뀐담에 헷갈리지 말기

Quiz

*

그는 나의 첫 사랑이다

He is my first love
*

너는 왜 그렇게 못 됐니?

Why are you so mean?
*

그녀는 뛰는 중이니, 아님 걷는 거니?

Is she running or walking?


UNIT5 - I do, I work, I like, etc.

* on weekends : 주말마다
*

always, never, often, sometimes, usually 의 위치

be, 조동사 + always

always + 일반동사 : We often sleep late on weekends
*

y 로 끝나는 동사를 3인칭 형으로 바꾸기

자음+y 로 끝나는 것만 ies 로 바꾼다

stay -> stays, fly -> flies



UNIT8 - I am doing and I do

*

~ing 형이 될 수 없는 동사

like, love, want, know, understand, remember, depend, prefer, hate, need, mean, believe, forget
*

지각동사

see : 보다 / looking / seeing : 사귀다

hear : 듣다 / listening

taste : 맛이~이다 / tasting : 맛을 보다

feel : 느끼다 / How are you feeling today?
*

예문

Jim is watching television.

I don't understand. (잘못된 예 : I'm not understanding)


UNIT9 - I have.. and I've got

* I have = I've got
* I don't have = I haven't got
* Do you have..? = Have you got..?
*

예문

I've got blue eyes.

We haven't got much time.



UNIT10 - was/were


UNIT11 - worked, got, went, etc

* They watched television last night.


UNIT20 - just, already, yet

* They have just arrived.
* They have already arrived.
* They have not arrived yet.
*

They did not arrive yet.



UNIT26 - What are you doing tommorow?

*

가까운 미래면서 계획이 잡혀있고 마음 먹었을때

He is playing tennis tommorow.
*

단 스케쥴 대로 진행되는 상황은 그냥 현재형 사용

The plane arrives at 7:30 tommorow morning.

What time does the movie start?


UNIT27 - I'm going to

*

이미 하기로 마음 먹은 상태, 단 계획은 없음

I'm going to watch TV tonight.
*

일어날 것이 뻔한 상황

It's going to rain. (근처에 먹구름이 무지 있을때)

I'm going to be late. (이미 늦잠을 자버려서 늦을 게 뻔할 때)



UNIT28 - Will

*

일반적인 미래, 불확실한 미래

Don't drink coffee. you will not sleep.
*

I think.. will..

I think Diana will pass her drivers' test.
*

이미 예정되어 있거나 결정된 사항에 대해서는 will 을 쓰지 말 것

We will go to the movies on Saturday (x)

Will you cook dinner tonight? (x)


UNIT29 - Will

*

제안할 때

I'll carry it for you.
*

Shall I/We

정중하게 제안할 때 (주어는 I 와 We 만 가능)


참조 사이트 : http://hoooriza.springnote.com/pages/1274698?print=1

지니가 AspectJ를 만났을때1

AspectJ ??

참조 사이트 : http://www.jlab.net/news/20040814/news.htm

Spring 개발을 위한 Eclipse 개발 환경 구성하기

깔끔하게 정리 잘 되어 있네요.

참조 사이트 : http://theeye.pe.kr/221

2008년 8월 18일 월요일

HWP 파일 다운로드시 저장하고 보면 잘 되지만 열기 했을 경우 안될때...

JSP/Servlet 환경에서 다운로드를 사용할 경우는
아래와 같은 순서를 가진다.

1. HttpServletResponse 로 부터 OutStream 을 얻는다.
2. Header, ContentsType 등을 설정한다.
3. FileStream 을 통해 전송될 파일의 스트림을 얻는다.
4. Response 로 부터 얻은 outputStream 에 파일을 쏜다.
5. 열려진 모든 자원을 닫는다.

뭐 다른것들은 괜찮지만 2번 항목에서 환경에 따라 헤더 설정이 틀려진다.
단독 WAS 환경인지, WAS 앞에 Apache 나 IIS 가 있는지 등등..

웬만한 부분에서는 아래와 같은 Header 설정이면 충분하다.

res.setContentType("appliction/octet-stream");
res.setHeader("Accept-Ranges","bytes");
res.setHeader("Content-Transfer-Encoding", "binary;");
res.setHeader("Pragma", "no-cache;");
res.setHeader("Expires", "-1;");

res.setHeader("Content-Disposition", "attachment; filename=" + fileName + ";");


문제는 위 설정으로 zip 파일이나, 다른 파일들은 저장, 열기가 잘 되지만 유독 HWP 파일에서 문제가 생긴다.

다운로드 창까지는 잘 뜨는데, 열기 버튼을 누르면 파일을 찾을 수 없다고 나온다.

저장해서 보면 잘 보이지만 ㅎㅎ

여차저차 해서 헤더에 아래와 같이 추가해줬다.

res.setHeader("Cache-control","private");

잘 된다 ^^

캐시 컨트롤을 private 으로 하겠다는 의미는 전송되는 개체가 공유 캐시에 보관되지 않고 특정 클라이언트만을 대상으로 해야 한다는 말이다.

참조 사이트 : http://www.javarush.com/tag/hwp%20%ED%8C%8C%EC%9D%BC%20%EC%97%B4%EA%B8%B0

JAVA 에서 ORACLE의 CLOB 타입 사용하기

JAVA 에서 ORACLE의 CLOB 타입 사용하기


INSERT시
1. CLOB 타입인 필드의 기본값을 empty_clob() 로 설정합니다.

2. setAutoCommit(false) 를 선언한다.

3. CLOB 타입 필드를 제외한 나머지 필드에 데이터를 저장한다.

4. CLOB 타입 필드에 데이터를 넣는 동안 다른 변경 작업을 막기 위해 LOCK을 건다.

5. CLOB 타입 필드에 데이터를 넣는다.

6. Commit() 한다.

7. setAutoCommit(true) 를 선언한다.


예제 소스 코드


import! java.io.*;
import! java.net.*;
import! java.sql.*;

import! oracle.sql.*;
import! oracle.jdbc.driver.*;

public class ClobTest {
public static void main(String[] argv) {

String drv = "oracle.jdbc.driver.OracleDriver";
String dburl = "jdbc:oracle:thin:@192.168.3.30:1521:GRDB21";
String user = "scott";
String password = "tiger";

Connection conn = null;
Statement st;
ResultSet rs;

try {
Class.forName(drv);
System.out.println("Drive Loading...");
conn = DriverManager.getConnection(dburl, user, password);
System.out.println("Connecting...");
st = conn.createStatement();

// 입력할 텍스트 만들기
StringBuffer sb = new StringBuffer();
String clobTest = "한글 테스트입니다
It is difficult
";
for (int i = 0; i < 300; i++)
sb.append(clobTest);



// 1. CLOB 데이터 입력하기 위해, empty_clob() 생성
try {
st.executeUpdate("insert into test_clob values (''02'', empty_clob())");
} catch (Exception ee) {
ee.printStackTrace(System.out);
}

// 2. setAutoCommit을 false로 꼭 해야 한다.
conn.setAutoCommit(false);


// 3. CLOB column에 대한 lock을 얻는다.
rs = st.executeQuery("select desc2 from test_clob where code = ''02'' for update");
if (rs. next()) {

// 4. 오라클의 함수들을 사용하기 위해 cast 하였다.
CLOB cl = ((OracleResultSet)rs).getCLOB("desc2");

// 스트림을 이용한 값 저장
BufferedWriter writer = new BufferedWriter(cl.getCharacterOutputStream());
writer.write(sb.toString());
writer.close();
}
conn.commit();
conn.setAutoCommit(true);

// 입력한 값 읽어 오기
rs = st.executeQuery("select * from test_clob where code = ''02''");

if (rs.next()) {
String code = rs.getString("code");

// CLOB column에 대한 스트림을 얻는다.
Reader rd = rs.getCharacterStream("desc2");

sb = new StringBuffer();
char[] buf = new char[1024];
int readcnt;
while ((readcnt = rd.read(buf, 0, 1024)) != -1) {
// 스트림으로부터 읽어서 스트링 버퍼에 넣는다.
sb.append(buf, 0, readcnt);
}
rd.close();

System.out.println("code : " + code);
System.out.println("desc2 : " + sb.toString());
}


}
catch (Exception e) {
e.printStackTrace(System.out);
}
}
}

코드 출처 : http://rudaks.co.kr/bbs/view.jsp?seq=379&bbs=db&PageNo=1





UPDATE 시
기본은 Insert 와 동일하나, CLOB 필드 업데이트 전에 CLOB 필드의 내용을 모두 비워줘야 한다.


예제 소스 코드

ex) UPDATE 테이블 SET CLOB필드 = empty_clob() WHERE 조건


SELECT 시
1. CLOB 가 아닌 데이터를 모두 SELECT 한다.

2. CLOB 인 데이터를 SELECT 한다.


예제 소스 코드




--- 우선은 밑의 거 두개를 import! 합니다.

< % @ page import!="oracle.jdbc.driver.*" % >

< % @ page import!="oracle.sql.*" % >



-- clob를 불러올 query를 만든다 .(clob만 select 합니다.)

--조건은 물론 pk이겠지요.



queryclob.append(" select body from r2enjoybbs ");

queryclob.append(" where bbsid = ? ");



--- 가져오는 부분

--- body1에다가 clob의 데이타를 넣는다.

StringBuffer body1 = new StringBuffer();

pstmt2 = con.prepareStatement(queryclob.toString());

pstmt2.setInt(1,bbsid);

rs2 = pstmt2.executeQuery();

if(rs2.next())

{

Reader input = rs2.getCharacterStream(1);

char[] buffer = new char[1024];

int byteRead;

while((byteRead=input.read(buffer,0,1024))!=-1)

{

body1.append(buffer,0,byteRead);

}

input.close();

}

rs2.close();

pstmt2.close();

코드 출처 : http://database.ssarang.net/?inc=read&aid=11196&criteria=oracle&subcrit=&id=&limit=20&keyword=clob+select&page=5


참조 사이트 : http://cafe.daum.net/boilra/5ZZ3/6?docid=1vPt|5ZZ3|6|20080319085849&q=empty_clob()&srchid=CCB1vPt|5ZZ3|6|20080319085849

iBATIS SQLMaps 활용법

* 글쓴이: 메룽_성완
*
* 조회수 : 211
*
* 07.11.02 13:08

http://cafe.daum.net/proutils/IWTE/16주소 복사

간결한 데이터 매퍼의 대명사
iBATIS SQLMaps 활용법



iBATIS SQLMaps(이하 아이바티스)는 이미 국내외 많은 개발자들이 사용하고 있는 퍼시스턴스 계층의 프레임워크이다. 실제로 국내에서 가장 크다는 포털 사이트인 네이버와 다음도 아이바티스를 사용하고 있으며, 다른 업체들도 아이바티스를 그대로 사용하거나 약간 변형시킨 형태로 사용하고 있는 것으로 알고 있다. 특집 3부에서는 아이바티스의 특징과 활용법에 대해 알아본다.



이동국 fromm0@gmail.com


과거 ORM의 대표인 하이버네이트와 데이터 매퍼인 아이바티스 간에 어느 프레임워크가 더 좋은가에 대한 논쟁도 있었을 만큼 많은 자바 개발자들은 데이터베이스에 관련된 ORM 프레임워크에 대한 관심이 크다. 이런 논쟁 가운데 아이바티스는 ORM이 아니기 때문에 논쟁의 대상이 될 수 없다는 의견도 있었다. 사용 목적이라는 관점에서 본다면 하이버네이트와 아이바티스는 논쟁의 대상이 될 수 있으나 ORM이냐 아니냐를 가름하는 관점에서 본다면, 역시 아이바티스는 ORM이 아니라고 할 수 있다.





ORM과 데이터 매핑





아이바티스의 핵심 개발자인 래리 메도스(Larry Meadors)는 다음처럼 ORM과 데이터 매퍼를 구분한다.



ORM = 데이터베이스 객체를 자바 객체로 매핑
Data mapping = SQL “구문”을 자바 객체로 매핑



여기서 말하는 개념은 두 프레임워크의 성격을 제대로 이해할 수 있게 해준다. 이러한 개념적인 차이점은 결과적으로 ORM의 캐싱 전략은 데이터베이스 객체에 매핑 된 자바 객체가 캐시에 저장되는 게 기본이고 데이터 매퍼는 객체가 아닌 SQL 구문 자체가 캐시에 저장이 되는 것과 같은 차이점을 낳게 된다. 그렇다고 아이바티스를 ORM의 범주에 포함시키는 데 큰 무리는 없다.



ORM이 생겨난 원인을 무엇일까? 가장 기본적인 것은 현재의 데이터베이스 프로그래밍에서 일반 JDBC 형태의 개발 방법이 개발자에게 필요 이상의 많은 타이핑과 자원관리를 요구한다는 것이다. ORM을 사용해본 개발자는 이 부분에 대해 ORM이 어느 정도 효과를 보인다고 생각할 것이다. 물론 새로운 프레임워크에 대한 학습과 해당 프레임워크가 요구하는 설정작업, 그리고 개발방식은 분명히 개발자에게 또 다른 부담으로 작용하는 것 또한 사실이다. 하지만 개인적으로 개발자는 항상 새로운 어떤 것을 배우고 습득해야 한다. 그런 면에서 이런 프레임워크로 인한 부담을 느끼지 말고 자기개발의 수단으로 생각했으면 좋겠다. 필자가 처음으로 아이바티스를 접했던 3년 전과는 달리 현재는 국내에 서적도 나와 있고 많은 관련문서가 있어서 개발자들이 아이바티스를 학습하는데 전혀 무리가 없을 듯하다. 그래서 이번에는 특정 부분에 집중하지 않고 아이바티스를 활용할 수 있는 부분들을 전체적으로 살펴보고 아이바티스 홈페이지를 통해 논의 되고 있는 아이바티스 3.0이 나아가고자 하는 방향을 여기서 간단히 살펴보고자 한다.





기능에 따른 사용형태





아이바티스를 사용하는 개발에서는 크게 설정 정보를 가지는 SQLMaps 파일과 SQL구문을 가지는 SQLMap 파일, 그리고 아이바티스 API를 사용하는 자바 코드로 구성이 된다고 볼 수 있다. 먼저 설정정보를 가지는 SQLMaps 파일을 살펴보자. SQLMaps 파일 설정은 이미 많이 알려져 있기 때문에 첨부된 예제소스를 참조하도록 한다. 샘플용 소스이기 때문에 typeAlias와 SQLMap 파일의 개수가 적다. 하지만 실제 프로젝트에 아이바티스를 적용할 때는 사전에 typeAlias에 대한 명명 규칙을 정해서 중복 되지 않도록 하거나 각각의 SQLMap 파일에 정의해서 StatementNamespaces로 각각을 구분해주어야 설정 파일의 증가로 복잡해질 때 혼동의 여지가 줄어들 것이다.



전역 세팅에 해당되는 설정과 typeAlias, 그리고 트랜잭션 관리자 및 SQLMap 파일에 대한 위치 정보를 가지고 있다. 전역 세팅에 해당되는 설정은 이외에도 더 있으며 상세한 설명은 이 글의 범위를 벗어나기에 생략한다. 각각의 설정 값에 대한 상세한 정보는 kldp.net에서 호스팅 중인 아이바티스 개발자 가이드 한글문서나 공식 영문문서를 보면 알 수 있다. typeAlias는 말 그대로 타입에 대한 별칭이다. 자바의 패키지 구조로 인해 이름이 긴 클래스를 사용할 경우 차후에 불필요한 중복 타이핑을 해야 하는 등 불편이 따른다. 때문에 여기서는 별칭 형태로 정의할 것이다.



트랜잭션 관리자 부분은 간단히 볼 때 데이터베이스 세팅이다. 간혹 커뮤니티에 아이바티스에서 데이터소스를 두 개 이상 설정할 수 없냐는 질문이 올라오는데 1.x 버전에서는 가능했으나 2.x 부터는 혼란의 소지가 있어 불가능하다. 꼭 필요한 경우라면 이 설정 파일을 데이터 소스 개수만큼 생성하여 동적으로 읽어 들이는 수밖에 없을 듯하다.



마지막으로 SQL 구문을 가지게 되는 SQLMap 파일이다. 여기서는 클래스 패스 기준으로 설정되었다. 아이바티스 사용자들의 공통적인 고민은 WAS의 재시작 없이 SQL구문이 갱신되는 기능일 것이다. 이 경우 아이바티스 단독으로만 사용할 때는 SQLMap 파일을 파일 경로로 지정해주는 방법을 사용할 수 있겠지만, 이 방법 또한 정상적으로 갱신이 안 되는 경우가 있다고 하니 필자로서는 스프링 프레임워크를 사용하는 방법을 권하고 싶다. 스프링 프레임워크를 사용하면 WAS를 재시작하지 않고도 SQL구문이 갱신되는 것을 AppFuse를 통해 간단히 체크해볼 수 있다.



<그림 1>은 이제부터 살펴볼 기본적인 CRUD 작업에 사용된 테이블의 구조다. 계정(account) 정보와 그 계정의 가족 정보(account_family)를 가지는 지극히 간단한 구조로 되어 있다. 하지만 여기서 살펴볼 아이바티스의 기능을 구현하기에는 더없이 적절한 구조라고 생각된다.
일반적인 CRUD 작업





일반적인 CRUD 작업



데이터 매퍼인 아이바티스는 SQL ‘구문’을 자바 객체로 매핑하기 때문에 기본적으로 정적인 CRUD 작업에 가장 최적화되어 있다고 할 수 있다. 물론 데이터베이스 함수 및 프로시저, 동적 SQL 처리에도 사용할 수는 있지만 약간의 제약이 있게 마련이다. 여기서는 SQL 구문의 재사용을 위해 요소를 사용했고 이를 요소를 사용해서 다른 구문에서 가져다가 사용하고 있다. 쿼리문을 일종의 특정 id값을 가지는 xml에 저장하고 그 id를 기준으로 호출해서 사용하는 게 아이바티스의 기본 사용법이다. 이 파일을 이해하는데 별무리가 없으리라 생각된다. 그러면 이 SQL 구문들을 어떻게 호출해서 사용하면 좋을까? 자바 코드를 보자.



<리스트 1> CRUD 작업을 위한 SQLMap 파일


select
acc_id,
acc_first_name,
acc_last_name,
acc_email
from account





insert into account (
acc_id,
acc_first_name,
acc_last_name,
acc_email
) values (
#id#, #firstName#, #lastName#, #emailAddress#
)




자바 코드에서는 각각의 SQL 구문을 호출하는 형태를 <리스트 2>와 같이 취하고 있다. 사전에 아이바티스 설정 파일을 통해 관련 정보를 얻는 부분이 static 구문 안에 처리가 되어 있다. 이 소스는 아이바티스를 처리하는 결과를 보기 위해 log4j 설정과 아이바티스 설정 정보를 읽어오는 두 가지로 구성이 되어 있다. log4j.xml 파일과 아이바티스 설정 파일의 위치를 클래스 패스 기준으로 가져오도록 했다. SqlMapClient가 제공하는 각각의 메소드에 SQLMap 파일에 정의된 SQL 구문의 id와 인자로 넣어줄 객체를 정의하는 형태를 취한다. 짐작하겠지만 SQL 구문의 id와 일치하는 SQL 구문에 해당 객체의 값이 인자로 전달되어서 내부적으로 java.sql의 PreparedStatement 객체 생성 후 일반 JDBC처럼 처리된다.



<리스트 2> CRUD 작업을 위한 자바 코드

private static SqlMapClient sqlMapper;

static {
try {
// iBATIS SQLMaps setting
Reader reader = Resources.getResourceAsReader("kr/or/openframework/dao/ibatis/SqlMapConfig.xml");
sqlMapper = SqlMapClientBuilder.buildSqlMapClient(reader);
reader.close()
} catch (IOException e) {
throw new RuntimeException("Something bad happened while building the SqlMapClient instance." + e, e);
}
}

public static Account selectAccountById(int id) throws SQLException {
return (Account) sqlMapper.queryForObject("selectAccountById", id);
}
public static String insertAccount(Account account) throws SQLException {
sqlMapper.insert("insertAccount", account);
return "SUCCESS";
}



프로시저



예제를 위해 필자는 다음처럼 간단한 프로시저를 생성했다. 여기서는 IN 타입의 파라미터만을 사용했지만 IN과 OUT, INOUT 타입 모두 지원한다. 여기서 사용된 프로시저는 인자로 들어온 값을 id로 해서 샘플용 데이터를 생성한다.



<리스트 3> 프로시저 생성 구문

drop procedure if exists procedurein;

delimiter // ;

create procedure procedurein (in p_param int)
begin
insert into account (
acc_id,
acc_first_name,
acc_last_name,
acc_email
) values (
p_param, "procedure_test", "procedure_test", "test@test.com"
);
end;
//

delimiter ; //



프로시저 호출은 일반적으로 콘솔 창에서 실행하는 것처럼 하고 { }로 감싸주면 된다. 처리 자체는 오히려 CRUD보다 간단하다고 할 수 있으나 직접 해보면 쉽지 않다는 것을 알 수 있다.



<리스트 4> 프로시저 호출을 위한 SQLMap 파일


{ call procedurein(#val#) }




<리스트 5> 프로시저 호출을 위한 자바 코드

public static String callInTypeProcedure(int id) throws SQLException {
sqlMapper.update("inProcedure", new Integer(id));
return "SUCCESS";
}



프로시저의 처리는 아이바티스 홈페이지나 메일링 리스트를 통해 끊임없이 제기되는 문제 중에 하나이다. 개발자와 데이터베이스마다 구현 방식이 조금씩 다른 탓도 있겠지만 여러 가지 눈에 띄는 어려움이 존재하기 때문에 아이바티스 문서를 보면 꼭 표준 형태의 프로시저를 사용하도록 권장하고 있다.



필자의 경우에도 오라클(Oracle)에서는 정상적으로 프로시저 형태의 샘플 코드를 테스트 했으나 MySQL로 작성하면서 이런저런 문제에 봉착했었다. 실제 프로젝트에서 아이바티스를 사용하여 프로시저를 처리할 때는 프로시저 사용 패턴을 정의하고 사전에 정상적으로 처리가 되는지 테스트 해보길 권하고 싶다.


케이스

메소드명
결과세트를 반환할 때

queryForList(), queryForObject()
한 개 이상의 결과 객체를 반환할 때

queryForList()
한개의 결과 객체를 반환할 때

queryForObject()
결과세트를 반환하지 않거나 OUT
파라미터에 결과세트를 세팅하지 않을 때

update()



앞의 예제는 IN 파리미터에 값을 전달하고 내부적으로 처리한 후에 어떠한 데이터도 반환하지 않기 때문에 update() 메소드가 사용되었다(프로시저 처리 시 결과 세트를 반환하고 데이터를 업데이트 할 경우, 요소의 commitRe quired 속성 값을 true로 세팅해줘야 한다).



N+1 문제



<리스트 6> N+1문제를 가지는 형태의 SQLMap 파일










select
acc_id as id,
acc_first_name as firstName,
acc_last_name as lastName,
acc_email as emailAddress
from account








<리스트 7> N+1문제를 가지는 형태의 자바 코드

@SuppressWarnings("unchecked")
public static List selectAccountWithFamilyUsingNPlus1(int id) throws SQLException {
List familys = (ArrayList)sqlMapper.queryForList("selectAccountWithFamilyUsingNplus1", new Integer(id));
return familys;
}



log4j를 이용해 실제로 수행되는 SQL문을 찍어보면 다음과 같이 표시된다.



Executing Statement: select acc_id as id, family_fullname as fullName from Account_family where acc_id = ?
Executing Statement: select acc_id, acc_first_name, acc_last_name, acc_email from account where acc_id = ?
Executing Statement: select acc_id, acc_first_name, acc_last_name, acc_email from account where acc_id = ?



2(N)개의 데이터를 가져오기 위해 실제로는 SQL 문을 한 번 더(+1) 수행하여 총 3번(Account_family에 한번, Account에 두 번)을 실행하게 되는 셈이다. 이것을 N+1 문제라고 하는데 이를 개선하기 위해 groupBy를 사용할 수 있다.



<리스트 8> N+1문제를 가지지 않는 형태의 SQLMap 파일












<리스트 9> N+1문제를 가지지 않는 형태의 자바 코드

@SuppressWarnings("unchecked")
public static List selectAccountWithFamilyAvoidNPlus1(int id) throws SQLException {
List familys = (ArrayList)sqlMapper.queryForList("selectAccountWithFamilyAvoidNplus1", new Integer(id));
return familys;
}



조인 구문과 groupBy를 사용하면 다음처럼 일반 join 문을 사용하여 처리를 하기 때문에 N+1과 같은 문제가 발생하지 않는다. 현재는 이 방법이 가장 추천된다고 할 수 있다. 하지만 방법적으로 하위 결과 맵을 사용하는 것이 추천되는 방법이라고 보기는 어려울 듯하다. 아이바티스 3.0에서는 좀 더 개선된 방법이 제공될 것으로 짐작된다.



Executing Statement: select a.acc_id, a.acc_first_name, a.acc_last_name, a.acc_email, f.family_fullname from account a left join account_family f on a.acc_id=f.acc_id where a.acc_id=?



동적 SQL



<리스트 10> 동적 SQL을 사용하는 SQLMap파일 내용





<리스트 11> 동적 SQL을 호출하는 자바코드

@SuppressWarnings("unchecked")
public static List selectAccountDynamic(Account account) throws SQLException {
List accounts = (List)sqlMapper.queryForList("selectAccountDynamic", account);
return accounts;
}



필자의 경우 인자로 넣어준 id의 값이 3이었기 때문에 다음처럼 WHERE acc_id =1이 추가되어 처리되었다. 동적 SQL의 경우 자바 코드가 아닌 XML의 태그로 동적 SQL을 생성하기 때문에 손에 익지 않아 불편할 수도 있다. 이미 아이바티스 개발팀은 이 동적 SQL 부분에 대한 개선작업을 진행하고 있다. 그 결과물로 3.0에서 도입될 방식에 대해 잠시 뒤에 살펴보도록 하겠다.



<리스트 12> 동적 SQL을 호출하는 결과

Executing Statement: select acc_id as id, acc_first_name as firstName, acc_last_name as lastName, acc_email as emailAddress from account WHERE acc_id = 1





아이바티스 3.0 소식





아이바티스 3.0 개발은 다음과 같은 방향으로 진행이 된다.



- 테스트 주도 개발
- 성능보다는 코드의 간결성
- 복잡한 디자인 보다는 간결한 디자인
- 하나의 JAR파일
- 다른 라이브러리의 의존성을 없앰
- 더 나은 플러그인 지원
- 추가적인 플러그인을 위한 프로젝트(http://sourceforge.net/ projects/ibatiscontrib/)



역시나 기본적으로 계속 간결한 프레임워크를 유지하면서 많은 사용자들이 원하는 기능은 플러그인 형태로 제공하는 것이 기본적인 개발 방향임을 짐작할 수 있다. 역시 아이바티스는 간단함 내지 간결함으로 표현할 수 있는 프레임워크이다.



그럼 저 기본 방향을 염두에 두고 현재 다소 형상화되어 보이는 3.0 기능에 대해 간단히 살펴보자.





인터페이스 바인딩





일단 다음과 같은 기존 코딩 방식에 대해 살펴보자. 기존 방식의 문제점은 매핑 구문명이 문자열 기반이라 매핑 구문명의 철자가 잘못되어 있을 경우 에러가 발생하게 되고 컴파일시가 아닌 런타임 시 에러가 발견할 수 있게 된다. 더군다나 문자열 기반이라 SQLMap 파일의 증가는 곧 사용할 구문에 대한 체크에 일부 시간을 소요할 수밖에 없도록 만드는 계기가 된다. 그리고 반환 타입은 형 변환을 통해 명시화되지만 실제로 반환 타입의 모호로 인해 형 변환 시 ClassCastException은 아이바티스 사용자라면 한번쯤 겪게 되는 에러가 된다. 즉 애매한 형 변환과 문자열 기반의 매핑 구문은 결과적으로 대부분의 에러가 런타임 시 발생하여 개발을 힘들게 만드는 요인이 되고 있다. 이런 점을 해결하기 위해 나온 것인 이 인터페이스 바인딩이다.



기존에 XML에 타입을 선언하고 문자열 기반으로 구문을 호출하는 것 대신에 좀 더 서술적이고 타입에 안전한 인터페이스의 사용으로 앞서 언급된 문제점을 많이 보완해 나가고 있다. 다음의 코드는 기존 방식의 호출을 새로운 인터페이스 바인딩 방법으로 호출한 것이다. 애매한 형 변환 작업이 없고 문자열 기반의 매핑 구문을 사용하지 않아 기본적으로 컴파일 시 많은 에러를 찾아낼 수 있다. 요즘처럼 IDE를 기본적으로 사용하는 환경이라면 얼마나 많은 도움이 될지는 의심할 여지가 없다.



결과적으로 인터페이스 바인딩을 통해 매핑 구문명과 파라미터 타입, 결과 타입을 자동적으로 알 수 있게 되는 셈이다.



다중 레벨 설정



아이바티스의 오랜 설정 방식인 XML은 3.0에서도 역시 좋은 방법이 되겠지만 3.0에서 디폴트 설정 방법이 되지는 않는다. 3.0에서는 다음과 같은 다중 레벨 설정이 가능하게 된다.



- 관례(Convention)
- 어노테이션(말 그대로 하면 주석) : 앞의 관례보다 우선시 된다.
- XML : 앞의 관계, 어노테이션보다 우선시 된다.
- 자바 API : 앞의 관례, 어노테이션, XML보다 우선시 된다.
관례는 메소드 시그니처로 정해진 규칙이라고 보면 된다.



Employee getEmployee (int id); 라는 메소드 시그니처는 다음과 같은 SQL과 동일 시 된다고 볼 수 있다.



SELECT id, firstName, lastName FROM Employee WHERE id = ?



여기서 메소드 반환 타입의 이름이 테이블 명이 되고 메소드의 인자로 주어진 값이 자동으로 WHERE 조건문을 구성한다고 볼 수 있다. 간단한 조건문이나 들어가는 형식의 전체 데이터를 뽑는 기능이라면 이 메소드를 선언하여 추가 작업 없이 해당 기능이 구현할 수 있다.



어노테이션으로 설정하기



XML 설정을 넘어서 어노테이션으로 메타데이터 정보를 설정하는 기능이 많이 도입되었다. 이에 아이바티스도 이러한 기능을 추가적으로 지원한다. 현재 아이바티스의 XML 파일은 다음과 같은 정보를 가진다.



- 설정
- 메타 정보
- 코드



설정은 환경적인 정보이다.
메타정보는 결과 맵과 파라미터 맵, 그리고 캐시 모델들을 나타내고 코드는 SQL과 동적 SQL 요소를 포함한다. 설정 정보는 properties 파일이나 XML에 담겨야하고, 코드는 자바 코드나 XML에 담겨야 한다. 단 메타정보만이 어노테이션에서 처리가 가능하도록 될 것이다.
어노테이션의 예제는 다음과 같다. 앞의 관례에 의한 방식에서 칼럼 별 타입을 정의하고 조건문을 구체화하기 위해 어노테이션이 사용된 것을 볼 수 있다.



<리스트 13> CRUD 작업을 위한 어노테이션 사용 예제

@Select({"SELECT #id(EMP_ID:NUMERIC), #firstName(FIRST_NAME:VARCHAR), #lastName(LAST_NAME:VARCHAR) ",
"FROM EMPLOYEE",
"WHERE EMP_ID = @id"})
Employee selectEmployee(int id);

@Insert({"INSERT INTO EMPLOYEE (EMP_ID, FIRST_NAME, LAST_NAME)",
"VALUES (@id, @firstName, @lastName)"})
void insertEmployee(Employee emp);

@Delete("DELETE EMPLOYEE WHERE EMP_ID = @id")
void deleteEmployee(int id);



<리스트 14>와 같은 형식의 어노테이션도 사용 가능하게 된다. 여기서는 ResultClass와 PropertyResult를 추가적으로 어노테이션을 통해 정의한 것이다.



<리스트 14> ResultClass, PropertyResults를 사용하는 어노테이션 예제

@ResultClass (Department.class)
@PropertyResults({
@Result(property="id", column="DEPT_ID"),
@Result(property="name", column="NAME"),
@Result(property="employees",
nestedQuery=@QueryMethod(type=CompanyMapper.class, methodName="getEmployeesForDeparment", parameters="id"))
})
@Select("SELECT #id, #name FROM DEPARTMENT WHERE COMP_ID = @id ")
List getDepartmentsForCompany(int id);



<리스트 15>와 같이 캐싱을 설정할 수도 있다.



<리스트 15> 캐싱 설정의 어노테이션 사용 예제

@CacheContext("Employee")
public class EmployeeMapper {
void insertEmployee(Employee emp);
Employee getEmployee(int id);
List findEmployeesLike(Employee emp);
}



기존 설정이라면 다음과 같은 의미를 가진다.



<리스트 16> 캐싱 설정의 기존방식 예제


...






@flushCache
List updateAndGetSpecialEmployees();
는 다음과 같은 의미를 가진다.







예제를 통해 보면 현재의 설정을 일일이 XML 파일에 적어야 할 사항을 어노테이션을 통해 설정을 함으로써 일단 XML 파일 내용의 갱신 문제에 좀 더 자유로워 질 수 있을 듯하다. 한편, 개발자가 초기 XML 설정 외에 대부분의 작업을 자바 소스에서 제어함으로써 작업도 한결 수월하게 할 수 있을 것이다.



XML 설정 향상



다음은 XML 설정 향상과 관련된 주요 내용들이다.



1. 필드와 생성자 파라미터 그리고 자바빈즈 프로퍼티에 결과 및 파라미터를 맵핑된다.

2. N+1 문제를 해결하는 조인 매핑과 groupBy는 사용이 더 쉬워진다. <리스트 17>의 요소를 사용해서 하위 결과 맵을 생성하는 작업이 필요 없도록 한다.

3. 결과 맵과 파라미터 맵은 기본적으로 ‘자동 매핑’ 되고 이름이 일치하지 않는 프로퍼티만을 명시한다.

4. 기능적으로 좀 더 뛰어나면서 간단한 타입 핸들러 구현체와 데이터 타입 변환 필터를 제공할 것이다.

5. 가장 큰 변화는 XML 파일이 Mapper.class의 복사본과 함께 처리한다는 점이다. 이를테면 EmployeeMapper.xml은 클래스패스에 존재하는 EmployeeMapper.class를 위해 로드 된다. 해당 매퍼 클래스의 객체를 생성하면 자동으로 같은 이름의 매퍼 XML 파일이 로드 되는 형식이라고 짐작된다. 객체 생성에 따라 매퍼 XML 파일이 로드 되는 형식이라면 SQL 구문의 갱신이 3.0에서는 어느 정도 해결될 듯하다.



<리스트 17> Mapper용 XML설정


















동적 SQL



현재의 요소를 사용하는 방법 외에도 3.0에서는 다음과 같은 방법이 가능하게 된다. 아무래도 자바 개발자에게는 XML 에서 문자열을 조작하는 것보다는 자바 로직으로 처리하는 편이 훨씬 수월할 것이다.



<리스트 18> SQLSource를 확장한 동적 SQL시용 예제

public class GetAccountListSQL extends SQLSource {
public String getSQL(Object param) {
Account acct = (Account) param;
append("select * from ACCOUNT");
prepend(“WHERE”); // 다음의 append 앞에 기본적으로 추가한다.
if (exists(acct.getEmailAddress())) {
append("AND", "ACC_EMAIL like #EmailAddress#"); // 필요하다면 첫 번째 인자를 붙이겠지만 그렇지 않다면 첫 번째 인자는 무시된다.
}
if (greaterThan(0,acct.getID())) {
append("AND", "ACC_ID = #ID#");
}
prepend(); // 앞의 prepend 뒤에 아무 코드가 없다면 앞의 prepend를 지운다.
append ("order by ACCT_LAST_NAME");
}
}



<리스트 18>은 다음과 같이 사용할 수 있다.



@SQLSource(GetAccountListSQL.class)
List getAccountList(Account acct);



또는 다음처럼도 가능하다.







그 외에 테이블 관계를 나타내기 위한 간단한 기능이 추가되고 SQL 생성을 지원하는 몇 가지 옵션도 추가될 것으로 보인다.



지금까지 개인적으로 원했던 기능 중에 3.0에서 추가되도록 활발히 진행 중인 것 위주로 살펴보았다. 하지만 곰곰이 생각해보면 아이바티스가 추구했던 간결함은 오히려 반감되고 있는 게 아닌가 하는 우려도 든다. 아이바티스의 장점은 간결함이다. JDBC에 익숙한 개발자라면 몇 시간의 교육만으로도 실제 프로젝트에 적용할 수 있을 정도다. 단점이라면 매핑 작업이 번거롭다는 점을 들 수 있을 것이다. 파라미터 맵과 결과 맵에 매핑해주는 작업이 JDBC를 그대로 사용하는 것보다 그렇게 쉬운 작업이 아니다. 그럼에도 불구하고 아이바티스를 많이 사용하게 되는 데에는 충분히 사용할만한 장점이 있기 때문일 것이다. 앞으로도 발전하는 아이바티스의 모습을 기대해 본다.



제공 : DB포탈사이트 DBguide.net
출처명 : 한국마이크로소프트 [2007년 9월호

spring+ibatis연결하기. V_2

* spring에 ibatis를 이용한 실행


우선 오라클9i를 기준으로 scott/tiger로 접속하여 위의 내용처럼

2개의 컬럼description과 price컬럼을 만들어 준다.

쿼리.



Create table PRODUCT (DESCRIPTION varchar2(20), PRICE number(10,2));


참조 차이트 : http://jiwook6.tistory.com/76


예제를 실행하기 위해 3개의 값을 입력한다.



insert into product values('PSP', 20000);

insert into product values('NINTENDO', 10000);

insert into product values('XBOX', 5000);



자 ibatis와 연결하자.

우선 ibatis설정을 알아보자.

버전은 ibatis-2.3.0.677 로써

탐색기로 Lib 폴더에ibatis-2.3.0.677.jar를 넣어주고 이클립스에서 새로고침해주자.



필요한 파일.

sqlMapConfig.xml

sqlResource.properties

sqlMap.xml



처음sqlMapConfig.xml파일은 데이타베이스와 연결하기 위한 설정파일이다. 디비의 접속 아이디와 비번 jdbc드라이버와 url주소를 입력한다.



실질적으로 아이디, 비번, 드라이버, url주소의 실제값은sqlResource.properties를 이용하여 접속한다. 유지보수하기 편하게 하기 위해 프로퍼티파일의 값만 변경하게 되면 어떠한 디비와도 연결이 가능하다.


마지막으로sqlMap.xml 파일은 실제 쿼리문이 들어가는 파일이다.

디비의 모든 값을 꺼내오는 것을 기본으로 시작해보자.


sqlMapConfig.xml

WEB-INF/

---------------------------------------------------------------------------









* 프로퍼티 파일의 경로 지정


lazyLoadingEnabled="true"

cacheModelsEnabled="true"

enhancementEnabled="true"

maxRequests="512"

maxSessions="128"

maxTransactions="32"

/>

* ibatis설정내용들.

















*JDBC연결할 내용기술, 프로퍼티에 값을 입력하여 여기서는 값을 받아온다.



* 실제 쿼리문이 들어가 설정파일.


---------------------------------------------------------------------------


sqlResource.properties

dao/

---------------------------------------------------------------------------

driver=oracle.jdbc.driver.OracleDriver

url=jdbc:oracle:thin:@192.168.1.10:1521:tomato

username=scott

password=tiger

---------------------------------------------------------------------------







sqlMap.xml

src/dao/sqlMap.xml

---------------------------------------------------------------------------









* Product클래스의 경로와 지정 이름을 결정한다.









* 데이타베이스에 저장된 컬럼의 이름을 지정하여 결과Map지정



* 실제 쿼리문을 작성하여 준다. 위에서는 select한 모든 결과값을 받아오는 쿼리문으로 배열형태로 반환된다.


---------------------------------------------------------------------------


Ibatis설정부분은 끝났다.

Spring에서 실행되는 부분을 보자.



springapp-servlet.xml 가장 핵심이 되는 서블릿.xml파일로써 ibatis의

sqlMapConfig.xml을 불러 or매핑을 해준다.

---------------------------------------------------------------------------













* 스프링에서 제공하는ibatis.SqlMapClientFactoryBean를 이용하여주고 설정sqlMapConfig.xml파일을 연결시켜준다.







* DAO클래스를 임플리먼츠한 ProductManagerDaoImpl클래스를 sqlMapClient

를 연결시켜준다.







* 컨트롤파일에 연결시켜준다.



messages











springappController













org.springframework.web.servlet.view.JstlView

/WEB-INF/jsp/

.jsp





---------------------------------------------------------------------------




DAO부분을 알아보자.

DAO는 인터페이스를 이용하여implements하여 작성한다.


ProductManagerDao.java작성

dao/

---------------------------------------------------------------------------

package dao;

import java.sql.SQLException;

import java.util.*;

public interface ProductManagerDao {

public List getProductManagerList() throws SQLException;

* 몸체가 없는 메서드를 선언한다.

* 테이블에 모든 데이터를 가져오는 메서드.

}
---------------------------------------------------------------------------


ProductManagerDaoImpl.java작성

dao/

---------------------------------------------------------------------------

package dao;

import java.sql.SQLException;

import java.util.List;

import java.util.Map;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.ibatis.sqlmap.client.SqlMapClient;

import bus.ProductManager;

public class ProductManagerDaoImpl extends SqlMapClientDaoSupport implements ProductManagerDao{

* 스프링에 포함된 SqlMapClientDaoSupport를 상속받는다. 이 클래스 안에는 ibatis의 SqlMapClient를 쉽게 구현할수 있는 getSqlMapClient()를 제공한다.

public List getProductManagerList() throws SQLException{

SqlMapClient client = super.getSqlMapClient();

List re= client.queryForList("selectList");

return re;

}

* 인터페이스에서 구현한 메서드의 몸체를 구현해준다.

* SqlMapClient를 상속받은 SqlMapClientDaoSupport를 통해 getSqlMapClient()를

이용해 구현한다.

* queryForList("selectList")를 통해 sqlMap.xml파일에서의 쿼리문을 통한

내용을 List형태로 받아온다.

- ("selectList")역시sqlMap.xml안에 쿼리문의 id이다.

}

---------------------------------------------------------------------------
이렇게 DAO부분을 작성해준다. 현재 작성된부분은 단순히 테이블에 작성된 모든 내용을 List로 받아 컨트롤로 넘겨준다.



컨트롤 파일 작성. SpringappController.java작성

---------------------------------------------------------------------------

package web;

import org.springframework.web.servlet.mvc.Controller;

import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

import java.util.Map;

import java.util.HashMap;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import dao.*;

public class SpringappController implements Controller {

protected final Log logger = LogFactory.getLog(getClass());

private ProductManagerDaoImpl pDao;

* Dao부분에서 작성한 클래스를 선언해준다.

public void setProductManagerDao(ProductManagerDaoImpl pDao)

{

this.pDao = pDao;

}

public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)

throws Exception {

String now = (new java.util.Date()).toString();

logger.info("returning hello view with " + now);

Map myModel = new HashMap();

myModel.put("now", now);

myModel.put("products", pDao.getProductManagerList());

* Dao부분에서 작성한 getProductManagerList()를 부름으로 Map에 저장한다.

return new ModelAndView("hello", "model", myModel);

}

}

---------------------------------------------------------------------------

위에서 작성된 Controller 파일에서 Dao를 부르는 부분을 작성하여 준다.

참조 사이트 : http://jiwook6.tistory.com/76