在 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
我正在 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
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