我正在为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,等电话。