java—如何使用SpringJDBCTemplate拦截原始sql

gtlvzcf8  于 2021-06-30  发布在  Java
关注(0)|答案(0)|浏览(620)

我有一个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启动我的产品代码
因此,如果有人有一个好主意如何做到这一点,我热切期待任何想法。

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题