본문 바로가기
ORM

myBatis plugin interceptor sql 바인딩 로깅 구현

by 수앙 2015. 9. 1.

myBatis 로깅을 좀 더 가독성을 높이기 위한 방법을 고민하다가 아래와 같이

myBatis 플러그인 인터셉터를 이용한 방법을 사용해보았다.

 

플러그인 등록 mybatis-config.xml

<plugins>
    <plugin interceptor="<path>.SqlLogInterceptor" />
</plugins>

 

플러그인 구현 SqlLogInterceptor.java

import java.util.Collection;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Intercepts({
    @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }),
    @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }),
    @Signature(type = Executor.class, method = "queryCursor", args = { MappedStatement.class, Object.class, RowBounds.class }),
    @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
})
public class MyBatisLogInterceptor implements Interceptor {

	@Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (log.isDebugEnabled()) {
            long stime = System.currentTimeMillis();

            Object proceed = invocation.proceed();

            long etime = System.currentTimeMillis();

            MappedStatement ms = null;
            Object parameter = null;
            BoundSql boundSql = null;

            for (Object arg : invocation.getArgs()) {
                if (arg instanceof MappedStatement) {
                    ms = (MappedStatement) arg;
                } else if (arg instanceof RowBounds
                        || arg instanceof ResultHandler
                        || arg instanceof CacheKey) {
                    // skip
                } else if (arg instanceof BoundSql) {
                    boundSql = (BoundSql) arg;
                } else if (arg != null) {
                    parameter = arg;
                }
            }
            
            Configuration configuration = ms.getConfiguration();
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (boundSql == null) {
                boundSql = ms.getBoundSql(parameter);
            }

            // remove empty line
            StringBuilder sql = new StringBuilder(boundSql.getSql().replaceAll("(?m)^[ \t]*\r?\n", ""));
            
            // @see: DefaultParameterHandler 참조
            if (parameter != null) {
            	MetaObject metaObject = configuration.newMetaObject(parameter);
            	
                for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
                    String property = parameterMapping.getProperty();
                    Object value = "<NotFound>";

                    if (boundSql.hasAdditionalParameter(property)) {
                        value = boundSql.getAdditionalParameter(property);
                    } else if (parameter == null) {
                    	value = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameter.getClass())) {
                    	value = parameter;
                    } else {
                    	if (metaObject == null) {
                            metaObject = configuration.newMetaObject(parameter);
                          }
                          value = metaObject.getValue(property);
                    }
                    
                    int start = sql.indexOf("?");
                    int end = start + 1;

                    if (value == null) {
                        sql.replace(start, end, "NULL");
                    } else if (value instanceof String) {
                        sql.replace(start, end, "'" + value.toString() + "'");
                    } else {
                        sql.replace(start, end, value.toString());
                    }
                }
            }
            
            if (proceed != null && proceed instanceof Collection) {
                int count = ((Collection<?>) proceed).size();
                sql.append(System.lineSeparator()).append(String.format(" -> (took %d msec, row count %d)", (etime - stime), count));
            } else {
                sql.append(System.lineSeparator()).append(String.format(" -> (took %d msec)", (etime - stime)));
            }
            sql.insert(0, System.lineSeparator() + "[mybatis query]" + System.lineSeparator());

            log.debug("{}", sql.toString());
            return proceed;
        }

        return invocation.proceed();
    }
	
}

 

끝.

'ORM' 카테고리의 다른 글

myBatis Java에서 Consumer 이용한 페이징 쿼리 처리  (0) 2022.09.17
myBatis list/array parameter type 처리  (0) 2018.10.23
Java + MyBatis  (0) 2011.09.21

댓글