티스토리 뷰

쿼리 로깅시 사용하면 편하다. 일일이 replace 할 필요없이 간단히 사용할 수 있다.

j-loggable.zip

참고사이트 : http://www.ibm.com/developerworks/kr/library/j-loggable.html


사용예

String sql = "select foo, bar from foobar where foo < ? and bar = ?";
long fooValue = 99;
String barValue = "christmas";

Connection conn = dataSource.getConnection();
PreparedStatement pstmt;

if(logEnabled) // use a switch to toggle logging.
pstmt = new LoggableStatement(conn,sql);
else
pstmt = conn.prepareStatement(sql);

pstmt.setLong(1,fooValue);
pstmt.setString(2,barValue);

if(logEnabled)
System.out.println("Executing query: "+
((LoggableStatement)pstmt).getQueryString());

ResultSet rs = pstmt.executeQuery();
필요할 때만 로그를 남길수 있도록 log4j 와 함께 사용하면 괜찮을 듯 하다.


* 위의 ibm 링크가 깨졌다.

JDBC 쿼리 로깅을 쉽게

Jens Wyke│IBM

JDBC java.sql.PreparedStatement 인터페이스로 간단히 확장함으로서 쿼리 로깅의 에러를 줄일 수 있다. 코드도 더 깔끔해진다. IBM e-비즈니스 컨설턴트인 Jens Wyke가 래핑 기술을 설명한다.

대부분의 경우, JDBC PreparedStatement는 데이터베이스 쿼리를 쉽게 수행할 수 있도록 한다. 또한 전체 애플리케이션 퍼포먼스에도 뚜렷한 향상을 보인다. 하지만 PreparedStatement 인터페이스는 쿼리 문장을 로깅할 때 부족한점이 있다. PreparedStatement의 강점이 다양성에 있는 만큼, 좋은 로깅 엔트리 라면 데이터베이스로 보내지는 SQL이 실제 매개변수 값을 대체한 매개변수 플레이스홀더를 어떻게 다루는지를 설명해야 한다.

이 글에서는 쿼리 로깅을 위해 JDBC PreparedStatement 인터페이스를 확장하는 방법을 배운다. LoggableStatement 클래스는 PreparedStatement 인터페이스를 구현하지만 로깅에 적합한 포맷으로 쿼리 스트링을 얻기위한 메소드를 추가한다. LoggableStatement 클래스를 사용하면 로깅 코드에서 에러를 줄일 수 있고 관리가 쉬운 코드를 만들어 낼 수 있다.

일반적인 로깅 솔루션

Listing 1은 데이터베이스 쿼리를 만들 때 PreparedStatement의 전형적인 사용 예제를 보여주고 있다. 예제로 SQL query SELECT를 사용하겠지만 DELETE, UPDATE, INSERT 같은 기타 SQL 문장들도 적용할 것이다.

Listing 1. 일반적인 SQL 데이터베이스 쿼리

String sql = "select foo, bar from foobar where foo < ? and bar = ?";
String fooValue = new Long(99);
String barValue = "christmas";

Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setLong(1,fooValue);
pstmt.setString(2,barValue);

ResultSet rs = pstmt.executeQuery();

// parse result...
Listing 1에서 쿼리를 위한 좋은 로그 엔트리라면 다음과 같을 것이다:

System.out.println("Executing query: select foo, bar from foobar where foo
< "+fooValue+" and bar = '+barValue+"'")
보다 나은 접근방식으로는 메소드를 만드는 것이다. replaceFirstQuestionMark를 호출해본다. 이것은 쿼리 스트링을 가지고 물음표를 매개변수 값으로 대체한다. (Listing 2). 메소드를 사용하면 SQL 문을 설명하기 위한 중복 스트링을 만들 필요가 없다.

Listing 2. 스트링 대체를 위해 replaceFirstQuestionMark 사용하기

// listing 1 goes here

sql = replaceFirstQuestionMark(sql, fooValue);
sql = replaceFirstQuestionMark(sql, barValue);
System.out.println("Executing query: "+sql);
구현은 쉽지만 솔루션은 이상적인 것은 아니다. SQL 템플릿이 변경될 때 마다 로깅 코드도 변경된다는 것이 문제이다. 실수하기 쉬운 부분이다. 쿼리는 바뀌지만 로깅 코드를 업데이트 하는 것을 잊을 수 있고 데이터베이스에 보내진 쿼리와 맞지 않는 로깅 엔트리로 끝낼 수 있다. 디버깅은 악몽이다!

각각의 매개변수(fooValue와 barValue)를 사용하도록 하는 디자인이 필요하다. 실제 값으로 대체된 매개변수 플레이스홀더가 있는 쿼리 스트링이 마음에 든다. java.sql.PreparedStatement는 그와 같은 메소드가 없기 때문에 우리가 그것을 구현해야 한다.

맞춤 솔루션

우리가 구현한 PreparedStatement는 JDBC 드라이버가 제공한 "실제 문장" 주위에서 래퍼로 작동한다. 래퍼 문장은 모든 메소드 호출(예를 들어, setLong(int, long)과 setString(int,String))을 "실제 문장"으로 전달한다. 이 전에 관련된 매개변수 값을 저장하여 로깅 아웃풋을 만드는데 사용될 수 있도록 한다.

Listing 3은 LoggableStatement 클래스가 java.sql.PreparedStatement를 구현하는 방법을 보여주고 있다. JDBC 커넥션과 인풋으로서 SQL 템플릿을 사용하여 만들어지는 방법을 보여준다.

Listing 3. java.sql.PreparedStatement를 구현하는 LoggableStatement

public class LoggableStatement implements java.sql.PreparedStatement {

// used for storing parameter values needed
// for producing log
private ArrayList parameterValues;

// the query string with question marks as
// parameter placeholders
private String sqlTemplate;

// a statement created from a real database
// connection
private PreparedStatement wrappedStatement;


public LoggableStatement(Connection connection, String sql)
throws SQLException {
// use connection to make a prepared statement
wrappedStatement = connection.prepareStatement(sql);
sqlTemplate = sql;
parameterValues = new ArrayList();
}
}
LoggableStatement 작동 방법

Listing 4는 LoggableStatement가 saveQueryParamValue() 메소드로 호출을 추가하고 setLong과 setString 메소드를 위한 "실제 문장"에 대한 상응 메소드를 호출하는 것을 보여주고 있다. saveQueryParamValue() 호출은 비슷한 방식으로 매개변수 설정(예를 들어, setChar, setLong, setRef, setObj)에 사용된 모든 메소드에 추가된다. executeQuery 메소드가 saveQueryParamValue()를 호출하지 않고 어떻게 래핑되는지를 보여주고 있다. 이것은 "매개변수 설정" 메소드가 아니기 때문이다.

Listing 4. LoggableStatement 메소드

public void setLong(int parameterIndex, long x)
throws java.sql.SQLException {
wrappedStatement.setLong(parameterIndex, x);
saveQueryParamValue(parameterIndex, new Long(x));
}

public void setString(int parameterIndex, String x)
throws java.sql.SQLException {
wrappedStatement.setString(parameterIndex, x);
saveQueryParamValue(parameterIndex, x);
}

public ResultSet executeQuery() throws java.sql.SQLException {
return wrappedStatement.executeQuery();
}
saveQueryParamValue() 메소드는 Listing 5에서 볼 수 있다. 각각의 매개변수 값을 String 표시로 변환하면서 getQueryString 메소드에 의해 나중에 사용될 수 있도록 이를 저장한다. 기본적으로 하나의 객체는 toString 메소드를 사용하는 String으로 변환되겠지만 객체가 String 이나 Date라면 싱글 쿼트 부호 ('')가 붙는다. getQueryString() 메소드로는 로그에서 대부분의 쿼리를 복사하여 변경하지 않고 대화형 SQL 프로세서에 붙일 수 있다. 테스팅과 디버깅에 쓰인다. 필요한 경우 메소드를 수정하여 다른 클래스의 매개변수 값을 변환할 수 있다.

Listing 5. saveQueryParamValue() 메소드

private void saveQueryParamValue(int position, Object obj) {
String strValue;
if (obj instanceof String || obj instanceof Date) {
// if we have a String, include '' in the saved value
strValue = "'" + obj + "'";
} else {
if (obj == null) {
// convert null to the string null
strValue = "null";
} else {
// unknown object (includes all Numbers), just call toString
strValue = obj.toString();
}
}
// if we are setting a position larger than current size of
// parameterValues, first make it larger
while (position >= parameterValues.size()) {
parameterValues.add(null);
}
// save the parameter
parameterValues.set(position, strValue);
}
모든 매개변수들이 표준 메소드를 사용하여 설정되면 LoggableStatement에 getQueryString() 메소드를 호출하여 쿼리 스트링을 얻는다. 모든 물음표는 실제 매개변수 값으로 대체된다.

LoggableStatement 사용하기

Listing 6은 Listings 1과 2의 코드가 LoggableStatement를 사용하기 위해 어떻게 변경되었는지를 보여주고 있다. LoggableStatement를 우리의 애플리케이션 코드에 도입하면 중복된 매개변수 문제를 해결할 수 있다. SQL 템플릿이 변경되면, PreparedStatement에 대한 매개변수 설정 호출을 업데이트하면 된다. 변경사항은 로깅 코드를 업데이트 하지 않고도 로깅 아웃풋에 반영된다.

Listing 6. LoggableStatement 작동

String sql = "select foo, bar from foobar where foo < ? and bar = ?";
long fooValue = 99;
String barValue = "christmas";

Connection conn = dataSource.getConnection();
PreparedStatement pstmt;

if(logEnabled) // use a switch to toggle logging.
pstmt = new LoggableStatement(conn,sql);
else
pstmt = conn.prepareStatement(sql);

pstmt.setLong(1,fooValue);
pstmt.setString(2,barValue);

if(logEnabled)
System.out.println("Executing query: "+
((LoggableStatement)pstmt).getQueryString());

ResultSet rs = pstmt.executeQuery();
참고자료

• developerWorks worldwide 사이트에서 이 기사에 관한 영어원문.
• 소스 코드 다운로드: LoggableStatement 클래스.
• Roman Vichr JDBC tips (developerWorks, October 2002).
• "What's new in JDBC 3.0" (developerWorks,
• Java platform, Standard Edition, JDBC 3.0 API 스팩 다운로드 : java.sun.com.
• David Gallardo 튜토리얼: "Java design patterns 101" (developerWorks, January 2002).
• Paul Monday 튜토리얼 "Java design patterns 201" (developerWorks, April 2002).
• Vince Huston Design Patterns site.
• Brian Goetz "Performance management -- do you have a plan?" (developerWorks, March 2003).
• developerWorks 자바 튜토리얼 페이지.
• developerWorks Java technology zone.

제공 : DB포탈사이트 DBguide.net

출처명 : 한국IBM

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함