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))