spring
Een SqlRowSet verkrijgen van SimpleJdbcCall
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))