用于调用存储过程的Spring JDBC模板


77

使用现代(大约在2012年)Spring JDBC模板调用存储过程的正确方法是什么?

说,我有一个同时声明INOUT参数的存储过程,如下所示:

mypkg.doSomething(
    id OUT int,
    name IN String,
    date IN Date
)

我遇到过CallableStatementCreator基于方法,我们必须显式注册INOUT参数化。在JdbcTemplate课堂上考虑以下方法:

public Map<String, Object> call(CallableStatementCreator csc, List<SqlParameter> declaredParameters)

当然,我知道我可以这样使用它:

List<SqlParameter> declaredParameters = new ArrayList<SqlParameter>();

declaredParameters.add(new SqlOutParameter("id", Types.INTEGER));
declaredParameters.add(new SqlParameter("name", Types.VARCHAR));
declaredParameters.add(new SqlParameter("date", Types.DATE));

this.jdbcTemplate.call(new CallableStatementCreator() {

    @Override
    CallableStatement createCallableStatement(Connection con) throws SQLException {
        CallableStatement stmnt = con.createCall("{mypkg.doSomething(?, ?, ?)}");

        stmnt.registerOutParameter("id", Types.INTEGER);
        stmnt.setString("name", "<name>");
        stmnt.setDate("date", <date>);

        return stmnt;
    }
}, declaredParameters);

declaredParameters我已经在csc实现中注册它们的目的是什么?换句话说,为什么我需要传递一个csc春天可以简单地在con.prepareCall(sql)内部完成的事情?基本上,我不能传递其中一个而不是传递两个吗?

或者,是否有比我到目前为止遇到的方法更好的方法(使用Spring JDBC模板)调用存储过程?

注意:您可能会发现许多标题相似的问题,但与此题名不同。


3
我可以看到,这个问题现在非常流行,并且被问到已经两年多了。如果Spring 4推出后,如果有人认为有一种更好的调用存储过程的方法,请发表答案或提出修改建议。
2014年

Answers:


93

在Spring中有很多方法可以调用存储过程。

如果CallableStatementCreator用于声明参数,则将使用Java的标准接口CallableStatement,即注销参数并分别设置它们。使用SqlParameter抽象将使您的代码更整洁。

我建议你看看SimpleJdbcCall。可以这样使用:

SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
    .withSchemaName(schema)
    .withCatalogName(package)
    .withProcedureName(procedure)();
...
jdbcCall.addDeclaredParameter(new SqlParameter(paramName, OracleTypes.NUMBER));
...
jdbcCall.execute(callParams);

对于简单的过程,您可以使用jdbcTemplateupdate方法:

jdbcTemplate.update("call SOME_PROC (?, ?)", param1, param2);

SimpleJdbcCall看起来真的很酷。我将对此进行测试,并告诉您在我看来情况如何。
adarshr'2

2
更新方法对我不起作用,即使我的语句在数据库中执行得很好,我仍然收到错误的SQL语法异常。SimpleJdbcCall运作得很好
otgw 2015年

我试图以与您说的相同的方式使用jdbcTemplate.update(),但出现错误提示"cannot invoke update on null object"
苏莱曼汗

1
@EpicPandaForce我有NamedParameterJdbcTemplate,而不是JdbcTemplate
Alex Bitek 2015年

8
我不知道它是否是特定于Oracle的语法,但是Oracle的语法是:(jdbcTemplate.update("{ call my_schema.my_pkg.SOME_PROC (?, ?) }", param1, param2);请注意花括号)。
inanutshellus '16

49

这是从Java调用存储过程的方法

1.使用CallableStatement:

 connection = jdbcTemplate.getDataSource().getConnection();
  CallableStatement callableStatement = connection.prepareCall("{call STORED_PROCEDURE_NAME(?, ?, ?)}");
  callableStatement.setString(1, "FirstName");
  callableStatement.setString(2, " LastName");
  callableStatement.registerOutParameter(3, Types.VARCHAR);
  callableStatement.executeUpdate();

在这里,我们从外部管理资源关闭

2.使用CallableStatementCreator

 List paramList = new ArrayList();
    paramList.add(new SqlParameter(Types.VARCHAR));
    paramList.add(new SqlParameter(Types.VARCHAR));
    paramList.add(new SqlOutParameter("msg", Types.VARCHAR));

    Map<String, Object> resultMap = jdbcTemplate.call(new CallableStatementCreator() {

    @Override
    public CallableStatement createCallableStatement(Connection connection)
    throws SQLException {

    CallableStatement callableStatement = connection.prepareCall("{call STORED_PROCEDURE_NAME(?, ?, ?)}");
    callableStatement.setString(1, "FirstName");
            callableStatement.setString(2, " LastName");
            callableStatement.registerOutParameter(3, Types.VARCHAR);
    return callableStatement;

    }
    }, paramList);

3.使用SimpleJdbcCall:

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate)

.withProcedureName("STORED_PROCEDURE_NAME");

Map<String, Object> inParamMap = new HashMap<String, Object>();
inParamMap.put("firstName", "FirstNameValue");
inParamMap.put("lastName", "LastNameValue");
SqlParameterSource in = new MapSqlParameterSource(inParamMap);


Map<String, Object> simpleJdbcCallResult = simpleJdbcCall.execute(in);
System.out.println(simpleJdbcCallResult);

4.使用org.springframework.jdbc.object的StoredProcedure类

The Code:
First Create subclass of StoredProcedure: MyStoredProcedure

class MyStoredProcedure extends StoredProcedure {

public MyStoredProcedure(JdbcTemplate jdbcTemplate, String name) {

super(jdbcTemplate, name);
setFunction(false);

}

}

Use MyStoredProcedure to call database stored procedure:


//Pass jdbcTemlate and name of the stored Procedure.
MyStoredProcedure myStoredProcedure = new MyStoredProcedure(jdbcTemplate, "PROC_TEST");

//Sql parameter mapping
SqlParameter fNameParam = new SqlParameter("fName", Types.VARCHAR);
SqlParameter lNameParam = new SqlParameter("lName", Types.VARCHAR);
SqlOutParameter msgParam = new SqlOutParameter("msg", Types.VARCHAR);
SqlParameter[] paramArray = {fNameParam, lNameParam, msgParam};


myStoredProcedure.setParameters(paramArray);
myStoredProcedure.compile();


//Call stored procedure
Map storedProcResult = myStoredProcedure.execute("FirstNameValue", " LastNameValue");

参考


如果我有一个多行返回过程,那会是什么样?它会是Map <String,Object>对象的列表吗?
肯特·布尔公牛

1
@Kent-你是正确的。对于多行,它将是List <Map <String,Object >>。
Sairam Kukadala

19

我通常更喜欢扩展基于Spring的StoredProcedure类来执行存储过程。

  1. 您需要创建您的类构造函数,并需要在其中调用StoredProcedure类构造函数。该超类构造函数接受DataSource和过程名称。

    示例代码:

    public class ProcedureExecutor extends StoredProcedure {
          public ProcedureExecutor(DataSource ds, String funcNameorSPName) {
            super(ds, funcNameorSPName);
            declareParameter(new SqlOutParameter("v_Return", Types.VARCHAR, null, new SqlReturnType() {
                    public Object getTypeValue(CallableStatement cs,
                         int paramIndex, int sqlType, String typeName) throws SQLException {
                    final String str = cs.getString(paramIndex);
                    return str;
                }           
            }));    
            declareParameter(new SqlParameter("your parameter",
                    Types.VARCHAR));
            //set below param true if you want to call database function 
            setFunction(true);
            compile();
            }
    
  2. 覆盖存储过程调用的执行方法,如下所示

    public Map<String, Object> execute(String someParams) {
                 final Map<String, Object> inParams = new HashMap<String, Object>(8);
                 inParams.put("my param", "some value");
                 Map outMap = execute(inParams);
                 System.out.println("outMap:" + outMap);
                 return outMap;
             }
    

希望这对您有所帮助。


1

调用存储过程的另一种方法是:

sql="execute Procedure_Name ?";
Object search[]={Id};
List<ClientInvestigateDTO> client=jdbcTemplateObject.query(sql,search,new 
   ClientInvestigateMapper());

在此示例中,“ ClientInvestigateDTO”是POJO类,“ ClientInvestigateMapper”是映射器类。“ client”存储您在调用存储过程时获得的所有结果。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.