update 优化 sql输出拦截器 支持根据表名忽略sql

This commit is contained in:
疯狂的狮子Li 2026-10-26 18:38:20 +08:00
parent dce62a9d5d
commit 42e7196b13
5 changed files with 124 additions and 0 deletions

View File

@ -82,6 +82,8 @@ mybatis-plus:
enabled: true
# 输出方式,可选 console、log
output: console
# 忽略高频轮询表的成功 SQL 日志;执行异常仍会输出
ignore-tables: [flow_event_wait]
--- # 数据源配置
spring:

View File

@ -85,6 +85,8 @@ mybatis-plus:
enabled: false
# 输出方式,可选 console、log
output: console
# 忽略高频轮询表的成功 SQL 日志;执行异常仍会输出
ignore-tables: [ flow_event_wait ]
--- # 数据源配置
spring:

View File

@ -3,6 +3,9 @@ package org.dromara.common.mybatis.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
* SQL 日志配置。
*
@ -22,4 +25,9 @@ public class SqlLogProperties {
*/
private String output = "console";
/**
* 不输出成功执行日志的表名;执行异常仍会输出。
*/
private List<String> ignoreTables = new ArrayList<>();
}

View File

@ -30,6 +30,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
/**
* 完整 SQL 日志拦截器。
@ -55,6 +56,8 @@ public class SqlLogInterceptor implements Interceptor {
*/
private static final String BLANK_REGEX = "\\s+";
private static final Pattern SQL_STRING_LITERAL = Pattern.compile("'(?:''|[^'])*'");
/**
* 日期时间格式。
*/
@ -75,8 +78,17 @@ public class SqlLogInterceptor implements Interceptor {
*/
private final SqlLogProperties sqlLogProperties;
private final List<Pattern> ignoredTablePatterns;
public SqlLogInterceptor(SqlLogProperties sqlLogProperties) {
this.sqlLogProperties = sqlLogProperties;
this.ignoredTablePatterns = sqlLogProperties.getIgnoreTables().stream()
.filter(StrUtil::isNotBlank)
.map(String::trim)
.map(table -> Pattern.compile("(?i)\\b(?:FROM|JOIN|UPDATE|INTO)\\s+"
+ "(?:[`\"\\[]?[A-Za-z_][A-Za-z0-9_]*[`\"\\]]?\\s*\\.\\s*)?"
+ "[`\"\\[]?" + Pattern.quote(table) + "[`\"\\]]?(?=\\s|[,);]|$)"))
.toList();
}
@Override
@ -104,11 +116,27 @@ public class SqlLogInterceptor implements Interceptor {
* @param throwable 执行异常
*/
private void printSql(MappedStatement mappedStatement, BoundSql boundSql, long elapsedTime, Throwable throwable) {
if (throwable == null && isIgnoredTableSql(boundSql.getSql())) {
return;
}
String fullSql = buildFullSql(mappedStatement.getConfiguration(), boundSql);
String message = buildLogMessage(mappedStatement, elapsedTime, fullSql, throwable);
printChunk(message);
}
boolean isIgnoredTableSql(String sql) {
if (ignoredTablePatterns.isEmpty()) {
return false;
}
String statement = sql.indexOf('\'') >= 0 ? SQL_STRING_LITERAL.matcher(sql).replaceAll("''") : sql;
for (Pattern pattern : ignoredTablePatterns) {
if (pattern.matcher(statement).find()) {
return true;
}
}
return false;
}
/**
* 构建日志内容。
*

View File

@ -0,0 +1,84 @@
package org.dromara.common.mybatis.interceptor;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.dromara.common.mybatis.config.properties.SqlLogProperties;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.Statement;
import java.util.List;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SqlLogInterceptorTest {
@Test
void ignoredTableStillExecutesAndOtherTableStillLogs() throws Exception {
SqlLogProperties properties = new SqlLogProperties();
properties.setIgnoreTables(List.of("flow_event_wait"));
SqlLogInterceptor interceptor = new SqlLogInterceptor(properties);
String url = "jdbc:h2:mem:sql_log_" + UUID.randomUUID().toString().replace("-", "") + ";DB_CLOSE_DELAY=-1";
UnpooledDataSource dataSource = new UnpooledDataSource("org.h2.Driver", url, "sa", "");
try (Connection connection = dataSource.getConnection(); Statement statement = connection.createStatement()) {
statement.execute("CREATE TABLE flow_event_wait (id BIGINT PRIMARY KEY)");
statement.execute("CREATE TABLE audit_record (id BIGINT PRIMARY KEY)");
statement.execute("INSERT INTO flow_event_wait VALUES (1)");
statement.execute("INSERT INTO audit_record VALUES (2)");
}
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setEnvironment(new Environment("h2", new JdbcTransactionFactory(), dataSource));
configuration.addInterceptor(interceptor);
configuration.addMapper(LogMapper.class);
SqlSessionFactory factory = new MybatisSqlSessionFactoryBuilder().build(configuration);
PrintStream originalErr = System.err;
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (PrintStream capture = new PrintStream(output, true, StandardCharsets.UTF_8)) {
System.setErr(capture);
try (SqlSession session = factory.openSession()) {
assertEquals(1L, session.getMapper(LogMapper.class).eventWaitId());
assertEquals(2L, session.getMapper(LogMapper.class).auditId());
}
} finally {
System.setErr(originalErr);
}
String logs = output.toString(StandardCharsets.UTF_8);
assertFalse(logs.contains("flow_event_wait"));
assertTrue(logs.contains("audit_record"));
}
@Test
void matchesTableReferencesWithoutMatchingLiteralsOrSimilarNames() {
SqlLogProperties properties = new SqlLogProperties();
properties.setIgnoreTables(List.of("flow_event_wait"));
SqlLogInterceptor interceptor = new SqlLogInterceptor(properties);
assertTrue(interceptor.isIgnoredTableSql("select * from flow_event_wait where id = ?"));
assertTrue(interceptor.isIgnoredTableSql("select * from `app`.`flow_event_wait`"));
assertTrue(interceptor.isIgnoredTableSql("select * from audit_record join FLOW_EVENT_WAIT w on w.id = audit_record.id"));
assertTrue(interceptor.isIgnoredTableSql("update flow_event_wait set status = ?"));
assertFalse(interceptor.isIgnoredTableSql("select 'from flow_event_wait' from audit_record"));
assertFalse(interceptor.isIgnoredTableSql("select * from flow_event_wait_history"));
}
interface LogMapper {
@Select("SELECT id FROM flow_event_wait")
Long eventWaitId();
@Select("SELECT id FROM audit_record")
Long auditId();
}
}