 |  |
|
난이도 : 초급 Cynthia M. Saracco, Senior Software Engineer, IBM
2007 년 4 월 24 일 DB2® Viper(beta)는 XML 데이터의 저장, 관리, 쿼리에 대한 새로운 지원을 추가했습니다. 이 글에서, 새로운 XML 데이터에 액세스 하는 자바™ 애플리케이션의 작성 방법을 설명합니다. XML 데이터의 삽입, 쿼리, 업데이트, 삭제 방법과, XML 데이터에 액세스 하는 저장 프로시저(stored procedure)를 생성하는 방법을 설명합니다.
DB2 Viper에 저장된 XML 데이터에 액세스 하는 자바 애플리케이션을 작성하는 것은 관계형 데이터에 액세스 하는 자바 애플리케이션을 작성하는 것과 크게 다르지 않다. 여러분이 Java Database Connectivity (JDBC)에 익숙하다면, 이미 여러분은 DB2 XML 애플리케이션을 작성하기 위해 필요한 많은 부분을 알고 있는 것이다.
이 글에서는, XML 데이터 삽입, XML과 비 XML(non-XML) 데이터 쿼리, XML 데이터 업데이트, XML 데이터 삭제, XML 데이터에 액세스 하는 저장 프로시저(stored procedure) 생성하는 등의 일반적인 프로그래밍 시나리오를 설명할 것이다. 하지만, 먼저 DB2 데이터베이스 애플리케이션을 개발하는 몇 가지 기본적인 사항을 점검해 보도록 하자.
일반적인 프로그래밍 "베스트 프랙티스" 따라가기
DB2의 XML 지원은 새로운 것이지만, 좋은 데이터베이스 애플리케이션 프로그래밍 프랙티스는 변하지 않는다. DB2의 상세한 XML 기술로 돌입하기 전에, 다음과 같은 일반적인 원리를 알아 두어야 한다.
-
자신에게 필요한 것만 요청하라: 해당 정보의 일부만 필요할 경우, 테이블의 전체 내용이나 많은 XML 문서의 전체 내용을 검색하지 말라. 이는 처리 비용만 높이고, 런타임 성능만 떨어트릴 뿐이다.
-
데이터베이스 서버 작업이 중복되지 않도록 하라: 애플리케이션 내에서 데이터 필터링 및 데이터 처리 작업을 수행하게 하는 대신, DB2가 직접 이를 처리하게 하라. 예를 들어, DB2가 지정된 순서로 결과를 리턴 하도록 했다면, 여러분이 직접 데이터를 소팅 할 필요가 없다. 이와 비슷하게, 확실한 결과만 리턴 되는지 DB2가 확인하도록 했다면, 이중 체크를 할 필요가 없다. 데이터 중심 프로세싱은 애플리케이션 서버에서가 아니라, 데이터베이스 서버에 의해서 가장 잘 수행된다.
-
관리하기 쉬운 코드를 만들어라: 애플리케이션에 복잡한 쿼리가 포함되어 있을 경우, 코드에 코멘트나 Javadoc을 포함시킨다.
-
트랜잭션의 범위를 신중하게 고려하라: 기본적으로 JDBC는 각 쿼리를 독립 트랜잭션으로 취급한다. 이것이 여러분의 필요에 맞는지를 결정하고, 여러분의 트랜잭션에 정의한 범위(그리고 고립 레벨)이 전체적인 동시성 요구 사항에 어떻게 영향을 주는지를 고려하라.
-
네트워크로 연결된 환경에서 트래픽을 최소화 하라: 애플리케이션과 DB2 간 데이터를 불필요하게 전송하지만 않아도 더 나은 런타임 성능을 경험할 수 있다. 여러분에게 필요한 데이터만 가져오는 것이 이를 위한 한 가지 방법이다. 데이터베이스 저장 프로시저를 호출하는 것도 작업의 특성에 따라서 도움이 될 수도 있다.
환경 구성하기
DB2는 XML 데이터와 함께 실행될 자바 애플리케이션을 개발 또는 실행하는데 있어 특별한 구성을 요하지 않는다. 실제로, 여러분은 여러분이 선택한 통합 개발 환경(IDE)를 사용하거나, 명령행(Command Line)에서 Java Developer Kit (JDK)으로 직접 작업하여 자바 프로그램들을 작성, 테스트, 디버깅 할 수 있다. 하지만, DB2 Viper에는 Developer Workbench가 포함되어 있고, 이 글에서 소개할 예제는 이 개발 환경을 사용한다. 다음 섹션에서는 Developer Workbench를 구성하는 방법을 설명하고, 데이터베이스 구성 매개변수에 대해서도 알아보도록 하겠다.
DB2 Developer Workbench
DB2 Developer Workbench는 무료 다운로드 할 수 있는 오픈 소스 프로젝트인 Eclipse 3.1 플랫폼에 기반하고 있다. 이 워크벤치에서 DB2 XML 애플리케이션을 컴파일 및 실행하려면, 프로젝트를 생성하고, 프로젝트의 빌드 경로에 알맞은 DB2 라이브러리를 포함시켜야 한다. DB2의 JDBC 3.0-호환 드라이버를 지원하는 라이브러리도 포함된다. 다음은 환경 구성 단계이다.
-
DB2 Workbench를 시작한다. Windows Start 메뉴에서, DB2 > IBM DB2 Developer Workbench V9.1 > Developer Workbench.를 선택한다.
-
새로운 프로젝트를 만든다. 우리는 처음에 간단한 프로젝트를 사용할 것이다. Java perspective (Window > Open Perspective -> Java)로 전환하고, File > New > Project.를 선택한다. 마법사를 따라서 프로젝트 이름을 정한다. 다른 아이템의 경우, 기본 설정을 유지한다.
-
DB2 라이브러리를 프로젝트의 빌드 경로에 추가한다. 프로젝트를 하이라이팅 하고, 오른쪽 마우스를 클릭하여, Properties를 선택한다. Java Build Path를 선택하고, Libraries 탭을 클릭한다. DB2 외부 .jar 파일들(db2jcc.jar, db2jcc_javax.jar, db2jcc_license_cu.jar)을 추가한다.
-
애플리케이션용 패키지를 만들 경우도 있다. 프로젝트를 하이라이팅 하고, 마우스 오른쪽 클릭하여, New > Package.를 선택한다.
프로젝트와 패키지 생성에 대한 자세한 내용은 온라인 도움말을 참조하라.
샘플 데이터
이 글의 예제들은 "DB2 Viper 시작하기" (한국 developerWorks, 2006년 7월)에서 만들었던 "clients" 테이블에서 작동한다. 간단히 설명하자면, 이 테이블은 다음과 같이 정의되었다.
Listing 1. 코드 리스팅 샘플
create table clients(
id int primary key not null,
name varchar(50),
status varchar(10),
contactinfo xml
)
|
그림 1은 이 테이블의 "contactinfo" 컬럼에 삽입 될 XML 파일 샘플을 나타내고 있다.
그림 1. "clients" 테이블에 삽입된 XML 파일
데이터베이스 구성 매개변수
본 튜토리얼의 예제들은 간단하고 적은 양의 XML 데이터로 작동하기 때문에 이들을 실행하기 위해 기본적인 데이터베이스 구성 매개변수들을 바꿀 필요가 없다. 하지만, 기본 값은 일부 실행 환경에는 불충분 할 수도 있다. 특히, 로그 사이즈, 자바 힙, 쿼리 문 힙, 애플리케이션 힙 설정은 늘어날 필요가 있다. 이러한 값들이 올바르지 않게 설정된다면, 런타임 성능이 느려지거나, 불충분한 로그 공간 때문에 큰 XML 문서들을 DB2 테이블에 삽입할 수 없을지도 모른다.
DB2 Control Center (Tools > Configuration Assistant 선택) 또는 DB2 명령행 프로세서에서 에서 DB2 데이터베이스 구성 매개변수들을 리뷰 또는 수정할 수 있다. 제품 매뉴얼을 참조하라.
데이터베이스 연결하기
DB2 XML 데이터로 작업하려면 여러분의 데이터를 포함하고 있는 데이터베이스로 연결해야 한다. 이 코드에는 특별한 것이 없다. 여느 DB2 데이터베이스에 연결하기 위해 작성했던 것과 같은 로직이다.
Listing 2에는 DB2 데이터베이스 연결을 구축하고 닫는 메소드와 헬퍼 클래스가 포함되어 있다.
Listing 2. 데이터베이스 연결을 구축하고 닫는 헬퍼 클래스
public class Conn {
// for simplicity, I've hard-coded account and URL data.
private static String user = "user1";
private static String pwd = "mypassword";
private static String url = "jdbc:db2:test";
// this method gets a database connection
public static Connection getConn(){
Connection conn=null;
// load the appropriate DB2 driver and
// get a connection to the “test” database
try {
Class.forName("com.ibm.db2.jcc.DB2Driver");
conn = DriverManager.getConnection(url, user, pwd);
. . .
}
catch (Exception e) { e.printStackTrace(); }
return conn;
} // end getConn();
// this method closes a database connection
public static void closeConn(Connection conn){
try {
if(conn == null) { return; }
conn.close();
}
catch (Exception e) { e.printStackTrace(); }
finally {
try { conn.close(); }
catch (Exception e) { }
}
} // end closeConn();
} // end class
|
XML 데이터를 삽입하고 쿼리 하는 등 더 광범위한 태스크를 수행하는 애플리케이션에서 이 메소드를 호출할 수 있다.
XML 데이터 삽입하기
초기 XQuery 스팩은 데이터베이스 쓰기 연산(데이터 삽입하기)에 대해 다루지 않았기 때문에, DB2는 SQL INSERT 문을 사용하여 프로그래머들이 새로운 XML 데이터를 XML 컬럼을 포함하고 있는 테이블에 작성할 수 있도록 한다. DB2는 XML 문서를 최대 2GB까지 저장할 수 있다.
문자 스트링, 바이너리 데이터(큰 객체가 포함됨), SQL sub-select 문에서 XML 데이터를 삽입(insert)할 수도 있지만, 자바 프로그래머들은 가끔 파일에 포함된 XML 데이터를 DB2에 삽입해야 할 때가 있다. 이 글에서는 파일과 문자 스트링에서 XML 데이터를 삽입하는 방법을 설명하도록 하겠다. 다른 삽입 시나리오에 대해서는 DB2 Viper 매뉴얼을 참조하라.
DB2 Viper에서는 이전에 등록된 XML 스키마에 대한 유효성 검증을 하거나, 또는 검증하지 않고 XML 문서들을 삽입할 수 있다. 이 글에서는 두 가지 방법 모두를 설명할 것이다.
유효성 검사 없이 파일 삽입하기
Listing 3의 insertFile() 메소드는 XML 파일에서 "clients.contactinfo" 컬럼으로 데이터를 삽입하는 방법을 나타내고 있다. 이 메소드는 나중에 사용할 목적으로 여러 변수들을 정의하는 것으로 시작한다. 처음 세 개는 "clients" 테이블의 아이디, 이름, 상태 컬럼에 해당하는 것이다. 네 번째는 "contactinfo" 컬럼에 삽입될 XML 파일의 이름이다. 간단히 하기 위해, 이 메소드에서는 값들이 하드 코딩 되었다. 실행 환경에서는, 인풋(input) 값들은 다르게 얻어진다.
데이터베이스로 연결한 후에, INSERT 문에 대한 간단한 스트링을 만든다. 여러분도 보다시피, 이것은 다른 DB2 INSERT 문처럼 보이고, 네 개의 인풋 컬럼 값에 매개변수 마커를 사용한다. INSERT 문이 제공되고, 네 개의 매개변수 마커가 설정된다. XML 컬럼에 대한 마커를 설정하려면 FileInputStream을 열고, XML 파일의 위치로 전달한다. 또한, 이 파일의 길이를 파악하고, 그 정보를 setBinaryStream() 메소드에 대한 인풋(input)으로서 사용한다. 마지막으로, 해당 문장을 실행하고, 에러를 검사한 다음 연결을 끊는다.
Listing 3. 파일에서 XML 데이터 삽입하기
public static void insertFile(){
try {
// for simplicity, I've defined variables with input data
int id = 1885;
String name = "Amy Liu";
String status = "Silver";
String fn = "c:/XMLFiles/Client1885.xml"; // input file
// get a connection
Connection conn = Conn.getConn();
// define string that will insert file without validation
String query = "insert into clients (id, name, status, contactinfo) values
(?, ?, ? ,?)";
// prepare the statement
PreparedStatement insertStmt = conn.prepareStatement(query);
insertStmt.setInt(1, id);
insertStmt.setString(2, name);
insertStmt.setString(3, status);
File file = new File(fn);
insertStmt.setBinaryStream(4, new FileInputStream(file), (int)file.length());
// execute the statement
if (insertStmt.executeUpdate() != 1) {
System.out.println("No record inserted.");
}
. . .
conn.close();
}
catch (Exception e) { . . . }
}
|
유효성 검증을 하고 파일 삽입하기
유효성 검증을 하고 XML 파일을 삽입할 때는 추가적인 프로그래밍이 필요하다. "DB2 Viper 시작하기" (한국 developerWorks, 2006년 7월)에서 설명했던 ClientInfo.xsd 파일을 생성 및 등록했다면, Listing 3의 코드에서 한 줄만 수정하여, DB2가 유효성 검사를 하고 XML 파일을 삽입하도록 한다. 이 코드에는 query 스트링의 정의가 포함된다.
Listing 4에서 보듯, 수정된 INSERT 문은 XML 데이터에 대한 매개변수 마커를 지정하기 전에 XMLValidate 함수를 호출한다. 이 함수 역시 유효성 검사에 사용될 XML 스키마 식별자를 지정해야 한다. "user1.mysample"로 알려진 이전에 등록된 스키마가 참조된다.
Listing 4. 유효성 검사를 하고 파일에서 XML 데이터 삽입하기
String query = "INSERT INTO clients (id, name, status contactinfo) " +
"VALUES (?, ?, ?, xmlvalidate(? according to xmlschema id user1.mysample))";
|
인풋 XML 파일에 지정된 스키마에 따라 유효한 데이터가 포함되어 있다면, DB2는 행(row)을 삽입한다. 그렇지 않을 경우, 전체 문장은 실패하고, 이 행에 대한 어떤 데이터도 삽입되지 않는다.
유효성 검사 없이 문자 스트링 삽입하기
Listing 5의 insertString() 메소드는 문자 스트링 변수에 할당된 XML 문서를 DB2에 삽입하는 방법을 설명하고 있다. 로직은 파일에서 데이터를 삽입하는 이전 예제와 크게 다르지 않다. 제공된 문의 setBinaryStream() 메소드를 사용하는 대신, setString() 메소드를 사용한다. xml 변수 정의에 있는 XML 문서는 이 예제에서는 하드 코딩 되었다.
주: 이스케이프 문자(백워드 슬래시)는 XML 문서의 일부인 인용 부호 앞에 포함된다. (아래 예제의 XML 버전 넘버)
Listing 5. 문자 스트링에서 XML 데이터 삽입하기
public static void insertString(){
try {
// for simplicity, I've defined variables with input data
int id = 1885;
String name = "Amy Liu";
String status = "Silver";
String xml =
"<?xml version=\"1.0\"?>" +
"<Client>" +
"<Address> " +
"<street>54 Moorpark Ave.</street>" +
"<city>San Jose</city>" +
"<state>CA</state>" +
"<zip>95110</zip>" +
"</Address>" +
"<phone>" +
"<work>4084630110</work>" +
"<home>4081114444</home>" +
"<cell>4082223333</cell>" +
"</phone>" +
"<fax>4087776688</fax>" +
"<email>sailer555@yahoo.com</email>" +
"</Client>";
// get a connection
Connection conn = Conn.getConn();
// define string that will insert file without validation
String query = "insert into clients (id, name, status, contactinfo) values
(?, ?, ? ,?)";
// prepare the statement
PreparedStatement insertStmt = conn.prepareStatement(query);
insertStmt.setInt(1, id);
insertStmt.setString(2, name);
insertStmt.setString(3, status);
insertStmt.setString(4, xml);
// execute the statement
if (insertStmt.executeUpdate() != 1) {
System.out.println("No record inserted.");
}
. . .
conn.close();
}
catch (Exception e) { . . . }
}
|
유효성 검사를 하고 문자 스트링 삽입하기
여러분도 예상했겠지만, 문자 스트링으로서 제공된 XML 문서의 유효성 검사는 추가 프로그래밍이 필요하다. 실제로, 코드의 한 줄만 수정되어야 한다. 바로 query 변수의 정의이다. INSERT 문을 수정하여 Listing 4에서 했던 것처럼, XMLValidate 함수를 호출한다.
다음은 수정된 문장이다.
Listing 6. 유효성 검사를 하고 문자 스트링에서 XML 데이터 삽입하기
String query = "INSERT INTO clients (id, name, status contactinfo) " +
"VALUES (?, ?, ?, xmlvalidate(? according to xmlschema id user1.mysample))";
|
XML 데이터 쿼리
자바 프로그램을 사용하여 XML 데이터를 DB2에 삽입하는 방법을 배웠으니, 이제 XML 데이터를 쿼리 할 준비가 되었다. 이 섹션에는 간단한 태스크(전체 XML 문서 가져오기)와 프로세싱으로 시작하여 보다 어려운 태스크(XML과 관계형 쿼리 술어에 기반한 XML 문서의 리턴 부분)로 이어진다.
DB2가 SQL과 XQuery를 주 언어로서 지원하지만, XQuery는 매개변수 마커(parameter marker)를 해결할 수 있는 방법을 제공하지 않는다. 다시 말해서, 하드 코딩 된 쿼리 술어 이상의 것을 요하는 애플리케이션의 XQuery들은 XMLQuery 또는 XMLExists 같은 SQL/XML 함수를 사용하여 SQL 문으로 래핑되어야 한다."SQL을 이용한 DB2 XML 데이터 쿼리" (한국 developerWorks, 2006년 12월)에서는 이러한 함수들에 대해 자세히 설명했다. 여기에서는 이들을 자바 프로그램에서 사용하는 방법을 설명하겠다. 하드 코딩 된 쿼리 술어를 가진 XQuery를 애플리케이션에 포함시키는 방법도 알아보자.
전체 XML 문서 가져오기
우리의 첫 번째 쿼리 기반 방식은 비교적 간단하다. 해당 클라이언트에 대한 전체 연락처 정보를 단순히 가져오는 것이다. 이러한 특징을 가진 쿼리는 SQL에서는 매우 쉽게 표현될 수 있다. 여러분이 JDBC에 익숙하다면, 이 코드를 이해하기 쉬울 것이다.
Listing 7의 simpleQuery() 메소드는 여러 변수들을 선언하고 Listing 2.에 정의된 헬퍼 메소드를 사용하여 데이터베이스 연결을 구축한다. query 스트링에는 간단한 SQL 문이 포함되어 있어 특정 고객에 대한 모든 연락처 정보를 선택한다. 문을 실행한 후에, 애플리케이션은 문자 스트링 변수(stringDoc)에 보내졌던 결과를 프린트 한다.
Listing 7. SQL을 사용하여 전체 XML 문서 가져오기
import java.sql.*;
. . .
public static void simpleQuery() {
PreparedStatement selectStmt = null;
String query = null, stringDoc = null;
ResultSet rs = null;
int clientID = 1885;
try{
// get a connection
Connection conn = Conn.getConn();
// define, prepare, and execute the query
// this will retrieve all XML data for a specific client
query = "select contactinfo from clients where id = " + clientID
selectStmt = conn.prepareStatement(query);
rs = selectStmt.executeQuery();
// check for results
if (rs.next() == false) {
System.out.println("Can't read document with id " + clientID);
}
// fetch XML data as a string and print the results
else {
stringDoc = rs.getString(1);
System.out.println(stringDoc);
}
. . .
conn.close();
}
catch (Exception e) { . . . }
}
|
이 프로그램은 지정된 고객에 대한 모든 XML 연락처 정보를 포함하고 있는 한 행의 데이터를 프린트 한다.
여기에서는 나타나있지 않지만, XQuery를 사용하여 한 개 이상의 XML 문서를 가져올 수도 있다. 매개변수 마커를 XQuery에 결합할 필요가 없다면 말이다. 이 글 후반에, XQuery를 사용하여 XML 데이터를 가져오는 자바를 설명하겠다.
XML 문서 부분들 가져오기
일반적인 프로그래밍 태스크에는 XML 문서들의 부분들을 가져오는 것도 포함된다. 이 예제의 자바 코드는 "Silver" 상태에 있는 고객의 이름과 주 이메일 주소를 가져온다. 고객 이름과 상태 정보는 SQL VARCHAR 컬럼에 저장되고, 이메일 주소는 "contactinfo" 컬럼의 XML 문서에 포함된다.
간략하게 하기 위해, 이전에 보여주었던 코드를 생략했다. 새롭거나 다른 라인들만 포함시켰다.
Listing 8. SQL/XML을 사용하여 관계형 데이터와 XML 조각 가져오기
. . .
String status = "Silver";
try{
// get a database connection
. . . .
// define, prepare, and execute a query that includes
// (1) a path expression that will return an XML element and
// (2) a parameter marker for a relational column value
String query = "SELECT name, xmlquery('$c/Client/email[1]' " +
" passing contactinfo as \"c\") " +
" from clients where status = ?";
PreparedStatement selectStmt = conn.prepareStatement(query);
selectStmt.setString(1, status);
ResultSet rs = selectStmt.executeQuery();
// iterate over and print the results
while(rs.next() ){
System.out.println("Name: " + rs.getString(1) +
" Email: " + rs.getString(2));
}
. . .
// release resources
}
catch (Exception e) { . . . }
|
이 코드는 XMLQuery 함수를 호출하는 SQL/XML 문을 실행한다. DB2가 목표 XML 문서의 루트 "Client" 엘리먼트 밑에 있는 첫 번째 "email" 엘리먼트를 검색하도록 하는 함수로 경로 식을 제공한다. (이 경로 식은 대소문자를 구분한다.) $c 변수와 SQL FROM 구문은 대상 문서들을 어디에서 찾을 수 잇는지를 나타낸다. "clients" 테이블의 "contactinfo" 컬럼에 있다. SQL WHERE 문은 대상 XML 문서를 클라이언트의 "status"가 특정 값(이 경우 "Silver")으로 되어 있는 행에서만 찾을 수 있도록 제한한다.
이 프로그램의 아웃풋(Output)은 다음과 비슷하다.
Listing 9. 이전 애플리케이션의 아웃풋 샘플
Name: Lisa Hansen Email:
Name: Amy Liu Email: <email>sailer555@yahoo.com</email>
. . . .
|
이 아웃풋 샘플에서, 지정된 고객(Lisa Hansen)에 대해 어떤 이메일 정보도 리턴 되지 않았다. 이 엘리먼트는 XML "contactinfo" 문서에 존재하지 않기 때문이다.
관계형 및 XML 술어 필터링
자바 프로그램은 또한 DB2에게 XML과 비 XML 데이터에 적용된 조건에 따라 쿼리 아웃풋을 필터링 하도록 명령할 수 있다. 다음 예제는 이전 예제를 기반으로 구현되었고, California, San Jose에 살고 있는 "Silver" 고객의 이름과 주 이메일 주소들을 리턴한다. 이 단일 쿼리는 XML과 비 XML 컬럼의 데이터를 나타내고, XML과 비 XML 컬럼의 내용에 기반하여 데이터를 제한한다.
아래 리스팅에는 이전 예제에서 수정된 코드 부분만 포함된다. 이 경우, SELECT 문은 이제 WHERE 문의 일부로서 XMLExists를 호출하여 그 결과를 특정 도시나 주(각각 city와 state 변수로 정의됨)에 살고 있는 고객으로 제한한다.
Listing 10. XML 엘리먼트 값에 기반하여 XML 데이터 필터링 하기
. . .
String status = "Silver";
String state = "CA";
String city = "San Jose";
. . .
try{
. . . .
String query = "SELECT name, xmlquery('$c/Client/email[1]' " +
" passing contactinfo as \"c\") " +
" from clients where status = ?"
" and xmlexists('$c/Client/Address[state=$state][city=$city]' " +
" passing contactinfo as \"c\", " +
" cast(? as char(2)) as \"state\", " +
" cast(? as varchar(30)) as \"city\" )";
PreparedStatement selectStmt = conn.prepareStatement(query);
selectStmt.setString(1, status);
selectStmt.setString(2, state);
selectStmt.setString(3, city);
. . .
}
|
대부분의 쿼리가 익숙할 것이므로, 이 섹션에서는 마지막 네 라인을 집중적으로 설명하겠다. XMLExists 함수는 DB2에게 주어진 XML 문서에 특정 도시와 주가 포함된 클라이언트 주소가 포함되어 있는지 여부를 파악하도록 명령한다. PASSING 문은 XML 문서를 찾을 수 있는 곳을 지정한다. "contactinfo" 컬럼이 그곳이다. CAST 함수는 두 번 호출되어 인풋 매개변수들의 값(도시와 주)을 적절한 데이터 유형으로 캐스팅 한다.
Lisa Hansen과 Amy Liu는 캐나 San Jose에 살고 있다고 가정할 때, 이 프로그램의 아웃풋은 Listing 9의 아웃풋과 비슷하다.
XQuery를 기본(top-level) 언어로서 사용하기
DB2가 기본 언어로 XQuery를 지원하지만, 초기 XQuery 스팩은 매개변수 마커에 대해서는 다루지 않았다. 결국, 자바 애플리케이션에서 XQuery 사용에 제한이 생겼다. 이전 섹션에서는 XQuery를 SQL에 삽입하여(XMLQuery와 XMLExists 함수를 사용함) 매개변수 마커들을 포함시키는 방법을 설명했다. 이 섹션에서는 자바 애플리케이션에서 순수 XQuery를 사용하여 이를 수행하는 방법을 설명하겠다.
다음 예제에는 "XQuery를 이용한 DB2 XML 데이터 쿼리" (한국 developerWorks, 2007년 1월)에서 설명한 것과 비슷한 XQuery가 포함되어 있다. 이 XQuery는 어떤 고객들이 California, San Jose에 살고 있는지를 결정한다. 각 고객을 위해, 모든 이메일 주소가 포함된 "emailList"를 포함하고 있는 XML 조각을 만든다. 마지막으로, "emailList" 시퀀스를 리턴한다.
Listing 11. XQuery를 사용하여 XML 조각 가져오기
try{
// get a database connection
Connection conn = Conn.getConn();
// define, prepare, and execute an XQuery (without SQL).
// note that we must hard-code query predicate values.
String query = "xquery for $y in db2-fn:xmlcolumn" +
"('CLIENTS.CONTACTINFO')/Client " +
"where $y/Address/city=\"San Jose\" and $y/Address/state=\"CA\" " +
"return <emailList> { $y/email } </emailList>";
PreparedStatement selectStmt = conn.prepareStatement(query);
ResultSet rs = selectStmt.executeQuery();
// iterate over all items in the sequence and print results.
while(rs.next() ){
System.out.println(rs.getString(1));
}
// release all resources
. . .
// catch and handle any exceptions
. . .
}
|
이 쿼리에서는 두 가지에 주목해야 한다. 우선, 쿼리 스트링이 "xquery" 키워드로 시작하고 있다. 이것은 DB2에게 XQuery 파서를 사용하여 쿼리를 처리하도록 명령한다. XQuery를 제 1 언어(outermost language)로 사용할 때 마다 이를 수행해야 한다. 두 번째는, 이 쿼리가 테이블과 컬럼 이름을 대문자로 언급하고 있다는 점이다. XQuery는 대소문자를 가린다. DB2는 정보를 내부 카탈로그에 작성할 때 테이블과 컬럼 이름들을 대문자로 하기 때문에, XQuery는 이 정보에 매치해야 한다.
이 프로그램의 아웃풋은 Listing 12에 나와있다. 하나의 "emailList" 아이템이 대상 고객에 대해 리턴 되기 때문에, 아웃풋을 빠르게 조사해 보면 네 명의 고객들이 해당된다는 것을 알 수 있다. 첫 번째 유효 레코드에는 한 개의 이메일 주소가 있다. 두 번째 레코드에는 어떤 것도 포함되어 있지 않다. (고객이 정보를 제공하지 않았을 것이다.) 이것의 "emailList"는 비어있다. 세 번째 레코드는 이 고객에 두 개의 이메일 주소가 레코드에 있다는 것을 나타내고 있다. 네 번째 레코드는 그 고객에 대한 한 개의 이메일 주소가 포함된다.
Listing 12. 이전 애플리케이션의 아웃풋 샘플
<emailList><email>newemail@someplace.com</email></emailList>
<emailList/>
<emailList><email>beatlesfan36@hotmail.com</email>
<email>lennonfan36@hotmail.com</email></emailList>
<emailList><email>sailer555@yahoo.com</email></emailList>
|
각 고객의 이름들이 우리 결과에는 포함되지 않았는지 궁금할 것이다. 대답은 간단하다. XQuery는 XML 데이터와 실행되고, 고객 이름은 SQL VARCHAR
컬럼에 저장된다. 따라서, 아웃풋에 고객 이름과 이메일 주소를 포함시키려면, SQL과 XQuery 모두를 포함하고 있는 쿼리를 작성해야 한다.
XML 데이터의 업데이트와 삭제
DB2에 저장된 XML 데이터를 업데이트 및 삭제하려면, SQL
UPDATE와 DELETE 문을 사용한다. 이 문에는 XML 컬럼 내에 저장된 XML 엘리먼트 값에 기반하여 대상 행과 컬럼을 제한하는 SQL/XML 함수들이 포함된다. 예를 들어, 특정 우편 번호에 살고 있는 고객에 대한 정보를 포함하고 있는 행을 삭제하거나 특정 주에 살고 있는 고객만을 위한 XML(비 XML 데이터)를 업데이트 할 수 있다.
UPDATE와 DELETE 문에서 SQL/XML 함수를 사용하는 문법은 SELECT 문에서 사용하는 문법과 같으므로, 전체 코드 샘플은 다시 반복하지 않겠다. 대신, 간략한 발췌 부분만 소개하겠다. 우선, DELETE 연산부터 보자.
Delete 예제
XML 데이터를 포함하는 행을 삭제하는 것은 간단하다. SQL DELETE 문을 WHERE 문과 함께 사용하여 삭제될 행만 제한한다. 예를 들어, 다음 코드는 클라이언트 ID 1885에 대한 행을 삭제한다.
Listing 13. 관계형 데이터 값에 기반하여 데이터 삭제하기
. . .
int clientID = 1885;
String query = "delete FROM clients WHERE id = ?";
. . .
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setInt(1, clientID);
if (stmt.executeUpdate() == 0) {
System.out.println("No records deleted.");
}
else { System.out.println("Record(s) deleted."); }
. . .
|
XML 엘리먼트 값에 기반하여 DELETE 연산을 제한하려면, WHERE clause. Listing 14는 XMLExists 함수를 사용하여 Maine ("ME")에 살고 있는 모든 클라이언트에 대한 정보가 삭제되도록 지정한다.
Listing 14. XML 엘리먼트 값에 기반하여 데이터 삭제하기
String state = "ME";
String query = "delete from clients " +
" where xmlexists('$y/Client/Address[state=$state]' " +
" passing clients.contactinfo as \"y\", " +
" cast(? as char(2)) as \"state\" )";
. . .
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setString(1, state);
. . .
|
Update 예제
SQL UPDATE 문 또는 저장 프로시저(DB2XMLFUNCTIONS.XMLUPDATE)를 사용하여 XML 컬럼에서 데이터를 업데이트 할 수 있다. 두 경우 모두, XML 컬럼에 대한 업데이트는 엘리먼트 레벨 보다는 문서 레벨에서 발생한다. 하지만, 저장 프로시저를 사용하여 업데이트 하는 프로그래머들은 전체 XML 문서를 DB2에 제공할 필요가 없다. 단지 업데이트 될 XML 엘리먼트를 지정하고, DB2는 수정되지 않은 문서 데이터를 보존하고 지정된 엘리먼트들을 업데이트 한다. UPDATE 문을 실행하는 프로그래머는 전체 문서(수정하고자 하는 엘리먼트가 아니라)를 지정해야 한다.
향후 기술자료에서는 XMLUPDATE 저장 프로시저에 대해 설명하고 샘플 코드도 제공할 것이다. 따라서 이 글에서는 이 부분을 설명하지 않고, 대신 이 섹션에서는 UPDATE 문을 실행하는 두 개의 코드 샘플을 설명하겠다. 두 개의 예제의 로직이 익숙할 것이다. 하나는 XML 파일을 사용하여 클라이언트 테이블을 업데이트 하고, 다른 하나는 XML을 포함하는 문자 스트링을 사용한다.
Listing 15는 파일에 포함된 XML 데이터를 사용하여 클라이언트 ID 1333에 대한 연락처 정보를 업데이트 한다. 업데이트 연산의 일부로서, 등록된 스키마에 기반하여 새로운 XML 데이터의 유효성 검사가 실행된다.
Listing 15. 파일에서 XML 데이터 업데이트 하기
int clientID = 1333;
String fn = "c:/XMLFiles/Client1333.xml"; // input file
String query = "update clients set contactinfo = " +
"xmlvalidate(? according to xmlschema id user1.mysample) " +
"where id = ?";
. . .
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setInt(2, clientID);
File file = new File(fn);
stmt.setBinaryStream(1, new FileInputStream(file), (int)file.length());
. . .
|
물론, XML 쿼리 술어를 사용하여 업데이트 하고자 하는 고객의 연락처 레코드를 지정할 수 있다. SQL/XML 함수를 사용하여 이를 수행한다. 고객이 팩스 번호를 업데이트 하고 싶지만, 그 고객은 자신의 클라이언트 ID를 기억하지 못하는 경우를 생각해 보자. 대신 그녀는 자신의 집 전화 번호를 주어 그녀의 정보를 찾을 수 있도록 한다. 다음 코드는 XMLExists를 사용하여 그녀의 집 전화 번호를 포함하고 있는 레코드만 업데이트 하도록 제한한다. 이 고객의 전체 연락처 정보는 수정된 XML 문서를 포함하고 잇는 자바 스트링으로서 제공된다.
Listing 16. 문자 스트링으로 XML 데이터 업데이트 하기
String homeph = "4081114444";
String xml =
"<?xml version=\"1.0\"?>" +
"<Client>" +
"<Address> " +
"<street>54 Moorpark Ave.</street>" +
"<city>San Jose</city>" +
"<state>CA</state>" +
"<zip>95110</zip>" +
"</Address>" +
"<phone>" +
"<work>4084630110</work>" +
"<home>4081114444</home>" +
"<cell>4082223333</cell>" +
"</phone>" +
"<fax>4087773111</fax>" +
"<email>sailer555@yahoo.com</email>" +
"</Client>";
String query = "update clients set contactinfo = ?" +
"where xmlexists('$y/Client/phone[home=$homeph]' " +
" passing clients.contactinfo as \"y\", " +
" cast(? as varchar(11)) as \"homeph\" )";
. . .
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setString(1, xml);
stmt.setString(2, homeph);
. . .
|
쿼리 빌더
애플리케이션을 위한 쿼리를 작성하고 싶다면, Developer Workbench는 SQL/XML과 XQuery를 생성하는 마법사를 제공한다. 대부분의 자바 프로그래머들은 매개변수 마커를 요하는 애플리케이션을 작성하기 때문에 종종 SQL/XML을 사용한다. 이 섹션에서는 SQL 쿼리 빌더를 사용하여 Listing 8에 포함된 것과 비슷한 SQL/XML 문을 생성하는 방법을 설명하겠다.
SQL/XML 문을 생성하려면 다음 단계를 따라간다.
- 워크스페이스를 준비한다.
- 쿼리의 특성을 지정한다.
- 쿼리를 실행한다.
각 단계를 살펴보도록 하자.
워크스페이스 준비
SQL 문은 워크벤치의 Data 퍼스펙티브에 있는 "Data project"의 일부로서 생성된다. 이 프로젝트를 생성하려면 다음 단계를 따라간다.
-
Data 퍼스펙티브를 연다.
Window > Open Perspective > Other > Data를 선택한다.
-
대상 데이터베이스로 연결한다. 좌측 하단 코너에 있는 Database Explorer 패인 안쪽에서 마우스 오른쪽을 클릭한다. New Connection을 선택하고 데이터베이스 이름, 사용자 이름, 패스워드를 지정한다.
-
새로운 데이타 프로젝트를 만든다. 좌측 상단 코너에 있는 Data Project Explorer 패인 내에서 오른쪽 마우스를 클릭한다. New > Project > Data > Data Development Project.를 선택한다. 프롬프트가 뜨면 프로젝트 이름을 정하고, 이를 앞서 만들었던 데이터베이스와 연결한다.
데이터베이스가 연결되고 Data 프로젝트가 생성되면, 쿼리를 구현할 준비가 된 것이다.
쿼리 구현하기
단순하게 하기 위해, 특정 상태에 있는 클라이언트의 주 이메일 주소를 리턴하는 SQL/XML 문을 만든다. 쿼리는 다음과 같다.
Listing 17. SQL/XML 쿼리 샘플
SELECT name, xmlquery('$c/Client/email[1]'
passing contactinfo as "c")
from clients where status = ?
|
다음 단계를 따라 쿼리를 생성한다.
-
SQL Builder를 시작한다. Data 프로젝트 내에서, SQL Scripts 폴더를 하이라이팅 하고 마우스 오른쪽을 클릭한다. New > SQL Statement를 선택한다. 프롬프트가 뜨면 기본 프로젝트 이름을 수락하고 SQL 문에 대한 이름을 지정한다. 기본 문 유형
(SELECT)을 수락하고 SQL 빌더를 사용한다. Finish.를 클릭한다.
-
쿼리 될 테이블을 지정한다. 중앙 패인에서 마우스 오른쪽을 클릭하고 Add Table.을 선택한다. 스키마 폴더를 확장하고 "clients" 테이블을 선택한다.
-
관심이 있는 컬럼을 지정한다. 이 예제의 경우, 결과 세트에 한 개의 컬럼과 한 함수
(XMLQuery)의 결과를 포함시켜야 한다. 다음 단계를 따라간다.
- 중앙 패인에 디스플레이 된 "names" 컬럼을 체크한다.
- 중앙 패인 밑에 있는 Column 탭에 디스플레이 된 첫 번째 행을 클릭한다. 이 셀이 맨 오른쪽에 있는 코너를 클릭하여 화살표 키를 디스플레이 하고 Build Expression.을 선택한다. Enter 키를 누른다.
- 디스플레이 된 메뉴에서 Function을 선택한다.
- 함수 카테고리로서 XML을, 함수로 XMLQuery를 선택한다. Parameter Value 1 옆에, Value 셀에 있는 화살표를 클릭하고 Edit Expression을 선택한다.
- String Constant Builder에 경로 식을 지정하고 (
$c/Client/email[1], Finish를 두 번 클릭한다.
- 생성된 SQL 문을 변경하여 XQuery 함수에
PASSING 문을 포함시킨다. 마지막 XQuery 함수는 다음을 읽는다:'$c/Client/email[1]' passing contactinfo as "c"
-
쿼리 술어(
WHERE문)을 지정한다. 이 예제에서, 관계형 컬럼을 위해 하나의 쿼리 술어를 추가해야 한다.
- SQL/XML 문 밑에 Conditions 탭 밑에서, Column 탭에 디스플레이 된 첫 번째 행을 클릭한다. 이 셀이 가장 오른쪽에 있는 화살표 키를 클릭하고 status 컬럼을 선택한다.
-
Operator 셀을 클릭하고, 등호 ("=") 연산자를 선택한다.
-
Value 셀의 가장 오른쪽에 있는 화살표 키를 클릭하고 Build Expression을 선택한다. Enter키를 누른다.
-
Constant를 선택하고, 프롬프트가 뜨면 String Constant를 선택한다.
- 사용자 인풋("status")에 대해 호스트 변수를 지정한다. Finish.를 클릭한다.
쿼리 실행하기
쿼리를 구현했다면, 실행할 준비가 된 것이다.
- Data 프로젝트에 쿼리를 배치하고, 마우스 오른쪽을 클릭하여 Run SQL을 선택한다.
- 프롬프트가 뜨면, 고객 상태("Gold" 또는 "Silver")에 대한 인풋 값을 지정하고 OK를 클릭한다.
- Data Output 패인에서 결과를 검토한다.
저장 프로시저(Stored procedures)
네트워크 환경에서, 저장 프로시저는 클라이언트 애플리케이션과 DB2 사이의 통신을 줄여줌과 동시에 런타임 성능을 향상시킨다. Viper에서는 저장 프로시저(stored procedure)에 XML 매개변수와 변수들이 포함된다.
저장 프로시저 개발에 대해 상세히 설명하는 것은 이 글의 범위를 벗어나지만, DB2 저장 프로시저가 XML 문서의 부분들을 가져오기 위해 어떻게 작성되는지 그 방법은 설명하도록 하겠다. 이 시나리오는 Developer Workbench의 마법사를 사용하여 필수 SQL 저장 프로시저 코드를 생성, 전개, 실행하도록 하겠다. 원한다면, DB2 명령행 프로세서를 사용하여 똑 같은 SQL 저장 프로시저를 개발 및 전개할 수 있다. 또한, XML 기반 저장 프로시저도 자바로 작성할 수 있다.
예를 들어, 특정 상태를 가진 클라이언트의 이름과 주 이메일 주소를 가져오는 저장 프로시저를 작성한다. 이 프로시저는 매우 단순하지만, 빌트인 마법사를 사용하여 XML 데이터를 쿼리 및 리턴하는 SQL 기반 프로시저를 생성하는 방법을 이해하는데 도움이 된다.
이 프로시저를 생성하려면, 다음 단계를 따른다.
- 워크스페이스를 준비한다.
- 프로시저의 내용을 지정한다.
- 프로시저를 전개 및 테스트 한다.
각 항목을 자세히 살펴보도록 하자.
워크스페이스 준비
저장 프로시저는 Data 프로젝트의 일부로 정의된다. 아직 정의하지 않았다면, Data 퍼스펙티브를 열고, 데이터베이스를 연결하고, Data 프로젝트를 만든다. 자세한 내용은 앞서 워크스페이스 준비 섹션을 참조하라.
프로시저 생성
우리의 SQL 기반 저장 프로시저는 하나의 SQL/XML 문을 호출하여 콜러(caller)의 인풋에 기반한 "clients" 테이블을 쿼리한다. 이 프로시저는 SQL VARCHAR 컬럼(클라이언트의 이름)과 XML 컬럼(클라이언트의 이메일)을 포함하고 있는 하나의 결과 세트를 리턴한다. 쿼리는 다음과 같다.
Listing 18. SQL/XML 쿼리 샘플
SELECT name, xmlquery('$c/Client/email[1]'
passing contactinfo as "c")
from clients where status = ?
|
XML 데이터에 액세스 하는 SQL 저장 프로시저를 구현하는 과정은 비 XML 데이터에 액세스 하는 SQL 프로시저를 구현하는 것과 다르지 않다. 다음과 같이 한다.
-
새로운 저장 프로시저를 정의한다. 새로운 Data 프로젝트를 확장하고, Stored Procedures를 하이라이팅 하고, 마우스 오른쪽을 클릭한다. New > Stored Procedure를 선택한다. 프롬프트를 따라서 프로젝트 이름을 확인하고 저장 프로시저 이름을 정한다. 기본 언어 유형을 SQL로 유지한다.
-
SQL 문을 지정한다. 프롬프트가 뜨면, 쿼리 문에 직접 타이핑 하거나, 마법사를 사용하여 생성한다. 다음 단계는 후자의 방법이다.
-
Create SQL을 클릭한다.
- 기본 문 유형
(SELECT)와 개발 프로세스(마법사를 통한 문 생성)를 수락한다.
- 문의 대상으로 clients 테이블을 선택한다.
- Columns 탭 밑에, 최종 결과 세트에 두 개의 컬럼을 포함시킨다. names를 선택하고, Add > Function > Next를 선택한다. 다음 창에서, 함수 카테고리를 XML로, 함수 시그너처를 XMLQuery로 지정한다. Finish.를 클릭한다.
- Conditions 탭 밑에, SQL
WHERE 문을 만든다. 컬럼으로 clients.status를, 연산자로 등호 부호 ("=")를, 값으로 :input을 지정한다.
- 결과 SQL 문을 수정하여 "contactinfo" 컬럼에 있는 첫 번째 이메일 주소를 검색하는 경로 식을 포함시킨다. 특히, XMLQUERY 행을 수정한다:
xmlquery('$c/Client/email[1]' passing contactinfo as "c")
- 쿼리를 파싱하여 신택스 에러(syntax errors)가 없는지를 확인한다.
-
전개(deployment) 정보를 지정한다. 특히, Debugging가능 옵션을 선택하는 것이 유용하다.
-
생성된 SQL 코드를 검사한다.
Show SQL.을 클릭한다. (Figure 2)
-
저장 프로시저를 완료한다.
Finish를 클릭한다.
그림 2. XML 데이터가 포함된 SQL 저장 프로시저에 대한 코드 샘플
프로시저 전개 및 테스트
프로시저가 생성되면, 이를 전개 및 테스트 한다. 다음 단계를 따른다.
-
프로시저를 전개한다. Data 프로젝트에 프로시저를 배치하고, 마우스 오른쪽을 클릭하고 Deploy를 선택한다. 기본 사항을 수락하고 Finish를 클릭한다. 우측 하단 코너에 Data Output 패인에서는 프로시저가 성공적으로 전개되었다고 나타난다.
-
프로시저를 실행한다. Data 프로젝트에 프로시저를 배치하고 마우스 오른쪽을 클릭하고 Run을 선택한다. 프롬프트가 뜨면, 고객 상태에 대한 인풋 값을 지정한다. ("Gold" 또는 "Silver") OK를 클릭하고, Data Output 패인에서 저장 프로시저의 결과를 본다.
Developer Workbench 밖에서 저장 프로시저를 호출할 수 있다. 프로시저의 이름을 "getInfo"로 했다면, DB2 명령행 프로세서를 호출하고, 데이터베이스로 연결하여 다음 문을 실행한다.
Listing 19. 저장 프로시저 호출하기
요약
DB2 XML 데이터로 작동하는 자바 애플리케이션을 작성할 때, 익숙한 JDBC 코드를 사용하여 쿼리를 실행하고 결과를 처리한다. IBM은 Eclipse 기반의 Developer Workbench를 제공하여 DB2상의 코딩, 테스트, 디버깅에 활용할 수 있도록 한다. 데이터베이스의 내용을 확인하고, XML과 비 XML 데이터에 액세스 하는 저장 프로시저를 작성하고, XML 데이터에 액세스 하는 XQuery를 작성하고, XML과 비 XML 데이터에 액세스 하는 SQL/XML 문을 작성하는 마법사가 이 워크벤치에 포함되어 있다.
감사의 말
이 글에 도움을 주신 Don Chamberlin, Grant Hutchison, Brian Payton에게 감사의 말을 전한다.
기사의 원문보기
참고자료 교육
-
DB2 Viper 웹 사이트: DB2의 XML support에 관해 배우기
-
"What's new in DB2 Viper: XML to the Core" (developerWorks, 2006년 2월)
-
"DB2 Viper 시작하기 (한글)" (한국 developerWorks, 2006년 7월): ML 데이터의 저장, 관리, 검색에 대한 새로운 기능을 제공한다.
-
"SQL을 이용한 DB2 XML 데이터 쿼리 (한글)" (한국 developerWorks, 2006년 12월): SQL과 SQL/XML을 사용하여 XML 칼럼에 저장된 데이터를 쿼리하는 방법을 배워본다.
-
"XQuery를 이용한 DB2 XML 데이터 쿼리 (한글)" (한국 developerWorks, 2007년 1월): XQuery를 사용하여 XML 칼럼에 저장된 데이터를 쿼리하는 방법을 배워본다.
-
"XQuery from the Experts," (Howard Katz, et. al., Addison-Wesley, 2003): XQuery 언어에 대해 배워보기. book excerpt
-
"Integration of SQL and XQuery in DB2" (forthcoming IBM Systems Journal issue)
-
"Firing up the Hybrid Engine" (DB2 Magazine, 2005년 3분기)
-
System RX: One Part Relational, One Part XML (SIGMOD 2005 Conference)
-
"Native XML Support in DB2 Universal Database"
-
"Managing XML for Maximum Return," (IBM White Paper, 2005년 11월)
-
한국 developerWorks Information Management 존: DB2의 기술 문서 찾기, 기술자료, 교육, 다운로드, 제품정보 등을 볼 수 있다.
-
developerWorks 기술 이벤트 및 웹캐스트.
-
"IBM Systems Journal: Celebrating 10 Years of XML"
-
DB2 Express-C 배워보기
제품 및 기술 얻기
토론
필자소개  | |  |
C. M. Saracco는 IBM Silicon Valley Laboratory의 DB2 XML 분야에서 일하고 있다. 데이터베이스 관리, XML, 웹 애플리케이션 개발을 수행하고 있다. |
기사에 대한 평가
|  |