如何獲取MyBatis Plus執(zhí)行的完整的SQL語句
注意:本示例介紹的方法僅支持MyBatis Plus
原理
自定義插件,將SQL語句中的?替換成具體的參數(shù)值。
實(shí)現(xiàn)
自定義插件
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.tiku.utils.LogUtil;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import java.sql.SQLException;
import java.text.DateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
public class MyBatisPlusSqlLogInterceptor implements InnerInterceptor {
private static boolean printSQL = true;
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
if (printSQL) {
MappedStatement mappedStatement = ms;
String sqlId = mappedStatement.getId();
Configuration configuration = mappedStatement.getConfiguration();
String sql = getSql(configuration, boundSql, sqlId);
final String ip = RequestUtil.getIp();
final String requestType = RequestUtil.getMethod();
final String url = RequestUtil.getRequestUrl();
final String parameters = RequestUtil.getParameters();
String userAgent = RequestUtil.getUserAgent();
UserAgent agent = UserAgent.parseUserAgentString(userAgent);
final String browser = agent.getBrowser().getName();
final String os = agent.getOperatingSystem().getName();
LogUtil.println("用戶訪問了:{},瀏覽器是:{},操作系統(tǒng)是:{},IP是:{},請(qǐng)求方式是:{},請(qǐng)求參數(shù)是:{},SQL語句是:{}", url, browser, os, ip, requestType, parameters, sql);
}
}
@Override
public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
if (printSQL) {
MappedStatement mappedStatement = ms;
String sqlId = mappedStatement.getId();
Configuration configuration = mappedStatement.getConfiguration();
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
String sql = getSql(configuration, boundSql, sqlId);
final String ip = RequestUtil.getIp();
final String requestType = RequestUtil.getMethod();
final String url = RequestUtil.getRequestUrl();
final String parameters = RequestUtil.getParameters();
String userAgent = RequestUtil.getUserAgent();
UserAgent agent = UserAgent.parseUserAgentString(userAgent);
final String browser = agent.getBrowser().getName();
final String os = agent.getOperatingSystem().getName();
LogUtil.println("用戶訪問了:{},瀏覽器是:{},操作系統(tǒng)是:{},IP是:{},請(qǐng)求方式是:{},請(qǐng)求參數(shù)是:{},SQL語句是:{}", url, browser, os, ip, requestType, parameters, sql);
}
}
private String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {
try {
String sql = showSql(configuration, boundSql);
StringBuilder str = new StringBuilder(100);
str.append(sqlId);
str.append(" ==> ");
str.append(sql);
str.append(";");
return str.toString();
} catch (Error e) {
LogUtil.error("解析 sql 異常", e);
}
return "";
}
private String showSql(Configuration configuration, BoundSql boundSql) {
Object parameterObject = boundSql.getParameterObject();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
if (parameterMappings != null && parameterMappings.size() > 0 && parameterObject != null) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
if (metaObject.hasGetter(propertyName)) {
Object obj = metaObject.getValue(propertyName);
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
} else if (boundSql.hasAdditionalParameter(propertyName)) {
Object obj = boundSql.getAdditionalParameter(propertyName);
sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
}
}
}
}
return sql;
}
private String getParameterValue(Object obj) {
String value = null;
if (obj instanceof String) {
value = "'" + obj.toString() + "'";
} else if (obj instanceof Date) {
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format(obj) + "'";
} else if (obj instanceof LocalDate) {
value = "'" + ((LocalDate) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "'";
} else if (obj instanceof LocalDateTime) {
value = "'" + ((LocalDateTime) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + "'";
} else {
if (obj != null) {
value = obj.toString();
} else {
value = "";
}
}
return value;
}
}上面代碼用到的兩個(gè)工具類:
- RequestUtil.java
import com.tiku.enums.ResultEnum;
import com.tiku.ex.GlobalException;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* @author hc
*/
public class RequestUtil {
/**
* 私有構(gòu)造方法,防止實(shí)例化
*/
private RequestUtil() {
}
/**
* 獲取所有的請(qǐng)求參數(shù)及值
*
* @return eg:pageNum=3&pageSize=5&state=0
*/
public static String getParameters() {
HttpServletRequest request = SpringContextUtil.getRequest();
if (null == request) {
return null;
}
Enumeration<String> paraNames = request.getParameterNames();
if (paraNames == null) {
return null;
}
StringBuilder sb = new StringBuilder();
while (paraNames.hasMoreElements()) {
String paraName = paraNames.nextElement();
sb.append("&").append(paraName).append("=").append(request.getParameter(paraName));
}
return sb.toString();
}
public static Map<String, String> getParameterMap(HttpServletRequest request) {
// 參數(shù)Map
Map<String, String[]> properties = request.getParameterMap();
// 返回值Map
Map<String, String> returnMap = new HashMap<>(16);
Iterator<Map.Entry<String, String[]>> entries = properties.entrySet().iterator();
Map.Entry<String, String[]> entry;
String name;
String value = "";
while (entries.hasNext()) {
entry = entries.next();
name = entry.getKey();
Object valueObj = entry.getValue();
if (null == valueObj) {
value = "";
} else {
String[] values = (String[]) valueObj;
for (String value1 : values) {
value = value1 + ",";
}
value = value.substring(0, value.length() - 1);
}
returnMap.put(name, value);
}
return returnMap;
}
/**
* 獲取用戶所有的請(qǐng)求參數(shù),以Map的形式返回
*
* @return
*/
public static Map<String, String[]> getParametersMap() {
HttpServletRequest request = SpringContextUtil.getRequest();
if (null == request) {
return new HashMap<>();
}
return request.getParameterMap();
}
/**
* 獲取請(qǐng)求頭中指定名稱的值
*
* @param name
* @return
*/
public static String getHeader(String name) {
HttpServletRequest request = SpringContextUtil.getRequest();
if (null == request) {
return null;
}
return request.getHeader(name);
}
public static String getReferer() {
return getHeader("Referer");
}
/**
* 獲取用戶代理對(duì)象
*
* @return
*/
public static String getUserAgent() {
return getHeader("User-Agent");
}
/**
* 獲取請(qǐng)求用戶的IP地址
*
* @return
*/
public static String getIp() {
HttpServletRequest request = SpringContextUtil.getRequest();
if (null == request) {
return null;
}
return IPUtil.getIpAddr(request);
}
/**
* 獲取用戶請(qǐng)求的Controller的路徑
*
* @return 比如:http://localhost/list
*/
public static String getRequestUrl() {
HttpServletRequest request = SpringContextUtil.getRequest();
if (null == request) {
return null;
}
return request.getRequestURL().toString();
}
/**
* 獲取URI
*
* @return
*/
public static String getRequestUri() {
HttpServletRequest request = SpringContextUtil.getRequest();
if (null == request) {
return null;
}
return request.getRequestURI();
}
/**
* 獲取用戶請(qǐng)求的方式,值有: POST GET 3 PUT DELETE
*
* @return
*/
public static String getMethod() {
HttpServletRequest request = SpringContextUtil.getRequest();
if (null == request) {
return null;
}
return request.getMethod();
}
/**
* 判斷用戶的請(qǐng)求是否是AJAX請(qǐng)求
*
* @param request
* @return
*/
public static boolean isAjax(HttpServletRequest request) {
if (null == request) {
request = SpringContextUtil.getRequest();
}
if (null == request) {
return false;
}
// 解析原錯(cuò)誤信息,封裝后返回,此處返回非法的字段名稱,原始值,錯(cuò)誤信息
// 使用HttpServletRequest中的header檢測(cè)請(qǐng)求是否為ajax, 如果是ajax則返回json, 如果為非ajax則返回view(即ModelAndView)
String contentTypeHeader = request.getHeader("Content-Type");
String acceptHeader = request.getHeader("Accept");
String xRequestedWith = request.getHeader("X-Requested-With");
return (contentTypeHeader != null && contentTypeHeader.contains("application/json"))
|| (acceptHeader != null && acceptHeader.contains("application/json"))
|| "XMLHttpRequest".equalsIgnoreCase(xRequestedWith);
}
/**
* 從HttpServletReqeust對(duì)象中獲取key對(duì)應(yīng)的值 先從Reqeust Parameter中獲取,如果沒有再從Header中獲取
*
* @param key
* @return
*/
public static String getValueFromRequest(String key) {
HttpServletRequest request = SpringContextUtil.getRequest();
return getValueFromRequest(request, key);
}
/**
* 從HttpServletReqeust對(duì)象中獲取key對(duì)應(yīng)的值 先從Reqeust Parameter中獲取,如果沒有再從Header中獲取
*
* @param request
* @param key
* @return
*/
public static String getValueFromRequest(HttpServletRequest request, String key) {
String token = request.getParameter(key);
// 從header中獲取值
if (token == null) {
token = request.getHeader(key);
}
if (token == null) {
throw new GlobalException(ResultEnum.TOKEN_INVALID);
}
return token;
}
}- UserAgent
該工具類是第三方j(luò)ar包中定義的:UserAgentUtils-1.21.jar
注冊(cè)插件
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new MyBatisPlusSqlLogInterceptor());
return interceptor;
}
}這樣在執(zhí)行CRUD操作數(shù)據(jù)時(shí),就會(huì)在控制臺(tái)中輸出相應(yīng)的完整的SQL語句
比如:
2024-05-20 07:46:22.365 [INFO ] [] 6868 -- SqlLogInterceptor.beforeQuery() Line: 48 Line:100 : 用戶訪問了:http://127.0.0.1:8081/tiku/user/v1/login,瀏覽器是:Chrome 12,操作系統(tǒng)是:Windows 10,IP是:127.0.0.1,請(qǐng)求方式是:POST,請(qǐng)求參數(shù)是:,SQL語句是:com.tiku.mapper.UserDetailsMapper.selectById ==> SELECT id,num,nickname,`name`,avatar,gender,birth,email,tel,qq,wechat,credit,interest,country_id,addr,verify_time,login_time,login_ip,login_addr,creator_id,updater_id,info,priority,create_time,update_time FROM sys_user_details WHERE id=1;
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用Spring Security OAuth2實(shí)現(xiàn)單點(diǎn)登錄
在本教程中,我們將討論如何使用Spring Security OAuth和Spring Boot實(shí)現(xiàn)SSO - 單點(diǎn)登錄。感興趣的朋友跟隨小編一起看看吧2019-06-06
IDEA中將SpringBoot項(xiàng)目提交到git倉庫的方法步驟
本文主要介紹了IDEA中將SpringBoot項(xiàng)目提交到git倉庫的方法步驟,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
JeecgBoot頁面Online報(bào)表配置過程(動(dòng)態(tài))
文章介紹了如何根據(jù)登錄者角色信息動(dòng)態(tài)配置報(bào)表,并詳細(xì)說明了Online報(bào)表的配置步驟,包括數(shù)據(jù)源選擇、字段顯示、查詢條件設(shè)置等,同時(shí),文章還涉及了頁面代碼部分,包括依賴引入、用戶信息獲取、參數(shù)配置和form表單配置等2026-01-01
springboot @ConditionalOnMissingBean注解的作用詳解
這篇文章主要介紹了springboot @ConditionalOnMissingBean注解的作用詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
uploadify上傳及后臺(tái)文件合法性驗(yàn)證的代碼解析
這篇文章主要介紹了uploadify上傳及后臺(tái)文件合法性驗(yàn)證的代碼解析,整段代碼分為后臺(tái)上傳方法,文件合法性驗(yàn)證類,前端上傳js,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
基于SpringBoot + Android實(shí)現(xiàn)登錄功能
在移動(dòng)互聯(lián)網(wǎng)的今天,許多應(yīng)用需要通過移動(dòng)端實(shí)現(xiàn)與服務(wù)器的交互功能,其中登錄是最常見且基礎(chǔ)的一種功能,本篇博客將詳細(xì)介紹如何使用 Spring Boot 和 Android 實(shí)現(xiàn)一個(gè)完整的登錄功能,需要的朋友可以參考下2024-11-11
IDEA?mybatis?Mapper.xml報(bào)紅的最新解決辦法
這篇文章主要介紹了IDEA?mybatis?Mapper.xml報(bào)紅的解決辦法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04
Java Random 隨機(jī)數(shù)的用法小結(jié)
本文主要介紹了Java Random隨機(jī)數(shù)的用法小結(jié),Random類功能全面,支持生成整數(shù)、浮點(diǎn)數(shù)、布爾值及實(shí)現(xiàn)洗牌等高級(jí)操作,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-07-07

