'mybatis'에 해당되는 글 9건

  1. 2011.09.06 mybatis dynamic query 조건에서 in 조건 사용하기
  2. 2011.08.29 mybatis interceptor 를 이용해서 쿼리로그를 디비에다 저장하기 2
  3. 2011.08.22 Oracle JDBC : "스트림이 이미 종료되었습니다" 혹은 "java.sql.SQLException: Stream has already been closed" 익셉션
  4. 2011.08.22 mybatis 에서 Oracle LONG, CLOB Select 시 에러날때 : getCLOB not implemented for class oracle.jdbc.driver.XXX

mybatis dynamic query 조건에서 in 조건 사용하기


dynamic query 에서 조건 태그에 들어가는 비교 연산자 뭐 흔히 사용하는 == ,  != ,  > ,  <  , <=  뭐 이런것들이 있다.

이런 기본적인것 이외에도 in 이라는 연산자가 있는데

이것을 이용하면 기존 or 로 비교구문을 쭉 이어서 썼던것을 단순하게 처리할수 있을것 같았다.

그걸 사용해 볼려고 User Guide를 보니 하니 뭔놈에 설명이 하나도 안되있고

검색어로 넣을 만한 단어거 너무 일반적이라서 구글에게 물어보기도 힘들었다.

처음에는 query 에서 쓰는것 처럼 <if test="param1 in ('a', 'b'. 'c', 'd')">쿼리</if>  요런식으로 해 보니까 에러는 안나지만 정상적으로 비교되지 않았다.



그러다가 문득

    쿼리

요렇게 ( ) 을 { } 으로 바꿔서 해보았다.

잘된다 -_-;;


내가 못찾아서 그런가  mybatis 는 설명이 너무 허약한것 같다.

mybatis interceptor 를 이용해서 쿼리로그를 디비에다 저장하기

시스템에서 실행되는 update, insert 등의 쿼리에 대해서 실행된 쿼리를 로그쌓기 전용 데이터베이스에 넣어 주세요~ 라는 요구사항을 받았다.

 뭐 Log4j 같은 것으로 DB로 로깅하는걸 문득 떠올려 보긴 했지만 고건 쫌 안될것 같고..

생각하다 보니 문득 mybatis 설정파일 설명에서 어렴풋이 보았던 plugin 태그가 생각났다. 고걸 이용하는 방법이다. 

plugin 이란게 보니까 spring 의 interceptor 랑 비스무리한것 같다. 여러가지 경우가 미리 정의되 있고 특정 경우를 처리할 Interceptor 를 구현하면 되는것 같다.

이것저것 설명하는 것 보다 일단 실제 설정파일의 설정부분과 소스부분을 보자.

mybatis 설정 xml 중 plugin 설정 부분
<plugins>
	<plugin interceptor="com.tistory.stove99.UpdateInterceptor"/>
</plugins>


인터셉터(update 성 쿼리가 실행될때 인터셉트!)
package com.tistory.stove99;

import java.sql.Statement;
import java.util.Properties;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;

@Intercepts({@Signature(type=StatementHandler.class, method="update", args={Statement.class})})
public class UpdateInterceptor implements Interceptor{
	
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler handler = (StatementHandler)invocation.getTarget();
		
		// 쿼리
		String sql = handler.getBoundSql().getSql();
		
		// 쿼리실행시 맵핑되는 파라메터들
		String param = handler.getParameterHandler().getParameterObject()!=null ? 
                             handler.getParameterHandler().getParameterObject().toString() : "";
		
		// DB에다 로그 insert
		/////////////////
		////////////////
		
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
	}
}

org.apache.ibatis.plugin.Interceptor 인터페이스를 implements 해서 intercept, plugin, setProperties 메소드를 구현해 주면 된다.
그리고 상단에 @Signature 어노테이션은 아마도 인터셉트할 시점에 대한 설정인것 같다. 


@Signature 설정시 type으로 설정할수 있는것은

Executor.class, ParameterHandler.class, ResultSetHandler.class, StatementHandler.class 요렇게

4가지가 있고 어떤것인가는 각자의 필에 따라~~ 대충 이해하면 된다.




method 에 설정할 수 있는것은 예제소스에서 type으로 설정한 StatementHandler.class 의 소스를 보면
public interface StatementHandler {

  Statement prepare(Connection connection)
      throws SQLException;

  void parameterize(Statement statement)
      throws SQLException;

  void batch(Statement statement)
      throws SQLException;

  int update(Statement statement)
      throws SQLException;

  List query(Statement statement, ResultHandler resultHandler)
      throws SQLException;

  BoundSql getBoundSql();

  ParameterHandler getParameterHandler();

}

요런 인터페스이다. 짐작할 수 있겠지만 @Signature 어노테이션에서 method와 args 에 설정한 것은 요 인터페이스에 있는 메소드중 하나에 대해서
정의를 한것이다.


대충 눈치로 이해를 해 보자면

@Signature(type=StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class})

요래 설정을 하면 실행되는 쿼리들중 select 쿼리가 실행됬을때 고것을 인터셉트 할것이다.




다른 type 들에 어떤 메소드들이 있는지는 소스를 보면 된다 -_-

mybatis API 문서를 봐도 될것 같은데 나의 검색범위 내에서는 API 문서를 못 찾겠다.
분명 어딘가에 있을건데 이상하게 잘 검색이 안되네 -_-;;

공식 문서는 아닌것 같고 아쉬운데로 요기라도~~



※ 첨부파일로 첨부된 User Guide 17 페이지를 보면 자세한 설명은 없고 쥐똥만큼은 설명이 되 있다~ 참고할 사람은 참고하길~~~

MyBatis-3-User-Guide.pdf

 

Oracle JDBC : "스트림이 이미 종료되었습니다" 혹은 "java.sql.SQLException: Stream has already been closed" 익셉션

Oracle JDBC 로 CLOB 나 LONG 같은, 값을 Stream 형태로 읽어야 하는 타입들을 Select 할때 발생하는 Exception 이다.

나같은 경우는 mybatis로 select 할때 쿼리문과 쿼리결과를 Log4j 로그로 찍었는데 시쭈구리하게도  logger 에서 ResultSet을 로그로 찍었기 때문에 나는 에러였다.

찍혀 있는 로그를 가만히 살펴보면 쿼리문이랑 ResultSet 결과가 먼저 쭉 찍힌뒤 그 뒤로 익셉션이 쭉 발생하고 있었다.
고말인 즉슨, 로거에서 찍을때는 정상적으로 값을 가져왔다는 말이된다.



가만 입다물고 생각해보면, 로거에서 ResultSet을 찍을려고 값을 가져오면서 Stream이 닫혀 버렸기 때문에 나는 에러 같았다.

기존 log4j.xml 설정은
<logger name="java.sql">
	<level value="debug" />
</logger>
 요렇게 되 있어서 java.sql 패키지에 포함된 모든클래스에서 발생하는 로그들을 다 찍게 되 있었다.




고것을
<logger name="java.sql.Connection">
	<level value="debug" />
</logger>

<logger name="java.sql.PreparedStatement">
	<level value="debug" />
</logger>
요래 바꿔서 ResultSet 에 대한 로그를 안찍게 바꾸니 해결되었다. 로그가 아예 필요없다면 다 지워도 되고~
나는 어떤 쿼리가 실행됬는가 까지는 꼭 알고 싶어서..



이것도 왠지 버그인것 같은 느낌이 문득 들지만 언젠가는 해결될것이라고 믿는다. 아니면 내가 잘못 쓰고 있는건가???? 

테스트 라이브러리 버전
mybatis :  3.0.6-SNAPSHOT.jar
oracle jdbc driver : ojdbc6.jar (11.2.0.2.0) 

mybatis 에서 Oracle LONG, CLOB Select 시 에러날때 : getCLOB not implemented for class oracle.jdbc.driver.XXX

MySQL 에서는 어떤지 잘 모르겠지만 아무튼 오라클에서는 Long Type이나 CLOB Type 컬럼을 select 할때 getCLOB() 가 구현되지 않았다면서 에러가 난다.

mybatis 에서 패치를 해줘야 되나 오라클에서 패치를 해 줘야 하나 쫌 거시기 하지만 아무튼 지금! 현재는 Long 을 가져오질 못한다 -_-

누군가는 패치를 해주긋지~

현재 라이브러리 버전은 요렇다.!
mybatis :  3.0.6-SNAPSHOT.jar
oracle jdbc driver : ojdbc6.jar (11.2.0.2.0)

지금 당장 하기는 해야 겠고 고쳐줄때 까지 기다릴수 없으니 해결하기 위해선 mybatis config 파일에서 LONG TYPE에 대해 TypeHandler 를 설정해 주면 된다.

mybatis config 파일
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
  "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
	<typeHandlers>
		<typeHandler handler="stove99.handler.CLOBHandler" javaType="String" jdbcType="LONGVARCHAR"/>
	</typeHandlers>
</configuration>
설정을 보면 대략 알겠지만 LONG 타입을 뜻하는 LONGVARCHAR jdbcType 에 대하여 처리할 핸들러를 설정해 주는 것이다.





TypeHandler 는 org.apache.ibatis.type.TypeHandler 인터페이스를 implements 해서 간단하게 메소드 3개를 구현하면 된다.
stove99.handler.CLOBHandler 의 소스는 다음과 같다.
package stove99.handler;

import java.io.StringReader;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

public class CLOBHandler implements TypeHandler{

	@Override
	// 파라메터 셋팅할때
	public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) 
           throws SQLException {
		String s = (String) parameter;
		StringReader reader = new StringReader(s);
		ps.setCharacterStream(i, reader, s.length());
	}

	@Override
	// Statement 로 SQL 호출해서 ResultSet 으로 컬럼값을 읽어올때
	public Object getResult(ResultSet rs, String columnName) throws SQLException {
		return rs.getString(columnName);
	}

	@Override
	// CallableStatement 로 SQL 호출해서 컬럼값 읽어올때
	public Object getResult(CallableStatement cs, int columnIndex) throws SQLException {
		return cs.getString(columnIndex);
	}
}

내 나름대로 이해하고 있는걸 적어놓은 것이니 미심쩍다는 사람들은 첨부파일 pdf 에서 14쪽을 참고하셈~

MyBatis-3.0.3-User-Guide.pdf

 
prev 1 2 next