Suche…


Einführung

Hier wird beschrieben, wie Sie ein SqlRowSet direkt über SimpleJdbcCall mit einer in Ihrer Datenbank gespeicherten Prozedur abrufen , die einen Cursor-Ausgabeparameter enthält

Ich arbeite mit einer Oracle-Datenbank. Ich habe versucht, ein Beispiel zu erstellen, das für andere Datenbanken geeignet sein sollte. In meinem Oracle-Beispiel werden Probleme mit Oracle beschrieben.

SimpleJdbcCall-Erstellung

Normalerweise möchten Sie Ihre SimpleJdbcCalls in einem Service erstellen.

In diesem Beispiel wird davon ausgegangen, dass Ihre Prozedur einen einzigen Ausgabeparameter hat, der ein Cursor ist. Sie müssen Ihre declareParameters an Ihre Prozedur anpassen.

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

}

Es gibt viele Optionen, die Sie hier verwenden können:

  • withoutProcedureColumnMetaDataAccess () ist erforderlich, wenn Sie Prozedurnamen überlastet haben oder SimpleJdbcCall nicht mit der Datenbank überprüfen möchten.
  • withReturnValue (), wenn die Prozedur einen Rückgabewert hat. Der erste Wert von declareParameters definiert den Rückgabewert. Wenn Ihre Prozedur eine Funktion ist, verwenden Sie bei der Ausführung withFunctionName und executeFunction .
  • withNamedBinding (), wenn Sie Argumente mit Namen anstelle von Position angeben möchten.
  • useInParameterNames () definiert die Reihenfolge der Argumente. Ich denke, dass dies erforderlich sein kann, wenn Sie Ihre Argumente als Liste anstelle einer Zuordnung von Argumentnamen zum Wert übergeben. Es kann jedoch nur erforderlich sein, wenn Sie ohneProcedureColumnMetaDataAccess () verwenden.

Oracle-Datenbanken

Es gibt eine Reihe von Problemen mit Oracle. So lösen Sie sie.

Angenommen, der Ausgabeparameter für die Prozedur ist der ref cursor , wird diese Ausnahme angezeigt.

java.sql.SQLException: Ungültiger Spaltentyp: 2012

Ändern Sie also Types.REF_CURSOR in OracleTypes.CURSOR in simpleJdbcCall.declareParameters ().


Unterstützung von OracleTypes

Möglicherweise müssen Sie dies nur tun, wenn Sie bestimmte Spaltentypen in Ihren Daten haben.

Das nächste Problem, dem ich begegnete, war, dass proprietäre Typen wie oracle.sql.TIMESTAMPTZ diesen Fehler in SqlRowSetResultSetExtractor verursacht haben:

Ungültiger SQL-Typ für Spalte; Geschachtelte Ausnahme ist java.sql.SQLException: Ungültiger SQL-Typ für Spalte

Daher müssen wir einen ResultSetExtractor erstellen, der Oracle-Typen unterstützt.
Nach diesem Code werde ich den Grund für das Passwort erläutern.

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

}

Bestimmte Oracle-Typen erfordern eine Verbindung, um den Spaltenwert aus einem ResultSet zu erhalten. TIMESTAMPTZ ist einer dieser Typen. Wenn also rowSet.getTimestamp(colIndex) aufgerufen wird, erhalten Sie diese Ausnahme:

Verursacht durch: java.sql.SQLException: Eine oder mehrere der authentifizierenden RowSet-Eigenschaften, die nicht unter oracle.jdbc.rowset.OracleCachedRowSet.getConnectionInternal (OracleCachedRowSet.java:560) unter oracle.jdbc.rowset.OracleCachedRowSet.. : 3717) unter org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet.getTimestamp

Wenn Sie in diesen Code einsteigen, werden Sie feststellen, dass OracleCachedRowSet das Kennwort oder einen JNDI-DataSource-Namen benötigt, um eine Verbindung zu erhalten. Wenn Sie die JNDI-Suche bevorzugen, vergewissern Sie sich, dass für OracleCachedRowSet DataSourceName festgelegt ist.

In meinem Dienst habe ich also automatisch das Passwort eingegeben und den Ausgabeparameter wie folgt deklariert:

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


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow