溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Mybatis攔截器打印sql問題怎么解決

發(fā)布時間:2023-03-31 11:37:27 來源:億速云 閱讀:80 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“Mybatis攔截器打印sql問題怎么解決”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

    1.log4j2配置修改

    關(guān)閉log4j2打印mybatis的sql配置,如果本來就沒開啟,那不需要。

    Mybatis攔截器打印sql問題怎么解決

    2.配置日志開關(guān)

    在springboot的yml文件配置變量logging.sql.enable=true,對攔截器做開關(guān)控制。

    Mybatis攔截器打印sql問題怎么解決

    3.添加攔截器插件

    // 采用自定義攔截器打印sql日志
    sessionFactory.setPlugins(new Interceptor[]{this.getSqlLogInterceptor()});

    Mybatis攔截器打印sql問題怎么解決

    4.攔截器邏輯描述

    4.1 注入開關(guān)

     @Value("${logging.sql.enable:true}")
     private Boolean sqlEnable;

    4.2 獲取sql

    Object target = invocation.getTarget();
    StatementHandler statementHandler = (StatementHandler) target;
     //獲取綁定的SQL對象
    BoundSql boundSql = statementHandler.getBoundSql();
     //得到需要執(zhí)行的sql語句,并進(jìn)行格式
    String sql = boundSql.getSql();

    4.2 獲取參數(shù)

    //需要綁定的參數(shù)映射對象
    List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();

    4.3 sql替換參數(shù)

    public String buidSql(String sql,Object[] parameters) {
            if (parameters == null || sql == null) {
                return "";
            }
            List<Object> parametersArray = Arrays.asList(parameters);
            List<Object> list = new ArrayList<Object>(parametersArray);
            while (sql.indexOf("?") != -1 && list.size() > 0 && parameters.length > 0) {
                Object obj = list.get(0);
                if(null!=obj && obj instanceof String){
                    sql = sql.replaceFirst("\\?", "'"+obj.toString()+"'");
                }else if(null!=obj){
                    sql = sql.replaceFirst("\\?", obj.toString());
                }
     
                list.remove(0);
            }
            return sql.replaceAll("(\r?\n(\\s*\r?\n)+)", "\r\n");
        }

    4.4 打印sql

    log.debug(String.format(
                            "\n###########################   Sql Start  ###########################" +
                                    "\n StartTime  :  %s" +
                                    "\n ExecuteID  :  %s" +
                                    "\n ExecuteSQL  :  %s" +
                                    "\n ExecuteTime  :  %s ms" +
                                    "\n###########################   Sql End  ###########################\n"
     
                            ,startTimeStr,executeID,this.buidSql(sql,args.toString().split(",")),exeTime));

    4.5打印效果

    Mybatis攔截器打印sql問題怎么解決

    攔截器完整代碼

    package com.cloudpaas.plugin.mybatis.interceptor;
     
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.mapping.BoundSql;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.mapping.ParameterMapping;
    import org.apache.ibatis.mapping.ParameterMode;
    import org.apache.ibatis.plugin.*;
    import org.apache.ibatis.reflection.MetaObject;
    import org.apache.ibatis.reflection.SystemMetaObject;
    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.springframework.beans.factory.annotation.Value;
     
    import java.lang.reflect.Field;
    import java.sql.Statement;
    import java.text.SimpleDateFormat;
    import java.util.*;
     
    @Intercepts({
            @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
            @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
            @Signature(type = StatementHandler.class, method = "batch", args = { Statement.class })
    })
    @Slf4j
    public class SqlLogInterceptor implements Interceptor {
        private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     
        @Value("${logging.sql.enable:true}")
        private Boolean sqlEnable;
     
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            if(!sqlEnable){
                return invocation.proceed();
            }
            Object target = invocation.getTarget();
            MetaObject mObject = SystemMetaObject.forObject(invocation.getTarget());
            MappedStatement mappedStatement = (MappedStatement)mObject.getValue("delegate.mappedStatement");
            // 執(zhí)行的mapper statement ID
            String executeID = mappedStatement.getId();
            //獲取當(dāng)前的開始時間戳
            long startTime = System.currentTimeMillis();
            //記錄當(dāng)前時間
            String startTimeStr=sdf.format(new Date());
            StatementHandler statementHandler = (StatementHandler) target;
            try {
                return invocation.proceed();
            } finally {
                long endTime = System.currentTimeMillis();
                //sql的執(zhí)行的時間
                long exeTime = endTime - startTime;
                try{
                    //獲取綁定的SQL對象
                    BoundSql boundSql = statementHandler.getBoundSql();
                    //得到需要執(zhí)行的sql語句,并進(jìn)行格式
                    String sql = boundSql.getSql();
                    sql=formatSql(sql);
                    //得到默認(rèn)的參數(shù)處理器
                    DefaultParameterHandler dph=(DefaultParameterHandler)statementHandler.getParameterHandler();
                    //利用反射機(jī)制,從DefaultParameterHandler獲取Configuration和TypeHandlerRegistry
                    Field configurationField=dph.getClass().getDeclaredField("configuration");
                    Field typeHandlerRegistryField=dph.getClass().getDeclaredField("typeHandlerRegistry");
                    //設(shè)置私有屬性可訪問
                    configurationField.setAccessible(true);
                    //設(shè)置私有屬性可訪問
                    typeHandlerRegistryField.setAccessible(true);
                    Configuration configuration=(Configuration) configurationField.get(dph);
                    TypeHandlerRegistry typeHandlerRegistry=(TypeHandlerRegistry) typeHandlerRegistryField.get(dph);
                    //sql的參數(shù)對象
                    Object parameterObject = boundSql.getParameterObject();
                    //需要綁定的參數(shù)映射對象
                    List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();
                    //處理sql的參數(shù),該部分參考的是DefaultParameterHandler中setParameters方法中的實(shí)現(xiàn)
                    StringBuffer args=new StringBuffer();
                    if(parameterMappingList!=null && parameterMappingList.size()>0){
                        for(ParameterMapping parameterMapping:parameterMappingList){
                            //如果該參數(shù)不是輸出參數(shù),則進(jìn)行處理
                            if (parameterMapping.getMode() != ParameterMode.OUT) {
                                Object value;
                                //參數(shù)的名字,屬性
                                String propertyName = parameterMapping.getProperty();
                                //先從附加的,主要是list、array等的處理
                                if (boundSql.hasAdditionalParameter(propertyName)) {
                                    value = boundSql.getAdditionalParameter(propertyName);
                                } else if (parameterObject == null) {
                                    value = null;
                                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                                    //typeHandlerRegistry注冊了某個類的處理
                                    value = parameterObject;
                                } else {
                                    //默認(rèn)的MetaObject 的處理,根據(jù)參數(shù)獲取值
                                    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);
                    }
                    log.debug(String.format(
                            "\n###########################   Sql Start  ###########################" +
                                    "\n StartTime  :  %s" +
                                    "\n ExecuteID  :  %s" +
                                    "\n ExecuteSQL  :  %s" +
                                    "\n ExecuteTime  :  %s ms" +
                                    "\n###########################   Sql End  ###########################\n"
     
                            ,startTimeStr,executeID,this.buidSql(sql,args.toString().split(",")),exeTime));
                }catch(Exception e){
                }
            }
        }
     
        @Override
        public Object plugin(Object target) {
            return Plugin.wrap(target, this);
        }
     
        @Override
        public void setProperties(Properties properties) {
     
        }
     
        private String formatSql(String sql) {
            // 輸入sql字符串空判斷
            if (sql == null || sql.length() == 0) {
                return "";
            }
            //格式sql 將回車換行制表符等替換成空,在將連續(xù)多個空格替換成1個空格,然后在去掉左右括號兩邊的空格,在去掉逗號左右兩個的空格
            return sql.replaceAll("[\\t\\n\\x0B\\f\\r]", "").replaceAll(" +", " ")
                    .replaceAll(" *\\( *", "(").replaceAll(" *\\) *", ")").replaceAll(" *, *", ",");
     
        }
     
        public String buidSql(String sql,Object[] parameters) {
            if (parameters == null || sql == null) {
                return "";
            }
            List<Object> parametersArray = Arrays.asList(parameters);
            List<Object> list = new ArrayList<Object>(parametersArray);
            while (sql.indexOf("?") != -1 && list.size() > 0 && parameters.length > 0) {
                Object obj = list.get(0);
                if(null!=obj && obj instanceof String){
                    sql = sql.replaceFirst("\\?", "'"+obj.toString()+"'");
                }else if(null!=obj){
                    sql = sql.replaceFirst("\\?", obj.toString());
                }
     
                list.remove(0);
            }
            return sql.replaceAll("(\r?\n(\\s*\r?\n)+)", "\r\n");
        }
    }

    “Mybatis攔截器打印sql問題怎么解決”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI