java 如何从 PreparedStatement 获取参数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4691521/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 07:33:11  来源:igfitidea点击:

How to get parameters from PreparedStatement?

javasqljdbcparameterssqlexception

提问by IAdapter

I'm writing generic logger for SQLException and I'd like to get parameters that were passed into PreparedStatement, how to do it ? I was able to get the count of them.

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

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

回答by Aaron Digulla

Short answer: You can't.

简短的回答:你不能。

Long answer: All JDBC drivers will keep the parameter values somewhere but there is no standard way to get them.

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

If you want to print them for debugging or similar purposes, you have several options:

如果您想出于调试或类似目的打印它们,您有多种选择:

  1. Create a pass-through JDBC driver (use p6spy or log4jdbc as a basis) which keeps copies of the parameters and offers a public API to read them.

  2. Use Java Reflection API (Field.setAccessible(true)is your friend) to read the private data structures of the JDBC drivers. That's my preferred approach. I have a factory which delegates to DB specific implementations that can decode the parameters and that allows me to read the parameters via getObject(int column).

  3. File a bug report and ask that the exceptions are improved. Especially Oracle is really stingy when it comes to tell you what's wrong.

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

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

  3. 提交错误报告并要求改进异常。尤其是 Oracle 在告诉你什么是错误的时候真的很吝啬。

回答by voho

Solution 1: Subclass

解决方案 1:子类

Simply create a custom implementation of a PreparedStatementwhich delegates all calls to the original prepared statement, only adding callbacks in the setObject, etc. methods. Example:

只需创建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);
            }
        };
    }

If you want to save parameters and get them later, there are many solutions, but I prefer creating a new class like ParameterAwarePreparedStatementwhich has the parameters in a map. The structure could be similar to this:

如果你想保存参数并在以后获取它们,有很多解决方案,但我更喜欢创建一个像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);
    }
}

Solution 2: Dynamic proxy

方案二:动态代理

This second solution is shorter, but seems more hacky.

第二个解决方案更短,但似乎更hacky。

You can create a dynamic proxy by calling a factory method on java.lang.reflect.Proxyand delegate all calls on the original instance. Example:

您可以通过调用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;
}

Then you intercept the setObject, etc. calls by looking at method names and looking to the second method arguments for your values.

然后通过查看方法名称并查看值的第二个方法参数来拦截setObject等调用。

回答by Riduidel

This article, from Boulder, ahtoulgh DB 2 "specific", gives a complete example of ParameterMetadata usage.

这篇文章来自 Boulder,ahtoulgh DB 2 “specific”,给出了一个完整的 ParameterMetadata 用法示例。