2011-01-14 234 views
15

我正在爲SQLException編寫通用記錄器,我想獲取傳入PreparedStatement的參數,如何執行?我能夠得到他們的數量。如何從PreparedStatement獲取參數?

ParameterMetaData metaData = query.getParameterMetaData(); 
parameterCount = metaData.getParameterCount(); 

回答

16

簡答:你不行。

長答案:所有JDBC驅動程序都會將參數值保存在某處,但沒有標準的方法來獲取它們。

如果你想打印它們進行調試或類似用途的,你有幾種選擇:

  1. 創建直通JDBC驅動程序(使用P6SPY或log4jdbc爲基礎),這使該參數的副本並提供一個公共API來閱讀它們。

  2. 使用Java Reflection API(Field.setAccessible(true)是你的朋友)讀取JDBC驅動程序的私有數據結構。這是我首選的方法。我有一個代表數據庫特定實現的工廠,可以對參數進行解碼,並允許我通過getObject(int column)讀取參數。

  3. 提交錯誤報告,並要求改善例外情況。尤其是甲骨文在告訴你什麼是錯的時候真的很吝嗇。

1

This article,自Boulder,ahtoulgh DB 2 「特異性的」,給人ParameterMetadata使用的一個完整的示例。

7

解決方案1:子類

只需創建一個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; 
} 

然後你通過看方法名,展望爲你的價值觀第二種方法的參數攔截的setObject,等電話。