无需打开直接搜索微信:本司针对手游进行,选择我们的四大理由: ...
2025-09-09 0
作为一名资深 Java 开发工程师,我曾在一个电商项目上线前遭遇过一次 “惊魂时刻”:生产环境中订单支付模块突然出现数据不一致问题,但由于未配置 SQL 日志记录,无法快速定位是哪条语句执行异常,最终花费了 4 个小时才排查出是 MyBatis 动态 SQL 拼接时的参数绑定错误。这件事让我深刻意识到,在 Spring Boot 整合 MyBatis 的开发过程中,SQL 语句记录不仅是调试的 “利器”,更是线上问题排查的 “救命稻草”。
本文将针对 Spring Boot3 与 MyBatis 整合场景,详细拆解 3 种主流的 SQL 记录方案,从适合新手的快速配置到适合进阶开发的自定义拦截器,再到企业级项目常用的第三方组件,每一种方案都附带完整代码示例和避坑要点,帮助互联网软件开发同行高效解决 SQL 调试难题。
日志配置是实现 SQL 记录最便捷的方式,无需编写额外代码,仅通过简单的配置即可实现 SQL 语句、参数及执行时间的输出。Spring Boot3 默认支持 Logback、Log4j2 等主流日志框架,以下分别介绍两种最常用的配置方式。
1.1 快速控制台输出:MyBatis 原生日志配置
MyBatis 内置了 stdout 日志实现,适合开发初期的快速调试。只需在application.yml(或application.properties)中添加一行配置,即可在控制台直接打印 SQL 语句。
配置代码:
mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 启用MyBatis原生控制台日志 map-underscore-to-camel-case: true # 可选:开启下划线转驼峰映射
效果展示:
启动项目后执行数据库操作,控制台会输出类似日志:
==> Preparing: SELECT id, user_name, age FROM t_user WHERE id = ? ==> Parameters: 1(Integer)<== Columns: id, user_name, age<== Row: 1, zhangsan, 25<== Total: 1
避坑要点:
该方案仅适合本地开发调试,严禁在生产环境使用,否则会导致日志输出过多,影响系统性能并泄露敏感数据;
若项目中同时配置了其他日志框架(如 Log4j2),可能会出现日志冲突,此时建议优先使用日志框架的级别配置方案。
1.2 灵活日志级别控制:基于 SLF4J 的包级别配置
在实际开发中,我们通常需要更灵活的日志控制(如只输出某一 Mapper 接口的 SQL,或调整日志输出格式),此时可通过配置日志框架的级别来实现。Spring Boot3 默认集成 SLF4J+Logback,以下以常用的application.yml配置为例。
配置代码:
logging: level: com.example.demo.mapper: debug # Mapper接口所在包路径设为debug级别 org.springframework.web: info # 其他组件设为info级别,减少日志冗余 pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n" # 自定义日志格式
核心原理:
MyBatis 的 SQL 日志仅在debug级别下输出,因此只需将 Mapper 接口所在的包级别设置为debug,即可精准捕获该包下所有接口的 SQL 操作,而不影响其他组件的日志输出。
进阶配置:
若需将 SQL 日志输出到文件(方便后期排查),可增加日志文件配置:
logging: file: name: logs/sql-debug.log # 日志文件路径 logback: rollingpolicy: max-file-size: 10MB # 单个文件最大10MB max-history: 7 # 保留7天日志
当日志配置无法满足个性化需求(如 SQL 语句加密、执行耗时统计、异常报警等)时,可通过自定义 MyBatis 拦截器实现更灵活的 SQL 记录逻辑。MyBatis 提供了 Executor、StatementHandler 等拦截点,其中StatementHandler拦截器可直接获取最终执行的 SQL 语句(含绑定后的参数),实用性最强。
2.1 拦截器开发全流程
步骤 1:编写拦截器类
实现org.apache.ibatis.plugin.Interceptor接口,并重写核心方法,重点处理 SQL 语句提取和记录逻辑。
代码实现:
package com.example.demo.interceptor;import org.apache.ibatis.executor.statement.StatementHandler;import org.apache.ibatis.plugin.*;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.reflection.SystemMetaObject;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.sql.Connection;import java.util.Properties;/** * 自定义MyBatis SQL记录拦截器 */@Intercepts({@Signature( type = StatementHandler.class, // 拦截StatementHandler接口 method = "prepare", // 拦截prepare方法(SQL预处理阶段) args = {Connection.class, Integer.class} // 方法参数)})public class SqlRecordInterceptor implements Interceptor { private static final Logger logger = LoggerFactory.getLogger(SqlRecordInterceptor.class); @Override public Object intercept(Invocation invocation) throws Throwable { // 1. 获取StatementHandler对象 StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); // 2. 通过MetaObject反射获取目标对象的属性(MyBatis提供的工具类) MetaObject metaObject = SystemMetaObject.forObject(statementHandler); // 3. 提取SQL语句(boundSql.sql属性存储最终执行的SQL) String sql = (String) metaObject.getValue("boundSql.sql"); // 4. 提取Mapper接口方法名(mappedStatement.id属性) String methodName = (String) metaObject.getValue("mappedStatement.id"); // 5. 自定义记录逻辑:如打印SQL、统计耗时、存储到数据库等 long startTime = System.currentTimeMillis(); try { // 执行原方法(继续SQL预处理流程) return invocation.proceed(); } finally { // 计算执行耗时 long costTime = System.currentTimeMillis() - startTime; logger.info("【SQL执行记录】方法:{} | SQL:{} | 耗时:{}ms", methodName, sql.replaceAll("\\s+", " "), costTime); // 此处可扩展:如耗时超过1000ms时发送报警通知 if (costTime > 1000) { logger.warn("【SQL慢查询警告】方法:{} 耗时超过1秒,需优化", methodName); // sendAlarm(methodName, sql, costTime); // 自定义报警方法 } } } @Override public Object plugin(Object target) { // 包装目标对象,返回代理对象 return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // 可通过配置文件传递参数(如慢查询阈值) String slowThreshold = properties.getProperty("slowThreshold", "1000"); logger.info("SQL慢查询阈值设置为:{}ms", slowThreshold); }}
步骤 2:注册拦截器
在 Spring Boot3 中,通过@Configuration类将拦截器注册到 MyBatis 中,有两种注册方式:
方式一:通过 MyBatis 配置类注册
package com.example.demo.config;import com.example.demo.interceptor.SqlRecordInterceptor;import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class MyBatisConfig { @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> { // 注册自定义拦截器 SqlRecordInterceptor interceptor = new SqlRecordInterceptor(); // 可选:设置拦截器参数 Properties properties = new Properties(); properties.setProperty("slowThreshold", "800"); // 慢查询阈值设为800ms interceptor.setProperties(properties); configuration.addInterceptor(interceptor); }; }}
方式二:通过@MapperScan注解注册(简化版)
若项目中使用@MapperScan扫描 Mapper 接口,可直接通过sqlSessionFactoryRef关联配置,但推荐使用方式一,更便于参数配置。
2.2 拦截器核心优势与避坑点
核心优势:
避坑要点:
对于中大型项目,推荐使用成熟的第三方组件datasource-proxy实现 SQL 记录。该组件通过代理数据源的方式拦截 SQL 操作,支持 MyBatis、JPA、JDBC 等多种持久层框架,功能强大且稳定性高。
3.1 集成步骤
步骤 1:添加依赖
在pom.xml(Maven)或build.gradle(Gradle)中添加datasource-proxy依赖:
<!-- datasource-proxy 核心依赖 --><dependency> <groupId>net.ttddyy</groupId> <artifactId>datasource-proxy</artifactId> <version>1.10.0</version> <!-- 兼容Spring Boot3的最新版本 --></dependency>
步骤 2:配置代理数据源
编写配置类,将 Spring Boot 默认的数据源包装为datasource-proxy的代理数据源,并配置 SQL 记录器。
代码实现:
package com.example.demo.config;import net.ttddyy.dsproxy.listener.logging.Slf4JLogLevel;import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.jdbc.datasource.DataSourceTransactionManager;import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;@Configurationpublic class DataSourceProxyConfig { /** * 配置代理数据源 */ @Bean public DataSource dataSource(DataSourceProperties dataSourceProperties) { // 1. 获取Spring Boot自动配置的数据源(HikariCP/Druid等) DataSource originalDataSource = dataSourceProperties.initializeDataSourceBuilder().build(); // 2. 包装为代理数据源,配置SQL记录器 return ProxyDataSourceBuilder.create(originalDataSource) .name("SQL-Proxy-DataSource") // 数据源名称,用于日志区分 .logQueryBySlf4j(Slf4JLogLevel.DEBUG) // 查询SQL日志级别设为DEBUG .logUpdateBySlf4j(Slf4JLogLevel.INFO) // 更新SQL日志级别设为INFO .logSlowQueryBySlf4j(Slf4JLogLevel.WARN, 1000) // 慢查询(>1000ms)设为WARN .formatSql(true) // 格式化SQL语句,便于阅读 .multilineSql(true) // 多行显示SQL .build(); } /** * 配置事务管理器(必须使用代理数据源) */ @Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }}
3.2 高级功能与实战场景
功能 1:SQL 语句格式化与脱敏
datasource-proxy支持自定义 SQL 格式化器和数据脱敏处理器,例如对手机号、身份证号等敏感字段进行脱敏:
// 添加数据脱敏处理器import net.ttddyy.dsproxy.proxy.ParameterSetOperation;import java.util.List;public class SensitiveDataProcessor { public void processParameters(List<ParameterSetOperation> parameters) { for (ParameterSetOperation param : parameters) { Object value = param.getValue(); if (value instanceof String) { String strValue = (String) value; // 手机号脱敏:138****1234 if (strValue.matches("^1[3-9]\\d{9}$")) { param.setValue(strValue.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")); } } } }}// 在代理数据源中注册处理器return ProxyDataSourceBuilder.create(originalDataSource) .beforeQuery((execInfo, param) -> new SensitiveDataProcessor().processParameters(param)) .beforeUpdate((execInfo, param) -> new SensitiveDataProcessor().processParameters(param)) .build();
功能 2:SQL 执行监控与埋点
结合 Prometheus+Grafana 可实现 SQL 执行指标监控,datasource-proxy提供了MetricsListener接口,支持自定义指标收集:
import io.micrometer.core.instrument.MeterRegistry;import net.ttddyy.dsproxy.listener.MetricsListener;@Beanpublic MetricsListener metricsListener(MeterRegistry meterRegistry) { MetricsListener listener = new MetricsListener(meterRegistry, "sql.execution"); listener.setQueryThresholdForSlowQuery(1000); return listener;}// 注册到代理数据源return ProxyDataSourceBuilder.create(originalDataSource) .listener(metricsListener(meterRegistry)) .build();
企业级优势:
为帮助大家根据项目场景快速选型,以下从适用场景、实现成本、功能扩展性三个维度进行对比:
方案类型 | 适用场景 | 实现成本 | 功能扩展性 | 生产环境推荐度 |
日志框架配置 | 本地开发调试、简单 SQL 记录 | 低(0 代码) | 弱 | ★☆☆☆☆ |
自定义拦截器 | 个性化需求(如定制报警) | 中(需开发) | 强 | ★★★☆☆ |
datasource-proxy | 中大型项目、企业级监控 | 低(配置化) | 极强 | ★★★★★ |
选型建议:
本文介绍的三种 SQL 记录方案,覆盖了从开发到生产的全流程需求。在实际项目中,建议采用 “分层策略”:开发环境用日志配置,测试环境加拦截器监控,生产环境部署 datasource-proxy + 监控平台。
最后分享两个实战避坑经验:
如果大家在实践过程中遇到拦截器注册失败、datasource-proxy 与 Druid 兼容性问题等,欢迎在评论区留言讨论,我会第一时间为大家解答!
相关文章
亲,这款游戏可以开挂的,确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到-人的牌一样。所以很多小伙伴就怀疑这...
2025-09-09 0
根据气象部门最新预报,河南省自9月11日起将迎来新一轮强降雨天气。此次降雨过程范围广、强度大,尤其是中部和北部地区可能面临大雨到暴雨的袭击。气象专家分...
2025-09-09 0
您好:这款游戏可以开挂,确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到-人的牌一样。所以很多小伙伴就怀疑这...
2025-09-09 0
科技日报记者 王禹涵 9月9日凌晨,位于陕西延安的延安东绕城高速公路控制性工程——南泥湾机场大桥,在67米高空成功完成转体施工。重达2.5万吨的T型刚...
2025-09-09 0
您好:这款游戏是可以开挂的,软件加微信【添加图中微信】确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到其他人...
2025-09-09 0
前言VS Code 是一款由微软开源免费、轻量级、功能强大的源代码编辑器。其轻量级体现在基础安装简洁,仅含核心编辑功能。功能强大则源于它支持丰富的语言...
2025-09-09 0
发表评论