2011-01-14 231 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,等电话。