기능 요구사항 |
웹사이트 관리자가 사용자(회원)의 개인정보가 포함된 데이터를 조회할 때 해당 요청의 SQL쿼리에 대한 쿼리로그를 DB에 저장하여 통계를 내는 기능을 구현하라 |
기능 구현방안 |
쿼리로그를 추출할 요청이 들어오면 응답하기 이전(혹은 이후)에 먼저 로그를 저장하는 방식으로 구현하고자 함 * 구현기술 방법* (1) Spring Interceptor (2) Spring AOP (@Before / @After) (3) Mybatis Interceptor |
.
.
.
(1)
Mybatis Interceptor를 사용하려면
DataBase(mybatis)연결을 위한 Spring JDBC 설정으로 xml 파일에
dataSource와 sqlSession bean객체를 생성하도록 하자!
root-context.xml
<-- dataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="" />
<property name="url" value="" />
<property name="username" value="" />
<property name="password" value="" />
</bean>
<-- SqlSession -->
<bean id="sqlSession" class="org.mybatis.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="${cofig.xml경로}" />
<property name="mapperLocation" value="${mapper.xml경로}" />
</bean>
<-- SqlSessionTemplate -->
<bean id="sqlSessionTemplate class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSession" />
</bean>
빨간영역은 본인이 생성한 config.xml과 mapper.xml의 경로를 각각 적는다
↓ 아래 참고 ↓
.
.
.
(2)
이제 Mybatis의 Interceptor plugin을 설정해보자!
mapper-config.xml
<?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>
<plugins>
<plugin interceptor="com.sym.log.cnt.web.LogInterceptor"></plugin>
</plugins>
</configuration>
Mybatis Interceptor는 mapper-config.xml의 <configuration>안에 명시해고,
interceptor=""의 내부값으론 인터셉터 동작을 구현할 클래스의 패키지루트를 적어주자!
.
.
.
(3)
인터셉터의 동작을 구현할 클래스(LogInterceptor.java)는 다음과 같이 구성한다.
▶ org.apache.ibatis.plugin.Interceptor 인터페이스 implements하기
(※ import 할때 패키지경로 주의!! )
▶ @Intercepts 어노테이션 명시
(@Signature(type, method, args)입력)
▶ @Override되는 메소드 중 intercept()메소드의 내부에 동작 구현
LogInterceptor.java
package com.sym.log.cnt.web;
import java.util.Properties;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
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.ibatis.session.RowBounds;
@Intercepts(@Signature(
type=Executor.class,
method="query",
args= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
public class LogInterceptor implements Interceptor{
@Override
public Object intercept(Invocation invocation) throws Throwable {
//QueryId
String queryID = ((MappedStatement)invocation.getArgs()[0]).getId();
System.err.println("Query ID : \n" + queryID);
//Query Parameter
Object param = invocation.getArgs()[1];
//Query String
String queryString = ((MappedStatement)invocation.getArgs()[0]).getBoundSql(param).getSql();
System.err.println("Query String : \n" + queryString);
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
나의 경우엔 실행되는 쿼리의 QueryId를 찾아 DB에 저장하는 것이 목적이였기때문에
이후 db저장하는 동작문을 같이 추가했었다!
.
.
.
(4)
*참고*
아래의 Executor인터페이스는
인터페이스 동작을 구현하는 클래스에서 @Signature에 요소로
뭐를 추가해야할 지 파악하는데 도움이 될 수 있다!!
Executor인터페이스
/*
* Copyright 2009-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor;
import java.sql.SQLException;
import java.util.List;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
/**
* @author Clinton Begin
*/
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
'Spring' 카테고리의 다른 글
[Spring] @PathVariable 어노테이션 (0) | 2020.06.16 |
---|