Szukaj…


Wprowadzenie

Opisuje w jaki sposób bezpośrednio uzyskać SqlRowSet korzystając SimpleJdbcCall z procedury przechowywane w bazie danych, która zawiera parametr wyjściowy kursora

Pracuję z bazą danych Oracle, próbowałem stworzyć przykład, który powinien działać dla innych baz danych, mój przykład Oracle szczegóły problemów z Oracle.

Tworzenie SimpleJdbcCall

Zazwyczaj będziesz chciał utworzyć SimpleJdbcCalls w usłudze.

W tym przykładzie założono, że procedura ma pojedynczy parametr wyjściowy, którym jest kursor; będziesz musiał dostosować parametry deklaracji do swojej procedury.

@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");
    }

}

Istnieje wiele opcji, których możesz użyć tutaj:

  • bezProcedureColumnMetaDataAccess () potrzebne, jeśli przeciążono nazwy procedur lub po prostu nie chcesz, aby SimpleJdbcCall sprawdzał poprawność względem bazy danych.
  • withReturnValue (), jeśli procedura ma wartość zwracaną. Pierwsza wartość podana do deklaracjiParametry określa wartość zwracaną. Ponadto, jeśli twoja procedura jest funkcją, użyj withFunctionName i executeFunction podczas wykonywania.
  • withNamedBinding (), jeśli chcesz podać argumenty przy użyciu nazw zamiast pozycji.
  • useInParameterNames () definiuje kolejność argumentów. Myślę, że może to być wymagane, jeśli podasz argumenty jako listę zamiast mapy nazwy argumentu do wartości. Chociaż może to być wymagane tylko w przypadku korzystania bezProcedureColumnMetaDataAccess ()

Bazy danych Oracle

Istnieje wiele problemów z Oracle. Oto jak je rozwiązać.

Zakładając, że parametrem wyjściowym procedury jest ref cursor , otrzymasz ten wyjątek.

java.sql.SQLException: Niepoprawny typ kolumny: 2012

Więc zmień Types.REF_CURSOR na OracleTypes.CURSOR w simpleJdbcCall.declareParameters ()


Wspieranie OracleTypes

Może to być konieczne tylko wtedy, gdy masz określone typy kolumn w swoich danych.

Następnym problemem, który napotkałem, było to, że zastrzeżone typy, takie jak oracle.sql.TIMESTAMPTZ spowodowały ten błąd w SqlRowSetResultSetExtractor:

Niepoprawny typ SQL dla kolumny; wyjątek zagnieżdżony to java.sql.SQLException: Niepoprawny typ SQL dla kolumny

Musimy więc utworzyć ResultSetExtractor, który obsługuje typy Oracle.
Po tym kodzie wyjaśnię powód hasła.

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

}

Niektóre typy Oracle wymagają połączenia, aby uzyskać wartość kolumny z zestawu wyników. TIMESTAMPTZ jest jednym z tych typów. Tak więc po rowSet.getTimestamp(colIndex) otrzymasz następujący wyjątek:

Przyczyna: java.sql.SQLException: co najmniej jedna z uwierzytelniających właściwości RowSet nie jest ustawiona w pliku oracle.jdbc.rowset.OracleCachedRowSet.getConnectionInternal (OracleCachedRowSet.java:560) w witrynie oracle.jdbc.rowset.OracleCachedRowSet.getTimja : 3717) at org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet.getTimestamp

Jeśli zagłębisz się w ten kod, zobaczysz, że OracleCachedRowSet potrzebuje hasła lub nazwy JNDI DataSource, aby uzyskać połączenie. Jeśli wolisz wyszukiwanie JNDI, po prostu sprawdź, czy OracleCachedRowSet ma ustawiony DataSourceName.

Więc w mojej usłudze autowire w haśle i deklaruję parametr wyjściowy w następujący sposób:

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


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow