我有一个spring启动应用程序,它使用一个池 HikaryDataSource
以及 NamedParameterJdbcTemplate
访问数据库。应用程序不使用hibernate。
出于审核原因,我需要分别存储每个修改sql查询的副本。
因此,我正在寻找在替换命名和/或位置参数之后拦截发送到数据库的sql查询的方法。
在过去,我使用p6spy来记录这样的语句,但是在当前用例中,将它们记录到日志文件是不够的。
经过一番搜索,我试图使用 ProxyDataSource
通过如下方式覆盖自动配置的数据源:
@Configuration
public class BackendConfiguration {
@Value("${spring.datasource.hikari.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.hikari.jdbc-url}")
private String url;
@Value("${spring.datasource.hikari.username}")
private String username;
@Value("${spring.datasource.hikari.password}")
private String password;
private DataSource hikaryDataSource() {
try {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(driverClassName);
hikariConfig.setJdbcUrl(url);
hikariConfig.setUsername(username);
hikariConfig.setPassword(password);
hikariConfig.setMaximumPoolSize(5);
hikariConfig.setConnectionTestQuery("SELECT 1");
hikariConfig.setPoolName("jdbc-pool");
return new HikariDataSource(hikariConfig);
} catch (Exception e) {
return null;
}
}
@Bean
public DataSource dataSource() {
return ProxyDataSourceBuilder
.create(hikaryDataSource())
.name("jdbc-proxy-datasource")
.beforeQuery(queryInterceptor("before"))
.afterQuery(queryInterceptor("after"))
.proxyResultSet()
.build();
}
private SingleQueryExecution queryInterceptor(final String position) {
return (ExecutionInfo execInfo, List<QueryInfo> queryInfoList) ->
queryInfoList.stream()
.map(QueryInfo::getQuery)
.forEach(query -> log.info("{} query={}", position, query));
}
}
然而,这种方法似乎截获得太早了。命名参数(例如 :id
)已替换为位置参数(例如 ?1
)但有效值尚未插入到before或after拦截器中。
我也试过使用 queryTransformer()
以及 listener()
上的方法 ProxyDataSourceBuilder
但结果是一样的。
是 ProxyDataSource
这个方法不对吗?我需要在医院做这个吗 JdbcConnection
如果是,我该怎么做?或者我只是使用了错误的 ProxyDataSource
?
编辑:
我决定多和你玩一会儿。而不是扭曲我的身体 HikaryDataSource
在一个 ProxyDataSource
我现在把它包起来 P6DataSource
并通过在spy.properties中添加以下行来提供自定义日志格式化程序
logMessageFormat=my.package.P6SpyLogFormatter
现在有机会拦截原始sql:
public class P6SpyLogFormatter implements MessageFormattingStrategy {
private final MessageFormattingStrategy defaultFormatter = new SingleLineFormat();
@Override
public String formatMessage(final int connectionId, final String now, final long elapsed, final String category, final String prepared, final String sql, final String url) {
interceptRaw(sql);
return defaultFormatter.formatMessage(connectionId, now, elapsed, category, prepared, sql, url);
}
private void interceptRawSqlfinal String sql) {
// do my thing here
}
}
原则上,这是很好的工作,但有一些缺点,我想做没有:
只有当p6spy日志级别被打开到足以写入日志时,才会调用格式化程序,一旦禁用它们,格式化程序就不再被调用,因此拦截程序也不再被调用。我想我可以通过提供我自己的logappender来解决这个问题,这个logappender只是丢弃了记录的行,但是这看起来像是一个黑客
我很犹豫是否要用p6spy启动我的产品代码
因此,如果有人有一个好主意如何做到这一点,我热切期待任何想法。
暂无答案!
目前还没有任何答案,快来回答吧!