SQLExceptionの汎用ロガーを作成していますが、PreparedStatementに渡されたパラメーターを取得したいのですが、どうすればよいですか?私はそれらの数を得ることができました。
ParameterMetaData metaData = query.getParameterMetaData();
parameterCount = metaData.getParameterCount();
簡単な答え:できません。
長い答え:すべてのJDBCドライバーはパラメーター値をどこかに保持しますが、それらを取得する標準的な方法はありません。
デバッグまたは同様の目的でそれらを印刷する場合は、いくつかのオプションがあります。
パラメーターのコピーを保持し、それらを読み取るためのパブリックAPIを提供するパススルーJDBCドライバーを作成します(基本としてp6spyまたはlog4jdbcを使用します)。
Java Reflection API(Field.setAccessible(true)
はあなたの友達です)を使用して、JDBCドライバーのプライベートデータ構造を読み取ります。これが私の推奨されるアプローチです。DB固有に委任するファクトリがあります。パラメータをデコードでき、getObject(int column)
を介してパラメータを読み取ることができる実装。
バグレポートを提出し、例外の改善を依頼してください。特にオラクルは、何が悪いのかを教えてくれると本当にけちです。
PreparedStatementのカスタム実装を作成するだけで、すべての呼び出しを元のプリペアドステートメントに委任し、setObjectなどのメソッドにコールバックを追加するだけです。例:
public PreparedStatement prepareStatement(String sql) {
final PreparedStatement delegate = conn.prepareStatement(sql);
return new PreparedStatement() {
// TODO: much more methods to delegate
@Override
public void setString(int parameterIndex, String x) throws SQLException {
// TODO: remember value of X
delegate.setString(parameterIndex, x);
}
};
}
パラメータを保存して後で取得したい場合は、多くの解決策がありますが、マップにパラメータを持つParameterAwarePreparedStatementのような新しいクラスを作成することをお勧めします。構造は次のようになります。
public class ParameterAwarePreparedStatement implements PreparedStatement {
private final PreparedStatement delegate;
private final Map<Integer,Object> parameters;
public ParameterAwarePreparedStatement(PreparedStatement delegate) {
this.delegate = delegate;
this.parameters = new HashMap<>();
}
public Map<Integer,Object> getParameters() {
return Collections.unmodifiableMap(parameters);
}
// TODO: many methods to delegate
@Override
public void setString(int parameterIndex, String x) throws SQLException {
delegate.setString(parameterIndex, x);
parameters.put(parameterIndex, x);
}
}
この2番目の解決策は短いですが、よりハッキーなようです。
Java.lang.reflect.Proxyでファクトリメソッドを呼び出して動的プロキシを作成し、元のインスタンスですべての呼び出しを委任できます。例:
public PreparedStatement prepareStatement(String sql) {
final PreparedStatement ps = conn.prepareStatement(sql);
final PreparedStatement psProxy = (PreparedStatement) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{PreparedStatement.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("setLong")) {
// ... your code here ...
}
// this invokes the default call
return method.invoke(ps, args);
}
});
return psProxy;
}
次に、メソッド名を調べ、値の2番目のメソッド引数を調べることにより、setObjectなどの呼び出しをインターセプトします。
この記事 、ボルダー、ahtoulgh DB 2 "specific"から、ParameterMetadataの使用法の完全な例が示されています。