如何从PreparedStatement获取参数?

我正在为SQLException编写通用记录器,我想获取传递给PreparedStatement的参数,该怎么做? 我能够得到他们的数量。

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

简短的回答:你做不到。

答案很简单:所有JDBC驱动程序都会将参数值保留在某处,但没有标准的方法来获取它们。

如果要打印它们以进行调试或类似目的,您有以下几种选择:

  1. 创建传递JDBC驱动程序(使用p6spy或log4jdbc作为基础),它保留参数的副本并提供公共API来读取它们。

  2. 使用Java Reflection API( Field.setAccessible(true)是您的朋友)来读取JDBC驱动程序的私有数据结构。 这是我的首选方法。 我有一个工厂,它委托DB特定的实现,可以解码参数,并允许我通过getObject(int column)读取参数。

  3. 提交错误报告并询问exception是否得到改善。 特别是Oracle在告诉你什么是错的时候真的很吝啬。

解决方案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 parameters; public ParameterAwarePreparedStatement(PreparedStatement delegate) { this.delegate = delegate; this.parameters = new HashMap<>(); } public Map 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等调用。

本文来自Boulder,ahtoulgh DB 2“specific”,给出了ParameterMetadata使用的完整示例。