springframework.jdbc.object.StoredProcedure是對應存儲過程調用的操作對象,它通過其父類org.springframework.jdbc.object.SqlCall獲得相應的底層API支持(CallableStatementCreator),然後在此基礎之上構建了調用存儲過程的執行方法。
StoredProcedure是抽象類,所以需要實現相應子類以封裝對特定存儲過程的調用,還記得我們在講解JdbcTemplate調用存儲過程時候定義的存儲過程嗎?
CREATE PROCEDURE CountTable(IN tableName varchar(1000),OUT sqlStr varchar(1000) , INOUT v INT)
BEGIN
set @flag = v;
set @sql = CONCAT('select count(*) into @res from ' , tableName , ' where ACTIVE_FLAG=?');
PREPARE stmt FROM @sql;
EXECUTE stmt using @flag;
DEALLOCATE PREPARE stmt;
set v = @res;
set sqlStr = @sql;
END
通過繼承StoredProcedure,我們可以為該存儲過程的調用提供一個對應的操作對象:
public class CountTableStoredProcedure extends StoredProcedure {
private static final String PROCEDURE_NAME = "CountTable";
public static final String IN_PARAMETER_NAME = "tableName";
public static final String OUT_PARAMETER_NAME = "sqlStr";
public static final String INOUT_PARAMETER_NAME = "v";
public CountTableStoredProcedure(DataSource dataSource)
{
super(dataSource,PROCEDURE_NAME);
// setFunction(true);
declareParameter(new SqlParameter(IN_PARAMETER_NAME,Types.VARCHAR));
declareParameter(new SqlOutParameter(OUT_PARAMETER_NAME,Types.VARCHAR));
declareParameter(new SqlInOutParameter(INOUT_PARAMETER_NAME,Types.INTEGER));
compile();
}
public CountTableResult doCountTable(String tableName,Integer v)
{
Map paraMap = new HashMap();
paraMap.put(IN_PARAMETER_NAME, tableName);
paraMap.put(INOUT_PARAMETER_NAME, v);
Map resultMap = execute(paraMap);
CountTableResult result = new CountTableResult();
result.setSql((String)resultMap.get(OUT_PARAMETER_NAME));
result.setCount((Integer)resultMap.get(INOUT_PARAMETER_NAME));
return result;
}
}
關於該存儲過程操作對象,部分細節我們有必要關注一下:
存儲過程操作對象對應的SQL是存儲過程的名稱,而不是真正意義上的SQL語句,當我們調用compile方法的時候,StoredProcedure的父類SqlCall會根據你提供的存儲過程名稱拼裝真正意義上的符合SQL92標准的存儲過程調用語句,類似於“{ call CountTable(?,?,?) }”的形式。
因為我們的CountTableStoredProcedure只針對CountTable存儲過程調用,所以,該存儲過程的名稱我們在類一開始就聲明為常量:
private static final String PROCEDURE_NAME = "CountTable";
如果有多個存儲過程的參數順序相同,結果處理也一樣的話,你也可以將存儲過程的名稱聲明為變量,這完全要取決於具體的應用場景。
在構造方法中,我們將“setFunction(true);”注釋掉了,因為我們調用的CountTable不是一個Function,如果你要調用的存儲過程類型為Function的話,你需要通過該方法將“function”的值設置為true,以告知StoredProcedure在處理調用的時候要區別對待。
在complie之前通過declareParameter聲明參數,這幾乎是雷打不動的慣例,不過,在StoredProcedure中使用declareParameter的時候卻要有所注意了:
針對存儲過程參數類型為IN,OUT和INOUT不同,declareParameter接受的參數類型也應該是SqlParameter,SqlOutParameter和SqlInOutParameter;
SqlParameter,SqlOutParameter和SqlInOutParameter的相應實例在構造的時候,必須指定對應的參數名稱,因為在調用存儲過程的時候,需要根據名稱傳入參數,更需要根據名稱取得調用結果;
StoredProcedure提供了execute方法執行存儲過程調用,通過Map的形式傳入調用所需要的IN或者INOUT類型的參數值,所以,在構建參數Map的時候,該Map中的Key應該與declareParameter時候聲明的參數名稱相同; 另外,execute執行後返回的結果也是Map形式,從該結果Map中取得具體的結果值的時候,也是通過declareParameter中聲明的OUT/INOUT參數名作為key來獲取的,所以,這就是我們將各個參數的名稱在類定義的開始聲明為常量的原因:
public static final String IN_PARAMETER_NAME = "tableName";
public static final String OUT_PARAMETER_NAME = "sqlStr";
public static final String INOUT_PARAMETER_NAME = "v";
無論是輸入參數Map還是輸出參數結果對應的Map,他們中的Key應該與通過declareParameter方法聲明的參數名稱一一對應。
通過擴展StoredProcedure,我們不但封裝了參數的聲明和結果的提取,我們還為調用方提供了強類型的調用方法,現在,調用方可以通過doCountTable方法的強類型參數聲明傳入參數值,並取得強類型的CountTableResult對象作為結果,而不是泛泛的一個Map。
對於存儲過程的調用者來說,它的代碼現在可以簡潔到兩行代碼:
// DataSource dataSource = ...;
CountTableStoredProcedure storedProcedure = new CountTableStoredProcedure(dataSource);
CountTableResult result= storedProcedure.doCountTable("tableName",1);
...
漂亮多了,不是嗎?
StoredProcedure提供了兩個execute方法執行存儲過程的調用,一個就是我們剛才使用的通過Map提供輸入參數的execute方法,另一個則是使用ParameterMapper類型提供輸入參數的execute方法。 那麼,為什麼要提供這個使用ParameterMapper類型提供輸入參數的execute方法那?
ParameterMapper定義的callback方法暴露了相應的Connection,如果說在構造輸入參數列表的時候,必須用到Connection的話,ParameterMapper恰好可以提供支持。比如,Oracle中定義的一存儲過程,接收數組類型作為參數,而在oracle中,你只能通過Oracle.sql.ARRAY和相應的Oracle.sql.ArrayDescriptor來定義數組類型的參數,ARRAY和ArrayDescriptor都需要用到相應的Connection進行構造。所以,對於Oracle中需要使用數組傳入參數的存儲過程來說,我們可以通過如下類似代碼進行調用:
public class OracleStoredProcedure
{
...
public Map call(...)
{
ParameterMapper paramMapper = new ParameterMapper(){
public Map createMap(Connection connection) throws SQLException {
Map inMap = new HashMap();
...
Integer[] params = new Integer[]{new Integer(1),new Integer(2)};
ArrayDescriptor desc = new ArrayDescriptor("numbers", connection);
ARRAY nums = new ARRAY(desc, connection, params);
inMap.put("ArrayParameterName", nums);
...
return inMap;
}};
return execute(paramMapper);
}
}
當然啦,我們的CountTableStoredProcedure在調用存儲過程的時候也可以使用ParameterMapper傳入相應的調用參數,只不過,ParameterMapper的createMap方法暴露的Connection對於我們來說沒有太大用處罷了。
http://blog.csdn.net/onlyzhangqin/archive/2008/08/08/2787292.aspx