【问题标题】:How to get parameters from PreparedStatement?如何从 PreparedStatement 中获取参数?
【发布时间】:2011-06-09 03:46:05
【问题描述】:

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

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

【问题讨论】:

    标签: java sql jdbc parameters sqlexception


    【解决方案1】:

    解决方案 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】:

      简短回答:你不能。

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

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

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

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

      3. 提交错误报告并要求改进异常。尤其是甲骨文在告诉你问题的时候真的很吝啬。

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          猜你喜欢
          • 2010-11-10
          • 2011-01-23
          • 1970-01-01
          • 1970-01-01
          • 2014-11-24
          • 2022-08-24
          • 2020-01-31
          相关资源
          最近更新 更多