程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Apache Geronimo和Spring框架,第3部分: 集成DAO與ORM

Apache Geronimo和Spring框架,第3部分: 集成DAO與ORM

編輯:關於JAVA

動態透明地讀取數據

簡介:本教程將擴展在本系列教程(共 6 個部分)的 第 2 部分 中創建的 Apache Geronimo 應用程 序。在 第 1 部分 中向您介紹了 Geronimo 應用服務器、Spring 框架和控制反轉(Inversion of Control,IoC)。然後又詳細闡述了如何開發、配置和部署第一個基於 Spring 框架的應用程序。在本部 分中,您將了解如何讓集成的技術 —— 如 Spring Java 數據庫連接(Java Database Connectivity,JDBC)和 Spring 數據訪問對象(Data Access Object,DAO)API —— 通過 從 Apache Derby 數據庫中動態讀取應用程序數據而發揮其作用。您還將了解如何使用 iBATIS 將對象關 系映射(Object Relational Mapping,ORM)集成到應用程序中,並享受在不觸及任何代碼的前提下通過 修改應用程序中的數據源實現依賴性注入的妙處。

開始之前

本系列教程適合於需要了解 Spring 框架的更多信息以及如何在 Apache Geronimo 應用服務器上使用 Spring 框架的強大功能的 Java™ Platform, Enterprise Edition (Java EE) 開發人員。

關於本系列教程

本系列教程共分為 6 個部分,向您介紹了 Spring 框架及 Spring 框架怎樣與 Geronimo 結合使用。我們將從檢驗各種 Spring 框架方法及其怎樣與 Geronimo 服 務器結合使用入手。在整個系列教程中,您將開發和部署個人電話本應用程序。該應用程序包括以下功能 :

顯示電話本

顯示每個條目的細節

向電話本中添加新條目

編輯、修改和刪 除條目

向條目中添加更多細節,例如主電子郵件地址

第 1 部分 介紹了 Spring 框架的各 個模塊,並介紹了每個模塊與在 Geronimo 應用服務器上開發 Java EE 應用程序的關系。該部分還說明 了 Spring 框架所基於的方法。

第 2 部分 介紹了如何使用 Spring 框架在 Geronimo 上構建第 一個骨架系統應用程序。

在第 3 部分中,您將采用通過 Derby 數據庫添加 JDBC 支持來擴展在 第 2 部分中獲得的 Geronimo 應用程序。您還將了解如何將 ORM 集成到使用 iBatis 的應用程序中。

在第 4 部分中,您將面對 Spring 面向方面編程 (AOP) 和 Spring Web 框架。使用 Spring AOP ,任何受 Spring 框架管理的對象都可變為面向方面的,並且本教程利用了通過 Spring AOP 提供的聲明 式事務管理服務。

第 5 部分介紹了 Spring Model-View-Controller (MVC)。該教程向您介紹了 Spring MVC 框架及 Web 視圖,使您可以了解 Spring MVC 的入門知識。

在本教程結束的第 6 部分,介紹了如何通過 Spring 框架使用 JavaServer Page™(JSP™)、Velocity、Tile 和 PDF 導出功能。您將使 用和體驗 Spring MVC 內置的各種 Web 視圖。

關於本教程

在 第 2 部分 中,開始使用 Spring 框架中的一些模塊來部署電話本應用程序。您還了解了如何在 Geronimo 上部署該電話本應用程 序。該應用程序中的數據被靜態地硬編碼到 JSP 頁面中。在本教程中,您將了解如何從 Derby 數據庫中 動態讀取數據,以及如何使用 Geronimo 的 Web 控制台創建表並通過使用 Spring JDBC 和 Spring DAO API 從中訪問數據。

此外,您還將了解如何使用 iBATIS ORM 框架將數據對象映射為關系數據庫 對象,並使用 ORM SQL 映射在 Apache Commons connection API 的幫助下讓應用程序能夠透明地讀取數 據。

最後,將把所有技術集成到應用程序中,該應用程序將使用 Spring DAO 及用 iBATIS ORM 定義的 SQL 映射,並使用 Spring JDBC 連接至數據庫。構建完應用程序後,您將看到依賴性注入的妙處 ;您只需在配置文件中更改幾行即可改變應用程序使用的數據源,而整個過程都無需觸及任何代碼。

本教程展示了如何將這些技術融合在一起為您提供設計應用程序模型實現的另一種方式。

先決條件

本教程假定您熟悉面向對象的編程 (OOP) 並且熟知 J2EE 和 Java EE 術語。雖然本教 程不會涵蓋這些內容,但是您必須熟悉基本的 SQL 語句,了解基本的 XML 語義,並且了解對象關系映射 的基本概念。了解面向方面編程更佳,但不作硬性要求。

系統要求

您的系統需要至少滿足 以下要求才能繼續學習本系列教程:

The Spring Framework, Version 1.2.8 —— 具 有所有依賴性的壓縮文件。

Apache Geronimo 1.1 —— Geronimo 是 Apache 的 J2EE 認證應用服務器。

Apache Derby 數據庫 —— 本教程使用 Derby,該數據庫是開源的 輕量級數據庫。Derby 是嵌入到 Geronimo 1.1 裡的,因此不需要再單獨安裝。

iBATIS 框架 —— iBATIS 的最新發行版是 2.1.7。

Apache commons dbcp 軟件包 —— 需要使用此軟件包進行 ORM 模塊開發。

Apache commons pool 軟件包 —— 這是 ORM 模塊所需的軟件包。

Apache Ant —— 確保正確配置 Ant 並且其 /bin 目錄位於 Path 系統變量中。

Java 1.4.2 —— 確保 Java 安裝並運行在系統中。

以下是安裝和配置開發、部署和運行示例應用 程序所需的軟件的指導信息。

Spring 框架和 Geronimo 安裝 —— 為了使樣例代碼能 夠運行,需要安裝運行 Geronimo 和 Spring 框架。有關安裝指南,請參閱 第 2 部分。

iBATIS 安裝 —— 需要使用 iBATIS SQL 映射作為 ORM 示例的對象關系映射工具。下載後,將壓縮 文件解壓縮到應用程序所在的驅動器中。解壓縮文件應當會創建一個名為 iBatis_2.1.7 的目錄(在我的 驅動器中,iBATIS 的安裝目錄是 K:\iBatis_2.1.7)。此時,不需要為 SWF 做任何配置。

Derby 數據庫 —— Geronimo 1.1 預打包附帶的 Derby 數據庫安裝不需要任何特殊配置。

Apache Commons dbcp 和 Commons pool 軟件包 —— 需要這些軟件包中的 JAR 與 ORM 示例相配合。其目的是展示不觸及任何源代碼而進行數據源和連接程序的更改是多麼簡單。這是通過 使用配置文件進行依賴性注入實現的。您將了解如何使用這些 API;目前先下載這些 API 並將它們解壓 縮到硬盤上。請記住,只需這兩個軟件包中的 JAR。

iBATIS 和 Spring

由於 iBATIS 在本 教程中具有特殊性,本節將介紹什麼是 iBATIS 以及必須對它感興趣的原因。在投入到應用程序的實際開 發之前,還必須大致了解一下 Spring 的兩種關鍵技術:DAO 模塊和 JDBC API。

什麼是 iBATIS ?

iBATIS 是一個開放源碼對象關系映射程序,它比其他映射程序更輕、更簡單,其功能是使用名 為 SQL 映射的簡單概念將對象映射到 SQL 語句。SQL 映射是一種對象關系映射框架 —— 如 Java 數據對象(Java Data Object,JDO)、Hibernate 等 —— 它將 Java 對象映射到 SQL 語句。它抽象出數據庫事務(例如加載數據庫驅動程序和獲取並管理連接)中所涉及的低級別的細節,也 提供高級別的 ORM 性能。

Spring DAO 和 iBATIS API

iBATIS API 提供了將對象映射到 SQL 語句的基本框架,而 Spring ORM API 提供了一種可以輕松地使用依賴性注入在運行時應用這些關系 的方法。使用 DAO 設計模式,可以抽象和封裝對數據源的所有訪問。iBATIS DAO 框架提供了管理連接的 功能。Spring DAO 可以提供同樣的功能,但是它比 iBATIS 實現更有優勢,因為所有配置細節都是在上 下文文件中定義的。

為什麼使用 iBATIS?

那麼為什麼選擇 iBATIS 用於應用程序?

將 SQL 語句映射到對象十分簡單

能夠從應用程序中抽象出高級別的細節

簡單的應 用程序數據庫不要求使用諸如 Hibernate 之類的更復雜的解決方案

iBATIS 數據庫層附帶了兩個 庫:SQL 映射和 Data Access Object 框架,接下來將詳細查看這兩個庫。

iBATIS SQL 映射

又稱為 Data Mapper 框架。iBATIS SQL 映射框架可以減少訪問關系數據庫通常所需的代碼量。 此框架的特別之處在於它有核心函數庫。Data Mapper 框架允許為 Java 應用程序設計和實現更優秀的持 久層,方法是使用簡單的 XML 描述符將對象與 SQL 語句或存儲過程耦合在一起。它還有助於分離應用程 序開發中的職能:允許開發人員靈活地將純 SQL 映射到 Java 對象,同時允許 DBA 優化實際的 SQL 語 句以提高性能。

在本教程的稍後部分中,將看到在 ORM 示例中使用 SQL 映射是多麼輕松。

iBATIS Data Access Object 框架

它是 iBATIS 框架提供的另一個抽象層 API。它隔離持 久性解決方案的具體細節,並提供通用 API 透明地訪問應用程序中的數據。

iBATIS DAO 是一個 精心打造、設計健壯的框架;本文的主要目標是展示 Spring Framework 所能提供的功能,這也是為什麼 要使用 Spring 的 DAO 層來抽象與持久層的通信。使用 Spring 的動態配置功能,可以很容易切換到任 何受支持的持久性框架,例如 Hibernate、JDO 或 Apache ObJectRelationalBridge (OJB)。

Spring 中的持久性

Spring 框架用來解決持久性問題的兩種最重要方法是 Spring DAO 和 Spring ORM 模塊。下面列出了其中的一些問題及 Spring 框架是如何幫助解決這些問題的:

不同 的數據源使測試變得困難。如上所述,Spring 的 IoC 方法使交換與對象關系 (O/R) 相關的各種實體的 實現和配置的位置變得十分簡單。這簡化了孤立地測試與持久性相關的代碼的各個片段的過程。

特定於供應商的數據訪問異常。Spring 可以將來自任選的 O/R 映射工具的異常轉換成已定義的異常集, 這些異常更易於理解。

事務管理。Spring 負責處理事務語義和處理回滾等操作的相應事務。

供應商固定,並允許混合-搭配的實現策略。使用 Spring 的解耦方法,可以在運行時使用和更改 各種 API、數據源和實現。使用此方法不必固定使用特定供應商的產品和服務,並可以根據需要混合搭配 。

軟件開發人員一直以來都在尋求一種持久保持模型的簡潔方法,但未取得很大成功。最佳的持 久性框架允許使用一種透明的方法而無需侵入域模型。Spring 框架用它的兩個模塊 DAO 和 ORM 來完成 此工作。

Spring DAO

Spring 框架設計允許直接集成到一些常見的 O/R 映射 API,例如 JDO、Hibernate 和 iBATIS。其主要目標是隱藏和抽象數據庫事務(如從應用程序代碼中創建連接以及管 理這些連接及其他內容)的繁瑣細節。Spring DAO 模塊使您可以定義在應用程序中負責數據中心操作的 接口(類必須實現的抽象類型)。

Spring JDBC

Spring JDBC API 提供了各種軟件包以服 務於所有數據庫需求。其核心軟件包包含 JdbcTemplate 類,該類負責與數據庫操作相關的所有函數。另 外它還提供了一種更通用的異常處理架構。

要了解關於這兩種技術的更多信息,請閱讀本系列教 程的 第 1 部分。接下來將開始擴展在 第 2 部分 中構建的電話本應用程序以使用 Spring JDBC 和 DAO 模塊。

回顧電話本應用程序和創建數據庫

要擴展應用程序以使用 Spring DAO 和 Spring ORM 模塊,必須先在 Derby 中添加並填充模型所需的表。在那之前,先要檢查工作區目錄結構、回顧已 有電話本應用程序的事件流程、查看新的事件流程,這些事件將反映出使用了 JDBC-DAO 和 ORM 的擴展 應用程序的功能。

工作區的目錄結構

圖 1 展示了應用程序的布局。因為您已經在本系列 教程中執行過此過程,現在可以直接下載本教程附帶的壓縮文件(請參閱 下載 部分)並將其解壓縮到硬 盤中。這應當會創建一個具有所有必需的文件及子文件夾的工作區目錄(在我的計算機中,它是 k:/workspace。在本教程的其余部分,我將把此目錄稱為 <WORKSPACE>)。

本教程中涵蓋 了 圖 1 中所示的所有目錄和文件。

圖 1. 解壓縮源文件後應用程序的目錄結構

回顧當前的電話本應 用程序

在創建新的事件流程之前,讓我們先來迅速回顧一下本系列教程的 第 2 部分 中在 Geronimo 內創建的事件(參見 圖 2)。

圖 2. Geronimo 應用服務器處理請求的流程

如 圖 2 所示,對 home.do 頁面的請求將轉到 Geronimo 應用服務器。運行在 Geronimo 容器 內部的 Tomcat Web 容器將處理對應用服務器的請求。Tomcat 隨後讀取配置文件(尤其是 web.xml), 發現 DispatcherServlet 已被配置為處理此請求,因此該請求被轉發到 DispatcherServlet。

DispatcherServlet 是 Spring MVC Framework 的一部分,因此 Spring 容器將從此處接管並嘗 試判斷如何處理此請求 —— 或者判斷應當由哪個控制器處理此請求。此容器使用依賴性注入 來執行此操作(IoC,也稱為依賴性注入,是一種用於解決組件的依賴性解析、配置和生命周期的設計模 式)。然後它使用在 phonebook-servlet.xml 中定義的 Application Context(Spring 框架中用於解析 組件間的依賴性的中心接口),phonebook-servlet.xml 文件幫助 Spring 容器把 /home.do 請求與 PhonebookController 關聯起來。然後請求被轉發給 PhonebookController 類。這將返回 home.jsp 頁 面。響應被轉發給 Tomcat 容器,該容器編譯 JSP 並將響應返回給浏覽器。

使用 Spring DAO 和 JDBC 處理 home-dao.jsp

看一看使用 Spring DAO 和 JDBC 後的事件。對 home-dao.jsp(應用程 序的 JDBC DAO 實現)的請求將經歷 圖 2 中所示的事件,直至請求到達 JSP 頁面。此頁面的前幾行告 訴容器只能使用 PhonebookDataProvider 服務獲取數據。Spring 容器將從 Application Context 中讀 取 PhonebookDataProvider 的 Bean 定義。它還使用依賴性注入將 PhonebookJdbcDao 與此實例的 PhonebookDataProvider 動態地關聯起來。實現 Spring DAO API 的 PhonebookJdbcDao 將使用 Spring 容器的 IoC(依賴性注入)或使用為該目的定義的 Bean 讀取要使用的數據源。按照預期,一切依序進行 ,PhonebookDataProvider 將使用 Spring JdcbTemplate 類從數據庫中讀取數據。這種解釋聽起來可能 有點太過復雜不容易理解,但是隨著後面應用程序的開發,這些解釋會變得清晰起來。圖 3 顯示了處理 此請求的事件流程。

圖 3. JDBC-DAO 應用程序的事件流程

使用 Spring ORM 和 iBATIS 處理 home-orm.jsp

接下來,將使用 ORM 從數據庫中讀取數據。對 home-orm.jsp(應用 程序的 ORM 實現)的請求將經歷 圖 2 中所示的事件,直至請求到達 JSP 頁面。home-orm.jsp 的前幾 行告訴容器只能使用 PhonebookDataProvider 服務獲取數據。

Spring 容器將從 Application Context 中讀取 PhonebookDataProvider 的 Bean 定義。它使用依賴性注入將 SqlMapPhonebookDao 與 此實例的 PhonebookDataProvider 動態地關聯起來。使用 Spring ORM API 的 SqlMapPhonebookDao 將 使用 Spring 容器的 IoC 或使用為該目的定義的 Bean 讀取要使用的數據源。Spring iBATIS API 的 SqlMapClientFactoryBean 將把 SQL 映射與 PhonebookEntry 對象關聯起來。並且所有這些都是使用 IoC 來實現的,因此各組件之間可能的耦合量最小。

所有關聯准備好後,PhonebookDataProvider 使用 Spring SqlMapClientSupport 類從數據庫中讀取數據。它將抽象所有來自用戶的繁瑣細節,同時提 供所有功能,例如事件處理、對象的生命周期管理等等。同樣地,這種解釋聽起來可能有點太復雜難以理 解,但是隨著後面應用程序的開發,這些解釋會變得清晰起來。圖 4 展示了 ORM 應用程序的事件流程。

圖 4. ORM 應用程序的事件流程

數據模型

現在 可以將注意力轉到 Derby,並添加應用程序所需的表。

數據模型很簡單,它描述了在數據庫中組 織業務數據的方法,由兩張表構成:

PB_ENTRY —— 應用程序中的每個電話本條目都 有名字、姓名和惟一 ID,這些數據是在這張表中維護的。

PB_DETAILS —— 與電話本 中的每個條目相關的所有細節都是在這張表中維護的。每個聯系人都可以在這張表中登記一個或多個電話 號碼,這些號碼與聯系人的惟一 ID 相關聯。

下面開始定義本教程的所有版本的應用程序均可使 用的透明模型。清單 1 展示了電話本應用程序的模型。

清單 1. PhonebookEntry 對象用作應用 程序的模型

public class PhonebookEntry {

  private int  entryID;
  private String fName;
  private String lName;
  private  int rowID;
  private String homeNumber;
  private String workNumber;
  private String cellNumber;
  private String email;

  public  PhonebookEntry() {
  }

  public int getEntryID() {
     return entryID;
  }

  public void setEntryID(int entryID) {
     this.entryID = entryID;
  }

  public String getFName() {
    return fName;
  }

  public void setFName(String fName)  {
    this.fName = fName;
  }
...
...
// other getter  and setter methods 

創建和填充應用程序的數據庫

創建樣例應用程序的第一 步是創建應用程序要連接的數據庫。然後必須創建表並將值放入其中,以便數據庫讀入。我已經創建了以 下兩個 SQL 腳本,您可以使用它們來實現上述操作,二者均位於 <WORKSPACE>/scripts 目錄:

createTables.sql 將創建應用程序必需的表。

loadTables.sql 將用值填充這些表。

使用 Derby 數據庫的最簡單方法是通過 Geronimo Web 控制台。請按照以下步驟創建數據庫:

通過更改到 Geronimo 安裝目錄並在命令行窗口中鍵入命令:java -jar server.jar,啟動 Geronimo。注:如果您剛開始使用 Geronimo 並需要了解如何安裝和運行 Geronimo,請參閱本教程末尾 的 參考資料 部分以獲得鏈接,幫助您開始使用 Apache Geronimo。

服務器啟動後,通過在浏覽 器中指向 URL http://localhost:8080/console 登錄到 Geronimo Web 控制台。注:需要以管理員身份 登錄以訪問 Administration Console。使用默認的用戶名 system 和默認的密碼 manager。

單擊 位於左側的 Console Navigation 面板底部的 DB Manager 鏈接以打開 Derby 數據庫管理器。控制台窗 口應當如 圖 5 所示。

圖 5. DB Manager 控制台

下一步是創建數據庫 。

在 Create DB 文本區域中,鍵入 phonebook 並單擊 Create 按鈕。這應當會在 Derby 中創建 數據庫。

將 清單 2 中的 SQL 語句粘貼到 SQL Commands 文本框中。

清單 2. 在電話本 數據庫中創建表

CREATE TABLE PB_ENTRY (
  ENTRY_ID INT NOT  NULL,
  ENTRY_FNAME VARCHAR(80) ,
  ENTRY_LNAME VARCHAR(80) ,
  CONSTRAINT PB_ENTRY_PK PRIMARY KEY (ENTRY_ID)
);

CREATE TABLE  PB_DETAILS (
 ENTRY_ID INT NOT NULL,
  ROW_ID INT NOT NULL,
  HOME_NUMBER VARCHAR(20) ,
 WORK_NUMBER VARCHAR(20) ,
 CELL_NUMBER VARCHAR (20) ,
  EMAIL VARCHAR(200),
 CONSTRAINT PB_DETAILS_PK PRIMARY KEY  (ENTRY_ID, ROW_ID),
 CONSTRAINT PB_DETAILS_FK FOREIGN KEY (ENTRY_ID) REFERENCES  PB_ENTRY(ENTRY_ID)
);

從數據庫列表中選擇 phonebook,然後單擊 Run SQL。這 應當會為應用程序創建所需的兩張表。此外,還可以在文本編輯框中打開 createTables.sql 腳本,並將 所有語句復制並粘貼到 SQL Commands 文本框中。

所需的表已經准備好。現在需要在表中放入一 些數據,因此執行以上描述的相同步驟,並運行以下 SQL 語句;請記住選擇 phonebook 作為數據庫(參 見 清單 3)。

清單 3. 用應用程序的數據填充表

INSERT INTO PB_ENTRY  (ENTRY_ID, ENTRY_FNAME, ENTRY_LNAME) VALUES
 (0,'Default','Entry');

INSERT INTO PB_ENTRY (ENTRY_ID, ENTRY_FNAME, ENTRY_LNAME) VALUES ((SELECT
  MAX(ENTRY_ID)+1 FROM PB_ENTRY),'Adam','Clark');

INSERT INTO PB_ENTRY  (ENTRY_ID, ENTRY_FNAME, ENTRY_LNAME) VALUES ((SELECT
 MAX(ENTRY_ID)+1 FROM  PB_ENTRY),'Charlie','Smearlas');

INSERT INTO PB_ENTRY (ENTRY_ID, ENTRY_FNAME,  ENTRY_LNAME) VALUES ((SELECT
 MAX(ENTRY_ID)+1 FROM  PB_ENTRY),'Don','Brownie');

INSERT INTO PB_ENTRY (ENTRY_ID, ENTRY_FNAME,  ENTRY_LNAME) VALUES ((SELECT
 MAX(ENTRY_ID)+1 FROM PB_ENTRY),'Harry','Potter');

INSERT INTO PB_DETAILS (ENTRY_ID,ROW_ID) VALUES (0,0);

INSERT INTO  PB_DETAILS
 (ENTRY_ID,ROW_ID,HOME_NUMBER,WORK_NUMBER,CELL_NUMBER,EMAIL) VALUES
 (1,(SELECT MAX(ROW_ID)+1 FROM PB_DETAILS),'1 978 234 7839','1 978 134 
  7830','1 978 378 7578','[email protected]');

INSERT INTO PB_DETAILS
  (ENTRY_ID,ROW_ID,HOME_NUMBER,WORK_NUMBER,CELL_NUMBER,EMAIL) VALUES
 (2,(SELECT MAX (ROW_ID)+1 FROM PB_DETAILS),'1 617 456 6783','1 617 290
 3556','1 617 980  2467','[email protected]');

INSERT INTO PB_DETAILS
  (ENTRY_ID,ROW_ID,HOME_NUMBER,WORK_NUMBER,CELL_NUMBER,EMAIL) VALUES
 (3,(SELECT MAX (ROW_ID)+1 FROM PB_DETAILS),'1 345 333 5680','1 533 290
 3556','1 678 980  2837','[email protected]');

INSERT INTO PB_DETAILS
  (ENTRY_ID,ROW_ID,HOME_NUMBER,WORK_NUMBER,CELL_NUMBER,EMAIL) VALUES
 (4,(SELECT MAX (ROW_ID)+1 FROM PB_DETAILS),'1 221 456 6453','1 567 389
 2356','1 908 354  2467','[email protected]');

也可以在文本編輯器中打開所提供的 loadTables.sql 腳本 並使用該腳本來將數據裝入表中。

至此為應用程序新創建的數據庫就准備好了。

創建 JDBC-DAO 版本的電話本應用程序

在本節中,將創建 JDBC-DAO 版本的電話本應用程序。

定義 DAO 接口

現在可以開始實施了,先為應用程序定義 DAO 接口。

清單 4 顯示了 DAO 接口,它只有一個方法,但已足夠。impl 類將在其中保存實際代碼。

清單 4. 應用 程序的 DAO 接口

/*
 * IPhonebookDAO.java 
 * This interface  lists the database operations that can be performed for the
 * phonebook  application. To make things simple, this one only has a method
 * to get  a list of all phonebook entries.
 */
public interface IPhonebookDAO {

  public List getPhonebookEntries() throws Exception;

}

DAO 接口的 JDBC 實現

IPhonebookDAO 只是一個接口;它不為應用程序提供任何 功能,但它確實 能夠使應用程序的設計更具可擴展性。在 清單 5 的 DAO 的實現中,使用了 Spring JDBC 核心庫中的 JdbcDaoSupport 類。

JdbcDaoSupport 是一個用於 JDBC 數據訪問對象的類。 它要求設定一個數據源,並為其子類提供一個 JdbcTemplate。

JdbcTemplate 是 JDBC 核心軟件 包中的中心類。此類通過處理創建及發布資源簡化了在應用程序中使用 JDBC 的過程。正如在 清單 5 中 可以看到的那樣,開發人員無需知道關於數據庫的任何信息也無需關心關閉打開的連接。JdbcTemplate 會負責處理這一切。它還捕捉 JDBC 異常並把這些異常轉換為在 Spring 中定義的更具信息性的一般的異 常層次結構。清單 5 顯示了 Java 代碼(它並不完整,因此請參考項目工作區的 src 目錄中的 Java 文 件)。

清單 5. PhonebookJdbcDao 使用 Spring JDBC 庫

/**
 * This  class represents the DAO implementation of your application.
 * It uses the  Spring JdbcDaoSupport class from the Spring JDBC module.
 */
public  class PhonebookJdbcDao extends JdbcDaoSupport implements 
 IPhonebookDAO{
  ...
  public java.util.List getPhonebookEntries() throws Exception {
     JdbcTemplate jt = getJdbcTemplate();
    return jt.query("SELECT * FROM  PB_ENTRY A, PB_DETAILS B WHERE 
 A.ENTRY_ID = B.ENTRY_ID AND A.ENTRY_ID  <> 0",
            new RowMapperResultReader(new  PhonebookRowMapper()));
  }
...

現在已經創建了接口,接下來需要定義 應用程序從中讀取數據的數據源。您將看到如何使用 Spring 連接到 Derby 數據庫。

在 Application Context 中定義 DataSource

使用在 Geronimo 中運行的 Spring 框架的 IoC 容器 來提供可以注入到 PhonebookJdbcDao 對象中的 DataSource。IoC 容器是提供依賴性注入的核心功能的 一種 Spring 容器。正如 清單 6 所示,定義一個新的 ApplicationContext.xml 文件,它與 servlet- context.xml 中定義的上下文不同。

必須定義一個指向已經創建的電話本數據庫的 Datasource Bean。該 Bean 還指示 Spring 容器使用 Derby 核心 API 的嵌入式驅動程序。之所以使用該嵌入式驅動 程序是因為 Derby 數據庫運行於 Spring 應用程序所在的相同的 JVM 中。

接下來的 Bean 定義 告訴 Spring 容器將這個新定義的 DataSource 注入到為應用程序定義的 JDBC DAO 對象。因此,現在應 用程序知道它只能使用 Derby JDBC 驅動程序連接到數據庫,而此數據庫是由運行在 Geronimo 應用服務 器中的 Derby 定義的。

最後,將為 DataProvider 對象定義另一個 Bean。在此,將告訴 Spring 容器在運行時把 DAO 對象注入到 PhonebookDataProvider 中。

正如 清單 6 所示,Spring 允許 實現所有這些組件之間的最大解耦 —— 而且會透明地進行。這讓測試變得簡單,也使動態更 改或添加其他組件變得更為輕松。假設需要使用不同的 DataSource,只需更改該 Bean 中的配置,該 Bean 就會使用新配置 —— 而根本不需要觸及任何代碼!

很棒,對不對?

清 單 6. 通過 Spring 容器為依賴性注入定義 DataSource 和其他 Bean

<?xml  version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD  BEAN//EN"
 "http://www.springframework.org/dtd/spring-beans.dtd">

<! --
- Application context definition for "phonebook" application.
-->
<beans>

  <!-- Bean Definitions required for data access  using Spring Jdbc DAO
 Support -->

  <bean  id="pbJdbcDataSource"
     class="org.springframework.jdbc.datasource.DriverManagerDataSource"
    destroy- method="close">
    <property
  name="driverClassName"><value>org.apache.derby.jdbc.EmbeddedDriv
er</value></property>
    <property
  name="url"><value>jdbc:derby:phonebook</value>
</property>
     <property
  name="username"><value>APP</value></property>
     <property
 name="password"><value></value></property>
   </bean>

  <bean id="phonebookDao"
  class="phonebook.dao.jdbc.PhonebookJdbcDao">
    <property  name="dataSource">
      <ref local="pbJdbcDataSource"/>
     </property>
  </bean>

  <bean  id="phonebookJdbcDataProvider"
 class="phonebook.dao.PhonebookDataProvider">
    <property name="pbDao">
      <ref  local="phonebookDao"/>
    </property>
  </bean>
</beans>

讓 web.xml 識別 Application Context

由於應用程序正在應 用服務器內運行,因此需要對其 web.xml 文件做一些更改,以便讓它能夠識別 ApplicationContext.xml 配置文件。

可以使用 Spring 的 ContextLoaderListener 來完成此項任務,並按照 Geronimo 應 用服務器的規則在 web.xml 中添加新的偵聽程序。清單 7 展示了定義此偵聽程序的 XML。

清單 7. 向 web.xml 中添加新的偵聽程序

<listener>
 <listener- class>org.springframework.web.context.ContextLoaderListe
ner</listener- class>
  </listener>
  <context-param>
    <param- name>contextConfigLocation</param-name>

 <param-value>/WEB- INF/ApplicationContext.xml
</param-value>
  </context- param>

在 JDBC/DAO 的頂部定義 PhonebookDataProvider 服務

PhonebookDataProvider 類匯集了上述所有內容(參見 清單 8)。它還是由客戶機(JSP 頁面) 實例化以從數據庫中讀取數據的類。如果查看最後一個定義的 bean phonebookJdbcDataProvider,則 Spring 容器將注入 PhonebookDao 對象以實現 IoC。現在回頭再看 圖 3 應當更有意義。

清單 8. 客戶機 (JSP) 的電話本 DataProvider 服務

/*
 *  PhonebookDataProvider.java 
 * This class works as a Service Provider class  for your application
 */

public class PhonebookDataProvider {

  private IPhonebookDAO pbDao;

  /** Creates a new instance of  PhonebookDataProvider */
  public PhonebookDataProvider() {
  }

  public void setPbDao(IPhonebookDAO pbDao) {
    this.pbDao = pbDao;
  }

  public List getPhonebookEntries() throws Exception{
     return pbDao.getPhonebookEntries();
  }
}

將主頁從 home.jsp 更改為 home-dao.jsp

現在來了解一下 JSP 如何利用 PhonebookDataProvider 服務。正如在 清單 9 所 見,客戶機只需要三行代碼就可以獲取電話本條目的列表。所有 JDBC 細節都已被處理;也無需顯式的錯 誤處理代碼。它就是一段干淨的代碼,而且具有高可配置性並易於測試!

JSP 中的代碼將獲取電 話本條目的列表並迭代這些條目來填充視圖。清單 9 提供了一個代碼小片段來展示如何將這段代碼添加 到 JSP 中。

清單 9. home-dao.jsp 將透明地使用 JDBC DAO 動態裝入來自數據庫的數據

...
<%
try{
  WebApplicationContext ctx =
  WebApplicationContextUtils.getWebApplicationContext(application);
   PhonebookDataProvider pb = (PhonebookDataProvider)
 ctx.getBean ("phonebookJdbcDataProvider");
  List pbDetails = pb.getPhonebookEntries();
%>

<html>
...
  <!-- Dynamically populate all the  fields read from the database -->
      <%
      for (int  i=0; pbDetails!=null && i<pbDetails.size();i++){
         PhonebookEntry pbEntry =
 (PhonebookEntry)pbDetails.get(i);
      % >

      <TR>
        <TD  align=center><input type=checkbox name=cb_1
 alt="Select to Delete"  align="middle"></TD>
        <TD align=center><% =pbEntry.getFName()+"
 "+pbEntry.getLName()%></TD>
         <TD
 align=center><%=pbEntry.getHomeNumber()%></TD>
         <TD
 align=center><%=pbEntry.getWorkNumber()%></TD>
         <TD
 align=center><%=pbEntry.getCellNumber()%></TD>
        <TD
 align=center><%=pbEntry.getEmail()%></TD>
      </TR>

</html>
...

略微更改 PhonebookController servlet

最後一項任務是使 PhonebookController servlet 識別這個新的 JSP 頁面。清單 10 中的代碼無需過多解釋。控制器將基於 URI 返回相應的視圖。

清單 10. PhonebookController 被更改為處理其他請求

/*
 * This class is a simple  Spring Controller. This controller is registered
 * as a Bean in the  Phonebook ApplicationContext.
 */
public class PhonebookController implements  Controller{

  /**
   * You use this Phonebook as central  Controller to handle all input 
   * requests coming to your application.  In this case, it returns a View
   * of page requested depending on  the URI.
   */
  public ModelAndView handleRequest(HttpServletRequest  request,
 HttpServletResponse response)
  throws ServletException,  IOException {
    // request for the JDBC DAO implementation page
     if (request.getRequestURI().indexOf("home-dao")!=-1) {
      return new  ModelAndView("/WEB-INF/jsp/home-dao.jsp");
    }
    // request for the  ORM DAO implementation page
    else if (request.getRequestURI().indexOf ("home-orm")!=-1) {
      return new ModelAndView("/WEB-INF/jsp/home- orm.jsp");
    }
    // Default page with static values 
     return new ModelAndView("/WEB-INF/jsp/home.jsp");

  }

}

構建、部署並運行

本教程附帶的源代碼的壓縮文件含有所需的所有類、配置文件 和 Ant 構建文件。第二個壓縮文件是含有全部所需內容的可配置的 .war 文件。可以使用任何一種方法 來獲取 phonebook.war 文件。

另外還需要確保 readme.txt 文件中提及的所有 JAR 都位於 <WORKSPACE>/phonebook/lib 目錄中。建議仔細閱讀該文件中的指導信息,並確保將所有必需的文 件復制到 <WORKSPACE>/phonebook/lib 中。

注:可以參考本系列教程的 第 2 部分 中的 構建說明。

使用 Geronimo 中的 Deploy 新工具部署 phonebook.war。如果運行正常,將在 Geronimo 控制台上看到一條消息,說明 Phonebook application deployed successfully。

現在 只需將浏覽器指向新頁面:http://localhost:8080/phonebook/home-dao.jsp。

如果運行正常, 主頁應當會如 圖 6 所示。

圖 6. 在應用服務器中運行的 home-dao.jsp

接下 來您將看到 iBATIS ORM 是怎樣適應應用程序的。

創建 iBATIS ORM 版本的電話本應用程序

在本節中,將使用 iBATIS 框架的 SQL 映射和 Spring ORM 模塊的 ORM 支持類讓應用程序讀取 數據。Spring 在資源管理、DAO 實現支持和事務策略方面提供了與 iBATIS 的簡單集成。

用來通 過 SQL 映射從 iBATIS 中讀取數據的配置

利用 Spring ORM API 提供的 DAO 支持來抽象實現中 的數據源細節,並且需要做出以下配置以使其成為可能:

SQL 映射的 Application Context —— 必須定義 Data Mapper 庫所連接的數據源。定義四個 Bean:一個用於數據源;另外三 個用於使應用程序能與 SQL 映射結合使用。

SQL 映射配置文件 —— 此文件將定義可 能需要的所有特定於 iBATIS 的配置設置,並且它還聲明了應當通過此配置文件訪問的 SQL 映射文件的 位置。

SQL 映射 —— 一個映射到惟一的業務實體(電話本)的 SQL 映射文件。SQL 語句也是在此文件中創建的。

此處首先要執行的是將 iBATIS .jar 文件復制到項目目錄中。把 ibatis-sqlmap.jar、ibatis-dao.jar 和 ibatis-common.jar 放入 <WORKSPACE>/phonebook/lib 目錄中。

創建 SQL 映射

iBatis 有一個定義好的模式用於將 Java 對象映射到 XML 中。 在 清單 11 中,定義一個 SQL SELECT 語句並讓 iBATIS 框架知道 resultClass 是數據模型 PhonebookEntry 對象。查看 清單 11 以更好地了解這一切是如何實現的。

清單 11. phonebook.xml 把業務模型映射到 SQL 語句

<sqlMap namespace="Phonebook">
  <select id="getPhonebookEntries"
  resultClass="phonebook.dao.PhonebookEntry">
    SELECT
    A.ENTRY_ID  as entryID,
    A.ENTRY_FNAME as fName,
    A.ENTRY_LNAME as  lName,
    B.ROW_ID as rowID,
    B.HOME_NUMBER as homeNumber,
     B.WORK_NUMBER as workNumber,
    B.CELL_NUMBER as cellNumber,
     B.EMAIL as email
    FROM PB_ENTRY A, PB_DETAILS B
    WHERE  A.ENTRY_ID = B.ENTRY_ID
    AND A.ENTRY_ID != 0
  </select>
</sqlMap>

創建 SQL 映射配置文件

這是 iBATIS API 所需的另一個文件 。它將告訴運行 iBATIS 框架的容器在何處可以找到為應用程序定義的 SQL 映射。清單 12 中定義的設 置是可選的;如果刪除這些設置,應用程序應當仍能正常運行。

創建 sqlmap-config.xml 文件並 將其放在 <WORKSPACE>/phonebook/web/META-INF 文件夾中(參見 清單 12)。

清單 12. sqlmap-config.xml 文件告知 iBatis 容器應用程序的 SQL 映射

<?xml  version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig PUBLIC  "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
 "http://www.ibatis.com/dtd/sql-map -config-2.dtd" >

<!-- Always be sure to use the correct XML  header as above -->

<sqlMapConfig>

  <settings  cacheModelsEnabled="true" enhancementEnabled="true"
        lazyLoadingEnabled="true" maxRequests="32"
       maxSessions="10"  maxTransactions="5"
       useStatementNamespaces="false" />

   <sqlMap resource="/phonebook/dao/orm/Phonebook.xml" />

</sqlMapConfig>

DAO 接口的 ORM 實現

使用 Spring 框架提供的 SqlMapClientDaoSupport 類來支持 iBATIS。這是一個實用工具類,它負責處理創建、管理和關閉連接這 些復雜的工作。

同 JdbcTemplate 類一樣,SqlMapClientTemplate 類通過 iBATIS SQL 映射的 SqlMapClient API 簡化了數據訪問。它還處理所有 SQL 異常並將這些異常轉換為一般的 Spring DAO 異 常。它與 JdbcTemplate 一樣使用 SQLExceptionTranslator 機制。

清單 13 顯示了 SqlMapPhonebookDao 類。

清單 13. DAO 接口的 ORM 實現

/**
 * This  is the ORM SQL Maps implementation for your application.
 * The  PhonebookEntry object is mapped directly to the database using the
 * iBATIS  framework. You can use any kind of database for RDBMS.
 */
public  class SqlMapPhonebookDao extends SqlMapClientDaoSupport 
 implements  IPhonebookDAO {

  public List getPhonebookEntries() throws Exception {
    return getSqlMapClientTemplate()
 queryForList ("getPhonebookEntries",null);
  }

}

注:如果在 Geronimo 中重 新部署應用程序,可能需要重新啟動服務器。由於某種原因,當重新部署應用程序時,Derby 數據庫不會 重新初始化電話本數據庫。

向 Spring Application Context 中添加 ORM

需要讓 Application Context 知道 ORM 實現,並且需要注入 SqlMapClientTemplate 類所需的 SqlMapClient 對象。還要定義用於 ORM 示例的不同數據源。之所以這麼做是想說明更改數據源以供測試或者更改應用 程序是多麼容易。

之前我曾經要求您安裝 Apache Commons dbcp 和 pools 軟件包,那是因為本 文要定義的數據源將使用 Apache Commons API。可以為此數據源使用同一個 Derby 嵌入式驅動程序。

清單 14 中顯示的第二個定義使用 SQLMapClientFactoryBean 來創建 SqlMapClient 對象的實例 並告訴它使用在前面的 JDBC-DAO 部分中定義的數據源。

第三個 Bean 將指示 Spring 框架在運 行時把 SqlMapClient 對象注入到 ORM DAO 實現對象中。

最後一個 Bean 是為 JDBC DAO 訪問定 義的。它告訴 Spring 框架在運行時把第三個 Bean 創建的 DAO 對象注入到 PhonebookDataProvider 類 中(參見 清單 14)。

清單 14. 向 Application Context 中添加與 ORM 相關的 Bean

<!-- Bean Definitions for ORM beans, required for dataAccess  via ORM SQL
 maps -->

  <bean id="pbOrmDataSource"
  class="org.apache.commons.dbcp.BasicDataSource"
 destroy-method="close">
     <property name="driverClassName"
  value="org.apache.derby.jdbc.EmbeddedDriver"/>
    <property name="url"  value="jdbc:derby:phonebook"/>
    <property name="username"  value="APP"/>
    <property name="password" value=""/>
   </bean>

  <bean id="sqlMapClient"
  class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
    <property  name="configLocation"
 value="/META-INF/sqlmap-config.xml"/>
     <property name="dataSource" ref="
 pbOrmDataSource"/>
   </bean>

  <bean id="phonebookOrmDao"
  class="phonebook.dao.orm.SqlMapPhonebookDao">
    <property  name="sqlMapClient" ref="sqlMapClient"/>
  </bean>

  <bean  id="phonebookOrmDataProvider"
 class="phonebook.dao.PhonebookDataProvider">
    <property name="pbDao">
      <ref  local="phonebookOrmDao"/>
    </property>
   </bean>

把主頁從 home.jsp 更改為 home-orm.jsp

最後一步是更改 home.jsp 來使用新定義的 ORM Data Provider。這與在本教程的 DAO 應用程序中創建的 home-dao.jsp 類似。惟一的區別在於需要訪問不同的 Bean 來訪問 ORM 對象。創建一個單獨的 .jsp 文件還可以並排 比較這兩個實現。

在 清單 15 中,可以看到 JSP 如何用 Bean 的名稱獲取 Bean,並使用它填充 視圖。可以查看 home-orm.jsp 以獲得完整代碼(請參閱 下載 部分以獲取有關鏈接)。

清單 15. home-orm.jsp 中的代碼片段顯示了如何獲取 PhonebookOrmDataProvider bean

WebApplicationContext ctx =  WebApplicationContextUtils.getWebApplicationContext(application);
|-------10--------20 --------30--------40--------50--------60--------70--------80--------9|
|-------- XML  error: The previous line is longer than the max of 90 characters ---------|
  PhonebookDataProvider pb = (PhonebookDataProvider) ctx.getBean ("phonebookOrmDataProvider");
|-------10--------20--------30--------40--------50------ --60--------70--------80--------9|
|-------- XML error: The previous line is  longer than the max of 90 characters ---------|
  List pbDetails =  pb.getPhonebookEntries();

構建並運行

好的!全部完成了。運行 Ant 來構建並 創建 .war 文件。使用 Geronimo 中的 Deploy New 工具進行部署。

讓浏覽器指向新頁面: http://localhost:8080/phonebook/home-orm.jsp。

主頁看上去應該沒什麼變化,但是這一次所 有數據都是使用 iBATIS Data Mapper API 從 Derby 數據庫中讀取的。

整合 JDBC-DAO 和 iBATIS ORM 版本的電話本應用程序

至此,您已經在本教程中了解了幾種技術:Spring JDBC、 Spring DAO、Spring ORM 和 iBATIS API,現在是時候把它們整合在一起了。

整合

首先需 要做的是更改 ORM SqlMapClient 所連接的數據源,以使該數據源可以用作為 JDBC 示例而定義的 JDBC 數據源。

要讓 iBATIS 使用 JDBC 數據源,僅需更改 ApplicationContext.xml 中的一行(參見 清單 16)。

清單 16. 讓 iBATIS 使用 JDBC 數據源

<bean  id="sqlMapClient"
  class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
    <property  name="configLocation"
 value="/META-INF/sqlmap-config.xml"/>
     <property name="dataSource" ref="pbOrmcDataSource"/>
   </bean>

更改此 Bean 引用的數據源 pbJdbcDataSource,如 清單 17 所示。

清單 17. 更改數據源

<bean id="sqlMapClient"
  class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
    <property  name="configLocation"
 value="/META-INF/sqlmap-config.xml"/>
     <property name="dataSource" ref=" pbJdbcDataSource "/>
  </bean>

注:在源代碼提供的 Application Context 中,只能手動做出此更改才能 看到它運行。以上提及的其他兩個示例都預打包在可部署的 .war 文件中,該文件和源代碼一樣由本教程 隨附。

這就是在此處需要做的全部工作。

構建並部署 .war 文件

構建、部署並運 行.war 文件,然後指向同一個 home-orm.jsp 頁面。您將看到同一個主頁,但現在數據是使用 Spring JDBC 驅動程序管理器從 Derby 數據庫中讀取的。

讓浏覽器指向 JSP 頁面: http://localhost:8080/phonebook/home-orm.jsp。

至此就完成了對本教程中涵蓋的所有技術的 整合!

Spring 的優點

您已經看到了進行諸如更改應用程序中的數據庫之類的重大更改而 無需觸及任何代碼是多麼輕松。可以利用 XML 配置進行組件解耦是使用 Spring 框架最大、最重要的優 點。除此之外,下面還有一些在應用程序中使用 Spring JDBC、DAO 和 ORM 框架的其他優點:

Spring 提供了數據庫連接和管理問題的完整抽象。您無需擔心忘記關閉連接。在本文中介紹的 Spring 容器類將為您處理所有那些問題。

Spring 管理應用程序所需的所有異常處理。還將檢查 出的異常(如 java.sql.SQLException)轉換為更具一般性的未檢查出的 Spring DAO 異常。

Spring 框架提供了一個簡單的數據源實現,它可在容器外部使用並且僅通過一個配置文件就可以 管理。它還提供了一個可以根據需要覆蓋的抽象數據源類。

Spring 提供了各種易於使用的 DAO 支持類,例如在應用程序中使用的兩個類(JdbcDaoSupport 和 SqlMapClientDaoSupport)。

Spring 框架支持多項 ORM 技術,例如 Hibernate、JDO 和本文中介紹的 iBATIS。而且,您可以 十分輕松地從一種實現切換到另一種實現。

Spring 易於測試 —— 通過不同的數據庫 和不同的實現。

結束語

在本系列教程的第 3 部分中,您了解了幾種易於使用的重要技術 :Spring JDBC、Spring DAO、Spring ORM 和 iBATIS。在下一部分中,您將開始學習更有趣的 Spring 模塊:Spring AOP 和 Spring Web Flow。使用 Spring 面向方面編程的 API,任何對象經過管理都可以 變為面向方面的。您將使用由 Spring AOP 提供的聲明性事務管理服務。

Spring Web Flow 是一 種聲明式定義 Web 流程的方法。在本系列教程後續部分中,您將擴展電話本應用程序使其擁有更多功能 (比如添加新條目、修改條目或刪除條目的功能)和更多頁面。敬請關注!

下載

描述 名字 大小 下載方法 第 3 部分 的源代碼 geronimo.spring3.source.zip 100KB HTTP 第 3 部分的 WAR 文件 geronimo.spring3.war.zip 4159KB HTTP
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved