簡體   English   中英

Spring JDBC 調用存儲過程的模板

[英]Spring JDBC Template for calling Stored Procedures

使用現代(大約 2012 年)Spring JDBC 模板調用存儲過程的正確方法是什么?

比如說,我有一個聲明INOUT參數的存儲過程,如下所示:

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

我遇到過基於CallableStatementCreator的方法,我們必須顯式注冊INOUT參數。 JdbcTemplate class 中考慮以下方法:

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

當我已經在我的csc實現中注冊時, declaredParameters的目的是什么? 換句話說,當 spring 可以簡單地在內部執行con.prepareCall(sql)時,為什么我需要傳入csc 基本上,我不能傳入其中一個而不是兩個嗎?

或者,有沒有比我迄今為止遇到的更好的方法來調用存儲過程(使用 Spring JDBC 模板)?

注意:您可能會發現許多題目看似相似但與本題不同的題目。

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

下面是從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");

參考

我通常更喜歡擴展基於 Spring 的StoredProcedure類來執行存儲過程。

  1. 您需要創建類構造函數並需要在其中調用StoredProcedure類構造函數。 這個超類構造函數接受數據源和過程名稱。

    示例代碼:

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

希望這對你有幫助。

另一種調用存儲過程的方法是:

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

在本例中,'ClientInvestigateDTO' 是 POJO 類,'ClientInvestigateMapper' 是映射器類。'client' 存儲調用存儲過程時獲得的所有結果。

在某些情況下,使用jdbcTemplate.query()調用過程不會捕獲過程引發的錯誤 我在 MS SqlServer 上看到了這種行為,其中一個過程在發生錯誤時使用RAISEERROR引發錯誤,並且使用查詢方法調用過程忽略了錯誤。

我切換到 SimpleJdbcTemplate 進行過程調用,這很有效。 注意:這可能不適用於程序。 在我的情況下,proc 正在執行 select 以返回數據,然后如果它沒有找到任何數據,則會引發錯誤。 使用 jdbcTemplate.query 會給我一個沒有錯誤的空數據集。

我正在回復您的評論:

或者,有沒有比我迄今為止遇到的更好的方法來調用存儲過程(使用 Spring JDBC 模板)?

對於評論部分的評論:

如果有人認為現在 Spring 4 已經出局,有更好的方法來調用存儲過程

如果您願意使用第三方庫, jOOQ 有一個代碼生成器,可用於為您的所有存儲過程、函數、包、UDT 等生成存根

在您的特定情況下,將有一個帶有doSomething()方法的Mypkg class ,您可以這樣調用:

int id = Mypkg.doSomething(
    configuration, // This wraps your JDBC connection
    name, date
);

而已。 正在處理 JDBC 的所有管道,當您有 UDT(Oracle OBJECTTABLE類型、 PACKAGE類型)或隱式游標時,這尤其好。

You said that you'd like to use Spring JDBC Template, but you can obviously just extract the JDBC Connection from it and supply that to jOOQ, so technically, you'll still be using Spring JDBC Template...

免責聲明:我為 jOOQ 背后的公司工作

如果是 DML 程序且沒有返回變量,我使用了:

Map params = ImmutableMap.builder().put("p1", p1).put("p2", p2).build();
jdbcTemplate.update("{ call pack.proc( :p1, :p1) }", params);

PS如果有返回變量,最好使用函數

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM