【概要】
TraceablePreparedStatementは、S2Daoで発行した「SQLの実行情報」を
取得するためのPreparedStatementです。
S2JDBCがDEBUGで出力するような表示用の「Bind変数に値を埋め込んだSQL」
を取得することも可能です。
【環境構築】
DBFlute-0.5.2ではデフォルトで生成されますので気にする必要がありません。
DBFlute-0.5.3以降ではデフォルトで生成しませんので、
build-[dbname].propertiesに
isMakeTraceablePreparedStatement = true
を追加して下さい。また、TraceablePreparedStatementはPreparedStatementに依存していますので、
Java 6 を利用の方は、build-[dbname].propertiesに
targetLanguageVersion = 6.0
を追加して下さい。
【利用方法】
TraceablePreparedStatementはSQLの情報を取得するところまでを受け持ちます。
その取得した情報をアプリケーションで利用するのに、一般的に以下の方法で実現します。「TraceablePreparedStatementで取得した情報をアプリ独自のThreadLocalに配置」
= = = = = = = = = = = = = = = = = = = = = = = = = = =
ex) 1回のリクエストで発行したSQL文をDBに格納する場合
= = = = = = = = = = = = = = = = = = = = = = = = = = =[アプリで作成するもの]
A. SQL情報を格納するThreadLocal
B. 独自のStatementFactory
C. リクエスト最後をフックするInterceptor
(TeedaであればPage、ServletFilterでも可)
A. SQL情報を格納するThreadLocal
// Requestで発行されたSQLを蓄積していくThreadLocalを管理するClass
public class RequestSqlContext {private static ThreadLocal
threadLocal = new ThreadLocal (); public static RequestSqlContext getRequestSqlContext() {
return threadLocal.get();
}public static void setRequestSqlContext(RequestSqlContext requestSqlContext) {
threadLocal.set(requestSqlContext);
}protected java.util.List
sqlList = new java.util.ArrayList (); public java.util.List
getSqlList() {
return sqlList;
}public void addSql(String sql) {
sqlList.add(sql);
}
}
B. 独自のStatementFactory
// 本物のPreparedStatementをWrapしてTraceablePreparedStatementを生成するStatementFactory
// TraceablePreparedStatementに設定するStatementHistoryWitness(履歴の目撃者!?)も同時に設定する。
// StatementHistoryWitnessは、TraceablePreparedStatement内部でCallbackされるwitnessTheHistory()を実装して、
// その中で、RequestSqlContextにSQLを詰め込む。
public class TraceableStatementFactory extends ...allcommon.s2dao.S2DaoStatementFactory {public PreparedStatement createPreparedStatement(Connection con, String sql) {
final PreparedStatement ps = super.createPreparedStatement(con, sql);
return new TraceablePreparedStatement(ps, sql, createStatementHistoryWitness());
}protected StatementHistoryWitness createStatementHistoryWitness() {
final StatementHistoryWitness statementHistoryWitness = new StatementHistoryWitness() {
public void witnessTheHistory(StatementHistory statementHistory) {
final ListexecutionBeanList = statementHistory.getPreparedExecutionBeanList();
for (PreparedExecutionBean executionBean : executionBeanList) {
final String displaySql = executionBean.getDisplaySql();
RequestSqlContext.addSql(displaySql);
}
}
};
return statementHistoryWitness;
}
}★jdbc.diconにて StatementFactory の指定を TraceableStatementFactory にして下さい。★
C. リクエスト最後をフックするInterceptor
(TeedaであればPage、ServletFilterでも可)public class RequestSqlTraceInterceptor extends AbstractInterceptor {
private SqlTraceTableBhv sqlTraceTableBhv;
public Object invoke(final MethodInvocation invocation) throws Throwable {
try {
// ThreadLocalにRequestSqlContextを格納
final RequestSqlContext requestSqlContext = new RequestSqlContext();
RequestSqlContext.setRequestSqlContext(requestSqlContext);return invocation.proceed();
} finally {
try {
final RequestSqlContext requestSqlContext = RequestSqlContext.getRequestSqlContext
final SqlTraceTable entity = new SqlTraceTable();
entity.setRequestSql(sqlList.toString());
sqlTraceTableBhv.insert(entity);
} catch (Throwable continued) {
// SQLトレースの処理中に何が起きても例外はthrowしない。
// 下手にthrowすると無限ループに陥る可能性もあるため。
log.warn("Skip insert sql trace!", continued);
}// ThreadLocalからRequestSqlContextを廃棄
RequestSqlContext.setRequestSqlContext(null);
}
}
}
無論、アプリケーションでの色々調整があって、全く上記の通りではないと
思いますので、その辺は各アプリケーションで自由に実装して下さい。
#
# S2Daoのバッチ更新「insertList()/updateList()」にも対応しています。
# 更新した分のSQL文が取得されます。
#