Spring 调用 Oracle 存储过程的 JcbcTemplate。 Spring 3.2
Spring JcbcTemplate to call Oracle Stored Proc. Spring 3.2
我有一些使用 CallableStatement 的直接 JDBC 代码。我一直在尝试利用 DataSource、JdbcTemplate 和 SimpleJdbcCall 将其转换为 Spring。我基本上已经尝试了我能找到的 Spring 文档中的每个教程、示例和片段。通过调整,所有 Spring 解决方案都会产生相同的结果:
org.springframework.jdbc.BadSqlGrammarException: CallableStatementCallback; bad SQL grammar [{call UPCLSCH.P_GET_CLASS_SCHEDULE()}]; nested exception is java.sql.SQLException: ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'P_GET_CLASS_SCHEDULE'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
这是正在准备语句的日志部分:
2015-12-29 17:17:18 DEBUG SimpleJdbcCall:214 - Added declared parameter for [p_get_class_schedule]: p_classsched_ref_out
2015-12-29 17:17:18 DEBUG SimpleJdbcCall:214 - Added declared parameter for [p_get_class_schedule]: p_term
2015-12-29 17:17:18 DEBUG SimpleJdbcCall:214 - Added declared parameter for [p_get_class_schedule]: p_scauid
2015-12-29 17:17:18 DEBUG SimpleJdbcCall:214 - Added declared parameter for [p_get_class_schedule]: p_pidm
2015-12-29 17:17:18 DEBUG SimpleJdbcCall:336 - JdbcCall call not compiled before execution - invoking compile
2015-12-29 17:17:18 DEBUG DataSourceUtils:110 - Fetching JDBC Connection from DataSource
2015-12-29 17:17:18 DEBUG DriverManagerDataSource:162 - Creating new JDBC DriverManager Connection to [jdbc:oracle:thin:@umadmn.umt.edu:7895:ADMNRED]
2015-12-29 17:17:21 DEBUG CallMetaDataProviderFactory:123 - Using org.springframework.jdbc.core.metadata.OracleCallMetaDataProvider
2015-12-29 17:17:21 DEBUG CallMetaDataProvider:278 - Retrieving metadata for UPCLSCH/AP_ADMN/P_GET_CLASS_SCHEDULE
2015-12-29 17:17:22 DEBUG DataSourceUtils:332 - Returning JDBC Connection to DataSource
2015-12-29 17:17:22 DEBUG SimpleJdbcCall:304 - Compiled stored procedure. Call string is [{call UPCLSCH.P_GET_CLASS_SCHEDULE()}]
2015-12-29 17:17:22 DEBUG SimpleJdbcCall:282 - SqlCall for procedure [p_get_class_schedule] compiled
2015-12-29 17:17:22 DEBUG SimpleJdbcCall:385 - The following parameters are used for call {call UPCLSCH.P_GET_CLASS_SCHEDULE()} with: {}
2015-12-29 17:17:22 DEBUG JdbcTemplate:937 - Calling stored procedure [{call UPCLSCH.P_GET_CLASS_SCHEDULE()}]
2015-12-29 17:17:22 DEBUG DataSourceUtils:110 - Fetching JDBC Connection from DataSource
2015-12-29 17:17:22 DEBUG DriverManagerDataSource:162 - Creating new JDBC DriverManager Connection to [jdbc:oracle:thin:@xxxxx.xxx.xxx:7895:PRIVATE]
2015-12-29 17:17:24 DEBUG DataSourceUtils:332 - Returning JDBC Connection to DataSource
这是有效的直接 JDBC 代码(没有连接细节):
private static List<ScheduledClass> callOracleStoredProcCURSORParameter() throws SQLException {
Connection connection = null;
CallableStatement callableStatement = null;
ResultSet rs = null;
List<ScheduledClass> scheduledClassList = new ArrayList<ScheduledClass>();
String getDBUSERCursorSql = "{call upclsch.p_get_class_schedule (?, ?, ?, ?)}";
try {
connection = getApConnection();
callableStatement = connection.prepareCall(getDBUSERCursorSql);
callableStatement.registerOutParameter("p_classsched_ref_out", OracleTypes.CURSOR);
callableStatement.setString("p_term", "201570"); //term code
callableStatement.setString("p_scauid", "rs213498");
callableStatement.setString("p_pidm", null);
callableStatement.executeUpdate();
rs = (ResultSet) callableStatement.getObject("p_classsched_ref_out");
while (rs.next()) {
ScheduledClass sc = new ScheduledClass();
sc.setCourseNumber(rs.getString("subject_code") + rs.getString("course_number"));
sc.setCourseTitle(rs.getString("course_title"));
scheduledClassList.add(sc);
}
} catch (SQLException e) {
e.printStackTrace();
}
return scheduledClassList;
}
这是我的非工作 Spring 代码(注意将 "in" 传递给 sjc.execute() 时产生相同结果的注释部分):
public void setDataSource(DataSource dataSource){
this.jt = new JdbcTemplate(dataSource);
jt.setResultsMapCaseInsensitive(true);
sjc = new SimpleJdbcCall(jt)
.withCatalogName("upclsch")
.withProcedureName("p_get_class_schedule");
}
public Map<String, Object> execute(String termCode, String netId){
sjc.useInParameterNames("p_term", "p_scauid", "p_pidm")
.declareParameters(new SqlOutParameter("p_classsched_ref_out", OracleTypes.CURSOR),
new SqlParameter("p_term", OracleTypes.VARCHAR),
new SqlParameter("p_scauid", OracleTypes.VARCHAR),
new SqlParameter("p_pidm", OracleTypes.VARCHAR));
// SqlParameterSource in = new MapSqlParameterSource()
// .addValue("p_scauid", netId, OracleTypes.VARCHAR)
// .addValue("p_term", termCode, OracleTypes.VARCHAR)
// .addValue("p_classsched_ref_out", OracleTypes.CURSOR);
Map<String, Object> results = sjc.execute();
return results;
}
我似乎无法在 TRACE 或 DEBUG 级别获得任何附加信息来查看我的参数是否排序不正确。因此,我正在寻求任何使用此技术完成此任务的人的帮助。我不打算扩展 StoredProcedure,因为 Spring 文档建议将其用于 3.2。
对于此用例,您可以使用 StoredProcedure。您可能没有将参数传递给过程。
请参阅下面的示例代码。
TestProcedure.java
import java.util.List;
import java.util.Map;
import org.springframework.jdbc.core.JdbcTemplate;
public class TestProcedure {
public void main(String[] args) {
System.out.println("Started");
JdbcTemplate jdbcTemplate = null;//get your jdbcTemplate
MyProcedure proc = new MyProcedure(jdbcTemplate);
Map<String, Object> resultMap = proc.execute("201570","rs213498",null);
List<Map> classschedList = (List)resultMap.get(MyProcedure.P_CLASSSCHED_REF_OUT);
}
}
我的程序
import java.util.HashMap;
import java.util.Map;
import oracle.jdbc.internal.OracleTypes;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.StoredProcedure;
public class MyProcedure extends StoredProcedure {
public static final String P_CLASSSCHED_REF_OUT = "p_classsched_ref_out";
public static final String P_TERM = "p_term";
public static final String P_SCAUID = "p_scauid";
public static final String P_PIDM = "p_pidm";
public static final String PROC_NAME = "upclsch.p_get_class_schedule";
public MyProcedure(JdbcTemplate jdbcTemplate) {
setDataSource(jdbcTemplate.getDataSource());
setSql(PROC_NAME);
setFetchSize(100);
declareParameter(new SqlOutParameter(P_CLASSSCHED_REF_OUT, OracleTypes.CURSOR, new MyRowMapper()));
declareParameter(new SqlParameter(P_TERM, OracleTypes.VARCHAR));
declareParameter(new SqlParameter(P_SCAUID, OracleTypes.VARCHAR));
declareParameter(new SqlParameter(P_PIDM, OracleTypes.VARCHAR));
compile();
}
/**
* Execute stored procedure.
*/
public Map<String, Object> executeProcedure(String term, String scauid, String pidm) {
// set the input params
Map<String, Object> inParameters = new HashMap<String, Object>();
inParameters.put(P_TERM, term);
inParameters.put(P_SCAUID, scauid);
inParameters.put(P_PIDM, pidm);
// now execute
Map<String, Object> outputMap = execute(inParameters); // Call on parent class
return outputMap;
}
}
MyRowMapper
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.springframework.jdbc.core.RowMapper;
public class MyRowMapper implements RowMapper<Map> {
public Map mapRow(ResultSet rs, int paramInt) throws SQLException {
Map response = new HashMap();
// here we populate the data using the returned cursor resultset
// response.setTerm(rs.getString("cursor_col1");
return response;
}
}
已解决: 我在逐步查看 Spring 源代码后弄明白了。对于那些可能感兴趣的人,它涉及声明过程实际使用的参数,然后使用 SqlParameterSource 来保存映射到声明名称的值。请注意,我以与添加参数相反的顺序将值添加到地图中。另外 注意 我添加了: .withoutProcedureColumnMetaDataAccess()
。这在像我所做的那样声明您自己的参数时很重要。
public class ScheduledClassesDAO {
private DataSource dataSource;
private JdbcTemplate jt;
private SimpleJdbcCall sjc;
public void setDataSource(DataSource dataSource){
this.jt = new JdbcTemplate(dataSource);
jt.setResultsMapCaseInsensitive(true);
sjc = new SimpleJdbcCall(jt)
.withCatalogName("upclsch")
.withProcedureName("p_get_class_schedule");
}
/**
* This method is used to return scheduled classes by calling a stored-proc.
* @param termCode String: The term/semester for this lookup.
* @param netId String: The netId of the student to lookup
* @return Map<String, Object>
*/
public Map<String, Object> execute(String termCode, String netId){
sjc.useInParameterNames("p_term", "p_scauid", "p_pidm")
.withoutProcedureColumnMetaDataAccess()
.declareParameters(new SqlOutParameter("p_classsched_ref_out", OracleTypes.CURSOR),
new SqlParameter("p_term", OracleTypes.VARCHAR),
new SqlParameter("p_scauid", OracleTypes.VARCHAR),
new SqlParameter("p_pidm", OracleTypes.NUMBER));
SqlParameterSource in = new MapSqlParameterSource()
.addValue("p_pidm", null)
.addValue("p_scauid", netId)
.addValue("p_term", termCode);
Map<String, Object> results = sjc.execute(in);
return results;
}
}
我有一些使用 CallableStatement 的直接 JDBC 代码。我一直在尝试利用 DataSource、JdbcTemplate 和 SimpleJdbcCall 将其转换为 Spring。我基本上已经尝试了我能找到的 Spring 文档中的每个教程、示例和片段。通过调整,所有 Spring 解决方案都会产生相同的结果:
org.springframework.jdbc.BadSqlGrammarException: CallableStatementCallback; bad SQL grammar [{call UPCLSCH.P_GET_CLASS_SCHEDULE()}]; nested exception is java.sql.SQLException: ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'P_GET_CLASS_SCHEDULE'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
这是正在准备语句的日志部分:
2015-12-29 17:17:18 DEBUG SimpleJdbcCall:214 - Added declared parameter for [p_get_class_schedule]: p_classsched_ref_out
2015-12-29 17:17:18 DEBUG SimpleJdbcCall:214 - Added declared parameter for [p_get_class_schedule]: p_term
2015-12-29 17:17:18 DEBUG SimpleJdbcCall:214 - Added declared parameter for [p_get_class_schedule]: p_scauid
2015-12-29 17:17:18 DEBUG SimpleJdbcCall:214 - Added declared parameter for [p_get_class_schedule]: p_pidm
2015-12-29 17:17:18 DEBUG SimpleJdbcCall:336 - JdbcCall call not compiled before execution - invoking compile
2015-12-29 17:17:18 DEBUG DataSourceUtils:110 - Fetching JDBC Connection from DataSource
2015-12-29 17:17:18 DEBUG DriverManagerDataSource:162 - Creating new JDBC DriverManager Connection to [jdbc:oracle:thin:@umadmn.umt.edu:7895:ADMNRED]
2015-12-29 17:17:21 DEBUG CallMetaDataProviderFactory:123 - Using org.springframework.jdbc.core.metadata.OracleCallMetaDataProvider
2015-12-29 17:17:21 DEBUG CallMetaDataProvider:278 - Retrieving metadata for UPCLSCH/AP_ADMN/P_GET_CLASS_SCHEDULE
2015-12-29 17:17:22 DEBUG DataSourceUtils:332 - Returning JDBC Connection to DataSource
2015-12-29 17:17:22 DEBUG SimpleJdbcCall:304 - Compiled stored procedure. Call string is [{call UPCLSCH.P_GET_CLASS_SCHEDULE()}]
2015-12-29 17:17:22 DEBUG SimpleJdbcCall:282 - SqlCall for procedure [p_get_class_schedule] compiled
2015-12-29 17:17:22 DEBUG SimpleJdbcCall:385 - The following parameters are used for call {call UPCLSCH.P_GET_CLASS_SCHEDULE()} with: {}
2015-12-29 17:17:22 DEBUG JdbcTemplate:937 - Calling stored procedure [{call UPCLSCH.P_GET_CLASS_SCHEDULE()}]
2015-12-29 17:17:22 DEBUG DataSourceUtils:110 - Fetching JDBC Connection from DataSource
2015-12-29 17:17:22 DEBUG DriverManagerDataSource:162 - Creating new JDBC DriverManager Connection to [jdbc:oracle:thin:@xxxxx.xxx.xxx:7895:PRIVATE]
2015-12-29 17:17:24 DEBUG DataSourceUtils:332 - Returning JDBC Connection to DataSource
这是有效的直接 JDBC 代码(没有连接细节):
private static List<ScheduledClass> callOracleStoredProcCURSORParameter() throws SQLException {
Connection connection = null;
CallableStatement callableStatement = null;
ResultSet rs = null;
List<ScheduledClass> scheduledClassList = new ArrayList<ScheduledClass>();
String getDBUSERCursorSql = "{call upclsch.p_get_class_schedule (?, ?, ?, ?)}";
try {
connection = getApConnection();
callableStatement = connection.prepareCall(getDBUSERCursorSql);
callableStatement.registerOutParameter("p_classsched_ref_out", OracleTypes.CURSOR);
callableStatement.setString("p_term", "201570"); //term code
callableStatement.setString("p_scauid", "rs213498");
callableStatement.setString("p_pidm", null);
callableStatement.executeUpdate();
rs = (ResultSet) callableStatement.getObject("p_classsched_ref_out");
while (rs.next()) {
ScheduledClass sc = new ScheduledClass();
sc.setCourseNumber(rs.getString("subject_code") + rs.getString("course_number"));
sc.setCourseTitle(rs.getString("course_title"));
scheduledClassList.add(sc);
}
} catch (SQLException e) {
e.printStackTrace();
}
return scheduledClassList;
}
这是我的非工作 Spring 代码(注意将 "in" 传递给 sjc.execute() 时产生相同结果的注释部分):
public void setDataSource(DataSource dataSource){
this.jt = new JdbcTemplate(dataSource);
jt.setResultsMapCaseInsensitive(true);
sjc = new SimpleJdbcCall(jt)
.withCatalogName("upclsch")
.withProcedureName("p_get_class_schedule");
}
public Map<String, Object> execute(String termCode, String netId){
sjc.useInParameterNames("p_term", "p_scauid", "p_pidm")
.declareParameters(new SqlOutParameter("p_classsched_ref_out", OracleTypes.CURSOR),
new SqlParameter("p_term", OracleTypes.VARCHAR),
new SqlParameter("p_scauid", OracleTypes.VARCHAR),
new SqlParameter("p_pidm", OracleTypes.VARCHAR));
// SqlParameterSource in = new MapSqlParameterSource()
// .addValue("p_scauid", netId, OracleTypes.VARCHAR)
// .addValue("p_term", termCode, OracleTypes.VARCHAR)
// .addValue("p_classsched_ref_out", OracleTypes.CURSOR);
Map<String, Object> results = sjc.execute();
return results;
}
我似乎无法在 TRACE 或 DEBUG 级别获得任何附加信息来查看我的参数是否排序不正确。因此,我正在寻求任何使用此技术完成此任务的人的帮助。我不打算扩展 StoredProcedure,因为 Spring 文档建议将其用于 3.2。
对于此用例,您可以使用 StoredProcedure。您可能没有将参数传递给过程。
请参阅下面的示例代码。
TestProcedure.java
import java.util.List;
import java.util.Map;
import org.springframework.jdbc.core.JdbcTemplate;
public class TestProcedure {
public void main(String[] args) {
System.out.println("Started");
JdbcTemplate jdbcTemplate = null;//get your jdbcTemplate
MyProcedure proc = new MyProcedure(jdbcTemplate);
Map<String, Object> resultMap = proc.execute("201570","rs213498",null);
List<Map> classschedList = (List)resultMap.get(MyProcedure.P_CLASSSCHED_REF_OUT);
}
}
我的程序
import java.util.HashMap;
import java.util.Map;
import oracle.jdbc.internal.OracleTypes;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.StoredProcedure;
public class MyProcedure extends StoredProcedure {
public static final String P_CLASSSCHED_REF_OUT = "p_classsched_ref_out";
public static final String P_TERM = "p_term";
public static final String P_SCAUID = "p_scauid";
public static final String P_PIDM = "p_pidm";
public static final String PROC_NAME = "upclsch.p_get_class_schedule";
public MyProcedure(JdbcTemplate jdbcTemplate) {
setDataSource(jdbcTemplate.getDataSource());
setSql(PROC_NAME);
setFetchSize(100);
declareParameter(new SqlOutParameter(P_CLASSSCHED_REF_OUT, OracleTypes.CURSOR, new MyRowMapper()));
declareParameter(new SqlParameter(P_TERM, OracleTypes.VARCHAR));
declareParameter(new SqlParameter(P_SCAUID, OracleTypes.VARCHAR));
declareParameter(new SqlParameter(P_PIDM, OracleTypes.VARCHAR));
compile();
}
/**
* Execute stored procedure.
*/
public Map<String, Object> executeProcedure(String term, String scauid, String pidm) {
// set the input params
Map<String, Object> inParameters = new HashMap<String, Object>();
inParameters.put(P_TERM, term);
inParameters.put(P_SCAUID, scauid);
inParameters.put(P_PIDM, pidm);
// now execute
Map<String, Object> outputMap = execute(inParameters); // Call on parent class
return outputMap;
}
}
MyRowMapper
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.springframework.jdbc.core.RowMapper;
public class MyRowMapper implements RowMapper<Map> {
public Map mapRow(ResultSet rs, int paramInt) throws SQLException {
Map response = new HashMap();
// here we populate the data using the returned cursor resultset
// response.setTerm(rs.getString("cursor_col1");
return response;
}
}
已解决: 我在逐步查看 Spring 源代码后弄明白了。对于那些可能感兴趣的人,它涉及声明过程实际使用的参数,然后使用 SqlParameterSource 来保存映射到声明名称的值。请注意,我以与添加参数相反的顺序将值添加到地图中。另外 注意 我添加了: .withoutProcedureColumnMetaDataAccess()
。这在像我所做的那样声明您自己的参数时很重要。
public class ScheduledClassesDAO {
private DataSource dataSource;
private JdbcTemplate jt;
private SimpleJdbcCall sjc;
public void setDataSource(DataSource dataSource){
this.jt = new JdbcTemplate(dataSource);
jt.setResultsMapCaseInsensitive(true);
sjc = new SimpleJdbcCall(jt)
.withCatalogName("upclsch")
.withProcedureName("p_get_class_schedule");
}
/**
* This method is used to return scheduled classes by calling a stored-proc.
* @param termCode String: The term/semester for this lookup.
* @param netId String: The netId of the student to lookup
* @return Map<String, Object>
*/
public Map<String, Object> execute(String termCode, String netId){
sjc.useInParameterNames("p_term", "p_scauid", "p_pidm")
.withoutProcedureColumnMetaDataAccess()
.declareParameters(new SqlOutParameter("p_classsched_ref_out", OracleTypes.CURSOR),
new SqlParameter("p_term", OracleTypes.VARCHAR),
new SqlParameter("p_scauid", OracleTypes.VARCHAR),
new SqlParameter("p_pidm", OracleTypes.NUMBER));
SqlParameterSource in = new MapSqlParameterSource()
.addValue("p_pidm", null)
.addValue("p_scauid", netId)
.addValue("p_term", termCode);
Map<String, Object> results = sjc.execute(in);
return results;
}
}