web-dev-qa-db-ja.com

PreparedStatementからパラメータを取得する方法は?

SQLExceptionの汎用ロガーを作成していますが、PreparedStatementに渡されたパラメーターを取得したいのですが、どうすればよいですか?私はそれらの数を得ることができました。

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

簡単な答え:できません。

長い答え:すべてのJDBCドライバーはパラメーター値をどこかに保持しますが、それらを取得する標準的な方法はありません。

デバッグまたは同様の目的でそれらを印刷する場合は、いくつかのオプションがあります。

  1. パラメーターのコピーを保持し、それらを読み取るためのパブリックAPIを提供するパススルーJDBCドライバーを作成します(基本としてp6spyまたはlog4jdbcを使用します)。

  2. Java Reflection API(Field.setAccessible(true)はあなたの友達です)を使用して、JDBCドライバーのプライベートデータ構造を読み取ります。これが私の推奨されるアプローチです。DB固有に委任するファクトリがあります。パラメータをデコードでき、getObject(int column)を介してパラメータを読み取ることができる実装。

  3. バグレポートを提出し、例外の改善を依頼してください。特にオラクルは、何が悪いのかを教えてくれると本当にけちです。

20
Aaron Digulla

解決策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:動的プロキシ

この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;
}

次に、メソッド名を調べ、値の2番目のメソッド引数を調べることにより、setObjectなどの呼び出しをインターセプトします。

6
voho

この記事 、ボルダー、ahtoulgh DB 2 "specific"から、ParameterMetadataの使用法の完全な例が示されています。

1
Riduidel