前言 在我们工作实际项目中,常常遇到使用Mybatis作为ORM框架,在使用的过程中,一般都会开启日志的打印功能,这样在控制台就会输出执行的SQL,定位SQL问题也是比较方便的。但是,我们就会发现,这样打印出来的SQL是预编译语句和参数是分开的。 此时如果需要去数据库执行上条SQL的时候,我们需要手动的把参数拼接到SQL语句中;参数少此操作还可以,参数一旦比较多,此操作相当的麻烦繁琐。下面我们就使用Mybatis拦截器的方式来实现打印完整SQL的功能。具体实现实现拦截器逻辑importlombok。extern。slf4j。Slf4j;importorg。apache。ibatis。cache。CacheKey;importorg。apache。ibatis。executor。Executor;importorg。apache。ibatis。mapping。BoundSql;importorg。apache。ibatis。mapping。MappedStatement;importorg。apache。ibatis。mapping。ParameterMapping;importorg。apache。ibatis。plugin。;importorg。apache。ibatis。reflection。MetaObject;importorg。apache。ibatis。session。Configuration;importorg。apache。ibatis。session。ResultHandler;importorg。apache。ibatis。session。RowBounds;importorg。apache。ibatis。type。TypeHandlerRegistry;importorg。springframework。util。CollectionUtils;importjava。text。DateFormat;importjava。util。Date;importjava。util。List;importjava。util。Locale;importjava。util。Properties;importjava。util。regex。Matcher;author公众号:SimpleMemoryversion1。0。0ClassNameFullSQLInterceptor。javaDescriptionTODOcreateTime2022年10月14日18:39:00Slf4jIntercepts(value{Signature(typeExecutor。class,methodupdate,args{MappedStatement。class,Object。class}),Signature(typeExecutor。class,methodquery,args{MappedStatement。class,Object。class,RowBounds。class,ResultHandler。class,CacheKey。class,BoundSql。class}),Signature(typeExecutor。class,methodquery,args{MappedStatement。class,Object。class,RowBounds。class,ResultHandler。class})})publicclassMybatisSQLInterceptorimplementsInterceptor{OverridepublicObjectintercept(Invocationinvocation)throwsThrowable{try{获取xml中的一个selectupdateinsertdelete节点,主要描述的是一条SQL语句MappedStatementmappedStatement(MappedStatement)invocation。getArgs()〔0〕;Objectparameternull;获取参数,if语句成立,表示sql语句有参数,参数格式是map形式if(invocation。getArgs()。length1){parameterinvocation。getArgs()〔1〕;log。error(sqlparam:{},parameter);}if(SqlCommandType。SELECT。equals(mappedStatement。getSqlCommandType())){}获取到节点的id,即sql语句的idStringsqlIdmappedStatement。getId();BoundSql就是封装myBatis最终产生的sql类BoundSqlboundSqlmappedStatement。getBoundSql(parameter);获取节点的配置ConfigurationconfigurationmappedStatement。getConfiguration();获取到最终的sql语句StringsqlgetSql(configuration,boundSql,sqlId);log。error(sql{},sql);}catch(Exceptione){}执行完上面的任务后,不改变原有的sql执行过程returninvocation。proceed();}封装了一下sql语句,使得结果返回完整xml路径下的sql语句节点idsql语句paramconfigurationconfigurationparamboundSqlboundSqlparamsqlIdsqlIdreturnjava。lang。StringpublicstaticStringgetSql(Configurationconfiguration,BoundSqlboundSql,StringsqlId){StringsqlshowSql(configuration,boundSql);StringBuilderstrnewStringBuilder(100);str。append(sqlId);str。append(:);str。append(sql);returnstr。toString();}如果参数是String,则添加单引号,如果是日期,则转换为时间格式器并加单引号;对参数是null和不是null的情况作了处理paramobjobjreturnjava。lang。StringprivatestaticStringgetParameterValue(Objectobj){Stringvaluenull;if(objinstanceofString){valueobj。toString();}elseif(objinstanceofDate){DateFormatformatterDateFormat。getDateTimeInstance(DateFormat。DEFAULT,DateFormat。DEFAULT,Locale。CHINA);valueformatter。format(newDate());}else{if(obj!null){valueobj。toString();}else{value;}}returnvalue;}进行?的替换paramconfigurationparamboundSqlreturnpublicstaticStringshowSql(Configurationconfiguration,BoundSqlboundSql){ObjectparameterObjectboundSql。getParameterObject();获取参数ListParameterMappingparameterMappingsboundSql。getParameterMappings();StringsqlboundSql。getSql()。replaceAll(〔s〕,);sql语句中多个空格都用一个空格代替if(!CollectionUtils。isEmpty(parameterMappings)parameterObject!null){TypeHandlerRegistrytypeHandlerRegistryconfiguration。getTypeHandlerRegistry();获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换 如果根据parameterObject。getClass()可以找到对应的类型,则替换if(typeHandlerRegistry。hasTypeHandler(parameterObject。getClass())){sqlsql。replaceFirst(?,Matcher。quoteReplacement(getParameterValue(parameterObject)));}else{MetaObjectmetaObjectconfiguration。newMetaObject(parameterObject);MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作for(ParameterMappingparameterMapping:parameterMappings){StringpropertyNameparameterMapping。getProperty();if(metaObject。hasGetter(propertyName)){ObjectobjmetaObject。getValue(propertyName);sqlsql。replaceFirst(?,Matcher。quoteReplacement(getParameterValue(obj)));}elseif(boundSql。hasAdditionalParameter(propertyName)){ObjectobjboundSql。getAdditionalParameter(propertyName);该分支是动态sqlsqlsql。replaceFirst(?,Matcher。quoteReplacement(getParameterValue(obj)));}else{sqlsql。replaceFirst(?,缺失);}打印出缺失,提醒该参数缺失并防止错位}}}returnsql;}OverridepublicObjectplugin(Objecttarget){returnPlugin。wrap(target,this);}OverridepublicvoidsetProperties(Propertiesproperties){}} 配置拦截器 在Mybatis的配置文件中配置拦截器 pluginsplugininterceptorcom。ruoyi。web。config。MybatisSQLInterceptorpluginplugins 在application。yml中配置Mybatis的全局的配置文件mybatisconfig。xml 实现效果 启动项目,查看控制台打印SQL语句情况。 2022101509:51:50。780ERROR30292〔io8089exec24〕c。r。web。config。MybatisSQLInterceptor:sqlcom。ruoyi。system。mapper。SysDeptMapper。selectDeptList:selectd。deptid,d。parentid,d。ancestors,d。deptname,d。ordernum,d。leader,d。phone,d。email,d。status,d。delflag,d。createby,d。createtimefromsysdeptdwhered。delflag0ANDdeptnamelikeconcat(,开发,)ANDstatus0orderbyd。parentid,d。ordernum 公众号:SimpleMemory