mybatis对sql语句的监控

一、配置文件

<plugins>
        <!-- 配置记录执行的sql语句、参数及执行时间的插件 -->
        <plugin interceptor="com.mapuni.mybatis.interceptor.SqlLogInterceptor" /> 

</plugins>

二、实现监控的代码

package com.mapuni.mybatis.interceptor;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
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.mapping.ParameterMode;
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.reflection.MetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.log4j.Logger;

import com.mapuni.cache.Config;
import com.mapuni.security.user.MapuniUserDetails;
import com.mapuni.utils.SqlLogsSaveThread;
import com.mapuni.utils.StringUtil;


/**
 * 描述:拦截mybatis的,记录当前执行的sql语句和参数
 * 
 * @author lvxl 2018年3月23日 上午11:05:21
 */
/*@Intercepts({//ParameterHandler 、ResultSetHandler 、StatementHandler 、Executor 
    @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
    @Signature(type = StatementHandler.class, method = "queryCursor", args = {Statement.class}),
    @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
    @Signature(type = StatementHandler.class, method = "batch", args = { Statement.class })
    })
public class SqlLogInterceptor implements Interceptor {
    private static Logger logger=Logger.getLogger(SqlLogInterceptor.class);
    private static boolean isPrint=false;
    private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    static{
        if("1".equals(Config.getConfig("sqlIsPrint"))){isPrint=true;}else{isPrint=false;}
    }
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("###########################进入了日志拦截器##################");
        Object target = invocation.getTarget();//得到拦截的对象,其实就是StatementHandler
        long startTime = System.currentTimeMillis();//获取当前的开始时间戳
        String startTimeStr=sdf.format(new Date());//记录当前时间
        StatementHandler statementHandler = (StatementHandler) target;
        try {
            return invocation.proceed();
        } finally {
            long endTime = System.currentTimeMillis();
            long exeTime = endTime - startTime;//sql的执行的时间
            try{
                //获取绑定的SQL对象
                BoundSql boundSql = statementHandler.getBoundSql();
                //得到需要执行的sql语句,并进行格式
                String sql = boundSql.getSql();
                sql=formatSql(sql);
                if(sql.startsWith("insert into xt_rz_sql(")){//此时是插入的sql日志内容,不用去处理
                    //什么都不用做
                }else{
                    //得到默认的参数处理器
                    DefaultParameterHandler dph=(DefaultParameterHandler)statementHandler.getParameterHandler();
                    //利用反射机制,从DefaultParameterHandler获取Configuration和TypeHandlerRegistry
                    Field configurationField=dph.getClass().getDeclaredField("configuration");
                    Field typeHandlerRegistryField=dph.getClass().getDeclaredField("typeHandlerRegistry");
                    configurationField.setAccessible(true);//设置私有属性可访问
                    typeHandlerRegistryField.setAccessible(true);//设置私有属性可访问
                    Configuration configuration=(Configuration) configurationField.get(dph);
                    TypeHandlerRegistry typeHandlerRegistry=(TypeHandlerRegistry) typeHandlerRegistryField.get(dph);
                    //sql的参数对象
                    Object parameterObject = boundSql.getParameterObject();
                    //需要绑定的参数映射对象
                    List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();
                    //处理sql的参数,该部分参考的是DefaultParameterHandler中setParameters方法中的实现
                    StringBuffer args=new StringBuffer();
                    if(parameterMappingList!=null && parameterMappingList.size()>0){
                        for(ParameterMapping parameterMapping:parameterMappingList){
                            //如果该参数不是输出参数,则进行处理
                            if (parameterMapping.getMode() != ParameterMode.OUT) {
                                Object value;
                                //参数的名字,属性
                                String propertyName = parameterMapping.getProperty();
                                //先从附加的,主要是list、array等的处理
                                if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                                    value = boundSql.getAdditionalParameter(propertyName);
                                } else if (parameterObject == null) {
                                    value = null;
                                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                                    //typeHandlerRegistry注册了某个类的处理
                                    value = parameterObject;
                                } else {
                                    //默认的MetaObject 的处理,根据参数获取值
                                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                                    value = metaObject.getValue(propertyName);
                                }
                                if(value!=null){
                                    if(value instanceof Date){
                                        value=sdf.format(value);//如果是日期,则格式化一下
                                    }
                                }
                                args.append(",").append(value);
                            }
                        }
                        args.deleteCharAt(0);//删除第一个逗号
                    }
                    //处理日志,将数据封装到map中
                    Map<String, String> sqlInfo=new HashMap<String,String>();
                    sqlInfo.put("pkid", StringUtil.getUUID_32());
                    sqlInfo.put("sql", sql);
                    sqlInfo.put("args", args.toString());
                    sqlInfo.put("startTime", startTimeStr);
                    sqlInfo.put("exeTime", String.valueOf(exeTime));
                    sqlInfo.put("userId", MapuniUserDetails.getCurrentUserId());
                    SqlLogsSaveThread.addSqlInfo(sqlInfo);//将SQL执行的相关信息,输入到日志记录线程中
                    if(isPrint){//根据配置,是否需要在控制台上输出sql的执行信息
                        System.out.println("sql:["+sql+"],参数:["+args+"],执行开始时间:["+startTimeStr+"],执行时间:["+exeTime+"ms],执行人:["+sqlInfo.get("userId")+"]");
                    }
                }
            }catch(Exception e){
                logger.error("记录SQL日志时出现异常:"+e.getMessage(),e);
            }
        }
    }
    
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
    *//**
     * 
     * 描述:将多行和多个空格的sql语句格式化为一行
     * @param sql 需要格式化的sql语句
     * @return 返回格式化后的sql语句
     *
     * lvxl
     * 2018年3月26日 下午3:52:25
     */
    /*
    private String formatSql(String sql) {
        // 输入sql字符串空判断
        if (sql == null || sql.length() == 0) {
            return "";
        }
        //格式sql 将回车换行制表符等替换成空,在将连续多个空格替换成1个空格,然后在去掉左右括号两边的空格,在去掉逗号左右两个的空格
        return sql.replaceAll("[\\t\\n\\x0B\\f\\r]", "").replaceAll(" +", " ")
                .replaceAll(" *\\( *", "(").replaceAll(" *\\) *", ")").replaceAll(" *, *", ",");

    }
}*/
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值