在 MyBatis Java 中记录最后的 MySQL 查询,然后在不同的服务器上手动执行它

Log final MySQL query in MyBatis Java to execute it manually afterwards on a different server

我正在 JAVA 中开发命令行应用程序,其中,基于某些因素,MySQL 查询在沙盒 MySQL 服务器上执行,经过一些手动验证后,这些查询需要在生产服务器上执行。我想将这些 MySQL 查询记录在沙箱服务器上的 .sql 文件中,稍后使用脚本执行它。

我尝试通过堆栈溢出进行搜索,发现了很多记录查询的示例,但记录的查询并不是最终执行的查询。例如,

Preparing: INSERT into table_name values(null, ?,?, ?,?,?, ?, ?,?); 
DEBUG [pool-4-thread-1] - ==> Parameters: 21655(Integer), 2658413(Integer), 04:05:00(Time), null, 1565.0(Double), 3(String), (String), 0(Integer)

此外,查询未以直接可执行格式记录。

我的 log4j.properties 文件:

log4j.rootLogger=INFO, stdout
log4j.logger.com.test.mappers.TableMapper=TRACE
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
log4j.logger.java.sql.PreparedStatement=DEBUG, stdout

log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG

log4j.logger.com.ibatis=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG

我可以在 .sql 文件中单独记录准确的最终 MySQL 查询吗?

尝试了很多之后,我发现这仅适用于 Mybatis 和 log4j 是不可能的,它需要一个可以将请求记录到 JDBC 驱动程序的代理驱动程序。

我发现了将 log4jdbc2 from LOG4JDBC_SQL 标记一起使用以将 SQL 查询发送到单独文件的想法。但是,此文件将包含执行所需的近似时间和连接数。所以为了摆脱它们,我写了一个自定义的间谍日志委托器。

log4jdbc2的安装配置可以参考here.

我将重写为获得所需结果所采取的步骤:

  • 为 log4jdbc2 添加了 maven 依赖。 log4jdbc2 依赖于 log4j-api 和 log4j-core,确保它们包含在项目中。

    <dependency>
        <groupId>org.bgee.log4jdbc-log4j2</groupId>
        <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
        <version>1.16</version>
    </dependency>
    
  • 已将 JDBCURL 从 jdbc:mysql://localhost:3306/mydb 更改为 jdbc:log4jdbc:mysql://localhost:3306/mydb

  • 将驱动程序更改为 net.sf.log4jdbc.sql.jdbcapi.DriverSpy

  • 创建了一个 appender 并使用 LOG4JDBC_SQL 标记创建了一个记录器以仅过滤 sql 个查询。

    <Appenders>
        <File name="queryLog" fileName="/Users/sahebpreetsingh/queries.sql"
        immediateFlush="false" append="false">
            <PatternLayout pattern="%msg%n" />
        </File>
    </Appenders>
    
    <Loggers>
        <logger name="log4jdbc.log4j2" level="info" additivity="false">
            <MarkerFilter marker="LOG4JDBC_SQL" onMatch="ACCEPT"
            onMismatch="DENY" />
            <appender-ref ref="queryLog" />
        </logger>
    </Loggers>
    

    此处的标记指定仅过滤 sql 个查询并忽略其余结果。

  • 扩展了现有的 Log4j2SpyLogDelegator 以节省时间并仅以可执行形式记录 sql 查询。

    package com.myapp.utils;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.apache.logging.log4j.Marker;
    import org.apache.logging.log4j.MarkerManager;
    
    import net.sf.log4jdbc.Properties;
    import net.sf.log4jdbc.log.log4j2.Log4j2SpyLogDelegator;
    import net.sf.log4jdbc.sql.Spy;
    
    public class QueryLogDelegator extends Log4j2SpyLogDelegator {
    
        private static final Logger LOGGER = LogManager.getLogger("log4jdbc.log4j2");
    
        private static final Marker SQL_MARKER = MarkerManager.getMarker("LOG4JDBC_SQL");
    
        private static final Marker SELECT_MARKER = MarkerManager.getMarker("LOG4JDBC_SELECT", SQL_MARKER);
    
        private static final Marker INSERT_MARKER = MarkerManager.getMarker("LOG4JDBC_INSERT", SQL_MARKER);
    
        private static final Marker UPDATE_MARKER = MarkerManager.getMarker("LOG4JDBC_UPDATE", SQL_MARKER);
    
        private static final Marker DELETE_MARKER = MarkerManager.getMarker("LOG4JDBC_DELETE", SQL_MARKER);
    
        private static final Marker CREATE_MARKER = MarkerManager.getMarker("LOG4JDBC_CREATE", SQL_MARKER);
    
        private String getSqlOperation(String sql) {
            if (sql == null) {
                return "";
            }
            sql = sql.trim();
    
            if (sql.length() < 6) {
                return "";
            }
            return sql.substring(0, 6).toLowerCase();
        }
    
        private boolean shouldSqlBeLogged(String operation) {
            return ((operation == null) || ((Properties.isDumpSqlSelect()) && ("select".equals(operation)))
                    || ((Properties.isDumpSqlInsert()) && ("insert".equals(operation))) || ((Properties.isDumpSqlUpdate()) && ("update".equals(operation)))
                    || ((Properties.isDumpSqlDelete()) && ("delete".equals(operation))) || ((Properties.isDumpSqlCreate()) && ("create".equals(operation))));
        }
    
        @Override
        public void sqlTimingOccurred(Spy spy, long execTime, String methodCall, String sql) {
            String operation = getSqlOperation(sql);
            if ((Properties.isDumpSqlFilteringOn()) && (!(shouldSqlBeLogged(operation)))) {
                return;
            }
    
            Marker marker = getStatementMarker(operation);
    
            if ((Properties.isSqlTimingErrorThresholdEnabled()) && (execTime >= Properties.getSqlTimingErrorThresholdMsec())) {
                LOGGER.error(marker, sql);
            } else if (LOGGER.isWarnEnabled())
                if ((Properties.isSqlTimingWarnThresholdEnabled()) && (execTime >= Properties.getSqlTimingWarnThresholdMsec())) {
                    LOGGER.warn(marker, sql);
                } else
                    LOGGER.info(marker, sql);
        }
    
        private Marker getStatementMarker(String operation) {
            if (operation == null)
                return SQL_MARKER;
            if ("select".equals(operation))
                return SELECT_MARKER;
            if ("insert".equals(operation))
                return INSERT_MARKER;
            if ("update".equals(operation))
                return UPDATE_MARKER;
            if ("delete".equals(operation))
                return DELETE_MARKER;
            if ("create".equals(operation)) {
                return CREATE_MARKER;
            }
            return SQL_MARKER;
        }
    
    }
    
  • 将记录器添加到 log4jdbc。log4j2.properties

    log4jdbc.dump.sql.addsemicolon=true
    log4jdbc.spylogdelegator.name=com.myapp.QueryLogDelegator