開始之前
近十年來,構(gòu)建健壯、可維護(hù)的服務(wù)器端 Java 應(yīng)用程序的 “正確” 方式一直是 Java 2 企業(yè)版(J2EE)平臺(tái)的專屬領(lǐng)域。J2EE 應(yīng)用程序用企業(yè) JavaBean(EJB)技術(shù)構(gòu)建,運(yùn)行在方便部署并提供豐富的容器服務(wù)(例如數(shù)據(jù)庫連接和池的管理)的服務(wù)器之上。這些服務(wù)器還通過對(duì)重要特性(例如安全性和事務(wù))提供部署時(shí)聲明式控制帶來了附加價(jià)值。雖然功能豐富,J2EE 開發(fā)過程包含了許多煩瑣和重復(fù)的任務(wù),還要?jiǎng)?chuàng)建和維護(hù)大量的源代碼文件。
許多輕量級(jí) Java 框架聲稱簡(jiǎn)化了服務(wù)器應(yīng)用程序開發(fā),但論及成熟和流行的程度,Spring 框架無可匹敵(請(qǐng)參閱 參考資料)。Spring 目前的版本是 Spring 2,從第 1 天起,其設(shè)計(jì)目的就在于簡(jiǎn)化服務(wù)器應(yīng)用程序的構(gòu)建過程。Spring 的開發(fā)沒有采用一體化的容器角度,而是為應(yīng)用程序的需求提供恰到好處的支持,因此不會(huì)因包羅萬象的容器環(huán)境造成負(fù)擔(dān)。Spring 消除了代碼膨脹:完全可以在容器之外編寫和測(cè)試業(yè)務(wù)對(duì)象,從而讓業(yè)務(wù)對(duì)象代碼保持簡(jiǎn)單、可測(cè)試、可維護(hù)和可重用。
隨著 Java EE 5 和 EJB 3.0 的出現(xiàn),J2EE 社區(qū)準(zhǔn)備好了迎接 Spring 開發(fā)人員社區(qū)。EJB 3.0 支持輕量級(jí) POJO(Plain Old Java Objects,老式普通 Java 對(duì)象)作為 EJB 組件的概念,并引入了 Java 持久性 API (JPA),JPA 是可以在容器外部運(yùn)行的持久性機(jī)制。這種持久性機(jī)制自動(dòng)實(shí)現(xiàn)業(yè)務(wù)對(duì)象和外部關(guān)系數(shù)據(jù)庫之間的信息移動(dòng)。Spring 框架的版本 2 繼續(xù)了自己的發(fā)展,也利用 JPA 作為持久性機(jī)制。
在這份教程中,您將使用 Spring 2 和 JPA 持久性。將用 Spring 2 框架創(chuàng)建一個(gè)服務(wù)器應(yīng)用程序,完成時(shí)可以訪問 DB2 Express-C 數(shù)據(jù)庫。Eclipse IDE 可以方便 Java 應(yīng)用程序的開發(fā)并促進(jìn)對(duì) Spring 2 框架的研究。
本教程采用按編碼學(xué)習(xí)這種純粹、簡(jiǎn)單的方式,目的是在盡可能短的時(shí)間內(nèi),指導(dǎo)您了解 Spring 2 框架的使用和應(yīng)用程序。您將在 Spring 2 框架的協(xié)助下,從頭開始逐步構(gòu)建一個(gè) Web 應(yīng)用程序。
這份教程并不想試圖覆蓋 Spring 2 的全部特性和選項(xiàng)。而是重點(diǎn)關(guān)注了使用 Spring 開發(fā)服務(wù)器應(yīng)用程序的一種切實(shí)有效的方式。鼓勵(lì)您參考其他 Spring 2 資源獲得與這個(gè)框架有關(guān)的更高級(jí)的應(yīng)用程序和技術(shù)(請(qǐng)參閱 參考資料)。
您將經(jīng)歷一個(gè)完整的 “從概念到應(yīng)用程序” 的周期,包括:
學(xué)完本教程后,您應(yīng)能夠理解 Spring 2 框架的工作原理以及它能為創(chuàng)建高度組件化、可維護(hù)的 Web 應(yīng)用程序帶來怎樣的幫助。在構(gòu)建這樣一個(gè)應(yīng)用程序的過程中,您將獲得實(shí)踐經(jīng)驗(yàn),還能把這里學(xué)到許多技術(shù)應(yīng)用到日常的開發(fā)任務(wù)中。
![]() ![]() |
![]()
|
您應(yīng)當(dāng)熟悉基本的面向?qū)ο笤O(shè)計(jì)概念和使用 Java SE 5 的 Java 開發(fā),包括泛型。您還應(yīng)當(dāng)熟悉關(guān)系數(shù)據(jù)庫概念,對(duì)于如何在 DB2 Express-C 中設(shè)置新數(shù)據(jù)庫也應(yīng)當(dāng)有基本的了解。
您還應(yīng)熟悉測(cè)試術(shù)語,包括單元測(cè)試和集成測(cè)試。最好有測(cè)試框架(例如 JUnit)方面的實(shí)際經(jīng)驗(yàn),但并非必需。
您應(yīng)當(dāng)有 Eclipse 方面的實(shí)際經(jīng)驗(yàn),能夠在 Eclipse 內(nèi)創(chuàng)建新 Java 項(xiàng)目、編譯 Java 代碼和調(diào)試項(xiàng)目。
![]() ![]() |
![]()
|
要試驗(yàn)這份教程中的工具和示例,硬件配置需求為:至少帶有 512MB 內(nèi)存(推薦 1GB)的系統(tǒng)。
需要安裝以下軟件:
Spring 2 框架操作概述
這一節(jié)介紹 Spring 2 框架,簡(jiǎn)要說明了它在常規(guī)服務(wù)器應(yīng)用程序開發(fā)中的優(yōu)勢(shì)。
從傳統(tǒng) API 的意義上來說,Spring 并不是編程框架。多數(shù)情況下,框架由 API 和可以在應(yīng)用程序中使用的代碼主干的集合組成。
Spring 2 被設(shè)計(jì)成非侵入的。實(shí)際上,它允許您編寫對(duì)象和業(yè)務(wù)邏輯,就像 Spring 不存在一樣。在編寫和測(cè)試這些對(duì)象之后,可以添加 Spring 2 支持特性。在某些情況下,添加這些特性時(shí)甚至不需要重新編譯源代碼。
例如,可以先創(chuàng)建和測(cè)試一個(gè) Java employee 對(duì)象,然后添加 Spring 2 支持,把對(duì)象的實(shí)例保存到關(guān)系數(shù)據(jù)庫中。也可以先編寫更新銀行賬戶的代碼,然后應(yīng)用 Spring 2 的事務(wù)功能來確保數(shù)據(jù)的完整性。
圖 1 顯示了典型的基于 Web 的服務(wù)器應(yīng)用程序。用戶通過用戶界面與應(yīng)用程序交互。應(yīng)用程序邏輯作為一組業(yè)務(wù)對(duì)象上的操作來執(zhí)行,構(gòu)成應(yīng)用程序的域模型。業(yè)務(wù)對(duì)象通常由保存在關(guān)系數(shù)據(jù)庫中的數(shù)據(jù)支持。
圖 1. 典型的基于 Web 的應(yīng)用程序的架構(gòu)
如果要從頭開發(fā)這個(gè)應(yīng)用程序,需要構(gòu)建圖 1 中的每個(gè)組件,并編寫定制代碼來實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的訪問。
在使用 Spring 構(gòu)建應(yīng)用程序時(shí),可首先關(guān)注完善域模型??梢杂煤?jiǎn)單的 Java POJO 為系統(tǒng)中的對(duì)象建模,并把系統(tǒng)中的服務(wù)定義成標(biāo)準(zhǔn)的 Java 接口。這樣的設(shè)計(jì)使您可以獨(dú)立于 Spring 或者其他框架/庫來創(chuàng)建和測(cè)試域模型。
然后即可把額外的 Spring 特性應(yīng)用到應(yīng)用程序,以測(cè)試過的域模型為基礎(chǔ)。例如,可以用 Spring 的 JPA 支持添加對(duì)象持久性 —— 把數(shù)據(jù)保存到關(guān)系數(shù)據(jù)庫和從關(guān)系數(shù)據(jù)庫檢索數(shù)據(jù)的能力。
![]() ![]() |
![]()
|
Spring 框架通過在類裝入的時(shí)候提供附加價(jià)值來保證非侵入性。在正常運(yùn)行 Java 應(yīng)用程序時(shí),JVM 只在需要類時(shí)才通過一組類裝入器裝入類。這是非常透明的,通常在不知覺的情況下發(fā)生。對(duì)于一定復(fù)雜的軟件,如 Eclipse IDE,可以這樣裝入數(shù)千個(gè)類。使用 Spring 框架,通過告訴 Spring 引擎(也叫作 Spring 容器)您的類如何搭配在一起、要給這些類添加什么特性,對(duì)框架進(jìn)行編程。Spring 引擎根據(jù)您的調(diào)配來構(gòu)建類。圖 2 以圖表的方式顯示了這個(gè)過程:
圖 2 顯示出:因?yàn)?Spring 引擎擁有對(duì)類的低級(jí)訪問,所以可以根據(jù)特定的配置,用額外特性增強(qiáng)它們。圖 2 還顯示了一種可以向 Spring 引擎提供指令的機(jī)制。典型情況下,這些指令以 XML bean 描述符文件的形式存在,在某些情況下,也可能以 Java 5 注釋的形式存在于源代碼內(nèi)。
例如,可以創(chuàng)建代表員工的簡(jiǎn)單 Java 對(duì)象,然后讓 Spring 引擎增強(qiáng)這些類,使得這些對(duì)象可以動(dòng)態(tài)地保存到關(guān)系數(shù)據(jù)庫表中,或從關(guān)系數(shù)據(jù)庫表檢索這些對(duì)象。本教程將介紹如何實(shí)現(xiàn)這些目的。
![]() ![]() |
![]()
|
除了類裝入時(shí)增強(qiáng),Spring 2 還提供了經(jīng)典 API 支持,以封裝復(fù)雜和煩瑣的操作。圖 3 顯示了這份教程的示例應(yīng)用程序使用的 Spring 支持庫。請(qǐng)對(duì)比圖 3 和 圖 1:
準(zhǔn)備 Spring
在這一節(jié),要開始構(gòu)建一個(gè)利用 Spring 2 框架的示例員工信息應(yīng)用程序。要確定和編碼應(yīng)用程序的業(yè)務(wù)對(duì)象、生成 setter 和 getter、編寫服務(wù)接口、并對(duì)類進(jìn)行單元測(cè)試。應(yīng)用程序開發(fā)周期的這個(gè)階段的執(zhí)行獨(dú)立于 Spring 框架。
這份教程的應(yīng)用程序設(shè)計(jì)方式的第一步就是 域模型分析 —— 通常叫作 “確定 POJO”。在這種情況下,POJO 代表 Plain Old Java Object。它們也代表應(yīng)用程序中的 業(yè)務(wù)對(duì)象,這份教程交替使用這兩個(gè)術(shù)語。
確定應(yīng)用程序域的業(yè)務(wù)對(duì)象,與初學(xué)面向?qū)ο笤O(shè)計(jì)時(shí)所做的練習(xí)相同。目的是確定要?jiǎng)?chuàng)建的系統(tǒng)中的對(duì)象和它們之間的交互關(guān)系。更具體地講,就要是發(fā)現(xiàn):
以在這份教程中要?jiǎng)?chuàng)建的簡(jiǎn)單服務(wù)器應(yīng)用程序?yàn)槔?。這是個(gè)顯示員工信息的系統(tǒng)。顧名思義,這個(gè)系統(tǒng)中有一個(gè)對(duì)象是公司的員工。員工信息可以很豐富,但在這個(gè)簡(jiǎn)單的應(yīng)用程序中,只需要知道以下信息:
在這個(gè)域中能夠確定的另一個(gè)業(yè)務(wù)對(duì)象就是代表地址的對(duì)象。地址之所以從員工信息中分離出來,是因?yàn)橄到y(tǒng)中的其他實(shí)體(如果想擴(kuò)展系統(tǒng)的話)可能也有地址,這樣就可以把所有地址放在一起了。為簡(jiǎn)單起見,假設(shè)地址對(duì)象只包含街道號(hào)碼和街道名稱。
在這個(gè)簡(jiǎn)單的系統(tǒng)中,每個(gè)員工都有一個(gè)地址,每個(gè)地址也只屬于一個(gè)員工。這叫作 1 對(duì) 1 關(guān)系。其他可能的關(guān)系包括 1 對(duì)多(例如,項(xiàng)目與員工的關(guān)系)、多對(duì) 1 (例如員工與部門的關(guān)系)和 多對(duì)多(例如員工與 HR 福利的關(guān)系)。其他這些關(guān)系模型在 Spring 2 應(yīng)用程序中都可以建立,但是超出了這份初級(jí)教程的范圍。
在這個(gè)系統(tǒng)中,在員工和地址對(duì)象上要有以下操作:
典型情況下,可以通過考慮系統(tǒng)需要的用戶界面和系統(tǒng)中需要實(shí)現(xiàn)的業(yè)務(wù)邏輯來確定這些操作。
這個(gè)示例中的操作特意被保持簡(jiǎn)單,以便使教程重點(diǎn)突出。在典型的生產(chǎn)場(chǎng)景中,很可能會(huì)有跨多個(gè)確定的對(duì)象集執(zhí)行的更復(fù)雜的操作,還可能會(huì)存在對(duì)象之間的直接交互。
確定了業(yè)務(wù)對(duì)象、交互和操作之后,就可以編碼和測(cè)試了。
![]() ![]() |
編寫的第一組對(duì)象是構(gòu)成系統(tǒng)的 POJO。可以用自己喜歡的開發(fā)編輯器或 IDE 創(chuàng)建這些對(duì)象。本教程假設(shè)您采用的是 Eclipse。
請(qǐng)?jiān)?Eclipse 中創(chuàng)建名為 Spring2Tutorial 的新 Java 項(xiàng)目,然后創(chuàng)建名為 com.ibm.dw.spring2.Employee
的類,如清單 1 所示:
|
清單 1 定義了在 Employee
對(duì)象實(shí)例中保存的全部信息。這些字段全被定義成 private,所以需要?jiǎng)?chuàng)建一些 getter 和 setter 方法,以便支持對(duì)這些信息的外部訪問。
![]() ![]() |
用 Eclipse 生成 getter、setter 和構(gòu)造函數(shù)
在 Eclipse 中生成 getter 和 setter 方法。請(qǐng)?jiān)诰庉嬈髦杏覔簦⑦x擇 Source->Generate Getter and Setters...。在圖 4 所示的彈出對(duì)話框中,單擊 Select All 然后再單擊 OK:
最后,必須為對(duì)象創(chuàng)建一些構(gòu)造函數(shù)。在 Eclipse 中,只需右擊并選擇 Source-> Generate Constructor。然后需要編輯生成的構(gòu)造函數(shù)。請(qǐng)看清單 2:
|
需要添加清單 2 所示的這個(gè)小小的構(gòu)造函數(shù),因?yàn)槿蘸笤趩卧獪y(cè)試階段要使用它。這就完成了 Employee
業(yè)務(wù)對(duì)象的編碼工作。
您可能注意到清單 2 的構(gòu)造函數(shù)中遺漏了 empid
。后面要用這個(gè)字段來包含 JPA 生成的與 Employee
實(shí)例關(guān)聯(lián)的主鍵。這個(gè)鍵由 JPA 管理,不應(yīng)當(dāng)被應(yīng)用程序修改。現(xiàn)在應(yīng)當(dāng)從源代碼刪除 setter 方法 setEmpid()
。
![]() ![]() |
每個(gè) Employee
對(duì)象都引用一個(gè) Address
對(duì)象。只要 Employee
對(duì)象 保存到數(shù)據(jù)庫,就需要保存 Address
對(duì)象。
Address
對(duì)象的代碼如清單 3 所示,其中也包含 getter、setter 和構(gòu)造函數(shù):
|
現(xiàn)在就完成了域模型中 POJO 的編碼,接下來需要從服務(wù)接口的角度實(shí)現(xiàn)程序程序的 POJO 上的操作。
![]() ![]() |
根據(jù)前面執(zhí)行的域分析,應(yīng)用程序要求一組在業(yè)務(wù)對(duì)象上的操作。
把需求集合轉(zhuǎn)換成代碼 —— 更準(zhǔn)確地說,是轉(zhuǎn)換成 Java 接口,結(jié)果將類似于清單 4。Employee
對(duì)象上的每個(gè)操作都成為 EmployeeService
接口中的一個(gè)方法。
|
請(qǐng)注意在清單 4 中,代碼是對(duì)域分析得到操作列表的直接轉(zhuǎn)化。這個(gè)接口不包含任何特定于 Spring 的編碼。在這個(gè)接口中,目前只知道需要在對(duì)象上進(jìn)行 的操作,對(duì)于如何做還一無所知。很快您就會(huì)看到如何實(shí)現(xiàn)這個(gè)接口中的方法,但是首先要進(jìn)行 POJO 單元測(cè)試。
![]() ![]() |
在編碼完域模型中的獨(dú)立 POJO 和服務(wù)之后,即可為 POJO 編寫單元測(cè)試。至此為止,仍然不需要執(zhí)行任何特定于 Spring 的步驟。實(shí)際上,您可能會(huì)不加修改地使用您在其他應(yīng)用程序(或同一應(yīng)用程序的其他部分)中得到、經(jīng)過測(cè)試的 POJO。
清單 5 顯示了 POJOUnitTest.java。這個(gè) JUnit 測(cè)試用例測(cè)試 Employee
和 Address
POJO。
|
![]() |
|
清單 5 中的測(cè)試代碼非常易于理解。這個(gè)測(cè)試根據(jù)先構(gòu)造函數(shù)、后逐個(gè)字段的方式設(shè)置 Employee
和 Address
的字段,最后對(duì)值進(jìn)行檢驗(yàn)。這些測(cè)試并未面面俱到;沒有測(cè)試每個(gè)字段,但是清單 5 確實(shí)表現(xiàn)了如何在麻煩的容器之外對(duì)模型進(jìn)行單元測(cè)試。
要在 Eclipse 內(nèi)運(yùn)行單元測(cè)試,請(qǐng)?jiān)趯?dǎo)航器視圖中右擊 POJOUnitTest.java 文件,然后選擇 Run as... -> JUnit Test。
Spring 中以 POJO 為中心的開發(fā)允許在容器之外獨(dú)立測(cè)試業(yè)務(wù)對(duì)象。與其他方式不同,采用以 POJO 為中心的設(shè)計(jì),可以非常迅速地執(zhí)行單元測(cè)試,所以,可以更頻繁地執(zhí)行此類測(cè)試(例如,作為構(gòu)建過程的一部分)。
![]() ![]() |
通過 Spring 2 的 JPA 支持獲得數(shù)據(jù)訪問
在這一節(jié),我們將利用 Spring 2 對(duì) Java 持久性 API(JPA)的支持把數(shù)據(jù)庫訪問添加到 Employee 和 Address。
為提供前面定義的服務(wù)接口的實(shí)現(xiàn),需要利用 Spring 2 的部分特性和支持庫。當(dāng)然,存在替代方法。可以用標(biāo)準(zhǔn)的 Java 數(shù)據(jù)庫訪問技術(shù),例如 JDBC,一個(gè)方法一個(gè)方法地開始實(shí)現(xiàn)接口。但是,看到 Spring 2 如何用 JPA 實(shí)現(xiàn)這一任務(wù)之后,您就會(huì)認(rèn)識(shí)到,把這項(xiàng)工作委托給 Spring 實(shí)際上要更容易。
在 Spring 2 中,集成了來自 EJB 3.0 和 Java EE 5 規(guī)范的 JPA 持久性棧(請(qǐng)參閱 參考資料),使之成為 Spring 支持?jǐn)?shù)據(jù)庫訪問的最簡(jiǎn)單而且也是標(biāo)準(zhǔn)的方式。
Spring 框架一直通過其他對(duì)象到關(guān)系的映射(ORM)技術(shù)支持持久性,但是這類映射任務(wù)要求對(duì)第三方非標(biāo)準(zhǔn)的技術(shù)性庫有相當(dāng)精妙和深入的了解。隨著 JPA 的到來,大批供應(yīng)商開始支持 JPA 標(biāo)準(zhǔn),因而對(duì)非標(biāo)準(zhǔn)的第三方持久性庫的支持的重要性有所降低。
Spring 2 支持 JPA,這使得為關(guān)系數(shù)據(jù)庫編寫、讀取、搜索、更新和刪除對(duì)象(POJO)的煩瑣工作變得透明??梢岳^續(xù)使用 Java 語言面向?qū)ο蟮恼Z法處理 POJO,JPA ORM 層負(fù)責(zé)數(shù)據(jù)庫表的創(chuàng)建、查詢、更新代碼和刪除代碼。
除了透明的數(shù)據(jù)庫操作,Spring 2 的 JPA 支持還把各種五花八門的特定于數(shù)據(jù)庫廠商的異常轉(zhuǎn)換成一套定義良好的異常,使得異常處理代碼大為簡(jiǎn)化。圖 5 演示了 Spring 2 的 JPA 支持:
![]() |
|
在圖 5 中,您將自己的對(duì)象反饋給 Spring 引擎,同時(shí)還有一些關(guān)于您希望如何把它們映射到關(guān)系數(shù)據(jù)庫表的線索(元數(shù)據(jù))。Spring JPA 支持負(fù)責(zé)處理剩下的工作??梢杂?Java 5 注釋的形式或外部 XML 定義文件(為了與 JDK 1.4 兼容)的形式向 JPA 引擎提供映射線索。
因?yàn)楦鞣N ORM 產(chǎn)品和數(shù)據(jù)庫都存在 JPA 實(shí)現(xiàn),所以您的實(shí)現(xiàn)代碼是可以在不同廠商的解決方案之間移植的(如果必要)。
對(duì)映射對(duì)象的操作通過 JPA 實(shí)體管理器來執(zhí)行。例如,用叫作 em
的實(shí)體管理器把一個(gè)相關(guān)對(duì)象樹寫入關(guān)系數(shù)據(jù)庫,代碼應(yīng)當(dāng)是:
|
JPA 實(shí)體管理器然后檢查您所提供的映射線索,并通過 myObjectTree
把對(duì)象樹的所有映射字段保存到關(guān)系數(shù)據(jù)庫。
您很快就會(huì)看到(在 用 Spring DAO 實(shí)現(xiàn)域服務(wù) 一節(jié)中),Spring 走得更遠(yuǎn),它還簡(jiǎn)化了使用 JPA 實(shí)體管理器的任務(wù)。
![]() ![]() |
要提供如何把 Employee
對(duì)象保存到數(shù)據(jù)庫的提示,可以向 Employee.java 源代碼添加 Java SE 5 注釋。這些線索通常叫作元數(shù)據(jù),因?yàn)樗鼈兪敲枋鰯?shù)據(jù)的數(shù)據(jù)。
清單 6 顯示了注釋版本的 Employee
對(duì)象,其中的注釋以粗體突出顯示:
|
這個(gè)示例中的所有注釋都在字段級(jí)。這是 JPA 注釋的最常見用法。也可以給與字段對(duì)應(yīng)的 getter 方法加注釋。如果想要保存到數(shù)據(jù)庫中的值是計(jì)算得來的,而不是對(duì)象的字段時(shí),這可能是必要的。
![]() ![]() |
表 1 描述了 清單 6 中每個(gè)字段的注釋,以及提供給 Spring 2 引擎的持久性線索:
字段/元素 | 注釋 | 說明 |
---|---|---|
Employee |
@Entity |
指出這是要保存到數(shù)據(jù)庫的類。默認(rèn)情況下類名稱被用作表名。 |
empid |
@Id |
指出這是表的主關(guān)鍵字段。 |
empid |
@GeneratedValue(strategy = GenerationType.TABLE) |
指定持久性引擎用來分配唯一主關(guān)鍵 ID 的策略。GenerationType.TABLE 指出應(yīng)當(dāng)使用可移植的基于表的 ID 序列。其他特定于數(shù)據(jù)庫的選項(xiàng)也可使用,但可能無法跨多個(gè)數(shù)據(jù)庫工作。 |
empno |
@Column(length = 6) |
這個(gè)字段包含員工編號(hào),由公司分配。請(qǐng)注意這不是主鍵。在這個(gè)應(yīng)用程序中,主鍵是由引擎生成和管理的。@Column() 標(biāo)記指定字段應(yīng)當(dāng)是六個(gè)字符長(zhǎng)。指定合適的字段長(zhǎng)度會(huì)有助于限制生成的表的大小。 |
firstName |
@Column(name = "FIRSTNME") |
指定這個(gè)字段在數(shù)據(jù)庫表中應(yīng)當(dāng)使用的字段名。 |
midInitial |
@Column(name = "MIDINIT") |
指定這個(gè)字段在數(shù)據(jù)庫表中應(yīng)當(dāng)使用的字段名。請(qǐng)注意,它與 Java 字段名不同。 |
lastName |
|
沒有注釋,所以將使用字段名 "LASTNAME" ,與 Java 字段名匹配。 |
phoneNumber |
@Column(name = "PHONENO") |
指定在數(shù)據(jù)庫表中使用的字段名。 |
job |
@Column(length = 8) |
指定數(shù)據(jù)庫字段的長(zhǎng)度。 |
educationLevel |
@Column(name = "EDLEVEL") |
指定數(shù)據(jù)庫字段名。 |
sex |
@Column(length = 1) |
指定數(shù)據(jù)庫字段的長(zhǎng)度。 |
salary |
@Column(precision=12, scale=2) |
指定浮點(diǎn)數(shù)據(jù)庫字段的算術(shù)精度。 |
bonus |
@Column(precision=12, scale=2) |
指定浮點(diǎn)數(shù)據(jù)庫字段的算術(shù)精度。 |
commission |
@Column(precision=12, scale=2) |
指定浮點(diǎn)數(shù)據(jù)庫字段的算術(shù)精度。 |
addr |
@OneToOne(cascade = CascadeType.ALL) |
指定這個(gè)表和另一個(gè)映射表中的 Address 對(duì)象之間的關(guān)系。cascade=ALL 指明添加、修改、刪除和刷新應(yīng)當(dāng)全部級(jí)聯(lián)到關(guān)聯(lián)表。 |
hiredate |
@Temporal(TemporalType.DATE) |
指定字段是日期字段(而不是時(shí)間或時(shí)間戳字段)。 |
birthdate |
@Temporal(TemporalType.DATE) |
指定字段是日期字段(而不是時(shí)間或時(shí)間戳字段). |
請(qǐng)注意 Employee
和 Address
實(shí)例是一對(duì)一的關(guān)聯(lián)(由 @OneToOne(casecade=CascadeType.ALL)
注釋指定)。這個(gè)注釋指定 Employee
對(duì)象上的所有實(shí)體管理器操作應(yīng)級(jí)聯(lián)到關(guān)聯(lián)的 Address
對(duì)象。這意味著新增任何 Employee
記錄都會(huì)在 RDBMS 中創(chuàng)建一個(gè)對(duì)應(yīng)的 Address
記錄。它還意味著對(duì) Employee
記錄所做的任何更新或刪除,都會(huì)級(jí)聯(lián)到關(guān)聯(lián)的 Address
記錄。這是經(jīng)常在 RDBMS 中發(fā)現(xiàn)的級(jí)聯(lián)刪除完整性約束的擴(kuò)展。在實(shí)踐中,會(huì)發(fā)現(xiàn)在執(zhí)行級(jí)聯(lián)選項(xiàng)時(shí),Java 代碼被極大地簡(jiǎn)化了:不再需要協(xié)調(diào)跨越多個(gè)表的操作。
有注釋的 Employee
源代碼是 JPA 實(shí)體管理器管理 Employee
或 Address
對(duì)象的持久性實(shí)例的藍(lán)本。您應(yīng)發(fā)現(xiàn),與容易出錯(cuò)的編寫創(chuàng)建和操作實(shí)際 RDBMS 表的實(shí)際代碼相比,這樣的注釋要簡(jiǎn)單得多。。
Employee
對(duì)象的表可以選擇性地基于上述注釋生成,其模式類似于清單 7:
|
![]() |
|
請(qǐng)比較清單 7 與 清單 6 中注釋的 Employee
,查看注釋在對(duì) Spring 引擎創(chuàng)建的表上的效果。
如果想得到所有可用的 JPA 注釋的詳細(xì)解釋和說明,請(qǐng)參考 JSR 220,它是企業(yè) JavaBeans 3.0 規(guī)范的最終發(fā)行文檔(請(qǐng)參閱 參考資料 )。
![]() ![]() |
Address
POJO 以相似的方式進(jìn)行注釋,如清單 8 所示:
|
迄今為止,所有注釋對(duì)您來說都有著某些意義。毫不奇怪,清單 8 中的注釋生成的表具有如清單 9 所示的模式:
|
![]() ![]() |
Spring 2 和 Java EE 5 的關(guān)系
JPA 持久性是 EJB 3.0 的一部分,后者又是 Java EE 5 規(guī)范的一部分,這意味著所有兼容的 Java EE 5 服務(wù)器(商業(yè)的、開放源碼的或其他)都有符合規(guī)范的實(shí)現(xiàn)。這實(shí)際上就保證了在不久的將來,將有健壯、高質(zhì)量的 JPA 實(shí)現(xiàn)可以使用。
請(qǐng)注意,雖然 Spring 2 利用了 EJB 3.0 規(guī)范的 JPA 持久性,但未強(qiáng)迫 Spring 2 的用戶利用 EJB 3.0 或 Java EE 5 規(guī)范的其他元素。
用 Spring DAO 實(shí)現(xiàn)域服務(wù)
在這一節(jié),您將用 Spring DAO(數(shù)據(jù)訪問對(duì)象)API 實(shí)現(xiàn)員工信息應(yīng)用程序的服務(wù)接口。
實(shí)現(xiàn) EmployeeService 接口
一旦 Spring 2 引擎知道了如何保持 Employee
和 Address
對(duì)象的實(shí)例,實(shí)現(xiàn) EmployeeService
接口的任務(wù)就變得非常簡(jiǎn)單。
可以在服務(wù)實(shí)現(xiàn)中利用 Spring DAO API。Spring DAO 實(shí)現(xiàn)了著名的 DAO 設(shè)計(jì)模式(請(qǐng)參閱 參考資料)。在這個(gè)模式中,DAO 提供了一致的數(shù)據(jù)訪問外觀。通過傳輸對(duì)象執(zhí)行數(shù)據(jù)提取和修改。DAO 封裝了實(shí)際的數(shù)據(jù)源,并提供了操作傳輸對(duì)象的方法。
從架構(gòu)上說,DAO API 隱藏了操作實(shí)際數(shù)據(jù)持久性 API 調(diào)用的復(fù)雜性。(除了 JPA 之外,Spring 還支持其他 ORM 技術(shù),例如 JDO、Hibernate、iBATIS SQL Maps 和 Apache OJB。)。使用 Spring 的 DAO,可以編寫能夠輕松適應(yīng)這些持久性 API 的數(shù)據(jù)訪問代碼。
除了對(duì)數(shù)據(jù)持久性 API 的抽象,Spring 的 DAO 支持把各種特定于廠商的數(shù)據(jù)訪問異常映射到一套歸檔良好的 Spring 數(shù)據(jù)訪問異常。
Spring DAO API 還提供了便于擴(kuò)展的支持類。通過擴(kuò)展它們,您可不必再編寫煩瑣而易于出錯(cuò)的 ORM 數(shù)據(jù)訪問代碼。所需的全部編碼都封裝在基類和支持庫中,而且經(jīng)過全面測(cè)試。這些類封裝了通常與應(yīng)用程序邏輯混雜在一起的連接和事務(wù)管理代碼。在 JPA 支持類的情況下,對(duì) JPA 實(shí)體管理器的使用完全封裝在支持類中,因此您不必考慮實(shí)體管理器和實(shí)體管理器工廠的處理。
一些真實(shí)的代碼可以為您展現(xiàn) Spring DAO API 的多功能性。清單 10 是 EmployeeService
接口的實(shí)現(xiàn),稱為 EmployeeDAO
,它使用了 Spring 2 的 JpaDaoSupport
類:
|
在清單 10 中,最值得注意的就是各方法實(shí)現(xiàn)中編碼極其簡(jiǎn)單。JpaDaoSupport
類處理了大多數(shù)煩瑣的日常工作。JpaTemplate
助手類能:
表 2 總結(jié)了清單 10 中的 JpaTemplate
方法,這是一種常用的方法:
方法 | 說明 |
---|---|
find(Class <T> cls, Object id); |
通過實(shí)例的主鍵找到保持的實(shí)例。 |
find(String query); |
使用查詢字符串找到保持的對(duì)象。這個(gè)強(qiáng)大的查詢語言是 EJB QL 的擴(kuò)展版本,在 JSR-220 中有完整描述(請(qǐng)參閱 參考資料)。 |
persist(Object obj); |
保存實(shí)例到數(shù)據(jù)庫。用 JPA 的說法,它用 JPA 實(shí)體管理器保持實(shí)例。 |
merge(Object obj); |
用所提供的實(shí)例中的信息更新保存的對(duì)象實(shí)例。 |
remove(Object obj); |
從數(shù)據(jù)庫中刪除保持的實(shí)例。 |
在幕后,JpaTemplate
輔助類利用 JPA 實(shí)體管理器處理全部操作。輔助類處理數(shù)據(jù)訪問期間例行的實(shí)體管理器檢索和關(guān)閉操作。
在處理某些具體需求時(shí),JpaTemplate
類中的其他方法可能有所幫助。請(qǐng)參考 Spring DAO API 的 JavaDoc 獲得更多細(xì)節(jié)(請(qǐng)參閱 參考資料)。
有了保持 Employee
和 Address
實(shí)例的能力和 EmployeeService
的具體實(shí)現(xiàn),接下來就可以根據(jù)真實(shí)關(guān)系數(shù)據(jù)庫進(jìn)行完整的測(cè)試了。
![]() ![]() |
至此,對(duì)于 Spring 框架什么時(shí)候和如何獲得機(jī)會(huì)去實(shí)際處理 POJO,仍然不清楚。謎題的數(shù)據(jù)訪問部分解決了,但仍有兩個(gè)問題存在:Spring 2 引擎怎么知道要做什么,如何指定要使用哪個(gè)關(guān)系數(shù)據(jù)庫?
立即就會(huì)解決這兩個(gè)問題;將看到如何向 Spring 引擎提供 bean 連接模板。秘密在于叫作 dwspring-service.xml 的 XML bean 描述符文件。這個(gè) bean 描述符文件是 Spring 2 框架操作概述 中提過的連接藍(lán)本。它描述了 Spring 應(yīng)用程序中不同的 bean 之間的關(guān)系。清單 11 顯示了這個(gè)文件:
|
要測(cè)試 EmployeeDAO
實(shí)現(xiàn)的動(dòng)作,可以使用叫作 HSQLDB 的內(nèi)存內(nèi)(請(qǐng)參閱 參考資料)。HSQLDB 的可執(zhí)行文件是 “有依賴項(xiàng)的 Spring 2” 下載的一部分。
在清單 11 中,專門配置 HSQLDB 實(shí)例的行用黑體表示。稍后(在 編寫針對(duì) RDBMS 的 DAO 集成測(cè)試)中,將看到如何修改這些行,來針對(duì) DB2 Express-C 運(yùn)行集成測(cè)試。
請(qǐng)記住 EmployeeDAO
實(shí)際上擴(kuò)展了 JpaDaoSupport
類。這個(gè)類希望在裝入的時(shí)候被 JPA EntityManagerFactory
“插入”。然后它可以用這個(gè)工廠得到所有數(shù)據(jù)訪問操作的 JPA EntityManager
。
圖 6 以圖形方式顯示 bean 在 dwspring2-service.xml 文件中如何連接在一起:
實(shí)際上,清單 11 是需要由 Spring 2 引擎創(chuàng)建的對(duì)象的連接計(jì)劃,圖 6 是這些對(duì)象的圖表。在清單 11 和圖 6 中要注意的一個(gè)重要項(xiàng)目就是 EmployeeDAO
如何通過叫作 employeeService
的bean 獲得。這個(gè)實(shí)例把自己的 entityManagerFactory
屬性設(shè)置成另一個(gè)名為 entityManagerFactory
的 bean:
|
ref=""
標(biāo)志是對(duì)上下文中定義的另一個(gè) bean 的引用 —— 通常是在同一個(gè)文件中。
![]() ![]() |
用外部創(chuàng)建的對(duì)象來填充屬性,就像剛才做的那樣,這叫作 注入——更具體地講,叫作依賴性注入 (DI),因?yàn)楸蛔⑷氲膶?duì)象通常是接收對(duì)象進(jìn)行正確操作所依賴的事物。DI 在 Spring 中框架中使用得很多。使用 DI,編寫組件代碼時(shí)不需要主動(dòng)查詢或查找依賴服務(wù)(例如,查詢 EntityManagerFactory
)。相反,可以就像依賴服務(wù)已經(jīng)存在一樣地編寫組件代碼,Spring 引擎會(huì)在代碼執(zhí)行之前把實(shí)際的依賴性注入組件。
如果查看 清單 11 中一直到 entityManagerFactory
連接的部分,您會(huì)注意到 Spring 注入了以下依賴項(xiàng):
dataSource
jpaVendorAdapter
loadTimeWeaver
dataSource
bean 是 org.springframework.jdbc.datasource.DriverManagerDataSource
的實(shí)例,用 HSQLDB RDBMS 的內(nèi)存中實(shí)例進(jìn)行了配置。
jpaVendorAdapter
屬性通過連接到 Spring 應(yīng)用程序?qū)嶋H JPA 實(shí)現(xiàn)的 bean 注入。在這個(gè)示例中,使用的是 JPA 引用實(shí)現(xiàn),通過 org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter
類來訪問。這個(gè)類接著需要用 databasePlatform property
進(jìn)行配置。這個(gè)屬性被設(shè)置成 oracle.toplink.essentials.platform.database.HSQLPlatform
,此配置支持對(duì) HSQLDB RDBMS 的訪問。這個(gè) bean 的 generateDdl
屬性控制著是否生成和執(zhí)行數(shù)據(jù)定義語言腳本。如果這個(gè)屬性設(shè)為 true
,那么這個(gè) bean 每次裝入時(shí),都會(huì)重新建立數(shù)據(jù)庫中的方案。為了集成測(cè)試的目的,應(yīng)當(dāng)保持這個(gè)屬性為 true
。
在 dataSource
bean 的配置中,創(chuàng)建了 org.springframework.jdbc.datasource.DriverManagerDataSource
的實(shí)例。它的參數(shù)設(shè)置有:
最后一個(gè) transactionManager
bean 是為后面的集成測(cè)試配置的。不需要連接這個(gè) bean,因?yàn)楹竺嬉玫臏y(cè)試基類會(huì)按類型查找這個(gè) bean。
編寫針對(duì) RDBMS 的 DAO 集成測(cè)試
在這一節(jié),編寫和運(yùn)行一個(gè)集成測(cè)試,根據(jù)數(shù)據(jù)庫測(cè)試員工信息應(yīng)用程序。
測(cè)試 EmployeeService 的 EmployeeDAO 實(shí)現(xiàn)
現(xiàn)在還剩下的唯一問題是:如何和在什么時(shí)候調(diào)用 Spring 2 引擎,它怎么知道該如何使用 dwspring2-service.xml 配置文件?
看一下 EmployeeServiceIntegrationTest.java 中的集成測(cè)試源代碼,答案就一目了然了。這個(gè)集成測(cè)試針對(duì)實(shí)際的 RDBMS 測(cè)試 EmployeeService
的 EmployeeDAO
實(shí)現(xiàn)。請(qǐng)參閱清單 12 中的代碼片斷:
|
這套集成測(cè)試是在 Spring 2 庫的 AbstractJpaTests
類的幫助下編寫的。通過實(shí)現(xiàn)清單 12 中強(qiáng)調(diào)的 getConfigLocations()
方法,可以提供一個(gè)或多個(gè)需要由 Spring 2 引擎解析的 bean 配置文件??梢杂卸鄠€(gè)配置文件,因?yàn)榉蛛x后端和用戶界面 bean 配置文件是一種常見實(shí)踐。
清單 12 中的 setEmployeeService()
是依賴性注入的示例。當(dāng) Spring 2 引擎裝入這個(gè) EmployeeServiceIntegrationTest
類時(shí)(派生自 AbstractJpaTests
),它發(fā)現(xiàn)一個(gè)沒有填充的依賴項(xiàng) —— 類型為 EmployeeService
的屬性。引擎就查找 dwspring2-service.xml 文件,查找配置好的類型為 EmployeeService
的bean,并通過 setEmployeeService()
方法注入。
![]() ![]() |
您可能注意到,在 dwspring2-service.xml 文件中,對(duì)于 employeeService
的注入缺少明確的注入指令。實(shí)際上,這個(gè)注入是自動(dòng)發(fā)生的。在 Spring 的術(shù)語中,這叫作自動(dòng)連接。
AbstractJpaTests
基類派生自 AbstractDependencyInjectionSpringContextTests
類。 AbstractDependencyInjectionSpringContextTests
通過把 Spring 的按類型自動(dòng)連接特性設(shè)為默認(rèn),簡(jiǎn)化了測(cè)試。如果在應(yīng)用程序的上下文(在這個(gè)示例中,由 dwspring2-service.xml 文件配置)中發(fā)現(xiàn)相同類型的 bean,那么它的子類(EmployeeServiceIntegrationTest
就是這樣一個(gè)子類)的任何依賴項(xiàng)(公共屬性)就被自動(dòng)注入。
![]() ![]() |
AbstractJpaTests
針對(duì)測(cè)試的一個(gè)有用特性,就是在事務(wù)中執(zhí)行測(cè)試,然后在測(cè)試后回滾所有效果的能力。這切實(shí)地加快了測(cè)試,因?yàn)樵诿看螠y(cè)試運(yùn)行期間,不再需要?jiǎng)h除和重建數(shù)據(jù)了。
清單 13 顯示了執(zhí)行每個(gè)測(cè)試的初始設(shè)計(jì)的代碼。這個(gè)設(shè)置代碼用三個(gè) Employee
和它們的相關(guān) Addresse
填充數(shù)據(jù)庫。必須在與每個(gè)測(cè)試相同的事務(wù)內(nèi)執(zhí)行這個(gè)代碼。否則,就不會(huì)看到注入的數(shù)據(jù),因?yàn)槭聞?wù)總會(huì)被回滾。要在相同事務(wù)中執(zhí)行設(shè)置,要覆蓋 onSetUpInTransaction()
方法,如清單 13 所示:
|
請(qǐng)注意,通過基于 JPA 的 employeeService
創(chuàng)建并保持 Employee
實(shí)例有多簡(jiǎn)單。因?yàn)?save()
方法調(diào)用 JPA 的 persist()
操作,而且操作會(huì)從 Employee
級(jí)聯(lián)到 Address
對(duì)象(在 Employee
POJO 的 JPA 注釋指定這個(gè)),所以也可以依靠 JPA 在地址表中創(chuàng)建新記錄。
各測(cè)試每次執(zhí)行時(shí),都會(huì)執(zhí)行 onSetUpInTransaction(
) 中的設(shè)置代碼。這確保了在每次測(cè)試之前,都有三個(gè)員工。
作為測(cè)試示例,清單 14 顯示了 EmployeeDAO
的 update()
方法的測(cè)試方法 testModifyEmployee()
:
|
清單 14 中的測(cè)試用例 testModifyEmployee()
用 EmployeeService
的 update()
把員工“John Lockheed”的姓氏從“Lockheed”改成“Williams”。然后它調(diào)用 findByEmployeeLastName("Williams")
,檢驗(yàn)最后有一個(gè)員工“Williams”。它還檢驗(yàn)沒有姓氏為“Lockheed”的員工存在。
EmployeeServiceIntegrationTest
中還有其他許多測(cè)試??梢匝芯窟@些代碼查看如何利用 EmployeeService
實(shí)現(xiàn)上的方法操縱實(shí)際數(shù)據(jù)(請(qǐng)參閱 下載)。
接下來,設(shè)置根據(jù) RDBMS 運(yùn)行這些集成測(cè)試的環(huán)境。
![]() ![]() |
JPA 是 EJB 3.0 規(guī)范的一部分。EJB 3.0 又是 Java EE 5 規(guī)范的核心組件。因?yàn)檫@點(diǎn),JPA 的開源引用實(shí)現(xiàn)成為了 GlassFish Java EE 5 引用服務(wù)器的一部分。
如果還沒有做,那么請(qǐng)立即下載和安裝 JPA 實(shí)現(xiàn)來執(zhí)行集成測(cè)試(請(qǐng)參閱 參考資料)。
要在代碼中訪問 JPA 功能,在構(gòu)建和運(yùn)行時(shí)類路徑中必須有 JPA 引用實(shí)現(xiàn):
要成功地編譯和運(yùn)行集成測(cè)試,在構(gòu)建和運(yùn)行時(shí)類路徑中必須有依賴的庫。表 3 顯示了在構(gòu)建和運(yùn)行時(shí)類路徑中必須有的 JAR 文件??梢栽?Eclipse 中設(shè)置這些路徑:
JAR 文件 | 原始下載 |
---|---|
commons-logging.jar | spring-framework-2.x-with-dependencies.zip |
db2cc.jar | IBM DB 2 Express-C 發(fā)布 |
db2cc_licence_cu.jar | IBM DB2 Express-C 發(fā)布版 |
hsqldb.jar | spring-framework-2.x-with-dependencies.zip |
junit.jar | spring-framework-2.x-with-dependencies.zip |
log4j-1.x.xx.jar | spring-framework-2.x-with-dependencies.zip |
persistence.jar | spring-framework-2.x-with-dependencies.zip |
spring.jar | spring-framework-2.x-with-dependencies.zip |
spring-core.jar | spring-framework-2.x-with-dependencies.zip |
spring-jpa.jar | spring-framework-2.x-with-dependencies.zip |
spring-mock.jar | spring-framework-2.x-with-dependencies.zip |
toplink-essential.jar | JPA 引用實(shí)現(xiàn)下載 |
![]() ![]() |
雖然在這個(gè) Spring JPA 集成場(chǎng)景中, persistence.xml 文件沒有提供配置信息,但 JPA 規(guī)范要求這個(gè)文件。
persistence.xml 文件是持久性單元 的說明。在 JPA 術(shù)語中,持久性單元包含 EntityManagerFactory
(和相關(guān)的配置信息),它創(chuàng)建的 EntityManagers
和這些 EntityManager
管理的類(以及這些類的元數(shù)據(jù),以注釋或 XML 形式)。
在這個(gè)示例中,persistence.xml 文件非常簡(jiǎn)單,如清單 15 所示:
|
在某些廠商的配置中,persistence.xml 文件可以包含相關(guān)的說明信息,但是 Spring/JPA 集成中沒有。只需要確定具有一份 META-INF/persistence.xml 的副本即可。
![]() ![]() |
構(gòu)建路徑配置之后,即可運(yùn)行集成測(cè)試。
要在 Eclipse 中運(yùn)行集成測(cè)試,請(qǐng)?jiān)?Navigator 面板中選中 EmployeeServiceIntegrationTest.java 文件,然后右擊選中 Run As -> JUnit Test。這就開始了集成測(cè)試。請(qǐng)記住這個(gè)測(cè)試是針對(duì)一個(gè)內(nèi)存中的數(shù)據(jù)庫運(yùn)行的,非???。在執(zhí)行期間,應(yīng)當(dāng)在 Eclipse 的控制臺(tái)窗口看到表創(chuàng)建、SQL 插入和查詢的日志消息。請(qǐng)參閱圖 7 查看集成測(cè)試的運(yùn)行示例:
![]() ![]() |
把數(shù)據(jù)源從內(nèi)存中的數(shù)據(jù)庫切換到 DB2 Express-C
一般來說,在使用 Spring DAO 和 DAO 設(shè)計(jì)模式時(shí),對(duì)應(yīng)用程序代碼來說,數(shù)據(jù)源是完全封裝并隱藏的。實(shí)際上,示例中的代碼完全獨(dú)立于使用的關(guān)系數(shù)據(jù)庫,而且獨(dú)立于使用的 JPA 廠商。這種靈活性允許您在執(zhí)行得快的數(shù)據(jù)庫(例如內(nèi)存中的數(shù)據(jù)庫)上測(cè)試應(yīng)用程序代碼,然后把同樣的代碼部署到健壯和可伸縮的商業(yè)級(jí)數(shù)據(jù)庫(例如 IBM DB2 Express-C)上。
要在不同的數(shù)據(jù)庫之間切換,只需要修改 dwspring2-service.xml bean 描述符文件。清單 16 顯示了從 HSQLDB 切換到 IBM DB2 Express-C 需要的修改(用黑體強(qiáng)調(diào)):
|
The modifications highlighted 清單 16 中突出的修改假設(shè)在 192.168.23.36 的端口 50000 (默認(rèn)的)上有一個(gè) DB2 Express-C 實(shí)例。需要?jiǎng)?chuàng)建叫作 dwspring
的數(shù)據(jù)庫,并向應(yīng)用程序用戶(在清單 16 中是 bill
;請(qǐng)把它改成自己的特定用戶)提供完整訪問。
現(xiàn)在再次運(yùn)行集成測(cè)試。測(cè)試連接到 DB2 Express-C 數(shù)據(jù)庫,創(chuàng)建表,并執(zhí)行全部測(cè)試。這次的執(zhí)行稍微慢了一些 —— 因?yàn)闇y(cè)試要跨網(wǎng)絡(luò)執(zhí)行,還要使用磁盤上的數(shù)據(jù)存儲(chǔ)——您會(huì)注意到在執(zhí)行中有些小變化。圖 8 顯示了在 Eclipse 中針對(duì) DB2 Express-C 的典型運(yùn)行:
應(yīng)用程序的數(shù)據(jù)層現(xiàn)在已經(jīng)完成并經(jīng)過了完整測(cè)試。這一層可以用于各種不同的用戶界面。例如,可以在上面放置一個(gè)命令行接口層,或者創(chuàng)建使用它的 GUI 胖客戶。在用戶界面和數(shù)據(jù)訪問層之間的解耦非常重要,因?yàn)樗鼓軌蜉p松重新設(shè)計(jì)和獨(dú)立地應(yīng)用經(jīng)過測(cè)試的數(shù)據(jù)層代碼。
理解 Spring MVC
在這一節(jié),您將使用 Spring MVC 為員工信息應(yīng)用程序創(chuàng)建基于 Web 的用戶界面。
用 Spring 實(shí)現(xiàn) MVC 設(shè)計(jì)模式
模型-視圖-控制器(MVC)設(shè)計(jì)模式是把 Web 層和數(shù)據(jù)層組件連接在一起,創(chuàng)建 Web 應(yīng)用程序的最好方式。幾乎所有的現(xiàn)代應(yīng)用程序框架都提供了對(duì)構(gòu)建 MVC 模式應(yīng)用程序的支持。
MVC 模式提供的一個(gè)關(guān)鍵優(yōu)勢(shì)就是應(yīng)用程序中數(shù)據(jù)和表示之間清晰的隔離。這種清晰的隔離讓數(shù)據(jù)和表示能夠相互獨(dú)立地發(fā)展。在生產(chǎn)系統(tǒng)中,這非常有價(jià)值,因?yàn)閿?shù)據(jù)和表示隨著時(shí)間的變化和需求的不同會(huì)有所變化。
對(duì)于 MVC 設(shè)計(jì)模式的深入介紹,請(qǐng)參閱 參考資料。以下討論僅為概述,僅適用于我們的示例應(yīng)用程序。
應(yīng)用程序的模型部分包含的元素封裝了數(shù)據(jù)和數(shù)據(jù)上的操作。實(shí)際上,已經(jīng)看到了示例應(yīng)用程序中的模型 —— 域模型。整個(gè)域模型創(chuàng)建和測(cè)試時(shí),沒有整合任何表示元素(用戶界面、報(bào)表,等等)。MVC 設(shè)計(jì)模式允許在獨(dú)立于視圖的情況下創(chuàng)建和測(cè)試模型。
應(yīng)用程序的視圖部分完全是用 Java Server Pages(JSP)技術(shù)和 JSP Standard Tag Library(JSTL)創(chuàng)建的。您很快就會(huì)發(fā)現(xiàn),可以獨(dú)立于模型創(chuàng)建視圖。在生產(chǎn)中這非常重要,因?yàn)?Web 頁面設(shè)計(jì)人員(而不是 Java 開發(fā)人員)可以處理視圖的創(chuàng)建和測(cè)試。
控制器是模型和視圖之間的關(guān)鍵連接。Spring MVC 是構(gòu)建基于 Web 的應(yīng)用程序的框架。在 Spring MVC 中,控制器處理傳入的 HTTP Web 請(qǐng)求。
有一組功能豐富的控制器庫組件可以用來開發(fā)子類。所有 Spring MVC 控制器庫類都實(shí)現(xiàn)了 org.springframework.web.servlet.mvc.Controller
接口。這個(gè)接口只有一個(gè)方法:
|
控制器檢查和處理傳入的請(qǐng)求,然后返回 ModelAndView
對(duì)象。Spring 有一個(gè)叫作 org.springframework.web.servlet.mvc.AbstractController
的抽象類,它實(shí)現(xiàn)了這個(gè)接口并處理大多數(shù)緩存和會(huì)話管理特性。在實(shí)踐中,所有專門的 Spring 控制器都派生自這個(gè)抽象類。表 4 描述了在示例應(yīng)用程序中使用的兩個(gè) Spring 控制器類:
控制器類 | 說明 |
---|---|
AbstractController |
抽象控制器基類。在控制器不需要處理傳入的命令/參數(shù)或處理表單時(shí)有用。在示例應(yīng)用程序中,它呈現(xiàn)初始頁面,列出數(shù)據(jù)庫中的全部員工。 |
AbstractCommandController |
處理傳入命令的抽象控制器類。這個(gè)類解析傳入的 HTTP 請(qǐng)求并把指定的 Java 對(duì)象實(shí)例綁定到請(qǐng)求參數(shù),從而實(shí)現(xiàn)在控制器邏輯中對(duì)參數(shù)的輕松處理。 |
關(guān)于 Spring 中可以使用的其他控制器基類的更多信息,請(qǐng)參閱 參考資料。
![]() ![]() |
這個(gè)應(yīng)用程序中的 MainController
處理進(jìn)入應(yīng)用程序的初始進(jìn)入請(qǐng)求。這是在用戶通過 URL http://localhost:8080/dwspring/index.cgi 進(jìn)入應(yīng)用程序的主頁時(shí)發(fā)生的。
這個(gè)控制器顯示系統(tǒng)中的所有員工,并允許用戶單擊員工號(hào)上的連接,得到員工細(xì)節(jié)。MainController
的代碼如清單 17 所示:
|
在清單 17 中,請(qǐng)注意 MainController
沒有顯示任何用戶界面。相反,它訪問數(shù)據(jù)層,得到員工列表。為了得到列表,它調(diào)用 EmployeeService
實(shí)現(xiàn)的 findAll()
方法。這個(gè)實(shí)現(xiàn)通過 Spring 的依賴性注入被 “注入” 控制器,是 EmployeeDAO
的實(shí)例。馬上就會(huì)在 dwspring2-servlet.xml bean 描述符中配置它(請(qǐng)參見 連接 Spring MVC bean)。
要呈現(xiàn)用戶界面,控制器創(chuàng)建一個(gè) ModelAndView
對(duì)象并返回它。ModelAndView
對(duì)象創(chuàng)建時(shí)有三個(gè)參數(shù),如表 5 所述:
位置 | 參數(shù) | 說明 |
---|---|---|
1 | 視圖名稱 | 這是命名視圖的字符串。這個(gè)字符串被傳遞給視圖解析器組件,解析為特定視圖/表示組件 —— 本例中是 JSP。視圖解析器在運(yùn)行時(shí)由 Spring 引擎連接 。在 dwspring2-servlet.xml bean 描述符文件中配置視圖解析器。 |
2 | 模型名稱 | 命名從域模型提取的數(shù)據(jù)的字符串。數(shù)據(jù)本身在第三個(gè)參數(shù)中傳遞。這個(gè)數(shù)據(jù)通常由視圖在執(zhí)行期間呈現(xiàn)。對(duì)于 JSP,這個(gè)名稱是變量的名稱。變量值可以用 JSP 命令或 JSTL 標(biāo)記呈現(xiàn)。 |
3 |
模型對(duì)象 | 這是視圖呈現(xiàn)的模型數(shù)據(jù)。在視圖中,可按照參數(shù) 2 提供的名稱引用。 |
要掌握 Spring MVC 的操作,重要的是理解請(qǐng)求流程。圖 9 顯示了通過 Spring MVC 組件的請(qǐng)求流:
在圖 9 中,虛線矩形中的是 Spring 組件。進(jìn)入請(qǐng)求先由 Spring 的 DispatcherServlet
處理。這個(gè) servlet 通過連接的 URL 映射器組件對(duì)請(qǐng)求的進(jìn)入 URL 進(jìn)行映射。這個(gè)映射器組件提供了控制請(qǐng)求的實(shí)際控制器。一旦控制器處理請(qǐng)求,就把視圖名稱傳遞回 Spring MVC。然后 Spring MVC 調(diào)用連接的視圖解析器組件,傳遞進(jìn)要呈現(xiàn)的模型數(shù)據(jù)。視圖解析器組件把視圖名稱解析為視圖對(duì)象。這個(gè)視圖對(duì)象被傳遞給模型數(shù)據(jù),并向用戶呈現(xiàn)。
Both the URL 映射器和視圖解析器都是 Spring 2 提供的組件,可以在 bean 描述符 XML 文件中指導(dǎo) Spring 引擎連接它們。
![]() ![]() |
在 Spring MVC 中,Web 應(yīng)用程序是在 Web 應(yīng)用程序上下文 中執(zhí)行的。這個(gè)上下文由 bean 描述符連接 XML 文件配置,就像數(shù)據(jù)層代碼一樣。
默認(rèn)情況下,用以下規(guī)則形成配置文件的名稱:采用 Spring DispatcherServlet
的 servlet 名稱,后面加上 _servlet.xml。
Spring DispatcherServlet
類通過 Web 容器(在這個(gè)示例中是 Tomcat)接受傳入的 Web 請(qǐng)求,并把它分配到某個(gè)控制器。
在這個(gè)示例中,DispatcherServlet
用 dwspring2
名稱配置,因此,配置文件應(yīng)當(dāng)在 dwspring2_servlet.xml。這個(gè)配置文件如清單 18 所示:
|
在清單 18 中,可以看到不同 bean 的連接。首先,mainController
bean 是 MainController
類的實(shí)例,被注入了一個(gè)到 employeeService
bean 的引用。當(dāng)然這個(gè) bean 是在 dwspring2-service.xml 中在數(shù)據(jù)層定義的 EmployeeDAO
的實(shí)例。
另一個(gè)控制器,叫作 empDetailsController
,也通過引用注入了相同的 employeeService
實(shí)例。在這一節(jié)后面將看到這個(gè)控制器的代碼。
被連接的第三個(gè) bean 是 controllermap
。這是 org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
的實(shí)例。這個(gè) Spring 提供的 bean 能根據(jù)映射屬性把進(jìn)入的 URL 請(qǐng)求映射到不同的控制器。在這個(gè)示例中,/home.cgi
被映射到 MainController
,/empdet.cgi 被映射到 empDetailsController
。
連接的最后一個(gè) bean 是視圖解析器。這個(gè) Spring 提供的 bean 把視圖名稱映射到實(shí)際視圖資源。這個(gè)示例中使用的是 org.springframework.web.servlet.view.InternalResourceViewResolver
的實(shí)例。這個(gè)視圖解析器根據(jù)進(jìn)入的視圖名稱,添加前綴和后綴,創(chuàng)建資源 URL。在清單 18 中,前綴被配置為 /jsp,后綴是 .jsp。例如,名為 home
的視圖被映射到 /jsp/home.jsp。這意味著 URL /jsp/home.jsp 呈現(xiàn)名為 home
的視圖。
![]() ![]() |
WEB-INF/web.xml 部署描述符包含 DispatcherServlet
的配置。清單 19 顯示了相關(guān)的代碼片斷:
|
指定 <load-on-startup>
確保在第一次啟動(dòng) Web 應(yīng)用程序時(shí),裝入 servlet。這還會(huì)觸發(fā) dwspring2-servlet.xml 配置文件的解析。
<servlet-mapping>
標(biāo)簽告訴 Tomcat 對(duì) for *.cgi 資源的全部 Web 請(qǐng)求路由到 DisplatcherServlet
。* 號(hào)代表通配符匹配。所以,任何 http://host:port/dwspring/*.cgi 形式的請(qǐng)求,都被轉(zhuǎn)發(fā)到 DispatcherServlet
。
![]() ![]() |
用 Spring MVC 創(chuàng)建基于 JSP/JSTL 的用戶界面
MainController
類把 Employee
的 List
傳遞給 home
視圖。 home
視圖由 InternalResourceViewResolver
解析成 /jsp/home.jsp。清單 20 是 home.jsp 的代碼。這個(gè)頁面只顯示在 ModelAndView
中以 employees
傳遞過來的員工列表。
|
在清單 20 中,可以看到用 JSTL 的 <c:forEach>
標(biāo)記迭代 ${employees}
列表中的每個(gè)員工。代碼在 ${emp.empno}
信息周圍創(chuàng)建鏈接。例如,圍繞 Joe Smith 這行生成的 URL 是 http://localhost:8080/dwspring/empdet.cgi?empID=1。
在點(diǎn)擊這個(gè) URL 鏈接時(shí),映射到 empdat.cgi 的資源被激活。根據(jù) web.xml 配置,Tomcat 知道要把全部 *.cgi 資源請(qǐng)求發(fā)送給 Spring 的 DispatcherServlet
,而且 Spring 的 DispatcherServlet
根據(jù) dwspring2-servlet.xml 中的配置,也知道用 SimpleUrlHandlerMapping
。SimpleUrlHandlerMapping
告訴 Spring 引擎把請(qǐng)求引導(dǎo)到 EmpDetailsController
。
![]() ![]() |
EmpDetailsController
的代碼如清單 21 所示:
|
EmpDetailsController
是 AbstractCommandController
的子類。 AbstractCommandController
是個(gè)有用的控制器,在處理根據(jù)按鈕或 URL 點(diǎn)擊需要執(zhí)行的命令時(shí),可以從它派生子類。
在這個(gè)示例中,用戶點(diǎn)擊員工編號(hào) URL,命令被用來顯示員工的詳細(xì)信息。
AbstractCommandController
主要的增值是:它解析進(jìn)入的請(qǐng)求,得到請(qǐng)求參數(shù),并把參數(shù)綁定到所定義的進(jìn)入命令類的實(shí)例。進(jìn)入請(qǐng)求參數(shù)的名稱與命令類的屬性名稱匹配。命令類叫作 EmployeeDetailsCommand
。EmployeeDetailsCommand
的代碼如清單 22 所示:
|
在清單 22 中,可以看到 EmployeeDetailsCommand
命令類的簡(jiǎn)單結(jié)構(gòu)。它只有一個(gè)屬性,叫作 empID
。AbstractCommandController
查詢叫作 empID
的進(jìn)入請(qǐng)求參數(shù),并把它綁定到 EmployeeDetailsCommand
類的實(shí)例。然后把它傳遞給 EmployeeDetailsController
,在這里調(diào)用 handle()
方法(請(qǐng)參閱 清單 21)。
一般來說,可以從 AbstractCommandController
派生子類,處理任意數(shù)量的進(jìn)入請(qǐng)求參數(shù)。需要做的全部工作就是用對(duì)應(yīng)的屬性定義命令類。
在 清單 21 中,EmployeeDetailsController
把傳遞過來的 cmd
對(duì)象的類型轉(zhuǎn)換回 EmployeeDetailsCommand
實(shí)例,并提取 empID
。然后用 empID
查詢特定 Employee
實(shí)例的 EmployeeService
。這是通過 DAO 的 employeeService.findById()
方法進(jìn)行的。
在 清單 21 中,生成的 Employee
記錄作為 作為 emp
,被傳遞給視圖。指定的視圖是 empdetails
視圖。同樣,Spring 引擎查看 InternalResourceViewResolver
來解析視圖。這個(gè)視圖解析器加上適當(dāng)?shù)那熬Y和后綴,并返回 /jsp/empdetails.jsp 作為視圖處理器。
empdetails.jsp 的代碼如清單 23 所示:
|
在清單 23 中,進(jìn)入的 ${emp}
變量被用來顯示 HTML 表格中的員工詳細(xì)信息。
請(qǐng)注意,頁面底部的鏈接映射回員工清單頁面。URL 是 home.cgi。如果點(diǎn)擊這個(gè)鏈接,映射過程就再次開始,并傳遞到 MainController
、然后 /jsp/home.jsp,依次類推。
![]() ![]() |
home.jsp 和 empdet.jsp 用 css/dwstyles.css 樣式表格式化它們的 HTML。樣式表代碼如清單 24 所示:
|
![]() ![]() |
創(chuàng)建 Eclipse WTP 動(dòng)態(tài) Web 項(xiàng)目
要查看完整應(yīng)用程序的效果,需要:
用 File->New->Project... 在 Eclipse 中創(chuàng)建新的動(dòng)態(tài) Web 項(xiàng)目。在 New Project 向?qū)е校?Web 分類下選擇 Dynamic Web Project,把項(xiàng)目命名為 spring2Web. See Figure 10:
接下來,添加源文件,并如圖 11 所示安排它們的位置。如果已經(jīng)下載了源文件發(fā)布(請(qǐng)參閱 下載),可以在文件管理器中拖放文件,把這些文件添加到 src 文件夾。
![]() ![]() |
與數(shù)據(jù)層代碼的集成測(cè)試環(huán)境不同,WAR 文件中的 Web 應(yīng)用程序必須包含它需要的全部庫 JAR。圖 12 顯示了應(yīng)當(dāng)拖放到 WEB-INF/lib 目錄的 JAR 文件:
在圖 12 中,請(qǐng)注意可以在有依賴項(xiàng)的 Spring 下載的 lib\j2ee 下找到 jstl.jar 和 servlet-api.jar。 standard.jar 標(biāo)記庫來自 lib\jakarta-taglibs 目錄。
除了庫 JAR 文件,圖 12 還顯示了其他配置文件的位置。請(qǐng)確保在繼續(xù)之前找到了需要的文件。
![]() ![]() |
從 Eclipse 項(xiàng)目導(dǎo)出 WAR 文件
要?jiǎng)?chuàng)建能部署到 Tomcat 的 WAR 文件,需要構(gòu)建項(xiàng)目并把它導(dǎo)出為 WAR 文件。
可以在導(dǎo)航器的 spring2Web
項(xiàng)目上右擊,選擇 Build Project,為 Tomcat 構(gòu)建項(xiàng)目。
把項(xiàng)目導(dǎo)出成 WAR 文件,請(qǐng)選擇 File->Export... 。在導(dǎo)出向?qū)е?,選擇 WAR File。Export 向?qū)?duì)話框如圖 13 所示。把導(dǎo)出的 WAR 文件命名為 dwspring.war 并單擊 Next。
Tomcat 5.5 作為 Spring MVC 應(yīng)用程序的宿主
這一節(jié)介紹如何配置 Apache Tomcat,這是個(gè)開源的 Web 層容器,它與 Spring 協(xié)作,容納示例應(yīng)用程序。
Tomcat 5.5 中的 JSP 和 servlet 支持
毫無疑問,Tomcat 是目前為止最流行和最成熟的開源的 Web 層服務(wù)器。作為 Web 層服務(wù)器,Tomcat 可以運(yùn)行和執(zhí)行包含 JSP 和 servlet 的 Web 應(yīng)用程序。將要使用的 Tomcat 5.5.x 支持 Servlet 2.4 和 JSP 2.0 標(biāo)準(zhǔn)(請(qǐng)參閱 參考資料)。
如果您還未安裝,請(qǐng)立即下載和安裝最新版的 Tomcat 5.5.x 來運(yùn)行示例(請(qǐng)參閱 參考資料)。請(qǐng)從服務(wù)器上選擇 ZIP 文件下載,并把它解壓縮到選中的目錄。
這一節(jié)提供一些 Tomcat 服務(wù)器的通用操作指南。請(qǐng)參閱 Tomcat 文檔獲得更多細(xì)節(jié)。
對(duì) Tomcat 服務(wù)器執(zhí)行得最頻繁的操作有:
在解壓縮 Tomcat 服務(wù)器的可執(zhí)行文件后,可以進(jìn)入服務(wù)器的 bin 子目錄,運(yùn)行 startup.bat 腳本啟動(dòng)服務(wù)器。彈出另一個(gè)運(yùn)行服務(wù)器的控制臺(tái)窗口。
要關(guān)閉服務(wù)器,請(qǐng)進(jìn)入 bin 子目錄并運(yùn)行 shutdown.bat 腳本。
要容易地部署應(yīng)用程序到 Tomcat,可以用內(nèi)置的管理器 Web 應(yīng)用程序或直接把 WAR 文件拷貝到 Tomcat 服務(wù)器的 webapps 子目錄。
如果想用管理器 Web 應(yīng)用程序,需要給一個(gè)用戶提供“manager”角色,啟用訪問。在開始 Tomcat 服務(wù)器之前,請(qǐng)查看 conf 子目錄中叫作 tomcat-users.xml 的文件。在這個(gè)文件中,查看:
|
把這行改成:
|
然后可以通過 http://localhost:8080/manager/html 訪問管理器應(yīng)用程序。
如果通過把 WAR 文件直接拷貝到 webapps 目錄來部署,可能要在 Tomcat 服務(wù)器檢測(cè)到更新并部署新 WAR 文件之前稍等一會(huì)。
與使用示例應(yīng)用程序時(shí)的體驗(yàn)一樣,可能需要把代碼的新版本重新部署到 Tomcat 服務(wù)器。
根據(jù)使用的 Tomcat 發(fā)行版,在部署/重新部署 dwspring.war 文件時(shí),有時(shí)會(huì)遇到部署問題。如果在向服務(wù)器部署時(shí)遇到問題,部署 WAR 的解決問題的途徑是:
為 Spring 2 準(zhǔn)備 Tomcat
在可以向 Tomcat 成功部署 dwspring.war 文件之前,需要進(jìn)行一些服務(wù)器設(shè)置。
在這一節(jié)執(zhí)行的主要過程有:
把 Spring 2 類裝入器添加到 Tomcat 服務(wù)器
當(dāng) Spring JPA 應(yīng)用程序在 Tomcat 上運(yùn)行時(shí),要讓 JPA 支持正常工作,需要在類裝入期間進(jìn)行字節(jié)碼“連接”。來自 Tomcat 的標(biāo)準(zhǔn)類裝入器不支持這個(gè)。需要用特定于 Spring 的類裝入器實(shí)現(xiàn)這個(gè)功能。
要把這個(gè)特定于 Spring 的類裝入器安裝到 Tomcat 服務(wù)器,首先要把 spring-tomcat-weaver.jar 拷貝到 Tomcat 的 server/lib 子目錄。這個(gè)目錄包含的庫屬于 Tomcat 服務(wù)器私有??梢栽?Spring 2.0 下載的 dist/weaver 目錄下找到 spring-tomcat-weaver.jar 庫。
接下來,必須讓 Tomcat 知道對(duì)于示例應(yīng)用程序,應(yīng)當(dāng)替換標(biāo)準(zhǔn)類裝入器??梢栽?WAR 文件的 META-INF/context.xml 文件中指定這點(diǎn)。清單 25 中的粗體代碼配置類裝入器:
清單 25. 在 META-INF/context.xml 文件中配置類裝入器
|
![]() ![]() |
![]()
|
把 Spring 2 的上下文裝入器偵聽器添加到 Tomcat
Spring 2 要求掛接到 Tomcat 的上下文裝入管道??梢栽?WAR 文件的 WEB-INF/web.xml 文件添加以下行進(jìn)行這個(gè)配置:
|
在 web.xml 文件中,這必須在 <servlet>
和 <servlet-mapping>
定義之前。
![]() ![]() |
![]()
|
把 DB2 JDBC 驅(qū)動(dòng)程序拷貝到 Tomcat 服務(wù)器
Tomcat 是 Web 層容器,所以能夠管理自己的數(shù)據(jù)庫連接,也能做連接池。數(shù)據(jù)庫源由 Tomcat 管理,可以通過標(biāo)準(zhǔn)的 Java 命名和目錄接口 (JNDI)查詢機(jī)制訪問。員工系統(tǒng)在 Tomcat 內(nèi)作為 Web 應(yīng)用程序運(yùn)行,應(yīng)當(dāng)通過 Tomcat 的 JNDI 得到數(shù)據(jù)源。
要讓 Tomcat 在 Web 應(yīng)用程序部署期間找到 JDBC 驅(qū)動(dòng)程序,需要把 JAR 文件拷貝到 Tomcat 的系統(tǒng)庫目錄。在啟動(dòng) Tomcat 服務(wù)器之前,請(qǐng)把兩個(gè) JDBC 驅(qū)動(dòng)程序 JAR 文件從 DB2 發(fā)布拷貝到 Tomcat 的 common\lib 目錄。這兩個(gè)文件的名稱是 db2cc.jar 和 db2cc_licence_cu.jar。
放在 common\lib 目錄中的庫可以供 Tomcat 服務(wù)器和 Web 應(yīng)用程序共同使用。
![]() ![]() |
![]()
|
在 Tomcat 5 上配置 DB2 數(shù)據(jù)源管理和 JNDI
可以把一個(gè)應(yīng)用程序可以訪問的 DB2 數(shù)據(jù)源配置成 Web 應(yīng)用程序上下文的 JNDI 資源。方法是把清單 26 中突出的代碼放在自己 WAR 的 META-INF/context.xml 文件中:
清單 26. 在 META-INF/context.xml 中配置 JNDI 資源
|
需要替換清單 26 中的 DB2 Express-C 服務(wù)器主機(jī)、用戶名和口令,反映自己的 DB2 Express-C 安裝情況。
清單 27 的配置通過名稱 java:comp/env/jdbc/dwspring2
提供了 JNDI 數(shù)據(jù)源。
還必須向部署描述符 web.xml 文件添加資源引用。請(qǐng)把這個(gè) <resource-ref>
元素添加到 web.xml 文件的末尾,如清單 27 所示:
清單 27. 添加數(shù)據(jù)源的 JNDI 資源引用
|
為 Tomcat 部署配置 Spring 2 應(yīng)用程序
要為 Tomcat 部署配置數(shù)據(jù)層代碼,仍然需要兩個(gè)細(xì)節(jié):
要讓 Spring 2 引擎連接需要的數(shù)據(jù)層 bean,首先必須中找到并處理數(shù)據(jù)層的 bean 描述符配置文件。
在這個(gè)示例中,文件叫作 dwspring-service.xml。需要在部署的時(shí)候,在提供給 Tomcat 服務(wù)器的上下文參數(shù)中指定這個(gè)文件的位置。
這個(gè)上下文參數(shù)需要是 WEB-INF/web.xml 部署描述符的第一個(gè)元素,在清單 28 中用粗體表示:
|
dwspring2-service.xml 是前面在測(cè)試時(shí)使用的配置文件的改動(dòng)版本。
這個(gè)配置文件被修改成通過 JNDI 使用 Tomcat 服務(wù)器的 JDBC 連接管理,而不是自己的連接管理。
![]() ![]() |
修改 Spring 2 連接查找 Tomcat JNDI 數(shù)據(jù)源
dwspring2-service.xml 中使用 Tomcat 5 得到 JNDI 數(shù)據(jù)源需要進(jìn)行的修改在清單 29 中已突出顯示:
|
清單 29 中用于創(chuàng)建數(shù)據(jù)源的 bean 現(xiàn)在是 org.springframework.jndi.JndiObjectFactoryBean
??梢杂眠@個(gè) bean 執(zhí)行對(duì)特定的容器管理資源的 JNDI 查詢。在這個(gè)示例中,Tomcat 管理的 DB2 Express-C 數(shù)據(jù)源的名稱被配置成 java:comp/env/jdbc/dwspring2
。
jpaVendorAdapter
的 GenerateDdl
屬性被設(shè)置成 false
。這是必需的,因?yàn)椴幌胱?Spring 2 在每次啟動(dòng)應(yīng)用程序時(shí)都刪除和重建所有數(shù)據(jù)庫表。只有在集成測(cè)試期間,這個(gè)屬性才應(yīng)設(shè)置成 true
。試用 Spring 2 Web 應(yīng)用程序
在這一節(jié),將以編程方式向員工數(shù)據(jù)庫添加一些數(shù)據(jù),并試用員工信息應(yīng)用程序。
在事務(wù)內(nèi)添加數(shù)據(jù)到 DB2 Express-C
在可以試用應(yīng)用程序之前,需要在數(shù)據(jù)庫中有些員工數(shù)據(jù)。因?yàn)榧蓽y(cè)試在每次運(yùn)行時(shí)都會(huì)刪除所有表(通過回滾事務(wù)),所以數(shù)據(jù)庫不包含任何已經(jīng)可以使用的數(shù)據(jù)。
當(dāng)然,可以用 DB2 Express-C 工具手工輸入數(shù)據(jù)。但在這里將學(xué)習(xí)如何用編程的方式添加數(shù)據(jù)。
集成測(cè)試基于 AbstractJpaTests
的一個(gè)精彩特性,是測(cè)試完成時(shí)數(shù)據(jù)庫的所有變化都被回滾掉這一事實(shí),這就允許下一步驟迅速執(zhí)行。如果每個(gè)步驟都在事務(wù)中運(yùn)行到結(jié)束,那么數(shù)據(jù)修改會(huì)在 RDBMS 中持久,這就需要在啟動(dòng)下一測(cè)試之前刪除它們。所以在理論上,如果想向數(shù)據(jù)庫添加數(shù)據(jù),這個(gè)類一點(diǎn)用也沒有。
幸運(yùn)的是,在基于 AbstractJpaTests
的集成測(cè)試中修改的任何數(shù)據(jù),都可以在測(cè)試中調(diào)用 setComplete()
方法,提交給 RDBMS。這個(gè)方法不是回滾數(shù)據(jù),而是提交數(shù)據(jù),從而讓修改持久。
FillTableWithEmployeeInfo
類是 Spring2Tutorial
項(xiàng)目的一部分,如清單 30 所示。這個(gè)類利用這個(gè)功能把六個(gè)員工的信息保持到 DB2 Express-C。
|
在 Eclipse 的導(dǎo)航器視圖,右擊 FillTableWithEmployeeInfo.java 并選擇 Run As... JUnit Test,用數(shù)據(jù)填充數(shù)據(jù)庫。
![]() ![]() |
要在部署到 Tomcat 之后訪問應(yīng)用程序的主頁,請(qǐng)?jiān)跒g覽器中輸入以下 URL:http://localhost:8080/dwspring/home.cgi。這假設(shè)正在本地機(jī)器上運(yùn)行 Tomcat 服務(wù)器。如果使用網(wǎng)絡(luò)上的其他服務(wù)器,只要替換 URL 中的主機(jī)名即可。
應(yīng)用程序的第一頁,MainController
和 home.jsp,如圖 14 所示:
每個(gè)員工編號(hào)都是一個(gè)可以點(diǎn)擊的 URL 鏈接,點(diǎn)擊之后顯示包含選中員工的員工細(xì)節(jié)頁面(圖 15):
EmpDetailsController
創(chuàng)建,通過 empdet.jsp 視圖呈現(xiàn)。結(jié)束語
Spring 2 是種功能全面的框架??梢杂眠\(yùn)行時(shí)通過 XML 配置文件動(dòng)態(tài)連接起來的軟件組件,創(chuàng)建基于 Web 的服務(wù)器端應(yīng)用程序。
可以用 POJO 實(shí)現(xiàn)業(yè)務(wù)對(duì)象。POJO 易于在容器之外創(chuàng)建和測(cè)試,也可以在其他應(yīng)用程序或同一應(yīng)用程序的其他部分重用。
Spring 2 的 JPA 集成允許在 POJO 源代碼內(nèi)通過 JPA 注釋把數(shù)據(jù)庫持久性元數(shù)據(jù)添加到 POJO。
有了 Spring DAO 支持,可以創(chuàng)建在 POJO 上操作的服務(wù),并用 JPA 負(fù)責(zé)持久性 —— 不需要顯式地編寫煩瑣的實(shí)體管理器和事務(wù)管理代碼。
Spring 的 JPA 測(cè)試支持允許在支持 JPA 的 POJO 和 DSO 上創(chuàng)建集成測(cè)試??梢栽趦?nèi)存中運(yùn)行的數(shù)據(jù)庫或外部服務(wù)器上的數(shù)據(jù)上真正的 RDBMS 上執(zhí)行這些測(cè)試。
可以用 Spring MVC 把基于 Web 的用戶界面添加到測(cè)試過的數(shù)據(jù)層棧。Spring MVC 可以與各種視圖技術(shù)一起工作,包括本教程中使用的 JSP 和 JSTL。通過保持模型(域模型)代碼與用戶界面代碼的清晰分離, Spring 簡(jiǎn)化了 Web 應(yīng)用程序的維護(hù)。
![]() |
|
聯(lián)系客服