Spring 框架已經作為邏輯層被廣泛的用於輕量級的J2EE開發中。Spring 框架可以很好的和Struts, WebWork, Hibernate等其他開源項目集成在一起。尤其在重構現有應用的使用,Spring 框架可以提供很多幫助。存儲過程可以用來封裝業務邏輯,控制用戶權限,以及做日常的增刪改查(CRUD)。在重構已有系統的時候,一般存儲過程中的業務邏輯並不需要改變,只是調用存儲過程的方式需要進行調整。Spring 框架提供了完整的存儲過程封裝,數據源管理的功能,使用Spring 框架,開發人員可以很容易的構建輕量級J2EE應用。本文將給出一個使用Spring 框架和Struts來調用存儲過程的完整例子,包括如何傳入參數,如何獲得傳出參數,如何獲得結果集以及如何獲得多個結果集。
讀者定位為具有DB2和web開發經驗的開發和設計人員。
讀者可以學習到如何使用Spring框架來調用存儲過程,可以通過文中的代碼快速構建一個使用Spring框架的Web應用。本文的例子將包括使用apache Struts進行網頁表單的提交和驗證,使用Spring框架來調用具有輸入輸出參數和返回結果集的存儲過程,結果集將通過DisplayTag來進行顯示。
Spring是一個廣泛應用於邏輯層的開源框架,它使構建J2EE應用程序的Java開發人員可以直達實質問題的核心,而不是在提供服務的細節上花費大量時間。您可以通過developerWorks的文章"Spring 系列: Spring 框架簡介"來了解Spring框架。Spring框架中一個重要的模塊就是Spring DAO,它允許開發者在不同數據訪問技術間切換,而且Spring提供了統一的異常處理機制,在切換的同時不用考慮異常處理。
當前很多應用程序使用存儲過程來封裝業務邏輯,在重構已有應用的時候,只是更改調用存儲過程的方式。在調用存儲過程方面現在大多直接采用JDBC的操作處理,這種方法需要創建數據庫連接、創建statement語句、關閉ResultSet、關閉statement、關閉數據庫連接、處理異常等等,一方面這些代碼與業務邏輯不相關,另一方面也可能會給應用引入bug。因此很多人更青睐於使用數據持久化框架。在數據持久化框架中Hibernate 倍受人們關注,它主要提供Java對象與關系數據庫的映射(O/R Mapping),這樣只需關心操作Java對象而不必關心底層數據。在Hibernate 3.0中也提供了對存儲過程查詢的支持,但是要求存儲過程必須返回一個結果集,並且關鍵的是應用要遵循O/R Mapping的設計要求來架構應用系統。Spring DAO為JDBC提供了抽象層,使得開發者更高效的使用JDBC,它提供了對存儲過程的支持,通過抽象類org.springframework.jdbc.object.StoredProcedure我們可以方便的調用存儲過程,支持輸入參數、輸出參數和多個結果集。下面將通過一個實例詳細的介紹使用Spring框架來調用存儲過程的方法。在頁面表單輸入客戶代碼custNum,然後通過存儲過程查詢數據庫中客戶信息,得到所有客戶信息和所查客戶信息,並將其返回結果在頁面上顯示。您可以下載實例代碼,代碼可以在Eclipse環境中運行,對照代碼看這篇文章更直觀。
1. 編寫DB2存儲過程
我們首先來看DB2的存儲過程。存儲過程一般被用來返回數據庫中表的數據或者對表的數據進行更改。在這裡我們使用存儲過程從表中獲得結果集。
首先我們需要建立DB2環境:
1) 把遠程DB2服務器編制到本地。在這裡我們遠程的DB2主機地址是zxl01.cn.ibm.com,端口是50001
清單1
db2 CATALOG TCPIP NODE node01 REMOTE zxl01.cn.ibm.com SERVER 50001
2) 在本地建立遠程DB2的別名。在這裡我們使用testDB作為別名。
清單2
db2 CATALOG DB testDB AT NODE node01
3) 創建customer數據表
清單3
DROP TABLE DSW.CUSTOMER
@
CREATE TABLE DSW.CUSTOMER
(
CUST_NUM VARCHAR(10) NOT NULL,
CUST_NAME VARCHAR(35) NOT NULL,
ADDRESS VARCHAR(35) NOT NULL,
CITY VARCHAR(35) NOT NULL,
COUNTRY VARCHAR(3) NOT NULL,
ADD_DATE TIMESTAMP NOT NULL
)
@
ALTER TABLE DSW.CUSTOMER
ADD CONSTRAINT CUSTOMER_PK
PRIMARY KEY (CUST_NUM)
@
4) 創建存儲過程
清單4
DROP PROCEDURE DSW.S_CUSTOMER
@
CREATE PROCEDURE DSW.S_CUSTOMER (
OUT poStatus INTEGER,
IN piCust_num VARCHAR(10)
)
LANGUAGE SQL
SPECIFIC DSW.S_CUSTOMER
RESULT SETS 2
P1: BEGIN NOT ATOMIC
-------------------------------------------------------------
-- CONDITION declaration
-------------------------------------------------------------
DECLARE sqlReset CONDITION for sqlstate '80100';
-- Generic Variables
DECLARE SQLCODE INTEGER DEFAULT 0;
DECLARE SQLSTATE CHAR(5) DEFAULT '00000';
-- Generic handler variables
DECLARE hSqlcode INTEGER DEFAULT 0;
DECLARE hSqlstate CHAR(5) DEFAULT '00000';
DECLARE v_Cnt INT;
-- Cursor for returning RS to the clIEnt
DECLARE cursor1 CURSOR WITH RETURN TO CLIENT FOR (
SELECT CUST_NUM, CUST_NAME FROM DSW.CUSTOMER
);
DECLARE cursor2 CURSOR WITH RETURN TO CLIENT FOR (
SELECT * FROM DSW.CUSTOMER
WHERE CUST_NUM = piCust_num
);
OPEN cursor1;
OPEN cursor2;
SET poStatus = 2;
RETURN poStatus;
END P1
@
清單5
INSERT INTO DSW.CUSTOMER (CUST_NUM,CUST_NAME,ADDRESS,CITY,COUNTRY)
VALUES('1','Steven','shang di','BeiJing','CHN')
@
INSERT INTO DSW.CUSTOMER (CUST_NUM,CUST_NAME,ADDRESS,CITY,COUNTRY)
VALUES('2','David','shang di','BeiJing','CHN')
@
INSERT INTO DSW.CUSTOMER (CUST_NUM,CUST_NAME,ADDRESS,CITY,COUNTRY)
VALUES('3','Tony','shang di','BeiJing','CHN')
@
INSERT INTO DSW.CUSTOMER (CUST_NUM,CUST_NAME,ADDRESS,CITY,COUNTRY)
VALUES('4','MaggIE','shang di','BeiJing','CHN')
@
2. 創建基於Spring+Struts框架的web應用
將Spring框架集成到現有的Web應用中十分容易,在web.xml中的Listener部分增加一個Spring的ContextLoaderListener同時設置contextConfigLocation參數,指向Spring的配置文件位置。如清單6所示。在web.XML中還有對於Struts的支持,讀者可以參考developerworks上的Struts專題來了解如何使用Struts。
清單 6. web.XML.
<?XML version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application
2.3//EN"
"http://Java.sun.com/dtd/web-app_2_3.dtd">
<web-app id="WebApp">
<display-name>SpringSproc</display-name>
<!-- Standard Action Servlet Configuration -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-service.XML
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.Html</welcome-file>
</welcome-file-list>
</web-app>行整合的方法有很多,developerworks上的文章"使用 Spring 更好地處理 Struts 動作"中介紹了幾種方法,可以參照。
3. 使用Spring framework建立數據源
在Spring的配置文件applicationContext-service.XML中我們對使用到的Java類和數據源進行了配置。通過指定driverClassName、url、username、passWord等屬性就可以創建數據源。在清單7中我們使用直接連接數據庫的方式,我們也可以使用在應用服務器中配置的DataSource來連接數據庫,只需要更改為不同的Reference就可以改變對數據庫的連接方式。在這方面,Spring框架顯示出了很強的靈活性。
清單7. applicationContext-service.XML.
<?XML version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="dataSourceJDBC"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>COM.ibm.db2.jdbc.app.DB2Driver</value>
</property>
<property name="url">
<value>jdbc:db2:testDB</value>
</property>
<property name="username">
<value>dsw</value>
</property>
<property name="passWord">
<value>pass4spring</value>
</property>
</bean>
<bean id="SearchImpl" class="com.springsproc.dao.jdbc.SearchDAOImpl">
<property name="dataSource">
<ref local="dataSourceJDBC" />
</property>
<property name="sprocName">
<value>DSW.S_CUSTOMER</value>
</property>
</bean>
</beans>
在Java代碼SearchAction.java中將連接實例化。這也是Spring中的核心思想之一:控制反轉(Inversion of Control)。清單8中(1)是把一個在applicationContext-service.XML中配置的bean實例化成對象,(2)調用getBean方法得到基於接口SearchDAO(具體見清單9) 的實例SearchImpl bean,然後再通過(3)調用具體的方法去執行存儲過程。in Struts SearchAction.Java.
private static ApplicationContext ctx = null;
public Object getBean(String name) { |(1)
if (ctx == null) {
ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext
(servlet.getServletContext());
}
return ctx.getBean(name);
}
SearchDAO mgr = (SearchDAO) getBean("SearchImpl"); |(2)
Map map = mgr.execute(custNum); |(3)
清單9. Code in SearchDAO.Java.
package com.springsproc.dao;
import Java.util.List;
public interface SearchDAO {
public Map execute(String custNum);
}
4. 使用Spring framework為存儲過程傳入參數
用戶在web頁面上填入custNum,提交後經Struts的validation驗證傳入action的FormBean中,然後再從FormBean中得到custNum,並由清單8中(3)傳入Spring framework。
5. 使用Spring framework獲得存儲過程的傳出參數和結果集
Spring framework提供了調用存儲過程的方法,下面對幾個相關的類和接口做簡單介紹,你也可以通過Spring API獲得更多詳細信息。
org.springframework.jdbc.object.StoredProcedure 這是一個抽象類,通過declareParameter方法來聲明存儲過程的輸入輸出參數,再由execute方法來調用存儲過程。
org.springframework.jdbc.core.SqlParameter 用來代表存儲過程的參數。
org.springframework.jdbc.core.ResultSetSupportingSqlParameter SqlParameter的子類,是SqlOutParameter、SqlReturnResultSet這些支持結果集參數類的公共基類。 .core.SqlOutParameter ResultSetSupportingSqlParameter的子類,用來代表存儲過程的輸出參數。
org.springframework.jdbc.core.SqlReturnResultSet ResultSetSupportingSqlParameter的子類,用來代表調用存儲過程的輸出結果集。
org.springframework.jdbc.core.RowMapper 這是一個接口,通常為JdbcTemplate的查詢方法或者存儲過程返回結果集使用。
了解了Spring為我們提供的這些關於存儲過程的方法和接口後,來看看具體的實現方法。如清單10所示。SearchDAOImpl.Java中有三個內部類,其中SearchCustomersProcedure來負責聲明數據庫連接和存儲過程名(2)、輸入參數(6)、輸出參數(5)以及輸出結果集(3)(4),DemoRowMapper和DemoRowMapper2來負責取得結果集中的數據。其中(3)創建一個名為"resultSet"的結果集,通過DemoRowMapper(7)來取得結果集中的數據,當有多個結果集的時候需要重復這個過程,如(6)(8)。最後由execute方法(1)填入輸入參數並執行存儲過程調用。
清單10. Code in SearchDAOImpl.Java.
public Map execute(String custNum) {
SearchCustomersProcedure sp = new SearchCustomersProcedure(dataSource);
Map map = new HashMap(1);
map.put("piCust_num", custNum);
Map results = sp.execute(map); |(1)
return results;
}
private class SearchCustomersProcedure extends StoredProcedure {
SearchCustomersProcedure(DataSource dataSource) {
super(dataSource, sprocName); |(2)
declareParameter(new SqlReturnResultSet("resultSet",
new DemoRowMapper())); |(3)
declareParameter(new SqlReturnResultSet("resultSet2",
new DemoRowMapper2())); |(4)
declareParameter(new SqlOutParameter("poGenStatus", Types.INTEGER));
|(5)
declareParameter(new SqlParameter("piCust_num", Types.VARCHAR));
|(6) }
}
private class DemoRowMapper implements RowMapper { |(7)
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Customer customer = new Customer();
customer.setCustNum(rs.getString(1));
customer.setCustName(rs.getString(2));
return customer;
}
}
private class DemoRowMapper2 implements RowMapper { |(8)
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Customer customer = new Customer();
customer.setCustName(rs.getString(1));
customer.setCustNum(rs.getString(2));
customer.setCustAddress(rs.getString(3));
customer.setCustCity(rs.getString(4));
customer.setCustCountry(rs.getString(5));
return customer;
}
}
6. 在Web頁面上顯示結果集 (使用DisplayTag)
在SearchAction從SearchDAOImpl取得數據以後,將數據以attribute方式存入request中,如清單11中(1)(2)所示。在頁面中使用DisplayTag將request中的數據以表格形式顯示出來。可以指定列名或者按某些列排序,如清單12中(1);也可以不做任何處理,只是將數據顯示出來,如清單12中(2)。可以在DisplayTag Homepage得到更多關於DisplayTag的詳細信息。.get("resultSet");
List list2 = (List)map.get("resultSet2");
String poGenStatus = map.get("poGenStatus").toString();
log.info("poGenStatus is:" + poGenStatus);
request.setAttribute("customerList",list); |(1)
request.setAttribute("MyCustomers",list2); |(2)
return mapping.findForward("list");
清單12. Code in Struts SearchByCustNumber.JSP
|(1)
<display:table name="customerList" cellspacing="2" cellpadding="2"
requestURI="" defaultsort="1" id="customers">
<%-- Table columns --%>
<display:column property="custNum" sort="true"
headerClass="sortable"
title="Customer number" headerStyleClass="header"/>
<display:column property="custName" sort="true"
headerClass="sortable"
title="Customer name" headerStyleClass="header"/>
</display:table>
|(2)
<display:table name="myCustomer" cellspacing="2" cellpadding="2"
requestURI="" defaultsort="1" id="myCustomer">
</display:table>
這樣使用Spring framework調用存儲過程的實例就完成了,運行該實例,如下圖所示:
使用Spring framework的優點
Spring框架提供了一些接口和類,封裝了對數據庫的處理,極大地簡化了對數據庫的操作。開發人員可以把更多的精力放在業務邏輯上。數據庫連接等信息寫在配置文件中,在運行期裝入bean中進行連接操作實例化的方法使系統更加靈活,可以在部署應用的時候很方便的更改,不僅提高了團隊開發的效率,也提高了系統的可維護性。
總結
本文以一個實例介紹了使用Spring框架調用存儲過程的方法,通過本文您已經可以使用Spring框架結合不同類型的存儲過程來對DB2中的數據進行操作。並且參照這個實例您可以創建基於Struts + Spring框架的web應用系統。