如何从PreparedStatement获取参数?

时间:2011-01-14 13:28:55

标签: java sql jdbc parameters sqlexception

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

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

3 个答案:

答案 0 :(得分:19)

简短的回答:你不能。

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

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

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

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

  3. 提交错误报告,并要求改进例外情况。特别是当谈到你出了什么问题时,甲骨文真的很吝啬。

答案 1 :(得分:6)

解决方案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 等调用。

答案 2 :(得分:1)

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