spring
SimpleJdbcCallからSqlRowSetを取得する
サーチ…
前書き
これは、直接カーソル出力パラメータを持つデータベース内のストアドプロシージャで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_CURSOR
をOracleTypes.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))