본문 바로가기
개발/개발에 유용한 팁

Mybatis 쿼리 바인딩 로그 출력

by 카리3 2020. 8. 24.

1. Interceptor.java 설정

package secretary.common.util;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
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.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

/*
   @Intercepts(@Signature(
			type=Executor.class,
			method="query",
			args= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))

*/
@Intercepts({ @Signature(type = StatementHandler.class, method = "update", args = { Statement.class }),
		@Signature(type = StatementHandler.class, method = "query", args = { Statement.class, ResultHandler.class }) })
public class MybatisLogInterceptor implements Interceptor {

	private Logger log = LogManager.getLogger(getClass());

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		/*
		 * Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement)
		 * args[0]; Object param = args[1]; BoundSql boundSql = ms.getBoundSql(param);
		 * String query_name = invocation.getMethod().getName(); String query_id =
		 * ms.getId(); System.out.println("query_id:" + query_id); // if
		 * (query_id.contains("xml맵퍼의 로그출력을 원하는 쿼리id값")) {
		 * System.out.println("====================================");
		 * System.out.println("1:" + invocation.getMethod().getName());
		 * System.out.println("====================================");
		 * System.out.println("2:" + ms.getId());
		 * System.out.println("====================================");
		 * System.out.println("3:" + boundSql.getSql());
		 * System.out.println("====================================");
		 * System.out.println("4:" + param);
		 * System.out.println("====================================");
		 * System.out.println("5:" + ms.getId()); // }
		 */
		StatementHandler handler = (StatementHandler) invocation.getTarget();

		String sql = bindSql(handler); // SQL 추출

		System.out.println("=====================================================================");
		System.out.println("{sql} " + sql);
		System.out.println("=====================================================================");

		return invocation.proceed();
	}

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

	@Override
	public void setProperties(Properties properties) {
	}

	/**
	 * <pre>
	 * bindSql
	 *
	 * <pre>
	 *
	 * @param boundSql
	 * @param sql
	 * @param param
	 * @return
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 */
	@SuppressWarnings("rawtypes")
	private String bindSql(StatementHandler handler) throws NoSuchFieldException, IllegalAccessException {
		BoundSql boundSql = handler.getBoundSql();

		// 쿼리실행시 맵핑되는 파라미터를 구한다
		Object param = handler.getParameterHandler().getParameterObject();
		// 쿼리문을 가져온다(이 상태에서의 쿼리는 값이 들어갈 부분에 ?가 있다)
		String sql = boundSql.getSql();

		// 바인딩 파라미터가 없으면
		if (param == null) {
			sql = sql.replaceFirst("\\?", "''");
			return sql;
		}

		// 해당 파라미터의 클래스가 Integer, Long, Float, Double 클래스일 경우
		if (param instanceof Integer || param instanceof Long || param instanceof Float || param instanceof Double) {
			sql = sql.replaceFirst("\\?", param.toString());
		}
		// 해당 파라미터의 클래스가 String인 경우
		else if (param instanceof String) {
			sql = sql.replaceFirst("\\?", "'" + param + "'");
		}
		// 해당 파라미터의 클래스가 Map인 경우
		else if (param instanceof Map) {
			List<ParameterMapping> paramMapping = boundSql.getParameterMappings();
			for (ParameterMapping mapping : paramMapping) {
				String propValue = mapping.getProperty();
				Object value = ((Map) param).get(propValue);
				if (value == null) {
					continue;
				}

				if (value instanceof String) {
					sql = sql.replaceFirst("\\?", "'" + value + "'");
				} else {
					sql = sql.replaceFirst("\\?", value.toString());
				}
			}
		}
		// 해당 파라미터의 클래스가 사용자 정의 클래스인 경우
		else {
			List<ParameterMapping> paramMapping = boundSql.getParameterMappings();
			Class<? extends Object> paramClass = param.getClass();

			for (ParameterMapping mapping : paramMapping) {
				String propValue = mapping.getProperty();
				Field field = paramClass.getDeclaredField(propValue);
				field.setAccessible(true);
				Class<?> javaType = mapping.getJavaType();
				if (String.class == javaType) {
					sql = sql.replaceFirst("\\?", "'" + field.get(param) + "'");
				} else {
					sql = sql.replaceFirst("\\?", field.get(param).toString());
				}
			}
		}

		// return sql
		return sql;
	}
}

2. root-context.xml   plugins 설정

    <bean id="sqlSessionFactory" class="secretary.common.util.RefreshableSqlSessionFactoryBean">
        <property name="configLocation" value="classpath:/spring/appServlet/mybatis-config.xml"></property>
        <property name="dataSource" ref="dataSource"></property>
        <property name="mapperLocations" value="classpath:/query/*.xml"></property>
        <property name="plugins">
            <array>
                <bean class="secretary.common.util.MybatisLogInterceptor"/>
            </array>
        </property>
    </bean>

'개발 > 개발에 유용한 팁' 카테고리의 다른 글

클린코드  (0) 2021.07.21
Regular Expression  (0) 2021.03.07
MessageBundle DB로 전환  (0) 2020.08.23
Mysql vo 만들기  (0) 2020.08.15
톰캣 재시작 없이 Java 수정  (0) 2020.08.15