Sök…


Introduktion

Detta beskriver hur du direkt kan få en SqlRowSet med SimpleJdbcCall med en lagrad procedur i din databas som har en markörutgångsparameter ,

Jag arbetar med en Oracle-databas, jag har försökt skapa ett exempel som borde fungera för andra databaser, mitt Oracle-exempel beskriver problem med Oracle.

SimpleJdbcCall skapande

Vanligtvis vill du skapa dina SimpleJdbcCalls i en tjänst.

Detta exempel antar att din procedur har en enda utgångsparameter som är en markör; måste du justera dina deklareraParametrar för att matcha din procedur.

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

}

Det finns många alternativ du kan använda här:

  • withoutProcedureColumnMetaDataAccess () behövs om du har överbelastat procedurnamn eller bara inte vill att SimpleJdbcCall ska validera mot databasen.
  • withReturnValue () om proceduren har ett returvärde. Första värdet som ges till deklareraParametrar definierar returvärdet. Om din procedur är en funktion använder du också medFunctionName och executeFunction när du kör.
  • withNamedBinding () om du vill ge argument med namn i stället för position.
  • useInParameterNames () definierar argumentordning. Jag tror att detta kan krävas om du skickar in dina argument som en lista istället för en karta med argumentnamn till värde. Även om det bara kan krävas om du använder utanProcedureColumnMetaDataAccess ()

Oracle-databaser

Det finns ett antal problem med Oracle. Så här löser du dem.

Förutsatt att din procedurutgångsparameter är ref cursor , får du detta undantag.

java.sql.SQLEexception: Ogiltig kolumntyp: 2012

Så ändra Types.REF_CURSOR till OracleTypes.CURSOR i simpleJdbcCall.declareParameters ()


Stöd för OracleTypes

Du kanske bara behöver göra detta om du har vissa kolumntyper i dina data.

Nästa problem jag stötte på var att proprietära typer som oracle.sql.TIMESTAMPTZ orsakade detta fel i SqlRowSetResultSetExtractor:

Ogiltig SQL-typ för kolumn; det kapslade undantaget är java.sql.SQLEexception: Ogiltig SQL-typ för kolumn

Så vi måste skapa en ResultSetExtractor som stöder Oracle-typer.
Jag kommer att förklara orsaken till lösenordet efter den här koden.

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

}

Vissa Oracle-typer kräver en anslutning för att få kolumnvärdet från en ResultSet. TIMESTAMPTZ är en av dessa typer. Så när rowSet.getTimestamp(colIndex) kallas, får du detta undantag:

Orsakat av: java.sql.SQLException: En eller flera av de autentiserande RowSet-egenskaperna som inte är inställda på oracle.jdbc.rowset.OracleCachedRowSet.getConnectionInternal (OracleCachedRowSet.java:560) på oracle.jdbc.rowset.OracleCachedRowSetRowSet.RowSet. : 3717) på org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet.getTimestamp

Om du gräver in den här koden ser du att OracleCachedRowSet behöver lösenordet eller ett JNDI DataSource-namn för att få en anslutning. Om du föredrar JNDI-uppslaget, kontrollera bara att OracleCachedRowSet har DataSourceName inställt.

Så i min tjänst, autowire jag i lösenordet och förklarar utgångsparametern så här:

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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow