Zoeken…


Invoering

Hier wordt beschreven hoe een SqlRowSet behulp SimpleJdbcCall direct te verkrijgen met een opgeslagen procedure in uw database die een cursor uitgang parameter heeft,

Ik werk met een Oracle-database, ik heb geprobeerd een voorbeeld te maken dat voor andere databases zou moeten werken, mijn Oracle-voorbeeld geeft details over problemen met Oracle.

SimpleJdbcCall creatie

Meestal wilt u uw SimpleJdbcCalls in een service maken.

In dit voorbeeld wordt ervan uitgegaan dat uw procedure een enkele uitvoerparameter heeft die een cursor is; u moet uw aangifte parameters aanpassen aan uw procedure.

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

}

Er zijn veel opties die u hier kunt gebruiken:

  • withoutProcedureColumnMetaDataAccess () nodig als u procedurenamen hebt overbelast of gewoon niet wilt dat SimpleJdbcCall valideert met de database.
  • withReturnValue () als procedure een retourwaarde heeft. Eerste waarde gegeven om te declareren Parameters definieert de retourwaarde. Als uw procedure een functie is, gebruikt u ook withFunctionName en executeFunction bij het uitvoeren.
  • withNamedBinding () als u argumenten wilt geven met namen in plaats van positie.
  • useInParameterNames () definieert de argumentvolgorde. Ik denk dat dit nodig kan zijn als je je argumenten doorgeeft als een lijst in plaats van een kaart met argumentnaam naar waarde. Hoewel het mogelijk alleen nodig is als u zonderProcedureColumnMetaDataAccess () gebruikt

Oracle-databases

Er zijn een aantal problemen met Oracle. Hier is hoe ze op te lossen.

Ervan uitgaande dat uw procedure uitvoerparameter ref cursor , krijgt u deze uitzondering.

java.sql.SQLException: ongeldig kolomtype: 2012

Wijzig dus Types.REF_CURSOR in OracleTypes.CURSOR in simpleJdbcCall.declareParameters ()


Ondersteunt OracleTypes

U hoeft dit alleen te doen als u bepaalde kolomtypen in uw gegevens heeft.

Het volgende probleem dat ik tegenkwam, was dat proprietary Types zoals oracle.sql.TIMESTAMPTZ deze fout in SqlRowSetResultSetExtractor veroorzaakten:

Ongeldig SQL-type voor kolom; geneste uitzondering is java.sql.SQLException: ongeldig SQL-type voor kolom

We moeten dus een ResultSetExtractor maken die Oracle-typen ondersteunt.
Ik zal de reden voor het wachtwoord na deze code uitleggen.

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

}

Bepaalde Oracle-typen vereisen een verbinding om de kolomwaarde van een ResultSet te verkrijgen. TIMESTAMPTZ is een van deze typen. Dus wanneer rowSet.getTimestamp(colIndex) wordt aangeroepen, krijgt u deze uitzondering:

Veroorzaakt door: java.sql.SQLException: een of meer van de authenticerende RowSet-eigenschappen die niet zijn ingesteld op oracle.jdbc.rowset.OracleCachedRowSet.getConnectionInternal (OracleCachedRowSet.java:560) op oracle.jdbc.rowset.OracleCachedRowSampRecetMecRecell : 3717) op org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet.getTimestamp

Als u deze code gebruikt, ziet u dat de OracleCachedRowSet het wachtwoord of een JNDI-gegevensbron nodig heeft om een verbinding te krijgen. Als u de voorkeur geeft aan het JNDI-onderzoek, controleert u of OracleCachedRowSet DataSourceName heeft ingesteld.

Dus in mijn service, voer ik het wachtwoord automatisch in en verklaar ik de uitvoerparameter als volgt:

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


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow