当前位置: 首页 > news >正文

Java + Spring Boot + MyBatis获取以及持久化sql语句的方法

在Java的Spring Boot项目中结合MyBatis获取实际执行的SQL语句,可以通过以下几种方法实现:

方法一:配置MyBatis日志级别

通过调整日志级别,MyBatis会输出执行的SQL语句及参数,适用于快速调试。

  1. 修改application.properties文件:

    properties

    复制

    下载

    # 设置特定Mapper接口的日志级别为DEBUG
    logging.level.com.example.mapper=DEBUG
    # 使用MyBatis标准日志输出(可选)
    mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
  2. 观察控制台输出:
    执行查询时,控制台将显示带占位符的SQL和参数列表,例如:

    复制

    下载

    ==>  Preparing: SELECT * FROM user WHERE id = ?
    ==> Parameters: 1(Integer)

方法二:自定义MyBatis拦截器

编写拦截器获取BoundSql并处理参数,生成完整SQL。

  1. 创建拦截器类:

    java

    复制

    下载

    @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    })
    public class SqlInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement ms = (MappedStatement) invocation.getArgs()[0];Object parameter = invocation.getArgs().length > 1 ? invocation.getArgs()[1] : null;BoundSql boundSql = ms.getBoundSql(parameter);Configuration configuration = ms.getConfiguration();String sql = boundSql.getSql();Object parameterObject = boundSql.getParameterObject();List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();// 格式化SQL,替换参数占位符String formattedSql = formatSql(sql, parameterObject, parameterMappings, configuration);System.out.println("Executing SQL: " + formattedSql);return invocation.proceed();}private String formatSql(String sql, Object parameter, List<ParameterMapping> mappings, Configuration config) {if (mappings.isEmpty() || parameter == null) return sql;MetaObject metaObject = config.newMetaObject(parameter);return mappings.stream().map(m -> getParameterValue(metaObject, m)).reduce(sql, (s, p) -> s.replaceFirst("\\?", p));}private String getParameterValue(MetaObject metaObject, ParameterMapping mapping) {Object value = metaObject.getValue(mapping.getProperty());return value instanceof String ? "'" + value + "'" : value.toString();}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
    }
  2. 注册拦截器到Spring容器:

    java

    复制

    下载

    @Configuration
    public class MyBatisConfig {@Beanpublic SqlInterceptor sqlInterceptor() {return new SqlInterceptor();}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();factoryBean.setDataSource(dataSource);factoryBean.setPlugins(new Interceptor[]{sqlInterceptor()});return factoryBean.getObject();}
    }

方法三:使用P6Spy监听JDBC操作

通过P6Spy代理JDBC驱动,记录完整SQL,适合生产环境。

  1. 添加P6Spy依赖:

    xml

    复制

    下载

    运行

    <dependency><groupId>p6spy</groupId><artifactId>p6spy</artifactId><version>3.9.1</version>
    </dependency>
  2. 修改数据源配置:

    properties

    复制

    下载

    spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/mydb
    spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
  3. 配置spy.properties
    src/main/resources下创建spy.properties

    properties

    复制

    下载

    module.log=com.p6spy.engine.logging.P6LogFactory
    appender=com.p6spy.engine.spy.appender.Slf4JLogger
    logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
    customLogMessageFormat=%(currentTime) | SQL: %(sqlSingleLine)

方法对比表

方法优点缺点
配置日志级别简单快捷,无需代码改动SQL与参数分离,无法直接复制执行
自定义拦截器灵活,可定制SQL格式需要处理参数替换逻辑,复杂度较高
P6Spy完整SQL记录,配置简单引入额外依赖,需修改数据源配置

选择适合你需求的方法,即可在Spring Boot中获取MyBatis执行的SQL语句。

在 Spring Boot + MyBatis 项目中,将执行的 SQL 语句持久化到数据库中存储,可以通过以下步骤实现:


1. 创建数据库表存储 SQL 日志

首先设计一个表来记录 SQL 执行信息,例如:

sql

复制

下载

CREATE TABLE sql_execution_log (id BIGINT AUTO_INCREMENT PRIMARY KEY,sql_statement TEXT NOT NULL,        -- 执行的 SQL 语句parameters VARCHAR(1000),           -- SQL 参数execution_time DATETIME,            -- 执行时间duration BIGINT,                    -- 执行耗时(毫秒)success TINYINT(1),                 -- 是否执行成功error_message TEXT,                 -- 错误信息(可选)created_by VARCHAR(100)             -- 操作人(可选)
);

2. 通过 MyBatis 拦截器捕获 SQL

使用自定义拦截器获取 SQL 并记录到数据库,以下是完整实现:

2.1 自定义拦截器类

java

复制

下载

@Intercepts({@Signature(type = Executor.class,method = "update",args = {MappedStatement.class, Object.class}),@Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SqlLogInterceptor implements Interceptor {@Autowiredprivate SqlLogService sqlLogService; // 注入日志服务@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement ms = (MappedStatement) invocation.getArgs()[0];Object parameter = invocation.getArgs()[1];BoundSql boundSql = ms.getBoundSql(parameter);// 获取 SQL 和参数String sql = boundSql.getSql();String params = getParameterString(boundSql.getParameterObject());// 记录执行开始时间和状态long startTime = System.currentTimeMillis();boolean success = true;String errorMsg = null;try {return invocation.proceed(); // 执行 SQL} catch (Exception e) {success = false;errorMsg = e.getMessage();throw e;} finally {long duration = System.currentTimeMillis() - startTime;// 异步保存日志(避免影响主流程性能)CompletableFuture.runAsync(() -> {SqlExecutionLog log = new SqlExecutionLog();log.setSqlStatement(sql);log.setParameters(params);log.setExecutionTime(new Date());log.setDuration(duration);log.setSuccess(success);log.setErrorMessage(errorMsg);sqlLogService.saveLog(log);});}}// 将参数对象转为字符串(简化示例)private String getParameterString(Object parameter) {if (parameter == null) return "null";try {return new ObjectMapper().writeValueAsString(parameter);} catch (JsonProcessingException e) {return "参数序列化失败";}}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}
2.2 定义日志实体类和 Mapper

java

复制

下载

// 实体类
@Data
public class SqlExecutionLog {private Long id;private String sqlStatement;private String parameters;private Date executionTime;private Long duration;private Boolean success;private String errorMessage;
}// Mapper 接口
@Mapper
public interface SqlLogMapper {@Insert("INSERT INTO sql_execution_log (sql_statement, parameters, execution_time, duration, success, error_message) " +"VALUES (#{sqlStatement}, #{parameters}, #{executionTime}, #{duration}, #{success}, #{errorMessage})")@Options(useGeneratedKeys = true, keyProperty = "id")void insert(SqlExecutionLog log);
}
2.3 日志服务类

java

复制

下载

@Service
public class SqlLogService {@Autowiredprivate SqlLogMapper sqlLogMapper;@Transactional(propagation = Propagation.REQUIRES_NEW) // 独立事务public void saveLog(SqlExecutionLog log) {sqlLogMapper.insert(log);}
}

3. 注册拦截器到 MyBatis

java

复制

下载

@Configuration
public class MyBatisConfig {@Beanpublic SqlLogInterceptor sqlLogInterceptor() {return new SqlLogInterceptor();}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();factoryBean.setDataSource(dataSource);factoryBean.setPlugins(new Interceptor[]{sqlLogInterceptor()});return factoryBean.getObject();}
}

4. 优化点

4.1 异步写入日志

使用 CompletableFuture.runAsync() 异步保存日志,避免阻塞主流程。若需更高性能,可结合消息队列(如 RabbitMQ/Kafka)或线程池。

4.2 独立事务

通过 @Transactional(propagation = Propagation.REQUIRES_NEW) 确保日志保存不受主事务回滚影响。

4.3 参数脱敏

在 getParameterString() 方法中过滤敏感信息(如密码):

java

复制

下载

private String getParameterString(Object parameter) {String json = new ObjectMapper().writeValueAsString(parameter);return json.replaceAll("\"password\":\"[^\"]+\"", "\"password\":\"***\"");
}

5. 验证结果

执行 MyBatis 操作后,检查 sql_execution_log 表中是否生成记录:

sql

复制

下载

SELECT * FROM sql_execution_log;

方法对比

方案优点缺点
MyBatis 拦截器灵活可控,直接集成到代码需要处理事务和性能问题
P6Spy + 数据库输出配置简单,无需代码改动需要依赖 P6Spy,扩展性低

根据需求选择合适方案,拦截器方案适合需要深度定制的场景,P6Spy 适合快速集成。

在Spring Boot项目中,通过P6Spy实现SQL日志的监控与数据库持久化,可以结合其拦截能力和自定义配置,具体步骤如下:


一、添加P6Spy依赖

在项目的pom.xml中引入P6Spy依赖(版本推荐3.9.1):

xml

复制

下载

运行

<dependency><groupId>p6spy</groupId><artifactId>p6spy</artifactId><version>3.9.1</version>
</dependency>

二、修改数据源配置

将原有的JDBC驱动替换为P6Spy的代理驱动,并调整数据库连接URL前缀:

properties

复制

下载

# application.properties
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/your_db?useSSL=false&serverTimezone=UTC

这里需将原数据库驱动(如MySQL的com.mysql.jdbc.Driver)通过P6Spy代理,URL中添加jdbc:p6spy:前缀28。


三、配置spy.properties

src/main/resources下创建spy.properties文件,配置日志输出策略和数据库实际驱动:

properties

复制

下载

# 指定日志模块及实际数据库驱动
module.log=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
driverlist=com.mysql.jdbc.Driver# 自定义日志格式类
logMessageFormat=com.example.config.P6SpyCustomLogger
appender=com.p6spy.engine.spy.appender.Slf4JLogger# 排除不必要日志类别
excludecategories=info,debug,result,batc,resultset# 慢SQL监控(可选)
outagedetection=true
outagedetectioninterval=2

此配置通过logMessageFormat指定自定义日志格式类,将SQL输出到应用日志中27。


四、自定义SQL日志格式

实现MessageFormattingStrategy接口,生成包含完整SQL及执行时间的日志:

java

复制

下载

public class P6SpyCustomLogger implements MessageFormattingStrategy {@Overridepublic String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) {return StringUtils.isNotBlank(sql) ?String.format("SQL执行耗时: %dms | SQL语句:\n%s", elapsed, sql.replaceAll("\\s+", " ")) : "";}
}

此示例将SQL格式化输出,并移除多余空格78。


五、日志持久化到数据库

若需将SQL日志存储至数据库,需额外设计表和异步写入逻辑:

1. 创建日志表

sql

复制

下载

CREATE TABLE sql_log (id BIGINT AUTO_INCREMENT PRIMARY KEY,sql_text TEXT,parameters TEXT,execution_time DATETIME,duration BIGINT,success TINYINT(1)
);
2. 异步写入服务

在自定义日志类中调用服务层异步保存日志:

java

复制

下载

@Service
public class SqlLogService {@Autowiredprivate SqlLogMapper sqlLogMapper;@Async@Transactional(propagation = Propagation.REQUIRES_NEW)public void saveLog(String sql, long duration) {SqlLog log = new SqlLog();log.setSqlText(sql);log.setExecutionTime(new Date());log.setDuration(duration);sqlLogMapper.insert(log);}
}// 在自定义Logger中注入SqlLogService并调用
public class P6SpyCustomLogger implements MessageFormattingStrategy {@Autowiredprivate SqlLogService sqlLogService;@Overridepublic String formatMessage(...) {sqlLogService.saveLog(sql, elapsed);return formattedLog;}
}

需确保日志服务通过@Async异步执行,避免阻塞主线程26。


六、高级配置

1. 参数脱敏

在日志格式化时过滤敏感信息(如密码):

java

复制

下载

private String filterSensitiveParams(String sql) {return sql.replaceAll("\"password\":\"[^\"]+\"", "\"password\":\"***\"");
}
2. 慢SQL告警

通过outagedetectioninterval设置慢SQL阈值(单位秒),超时的SQL会单独记录28。


方案对比

方案优点缺点
P6Spy日志输出配置简单,无需代码侵入需结合异步服务实现持久化
MyBatis拦截器直接集成业务逻辑,灵活需处理参数替换和性能问题

通过以上步骤,P6Spy可完整记录SQL执行细节,并通过异步服务将日志持久化到数据库。此方案适用于调试、性能分析和审计场景,且对原有代码无侵入性

在 SQL Server 2008 R2 中使用 P6Spy 配置数据源,需调整驱动和连接参数。以下是详细步骤:


一、修改数据源配置

在 application.properties 中配置 P6Spy 代理驱动和 SQL Server 连接信息:

properties

复制

下载

# application.properties
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.url=jdbc:p6spy:sqlserver://localhost:1433;databaseName=YourDBName
spring.datasource.username=your_username
spring.datasource.password=your_password# SQL Server 其他可选参数(按需添加)
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=10
关键配置说明
  • 驱动类名:使用 com.p6spy.engine.spy.P6SpyDriver 作为代理驱动。

  • URL 格式

    • 原 SQL Server URL 格式:jdbc:sqlserver://主机:端口;databaseName=数据库名

    • 修改为 P6Spy 代理后的 URL:添加 jdbc:p6spy: 前缀,即 jdbc:p6spy:sqlserver://...

  • 认证方式

    • 若使用 Windows 集成身份验证,需在 URL 中添加 integratedSecurity=true,并确保 sqljdbc_auth.dll 在类路径中。


二、添加 SQL Server JDBC 驱动依赖

在 pom.xml 中添加 SQL Server 官方 JDBC 驱动:

xml

复制

下载

运行

<dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>mssql-jdbc</artifactId><version>9.4.1.jre8</version> <!-- 适配 SQL Server 2008 R2 的版本 -->
</dependency>

注意

  • SQL Server 2008 R2 建议使用 mssql-jdbc 6.0 或 7.x 版本(需 JDK 1.8+)。

  • 若使用旧版驱动(如 sqljdbc4),需手动下载 JAR 并添加到项目中。


三、配置 spy.properties

在 src/main/resources 下创建 spy.properties,配置 P6Spy 的日志行为和实际驱动:

properties

复制

下载

# spy.properties# 指定实际数据库驱动类(SQL Server)
driverlist=com.microsoft.sqlserver.jdbc.SQLServerDriver# 日志模块配置
module.log=com.p6spy.engine.logging.P6LogFactory
appender=com.p6spy.engine.spy.appender.Slf4JLogger# 自定义日志格式(可选)
logMessageFormat=com.example.config.P6SpyCustomLogger# 排除无关日志
excludecategories=info,debug,result,batc,resultset# 慢SQL监控(可选)
outagedetection=true
outagedetectioninterval=2
关键参数说明
  • driverlist:必须设置为 SQL Server 的驱动类 com.microsoft.sqlserver.jdbc.SQLServerDriver

  • appender:使用 Slf4JLogger 将日志输出到应用日志系统(如 Logback)。


四、处理 Windows 集成身份验证

若需使用 Windows 身份验证(非用户名/密码方式),需额外配置:

  1. 修改 URL

    properties

    复制

    下载

    spring.datasource.url=jdbc:p6spy:sqlserver://localhost:1433;databaseName=YourDBName;integratedSecurity=true
  2. 添加 sqljdbc_auth.dll

    • 从 Microsoft JDBC 驱动包 中提取 sqljdbc_auth.dll

    • 将文件放在 C:\Windows\System32(Windows)或 Java 库路径中。


五、验证配置

  1. 启动应用:检查控制台是否输出 P6Spy 的初始化日志,例如:

    复制

    下载

    P6SpyOptions 初始化成功
  2. 执行 SQL:触发数据库操作后,观察日志中是否包含完整的 SQL 语句:

    复制

    下载

    SQL执行耗时: 15ms | SQL语句: SELECT * FROM users WHERE id = 1
  3. 检查数据库连接:若出现连接失败,检查以下内容:

    • 驱动类名和 URL 是否正确。

    • SQL Server 服务是否运行(端口 1433 是否开放)。

    • 防火墙是否阻止连接。


六、完整配置示例

1. application.properties

properties

复制

下载

# 数据源配置
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.url=jdbc:p6spy:sqlserver://localhost:1433;databaseName=TestDB
spring.datasource.username=sa
spring.datasource.password=your_password# Hikari 连接池配置
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=10
2. spy.properties

properties

复制

下载

driverlist=com.microsoft.sqlserver.jdbc.SQLServerDriver
module.log=com.p6spy.engine.logging.P6LogFactory
appender=com.p6spy.engine.spy.appender.Slf4JLogger
logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
customLogMessageFormat=%(currentTime) | SQL: %(sqlSingleLine)

常见问题排查

问题解决方案
驱动类未找到检查 mssql-jdbc 依赖是否引入,或手动添加 JAR 包。
连接超时确认 SQL Server 实例已启动,且防火墙允许 1433 端口通信。
身份验证失败检查用户名/密码是否正确,或配置 integratedSecurity=true 及 sqljdbc_auth.dll
P6Spy 未生效确保 spy.properties 位于 src/main/resources 目录下。

通过以上配置,即可在 SQL Server 2008 R2 中通过 P6Spy 捕获 SQL 并持久化到数据库。

相关文章:

  • Redux 容器 | 原理解析
  • shell编程基础知识及脚本示例
  • 设计模式每日硬核训练 Day 16:责任链模式(Chain of Responsibility Pattern)完整讲解与实战应用
  • 分析型数据库入门指南:如何选择适合你的实时分析工具?
  • 哈希表基础
  • Ollama 在本地分析文件夹中的文件
  • 本安型交换机 + TSN:煤矿智能化的关键拼图
  • AI大模型从0到1记录学习 linux day21
  • 【论文阅读】-周总结-第5周
  • IDEA中使用Git
  • Vue2、Vue3区别之响应式原理
  • 深入理解 Java 单例模式:从基础到最佳实践
  • 【项目篇之垃圾回收】仿照RabbitMQ模拟实现消息队列
  • 查回来的数据除了 id,其他字段都是 null
  • 自然语言处理之机器翻译:注意力机制在低资源翻译中的突破与哲思
  • LeetCode每日一题4.27
  • 【dockerredis】用docker容器运行单机redis
  • C#中属性和字段的区别
  • pytorch搭建并训练神经网络
  • Golang 遇见 Kubernetes:云原生开发的完美结合
  • 全国电影工作会:聚焦扩大电影国际交流合作,提升全球影响力
  • 【社论】用生态环境法典守护生态文明
  • 伊朗南部港口火势蔓延,部分集装箱再次发生爆炸
  • 闲暇时间的“堕落”
  • 日均新开三家“首店”,上海的“首发经济”密码是什么?
  • 第152次中老缅泰湄公河联合巡逻执法行动圆满结束