spring
Получение SqlRowSet из SimpleJdbcCall
Поиск…
Вступление
Это описывает, как напрямую получить SqlRowSet с помощью SimpleJdbcCall с хранимой процедурой в вашей базе данных, которая имеет параметр вывода курсора ,
Я работаю с базой данных Oracle, я попытался создать пример, который должен работать для других баз данных, мои примеры примеров Oracle для 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, определяет возвращаемое значение. Кроме того, если ваша процедура является функцией, используйте при вызове функции FunctionName и executeFunction .
- withNamedBinding (), если вы хотите дать аргументы, используя имена вместо позиции.
- useInParameterNames () определяет порядок аргументов. Я думаю, это может потребоваться, если вы передадите свои аргументы в виде списка вместо карты имени аргумента для значения. Хотя это может потребоваться только в том случае, если вы используете withoutProcedureColumnMetaDataAccess ()
Базы данных Oracle
Существует ряд проблем с Oracle. Вот как их решить.
Предполагая, что ваш выходной параметр процедуры является ref cursor
, вы получите это исключение.
java.sql.SQLException: Недопустимый тип столбца: 2012
Поэтому измените Types.REF_CURSOR
на OracleTypes.CURSOR
в simpleJdbcCall.declareParameters ()
Поддержка OracleTypes
Вам может понадобиться сделать это, только если у вас есть определенные типы столбцов в ваших данных.
Следующая проблема, с которой я столкнулся, заключалась в том, что проприетарные типы, такие как oracle.sql.TIMESTAMPTZ
вызвали эту ошибку в SqlRowSetResultSetExtractor:
Недопустимый тип SQL для столбца; Вложенное исключение - java.sql.SQLException: недопустимый тип SQL для столбца
Поэтому нам нужно создать ResultSetExtractor, который поддерживает типы Oracle.
Я объясню причину пароля после этого кода.
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 требуется подключение для получения значения столбца из ResultSet. TIMESTAMPTZ является одним из этих типов. Поэтому, когда rowSet.getTimestamp(colIndex)
, вы получите это исключение:
Вызывается: java.sql.SQLException: Один или несколько свойств аутентификации RowSet, не заданных в oracle.jdbc.rowset.OracleCachedRowSet.getConnectionInternal (OracleCachedRowSet.java:560) в oracle.jdbc.rowset.OracleCachedRowSet.getTimestamp (OracleCachedRowSet.java : 3717) в org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet.getTimestamp
Если вы вникнете в этот код, вы увидите, что для получения соединения для OracleCachedRowSet требуется пароль или имя источника данных JNDI. Если вы предпочитаете поиск JNDI, просто убедитесь, что в OracleCachedRowSet установлено значение DataSourceName.
Поэтому в моей службе я Autowire в пароле и объявляю выходной параметр следующим образом:
new SqlOutParameter("cursor_param_name", OracleTypes.CURSOR, new OracleSqlRowSetResultSetExtractor(oraclePassword))