使用Spring JdbcTemplate调用Oracle存储过程。Spring 3.2

4

我有一些使用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版本中这样做。


堆栈跟踪显示没有参数传递到数据库,您需要在执行调用中传递参数数组(termCode、netId、null)。 - Magnus
我也尝试过那种方法。如果你看被注释掉的那一部分,你会发现所有的部分都被添加到了一个名为“in”的SqlParamaterSource中。当我尝试这种方法时,我使用: sjc.execute(in),而不是在代码中看到的空调用。 - user3596751
但是,我一定会尝试使用你的方法(@Magnus)添加它们。 - user3596751
我刚刚尝试了这种方式:Map<String, Object> results = sjc.execute(termCode, netId, null); 日志与上面的相同。 - user3596751
这是在调试模式下的样子:sjc:SimpleJdbcCall@1658 termCode: "201570" netId: "rs213498" - user3596751
这是日志输出:2015-12-29 21:53:38 DEBUG CallMetaDataProvider:278 - 检索 UPCLSCH/AP_ADMN/P_GET_CLASS_SCHEDULE 的元数据 2015-12-29 21:53:39 DEBUG DataSourceUtils:332 - 将 JDBC 连接返回到数据源 2015-12-29 21:53:39 DEBUG SimpleJdbcCall:304 - 编译存储过程。调用字符串为 [{call UPCLSCH.P_GET_CLASS_SCHEDULE()}] 2015-12-29 21:53:39 DEBUG SimpleJdbcCall:282 - 编译存储过程 [p_get_class_schedule] 的 SqlCall 2015-12-29 21:53:39 DEBUG SimpleJdbcCall:385 - 使用以下参数进行调用 {call UPCLSCH.P_GET_CLASS_SCHEDULE()}:{} - user3596751
2个回答

4

已解决:在阅读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;
    }
}

2
写道:“请注意,我将值添加到映射中的顺序与添加参数的顺序相反”。您正在使用MapSqlParameterSource-添加值到映射中的顺序并不重要。 - Ryan

3
您可以使用存储过程来处理这个用例。您可能没有将参数传递给该过程。
请参见下面的示例代码。 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);
    }

}

MyProcedure

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接