サーチ…


前書き

これは、直接カーソル出力パラメータを持つデータベース内のストアドプロシージャでSimpleJdbcCallを使用してSqlRowSetを取得する方法について説明し、

私はOracleデータベースで作業しています。私は他のデータベースでも動作するはずのサンプルを作成しようとしました。

SimpleJdbcCallの作成

通常は、SimpleJdbcCallsをサービス内に作成する必要があります。

この例では、プロシージャーにカーソルである単一の出力パラメーターがあると想定しています。プロシージャに合わせてdeclareParametersを調整する必要があります。

@Service
public class MyService() {

@Autowired
    private DataSource dataSource;
    
    // Autowire your configuration, for example
    @Value("${db.procedure.schema}")
    String schema;
    
    private SimpleJdbcCall myProcCall;
    
    // create SimpleJdbcCall after properties are configured
    @PostConstruct
    void initialize() {
        this.myProcCall = new SimpleJdbcCall(dataSource)
                        .withProcedureName("my_procedure_name")
                        .withCatalogName("my_package")
                        .withSchemaName(schema)
                        .declareParameters(new SqlOutParameter(
                            "out_param_name",
                            Types.REF_CURSOR, 
                            new SqlRowSetResultSetExtractor()));
    }

    public SqlRowSet myProc() {
        Map<String, Object> out = this.myProcCall.execute();
        return (SqlRowSet) out.get("out_param_name");
    }

}

ここでは多くのオプションを使用できます:

  • withoutProcedureColumnMetaDataAccess()は、プロシージャー名がオーバーロードされているか、SimpleJdbcCallがデータベースに対して妥当性を確認しないようにするために必要です。
  • プロシージャに戻り値がある場合はwithReturnValue() 。 declareParametersに与えられた最初の値は戻り値を定義します。また、プロシージャが関数の場合は、実行時にwithFunctionNameおよびexecuteFunctionを使用します。
  • positionの代わりに名前を使用する場合は、 withNamedBinding()を使用します。
  • useInParameterNames()は、引数の順序を定義します。私は、引数名をvalueにマップするのではなく、リストとして引数を渡す場合、これが必要になると思います。 withoutProcedureColumnMetaDataAccess()を使用する場合にのみ必要ですが、

Oracleデータベース

Oracleにはいくつかの問題があります。解決方法は次のとおりです。

プロシージャの出力パラメータがref cursorであると仮定すると、この例外が発生します。

java.sql.SQLException:無効な列の型:2012

simpleJdbcCall.declareParameters()の Types.REF_CURSOROracleTypes.CURSORに変更しTypes.REF_CURSOR


OracleTypesのサポート

データに特定の列タイプがある場合にのみ、これを行う必要があります。

次の問題は、 oracle.sql.TIMESTAMPTZなどの独自の型がSqlRowSetResultSetExtractorでこのエラーを発生させたことです。

列のSQL型が無効です。ネストされた例外はjava.sql.SQLExceptionです:列のSQL型が無効です

したがって、Oracle型をサポートするResultSetExtractorを作成する必要があります。
私はこのコードの後に​​パスワードの理由を説明します。

package com.boost.oracle;

import oracle.jdbc.rowset.OracleCachedRowSet;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSet;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * OracleTypes can cause {@link org.springframework.jdbc.core.SqlRowSetResultSetExtractor}
 * to fail due to a Oracle SQL type that is not in the standard {@link java.sql.Types}.
 *
 * Also, types such as {@link oracle.sql.TIMESTAMPTZ} require a Connection when processing
 * the ResultSet; {@link OracleCachedRowSet#getConnectionInternal()} requires a JNDI
 * DataSource name or the username and password to be set.
 *
 * For now I decided to just set the password since changing SpringBoot to a JNDI DataSource
 * configuration is a bit complicated.
 *
 * Created by Arlo White on 2/23/17.
 */
public class OracleSqlRowSetResultSetExtractor implements ResultSetExtractor<SqlRowSet> {

    private String oraclePassword;

    public OracleSqlRowSetResultSetExtractor(String oraclePassword) {
        this.oraclePassword = oraclePassword;
    }

    @Override
    public SqlRowSet extractData(ResultSet rs) throws SQLException, DataAccessException {
        OracleCachedRowSet cachedRowSet = new OracleCachedRowSet();
        // allows getConnectionInternal to get a Connection for TIMESTAMPTZ
        cachedRowSet.setPassword(oraclePassword);
        cachedRowSet.populate(rs);
        return new ResultSetWrappingSqlRowSet(cachedRowSet);
    }

}

特定のOracleタイプでは、ConnectionがResultSetから列の値を取得する必要があります。 TIMESTAMPTZは、これらのタイプの1つです。したがって、 rowSet.getTimestamp(colIndex)が呼び出されると、この例外が発生します。

原因:java.sql.SQLException:oracle.jdbc.rowset.OracleCachedRowSet.getTimestamp(OracleCachedRowSet.java)のoracle.jdbc.rowset.OracleCachedRowSet.getConnectionInternal(OracleCachedRowSet.java:560)に1つ以上の認証用RowSetプロパティが設定されていません。 :3717)at org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet.getTimestamp

このコードを調べると、Connectionを取得するためにOracleCachedRowSetにパスワードまたはJNDIデータソース名が必要であることがわかります。 JNDIルックアップを使用する場合は、OracleCachedRowSetにDataSourceNameが設定されていることを確認してください。

だから、私のサービスでは、私はAutowireのパスワードで、このような出力パラメータを宣言します:

new SqlOutParameter("cursor_param_name", OracleTypes.CURSOR, new OracleSqlRowSetResultSetExtractor(oraclePassword))


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow