Поиск…


Вступление

Это описывает, как напрямую получить 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))


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow