我正在爲SQLException編寫通用記錄器,我想獲取傳入PreparedStatement的參數,如何執行?我能夠得到他們的數量。如何從PreparedStatement獲取參數?
ParameterMetaData metaData = query.getParameterMetaData();
parameterCount = metaData.getParameterCount();
我正在爲SQLException編寫通用記錄器,我想獲取傳入PreparedStatement的參數,如何執行?我能夠得到他們的數量。如何從PreparedStatement獲取參數?
ParameterMetaData metaData = query.getParameterMetaData();
parameterCount = metaData.getParameterCount();
簡答:你不行。
長答案:所有JDBC驅動程序都會將參數值保存在某處,但沒有標準的方法來獲取它們。
如果你想打印它們進行調試或類似用途的,你有幾種選擇:
創建直通JDBC驅動程序(使用P6SPY或log4jdbc爲基礎),這使該參數的副本並提供一個公共API來閱讀它們。
使用Java Reflection API(Field.setAccessible(true)
是你的朋友)讀取JDBC驅動程序的私有數據結構。這是我首選的方法。我有一個代表數據庫特定實現的工廠,可以對參數進行解碼,並允許我通過getObject(int column)
讀取參數。
提交錯誤報告,並要求改善例外情況。尤其是甲骨文在告訴你什麼是錯的時候真的很吝嗇。
This article,自Boulder,ahtoulgh DB 2 「特異性的」,給人ParameterMetadata使用的一個完整的示例。
只需創建一個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);
}
}
這第二種解決方案是短,但似乎更哈克。
您可以通過調用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;
}
然後你通過看方法名,展望爲你的價值觀第二種方法的參數攔截的setObject,等電話。