在Ruby中,一切都是對象。對于那些喜歡使用高度面向對象的語言(例如Smalltalk,Eiffel或CLOS)的用戶來說,這是非常受歡迎的。例如1,2,3或10.8等等都是對象,而不是如Java或C++中的原始類型;字符串是對象,類和方法也都是對象。例如,下面都是有效的Ruby代碼(在Ruby中,注釋行是以"#"符號界定的):
#對象-34的絕對值 -34.abs #對一個浮點數進行四舍五入處理 10.8.round #返回一個字符串對象的大寫且逆轉的副本 "This?is?Ruby".upcase.reverse #返回數學sin方法的參數個數 Math.method(:sin).arity
|
?
圖5.Ruby是全對象化的:在Ruby中,整數,浮點數,字符串,甚至類和方法都是對象。這里的代碼展示了針對這些類型對象的方法調用。
在Ruby中,所有功能都是通過調用對象上的方法(或操作)實現的。事實上,Ruby中的方法調用就象其它程序語言中的函數或過程調用一樣。
就象在所有面向對象程序語言中一樣,對象是從類中創建的。Ruby庫中提供了許多預構建的類。你可以修改這些類或構建你自己的類。Ruby中的類是使用"class"關鍵字定義的。類名開始是一個大寫字母。類定義以"end"關鍵字結束。因此,一個Rectangle類的定義可能有如下形式:
為了把方法添加到類,可以使用def關鍵字。方法的定義也應該以end關鍵字結束。跟隨def關鍵字和方法名后面就是方法參數。把一個area方法添加到上面的Rectangle類的代碼看上去如下所示:
class?Rectangle def?area?(hgt,wdth) return?hgt*wdth end end? |
對于那些熟悉其它程序語言的用戶,他可能注意到一些差別。Ruby并不使用任何花括號來限定類或方法,也不使用分號或其它字符來表示程序語句行的結束。Ruby的目標,根據它的創建者說明,是簡單、易用并使編碼成為一件"趣事"。誰想記住所有的那些分號?沒有意思!在Ruby中,只要你把語句放在一行上,不需要分號或其它代碼行結束標記。順便說一下,在area方法參數周圍的括號是不必要的。在默認情況下,Ruby返回一個方法中最后的內容,因此return關鍵字也可以省略。因此,你可以建立如下簡單編碼的Rectangle類:
class?Rectangle def?area?hgt,?wdth hgt*wdth end end |
? 盡管上面代碼是有效的,但是小括號還是被推薦使用于方法參數表達的,這主要是為了實現較好的可讀性。
?????? 實例變量和屬性
類也可以有實例變量(在一些語言中也稱為屬性)。例如,由Rectangle類創建的對象應該都有一個高度和寬度。在Ruby中,實例變量不必顯式地在類中聲明,只是必須在它們的命名中以一個特殊字符來標記和使用。具體地說,所有的實例變量名都以"@"開頭。為了實現當調用area方法時,存儲矩形實例的高度和寬度,你僅需把實例變量添加到area方法即可:
class?Rectangle def?area?(hgt,?wdth) @height=hgt @width?=?wdth @height*@width end end |
? 更確切地說,當創建一個Rectangle實例時,應該指定高度和寬度,而實例變量在此時才確定。另外,Ruby提供了一種特殊的方法initialize,它允許你建立或準備類的新實例:
class?Rectangle def?initialize?(hgt,?wdth) @height?=?hgt @width?=?wdth end def?area?() @height*@width end end |
為了創建一個新的Rectangle對象或實例,你要調用標準的Ruby類構造器方法"new":
?
或,你可以使用沒有括號的形式:
?
這個例子創建了一個新的Rectangle對象并且調用了initialize方法,其中傳入參數4和7。注意,在下面的代碼中添加了height和width方法以便共享高度和寬度信息:
class?Rectangle def?initialize?(hgt,?wdth) @height?=?hgt @width?=?wdth end def?height return?@height end def?width return?@width end def?area?() @height*@width end end |
?
同樣,為了使另外某個方法能夠更新或設置一個Rectangle對象的高度和寬度,需要定義其它一些設置方法:
class?Rectangle def?initialize?(hgt,?wdth) @height?=?hgt @width?=?wdth end def?height return?@height end def?height=(newHgt) @height=newHgt end def?width return?@width end def?width=(newWdth) @width=newWdth end def?area?() @height*@width end end |
?
譯者注 本文中的mutator和accessor相當于其它語言中的setter和getter。
上面的mutator方法("height="和"width=")可能看起來有點神秘,但是它們確實只是一些方法。不要讓命名中的等號蒙騙了你。在方法名最后的額外字符對于Ruby并不意味著什么,但是它提高了代碼的可讀性。請看下列代碼:
?
在此,一個Rectangle對象的高度正被賦值(改變)。事實上,這僅是一個對Rectangle對象的"height="方法的調用。
因為授予到一個對象的實例變量的存取權限非常普通,所以Ruby提供了一組關鍵字來實現一次性定義實例變量和accessor/mutator方法,從而使這些實例變量成為"public"屬性。這些關鍵字是attr_reader,attr_accessor。它們有助于極大地簡化代碼:
class?Rectangle attr_accessor?:height,?:width def?initialize?(hgt,?wdth) @height?=?hgt @width?=?wdth end def?area?() @height*@width end end |
?
在上面的示例代碼中,attr_accessor給出了Rectangle相應于height和width屬性的getter和setter。
用交互式Ruby構建應用程序 現在,你已經知道如何構建一個簡單的Ruby應用程序。為了展示Ruby解釋器的交互性,讓我們啟動一個交互的Ruby幫助和控制臺工具(使用Ruby安裝的fxri工具),見圖6。

圖6.啟動fxri:從Windows開始菜單中打開交互式Ruby工具fxri。
在窗口的右下方是一個交互式的Ruby命令提示符(即"irb(main):001:0>"),它在窗口打開時顯示出來。你可以進入到irb命令提示符行中并輸入"Rectangle.new(6,5).area()"。之后,你應該看到如下結果:
irb(main):013:0>?Rectangle.new(6,5).area() =>?30 |
?
簡潔有力!
在Ruby中,類從來不關閉。這意味著,你總是可以在一個現有類上添加或重定義方法。例如,你還可以在你創建的Rectangle類上添加一個circumference方法。在命令提示符上,請逐行輸入下列代碼:
class?Rectangle def?circumference?() @height?*?2?+?@width?*?2 end end |
?

圖7.輸入Rectangle類:把Rectangle類的定義輸入到fxri交互式Ruby解釋器中,見圖中的右下方。
Rectangle類又被定義了一次?不,Ruby解釋器知道你正在修改當前的Rectangle類,它把一個新方法添加到現有Rectangle類中?,F在,在命令提示符上輸入下列一行:"Rectangle.new(2,3).circumference()"。你應該看到類似如下的結果:
irb(main):014:0>?class?Rectangle irb(main):015:1>?def?circumference() irb(main):016:2>?@height?*?2?+?@width?*?2 irb(main):017:2>?end irb(main):018:1>?end =>?nil irb(main):019:0>?Rectangle.new(2,3).circumference =>?10 |
?
為了重新定義Rectangle類中的任何方法,例如area方法,只需簡單地重新輸入具有新的area方法定義的類定義即可:
irb(main):020:0>?class?Rectangle irb(main):021:1>?def?area() irb(main):022:2>?@height*2 irb(main):023:2>?end irb(main):024:1>?end =>?nil irb(main):025:0>?Rectangle.new(6,5).area =>?12 |
?
在上面的簡單例子中,area方法被重新定義以便總是返回原來高度的2倍。
一個類永遠不會被關閉的思想可以應用于你自己定義的類和該語言中的內嵌類中。為了說明問題,讓我們把一個area方法添加到String類。在命令提示符中輸入下列代碼:
class?String def?area() length() end end |
?
現在,你在定義一個字符串的"area"方法以返回該字符串的長度。現在,你可以把你的名字作為一個字符串來試用一下這個新方法,見下面代碼:
irb(main):026:0>?class?String irb(main):027:1>?def?area() irb(main):028:2>?length() irb(main):029:2>?end irb(main):030:1>?end =>?nil irb(main):031:0>?"Jim".area =>?3? |
在本文示例中,我們使用Ruby的交互式特性及其開發環境來測試這種語言,而且我們僅使用了較小的代碼片斷。
Ruby是一種面向對象的元編程語言,是它讓許多開發者感到驚訝,甚至令他們提出這樣的疑問:是否真正存在比Java和C#更好的語言?本文將對Ruby語言作初步的探討并試圖回答這一問題。
一、 引言
你是否聽說過Ruby?如今,它成了軟件開發界的一個流行話題。該語言在去年春天的一次Java會議上引起我的注意,當時象Bruce Tate,Dave Thomas等著名人物都在談論Ruby并且告訴在場的觀眾Ruby值得一看。
現在,如果你象我一樣正在從事軟件開發,那么我們就有共識:盡管學習一種新的編程語言可能是一件趣事,但是只有你對它具有深入了解之后,你才有資格以一種懷疑眼光來看待另一種編程語言。畢竟,在上世紀八、九十年代的編程語言之爭最終得出結論-從根本上看存在兩大陣營:Java世界和微軟基于.NET支持的開發語言。并不是我不想學習另一種語言,其實我只是期望通過選擇其它編程語言才能獲得一定技術優勢的日子早點結束。然而,由于前面幾位著名人士的影響,我決定一試Ruby。
好,假定"我已經到過山頂",那么本文就是我對Ruby的研究報告。
二、 安裝Ruby
Ruby是一種開源的編程語言,由日本的Yukihiro Matsumoto在九十年代中期開發。你可以在www.ruby-lang.org站點得到Ruby。這種語言最初被作為一種腳本語言創建,可應用于許多平臺上,包括Linux、各種類UNIX、MS-DOS、Windows、BeOS、Amiga、Acorn Risc OS和MacOS X。當前Ruby的最新版本是1.8.4。對于使用Windows平臺的用戶,你可以點按這里來得到一個"one-click"型Windows安裝程序。隨同基本的Ruby二進制文件和庫文件,這一下載中還包含一些有用的(并且是免費的)IDE和工具,包括幫助文檔和示例代碼,RubyGems包管理器,FreeRIDE(免費的Ruby IDE),Fox GUI庫,fxri(一種搜索引擎和Ruby文檔的GUI指南,還有一個交互式命令行工具)和SciTE(Scintilla文本編輯器IDE)。在寫本文時,Windows安裝程序所提供的Ruby的"穩定"版本是1.8.2,還有一個1.8.4版本的預覽版。注意,本文中的示例代碼是用Windows安裝程序所提供的1.8.2版本編寫的。
使用Windows安裝程序安裝Ruby是相當直接的事情。你只要下載并運行一個簡單的可執行安裝文件(ruby182-15.exe),這個程序就會啟動一個標準的安裝向導。下載文件大約有15MB,在向導把Ruby安裝到Windows平臺上后占大約40MB的硬盤空間。
對于那些偏愛自己的編輯器的程序員,大量的編輯器都提供了對Ruby的支持,包括emacs,vim,JEdit,Jed,Nedit和Textpad。當然,還有著名的Ruby Eclipse工程。Ruby開發工具(RDT)是一種Eclipse插件,當前仍處于早期開發中,但是你可以從此處下載試用它。另外,市場上還有一些便宜的Ruby IDE,Arachno Ruby就是其中之一。
運行Ruby
就象許多解釋性語言一樣,Ruby提供給程序員多種開發代碼的方法。你可以使用命令行工具以交互方式運行Ruby或者創建一個Ruby程序文件,然后要求Ruby的解釋器執行此程序。
在Windows中,打開命令行提示符窗口,在提示符上輸入"Ruby"并回車(注意:你應該能夠使系統沿Ruby的\bin目錄找到Ruby可執行文件)。那么,Ruby就會運行并等候你輸入程序。輸入下面的程序,然后按Ctrl+D再按回車鍵,你就會看到Ruby執行你的程序,如圖1所示。
def convertCtoF (celsius) print(celsius.to_s + " degrees celsius is " + ((celsius * 9)/5 + 32).to_s + " degrees in fahrenheit\n") end convertCtoF(20) |

圖1.在Ruby中以交互方式運行攝氏到華氏溫度轉換計算
圖1中的轉換程序也可以用一種Ruby IDE或簡單文本編輯器實現并保存到一個文件中-例如convertCtoF.rb(.rb是Ruby程序的常用文件類型)?,F在,Ruby解釋器將執行這個文件中的Ruby程序,見圖2。

圖2.運行convertCtoF.rb
那些熟悉Smalltalk,Common Lisp Object System(CLOS),或其它解釋性編程環境的用戶肯定都會熟悉交互的開發環境。交互特點允許你用小塊編程代碼進行試驗。通過使用一個特殊的Ruby批處理文件irb.bat,你就能夠克服Ruby解釋器的非交互性特征。圖3顯示了使用irb.bat命令啟動的Ruby?,F在,代碼可以被逐行地輸入,解釋和測試。

圖3.交互式Ruby
交互式Ruby特征也被嵌入到若干工具中。例如,Ruby文檔的圖形接口fxri,不僅可作為一種語言指南,而且可以用作一種交互式Ruby解釋器(見圖4)。

圖4.fxri的交互式Ruby能力:在此,fxri也用來運行和圖3相同的Ruby命令,但這是從文檔工具內部運行的。
摘要: 1?
?
J2EE
多層應用分析
1.1? ???????
J2EE
層次結構
J2EE
的三層結構在業界是指表示...
閱讀全文
你正在學習CSS布局嗎?是不是還不能完全掌握純CSS布局?通常有兩種情況阻礙你的學習:
第一種可能是你還沒有理解CSS處理頁面的原理。在你考慮你的頁面整體表現效果前,你應當先考慮內容的語義和結構,然后再針對語義、結構添加CSS。這篇文章將告訴你應該怎樣把HTML結構化。
另一種原因是你對那些非常熟悉的表現層屬性(例如:cellpadding,、hspace、align="left"等等)束手無策,不知道該轉換成對 應的什么CSS語句。 當你解決了第一種問題,知道了如何結構化你的HTML,我再給出一個列表,詳細列出原來的表現屬性用什么CSS來代替。
結構化HTML
我們在剛學習網頁制作時,總是先考慮怎么設計,考慮那些圖片、字體、顏色、以及布局方案。然后我們用Photoshop或者Fireworks畫出來、切割成小圖。最后再通過編輯HTML將所有設計還原表現在頁面上。
如果你希望你的HTML頁面用CSS布局(是CSS-friendly的),你需要回頭重來,先不考慮“外觀”,要先思考你的頁面內容的語義和結構。
外觀并不是最重要的。一個結構良好的HTML頁面可以以任何外觀表現出來,CSS Zen Garden是一個典型的例子。CSS Zen Garden幫助我們最終認識到CSS的強大力量。
HTML不僅僅只在電腦屏幕上閱讀。你用photoshop精心設計的畫面可能不能顯示在PDA、移動電話和屏幕閱讀機上。但是一個結構良好的HTML頁面可以通過CSS的不同定義,顯示在任何地方,任何網絡設備上。
開始思考
首先要學習什么是"結構",一些作家也稱之為"語義"。這個術語的意思是你需要分析你的內容塊,以及每塊內容服務的目的,然后再根據這些內容目的建立起相應的HTML結構。
如果你坐下來仔細分析和規劃你的頁面結構,你可能得到類似這樣的幾塊:
標志和站點名稱
主頁面內容
站點導航(主菜單)
子菜單
搜索框
功能區(例如購物車、收銀臺)
頁腳(版權和有關法律聲明)
我們通常采用DIV元素來將這些結構定義出來,類似這樣:
<div id="header"></div>
<div id="content"></div>
<div id="globalnav"></div>
<div id="subnav"></div>
<div id="search"></div>
<div id="shop"></div>
<div id="footer"></div>
這不是布局,是結構。這是一個對內容塊的語義說明。當你理解了你的結構,就可以加對應的ID在DIV上。DIV容器中可以包含任何內容塊,也可以嵌套另一個DIV。內容塊可以包含任意的HTML元素---標題、段落、圖片、表格、列表等等。
根據上面講述的,你已經知道如何結構化HTML,現在你可以進行布局和樣式定義了。每一個內容塊都可以放在頁面上任何地方,再指定這個塊的顏色、字體、邊框、背景以及對齊屬性等等。
使用選擇器是件美妙的事
id的名稱是控制某一內容塊的手段,通過給這個內容塊套上DIV并加上唯一的id,你就可以用CSS選擇器來精確定義每一個頁面元素的外觀表現,包括標 題、列表、圖片、鏈接或者段落等等。例如你為#header寫一個CSS規則,就可以完全不同于#content里的圖片規則。
另外一個例子是:你可以通過不同規則來定義不同內容塊里的鏈接樣式。類似這樣:#globalnav a:link或者 #subnav a:link或者#content a:link。你也可以定義不同內容塊中相同元素的樣式不一樣。例如,通過#content p和#footer p分別定義#content和#footer中p的樣式。從結構上講,你的頁面是由圖片、鏈接、列表、段落等組成的,這些元素本身并不會對顯示在什么網絡 設備中(PDA還是手機或者網絡電視)有影響,它們可以被定義為任何的表現外觀。
一個仔細結構化的HTML頁面非常簡單,每一個元素都被用于結構目的。當你想縮進一個段落,不需要使用blockquote標簽,只要使用p標簽,并對p 加一個CSS的margin規則就可以實現縮進目的。p是結構化標簽,margin是表現屬性,前者屬于HTML,后者屬于CSS。(這就是結構于表現的 相分離.)
良好結構的HTML頁面內幾乎沒有表現屬性的標簽。代碼非常干凈簡潔。例如,原先的代碼<table width="80%" cellpadding="3" border="2" align="left">,現在可以只在HTML中寫<table>,所有控制表現的東西都寫到CSS中去,在結構化的HTML中, table就是表格,而不是其他什么(比如被用來布局和定位)。
親自實踐一下結構化
上面說的只是最基本的結構,實際應用中,你可以根據需要來調整內容塊。常常會出現DIV嵌套的情況,你會看到"container"層中又有其它層,結構類似這樣:
<div id="navcontainer">
<div id="globalnav">
<ul>a list</ul>
</div>
<div id="subnav">
<ul>another list</ul>
</div>
</div>
嵌套的div元素允許你定義更多的CSS規則來控制表現,例如:你可以給#navcontainer一個規則讓列表居右,再給#globalnav一個規則讓列表居左,而給#subnav的list另一個完全不同的表現。
用CSS替換傳統方法
下面的列表將幫助你用CSS替換傳統方法:
HTML屬性以及相對應的CSS方法
HTML屬性
CSS方法說明
align="left"
align="right" float: left;
float: right; 使用CSS可以浮動 任何元素:圖片、段落、div、標題、表格、列表等等
當你使用float屬性,必須給這個浮動元素定義一個寬度。
marginwidth="0" leftmargin="0" marginheight="0" topmargin="0" margin: 0; 使用CSS, margin可以設置在任何元素上, 不僅僅是body元素.更重要的,你可以分別指定元素的top, right, bottom和left的margin值。
vlink="#333399" alink="#000000" link="#3333FF" a:link #3ff;
a:visited: #339;
a:hover: #999;
a:active: #00f;
在HTML中,鏈接的顏色作為body的一個屬性值定義。整個頁面的鏈接風格都一樣。使用CSS的選擇器,頁面不同部分的鏈接樣式可以不一樣。
bgcolor="#FFFFFF" background-color: #fff; 在CSS中,任何元素都可以定義背景顏色,不僅僅局限于body和table元素。
bordercolor="#FFFFFF" border-color: #fff; 任何元素都可以設置邊框(boeder),你可以分別定義top, right, bottom和left
border="3"cellspacing="3" border-width: 3px; 用CSS,你可以定義table的邊框為統一樣式,也可以分別定義top, right, bottom and left邊框的顏色、尺寸和樣式。
你可以使用 table, td or th 這些選擇器.
如果你需要設置無邊框效果,可以使用CSS定義: border-collapse: collapse;
<br clear="left">
<br clear="right">
<br clear="all">
clear: left;
clear: right;
clear: both;
許多2列或者3列布局都使用 float屬性來定位。如果你在浮動層中定義了背景顏色或者背景圖片,你可以使用clear屬性.
cellpadding="3"
vspace="3"
hspace="3" padding: 3px; 用CSS,任何元素都可以設定padding屬性,同樣,padding可以分別設置top, right, bottom and left。padding是透明的。
align="center" text-align: center;
margin-right: auto; margin-left: auto;
Text-align 只適用于文本.
象div,p這樣的塊級可以通過margin-right: auto; 和margin-left: auto;來水平居中
一些令人遺憾的技巧和工作環境
由于瀏覽器對CSS支持的不完善,我們有時候不得不采取一些技巧(hacks)或建立一種環境(Workarounds)來讓CSS實現傳統方法同樣的效 果。例如塊級元素有時侯需要使用水平居中的技巧,盒模型bug的技巧等等。所有這些技巧都在Molly Holzschlag的文章《Integrated Web Design: Strategies for Long-Term CSS Hack Management》中有詳細說明
Spring?MVC?框架。用銀行示例介紹如何建模和構建簡單的應用程序。示例應用程序包含了已經學過的一些技術(例如依賴注入),但是主要演示?Spring?MVC?的特性。
在開始之前,請?下載這篇文章的源代碼。請參閱?參考資料?訪問?Spring?框架和?Tom
cat?5.0,運行示例需要它們。
Spring?MVC?框架
Spring?框架提供了構建?Web?應用程序的全功能?MVC?模塊。使用?Spring?可插入的?MVC?架構,可以選擇是使用內置的?Spring?Web?框架還是?Struts?這樣的?Web?框架。通過策略接口,Spring?框架是高度可配置的,而且包含多種視圖技術,例如?JavaServer?Pages(JSP)技術、Velocity、Tiles、iText?和?POI。Spring?MVC?框架并不知道使用的視圖,所以不會強迫您只使用?JSP?技術。Spring?MVC?分離了控制器、模型對象、分派器以及處理程序對象的角色,這種分離讓它們更容易進行定制。
Spring?的?Web?MVC?框架是圍繞?DispatcherServlet?設計的,它把請求分派給處理程序,同時帶有可配置的處理程序映射、視圖解析、本地語言、主題解析以及上載文件支持。默認的處理程序是非常簡單的?Controller?接口,只有一個方法?ModelAndView?handleRequest(request,?response)。Spring?提供了一個控制器層次結構,可以派生子類。如果應用程序需要處理用戶輸入表單,那么可以繼承?AbstractFormController。如果需要把多頁輸入處理到一個表單,那么可以繼承?AbstractWizardFormController。
示例應用程序有助于直觀地學習這些特性。銀行應用程序允許用戶檢索他們的帳戶信息。在構建銀行應用程序的過程中,可以學到如何配置?Spring?MVC?框架和實現框架的視圖層,視圖層包括?JSTL?標記(用于顯示輸出的數據)和JavaServer?Pages?技術。
配置?Spring?MVC
要開始構建示例應用程序,請配置?Spring?MVC?的?DispatcherServlet。請在?web.xml?文件中注冊所有配置。清單?1?顯示了如何配置?sampleBankingServlet。
清單?1.?配置?Spring?MVC?DispatcherServlet
<servlet>
<servlet-name>sampleBankingServlet</servlet-name>
<servlet-class>
org.springframework.we.servlet.DispatcherServlet
<servlet-class>
<load-on-startup>1<load-on-startup>
<servlet>
DispatcherServlet?從一個?XML?文件裝入?Spring?應用程序上下文,XML?文件的名稱是?servlet?的名稱后面加上?-servlet?。在這個示例中,DispatcherServlet?會從?sampleBankingServlet-servlet.xml?文件裝入應用程序上下文。
配置應用程序的?URL
下一步是配置想讓?sampleBankingServlet?處理的?URL。同樣,還是要在?web.xml?中注冊所有這些信息。
清單?2.?配置想要處理的?URL
<servlet-mapping>
<servlet-name>?sampleBankingServlet<servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
裝入配置文件
下面,裝入配置文件。為了做到這點,請為?Servlet?2.3?規范注冊?ContextLoaderListener?或為?Servlet?2.2?及以下的容器注冊?ContextLoaderServlet。為了保障后向兼容性,請用?ContextLoaderServlet。在啟動?Web?應用程序時,ContextLoaderServlet?會裝入?Spring?配置文件。清單?3?注冊了?ContextLoaderServlet。
清單?3.?注冊?ContextLoaderServlet
<servlet>
<servlet-name>context>servlet-name>
<servlet-class>
org.springframework.web.context.ContextLoaderServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
contextConfigLocation?參數定義了要裝入的?Spring?配置文件,如下面的?servlet?上下文所示。
<context-param>
<param-value>contextConfigLocation</param-value>
<param-value>/WEB-INF/sampleBanking-services.xml</param-value>
</context-param>
sampleBanking-services.xml?文件代表示例銀行應用程序服務的配置和?bean?配置。如果想裝入多個配置文件,可以在?<param-value>?標記中用逗號作分隔符。
Spring?MVC?示例
示例銀行應用程序允許用戶根據惟一的?ID?和口令查看帳戶信息。雖然?Spring?MVC?提供了其他選項,但是我將采用?JSP?技術作為視圖頁面。這個簡單的應用程序包含一個視圖頁用于用戶輸入(ID?和口令),另一頁顯示用戶的帳戶信息。
我從?LoginBankController?開始,它擴展了?Spring?MVC?的?SimpleFormController。SimpleFormContoller?提供了顯示從?HTTP?GET?請求接收到的表單的功能,以及處理從?HTTP?POST?接收到的相同表單數據的功能。LoginBankController?用?AuthenticationService?和?AccountServices?服務進行驗證,并執行帳戶活動?!?配置視圖屬性?”一節中的?清單?5?描述了如何把?AuthenticationService?和?AccountServices?連接到?LoginBankController。?清單?4?顯示了?LoginBankController?的代碼。
配置視圖屬性
下面,我必須注冊在接收到?HTTP?GET?請求時顯示的頁面。我在?Spring?配置中用?formView?屬性注冊這個頁面,如清單?5?所示。sucessView?屬性代表表單數據提交而且?doSubmitAction()?方法中的邏輯成功執行之后顯示的頁面。formView?和?sucessView?屬性都代表被定義的視圖的邏輯名稱,邏輯名稱映射到實際的視圖頁面。
清單?5.?注冊?LoginBankController
<bean?id="loginBankController"
class="springexample.controller.LoginBankController">
<property?name="sessionForm"><value>true</value></property>
<property?name="commandName"><value>loginCommand</value></property>
<property?name="commandClass">
<value>springexample.commands.LoginCommand</value>
</property>
<property?name="authenticationService">
<ref?bean="authenticationService"?/>
</property>
<property?name="accountServices">
<ref?bean="accountServices"?/>
</property>
<property?name="formView">
<value>login</value>
</property>
<property?name="successView">
<value>accountdetail</value>
</property>
</bean>
commandClass?和?commandName?標記決定將在視圖頁面中活動的?bean。例如,可以通過?login.jsp?頁面訪問?loginCommand?bean,這個頁面是應用程序的登錄頁面。一旦用戶提交了登錄頁面,應用程序就可以從?LoginBankController?的?onSubmit()?方法中的命令對象檢索出表單數據。
視圖解析器
Spring?MVC?的?視圖解析器?把每個邏輯名稱解析成實際的資源,即包含帳戶信息的?JSP?文件。我用的是?Spring?的?InternalResourceViewResolver,如?清單?6?所示。
因為我在?JSP?頁面中使用了?JSTL?標記,所以用戶的登錄名稱解析成資源?/jsp/login.jsp,而?viewClass?成為?JstlView。
驗證和帳戶服務
就像前面提到的,LoginBankController?內部連接了?Spring?的?AccountServices?和?AuthenticationService。AuthenticationService?類處理銀行應用程序的驗證。AccountServices?類處理典型的銀行服務,例如查找交易和電匯。清單?7?顯示了銀行應用程序的驗證和帳戶服務的配置。
清單?7.?配置驗證和帳戶服務
<beans>
<bean?id="accountServices"
class="springexample.services.AccountServices">
</bean>
<bean?id="authenticationService"
class="springexample.services.AuthenticationService">
</bean>
</beans>
以上服務在?sampleBanking-services.xml?中注冊,然后裝入?web.xml?文件中,就像?前面討論的那樣。控制器和服務配置好后,這個簡單的應用程序就完成了?,F在我們來看看部署和測試它時會發生什么!
部署應用程序
我把示例應用程序部署在?Tomcat?servlet?容器中。Tomcat?是?Java?Servlet?和?Java?ServerPagest?技術的官方參考實現中使用的?servlet?容器。如果以前沒這么做過,請?下載?jakarta-tomcat-5.0.28.exe?并運行它把?Tomcat?安裝到自己喜歡的任何位置,例如?c:\tomcat5.0。
接下來,下載示例代碼?并釋放到驅動器(例如?c:\?)上。創建了?Spring?項目的文件夾之后,打開它并把?spring-banking?子文件夾拷貝到?c:\tomvat5.0\webapps。spring-banking?文件夾是一個?Web?檔案,里面包含?Spring?MVC?示例應用程序。lib?文件夾包含應用程序需要的?Spring?框架、與Spring?相關的?MVC?庫以及?JSTL?標記庫和?jar?文件。
要啟動?Tomcat?服務器,請使用以下命令:
cd?bin?C:\Tomcat?5.0\bin>?catalina.bat?start
Tomcat?應當啟動并部署?Spring?MVC?示例應用程序。
測試應用程序
要測試應用程序,請打開?Web?瀏覽器,指向?
http://localhost:tomcatport/springbanking?并用?Tomcat?服務器實際運行的端口替換?tomcatport。應當看到圖?1?所示的登錄屏幕。輸入用戶?ID?“admin”和口令“password”,并按下登錄按鈕。其他用戶?ID?或口令會造成來自驗證服務的錯誤。
圖?1.?Spring?MVC?示例登錄屏幕

登錄成功之后,會看到圖?2?所示的帳戶細節頁面。
圖?2.?Spring?MVC?示例帳戶細節頁面
結束語
在三部分的?Spring?系列?的第三篇文章中,我介紹了?Spring?MVC?框架的特性。我演示了如何配置和開發?Spring?MVC?應用程序、如何配置?Spring?MVC?控制器和向其中插入依賴項、如何用?JavaServer?Pages?技術開發應用程序視圖,以及如何把自己的頁面與?Spring?MVC?的視圖層集成。總結這篇文章時,我演示了如何在?Tomcat?servlet?容器中部署應用程序以及如何在瀏覽器中測試它。
引言
數據庫的設計范式是
數據庫設計所需要滿足的規范,滿足這些規范的
數據庫是簡潔的、結構明晰的,同時,不會發生插入(insert)、刪除(delete)和更新(update)操作異常。反之則是亂七八糟,不僅給
數據庫的編程人員制造麻煩,而且面目可憎,可能存儲了大量不需要的冗余信息。
設計范式是不是很難懂呢?非也,大學教材上給我們一堆數學公式我們當然看不懂,也記不住。所以我們很多人就根本不按照范式來設計
數據庫。
實質上,設計范式用很形象、很簡潔的話語就能說清楚,道明白。本文將對范式進行通俗地說明,并以筆者曾經設計的一個簡單論壇的
數據庫為例來講解怎樣將這些范式應用于實際工程。
范式說明 第一范式(1NF):
數據庫表中的字段都是單一屬性的,不可再分。這個單一屬性由基本類型構成,包括整型、實數、字符型、邏輯型、日期型等。
例如,如下的
數據庫表是符合第一范式的:
而這樣的
數據庫表是不符合第一范式的:
字段1 | 字段2 | 字段3 | 字段4 |
? | ? | 字段3.1 | 字段3.2 | ? |
很顯然,在當前的任何關系數據庫管理系統(DBMS)中,傻瓜也不可能做出不符合第一范式的數據庫,因為這些DBMS不允許你把數據庫表的一列再分成二列或多列。因此,你想在現有的DBMS中設計出不符合第一范式的數據庫都是不可能的。
第二范式(2NF):數據庫表中不存在非關鍵字段對任一候選關鍵字段的部分函數依賴(部分函數依賴指的是存在組合關鍵字中的某些字段決定非關鍵字段的情況),也即所有非關鍵字段都完全依賴于任意一組候選關鍵字。假定選課關系表為SelectCourse(學號, 姓名, 年齡, 課程名稱, 成績, 學分),關鍵字為組合關鍵字(學號, 課程名稱),因為存在如下決定關系:
(學號, 課程名稱) → (姓名, 年齡, 成績, 學分)
這個數據庫表不滿足第二范式,因為存在如下決定關系:
(課程名稱) → (學分)
(學號) → (姓名, 年齡)
即存在組合關鍵字中的字段決定非關鍵字的情況。
由于不符合2NF,這個選課關系表會存在如下問題:
(1) 數據冗余:
同一門課程由n個學生選修,"學分"就重復n-1次;同一個學生選修了m門課程,姓名和年齡就重復了m-1次。
(2) 更新異常:
若調整了某門課程的學分,數據表中所有行的"學分"值都要更新,否則會出現同一門課程學分不同的情況。
(3) 插入異常:
假設要開設一門新的課程,暫時還沒有人選修。這樣,由于還沒有"學號"關鍵字,課程名稱和學分也無法記錄入數據庫。
(4) 刪除異常:
假設一批學生已經完成課程的選修,這些選修記錄就應該從數據庫表中刪除。但是,與此同時,課程名稱和學分信息也被刪除了。很顯然,這也會導致插入異常。
把選課關系表SelectCourse改為如下三個表:
學生:Student(學號, 姓名, 年齡);
課程:Course(課程名稱, 學分);
選課關系:SelectCourse(學號, 課程名稱, 成績)。
這樣的數據庫表是符合第二范式的,消除了數據冗余、更新異常、插入異常和刪除異常。
另外,所有單關鍵字的數據庫表都符合第二范式,因為不可能存在組合關鍵字。
第三范式(3NF):在第二范式的基礎上,數據表中如果不存在非關鍵字段對任一候選關鍵字段的傳遞函數依賴則符合第三范式。所謂傳遞函數依賴,指的是如果存在"A → B → C"的決定關系,則C傳遞函數依賴于A。因此,滿足第三范式的數據庫表應該不存在如下依賴關系:
關鍵字段 → 非關鍵字段x → 非關鍵字段y
假定學生關系表為Student(學號, 姓名, 年齡, 所在學院, 學院地點, 學院電話),關鍵字為單一關鍵字"學號",因為存在如下決定關系:
(學號) → (姓名, 年齡, 所在學院, 學院地點, 學院電話)
這個數據庫是符合2NF的,但是不符合3NF,因為存在如下決定關系:
(學號) → (所在學院) → (學院地點, 學院電話)
即存在非關鍵字段"學院地點"、"學院電話"對關鍵字段"學號"的傳遞函數依賴。
它也會存在數據冗余、更新異常、插入異常和刪除異常的情況,讀者可自行分析得知。
把學生關系表分為如下兩個表:
學生:(學號, 姓名, 年齡, 所在學院);
學院:(學院, 地點, 電話)。
這樣的數據庫表是符合第三范式的,消除了數據冗余、更新異常、插入異常和刪除異常。
鮑依斯-科得范式(BCNF):在第三范式的基礎上,數據庫表中如果不存在任何字段對任一候選關鍵字段的傳遞函數依賴則符合第三范式。
假設倉庫管理關系表為StorehouseManage(倉庫ID, 存儲物品ID, 管理員ID, 數量),且有一個管理員只在一個倉庫工作;一個倉庫可以存儲多種物品。這個數據庫表中存在如下決定關系:
(倉庫ID, 存儲物品ID) →(管理員ID, 數量)
(管理員ID, 存儲物品ID) → (倉庫ID, 數量)
所以,(倉庫ID, 存儲物品ID)和(管理員ID, 存儲物品ID)都是StorehouseManage的候選關鍵字,表中的唯一非關鍵字段為數量,它是符合第三范式的。但是,由于存在如下決定關系:
(倉庫ID) → (管理員ID)
(管理員ID) → (倉庫ID)
即存在關鍵字段決定關鍵字段的情況,所以其不符合BCNF范式。它會出現如下異常情況:
(1) 刪除異常:
當倉庫被清空后,所有"存儲物品ID"和"數量"信息被刪除的同時,"倉庫ID"和"管理員ID"信息也被刪除了。
(2) 插入異常:
當倉庫沒有存儲任何物品時,無法給倉庫分配管理員。
(3) 更新異常:
如果倉庫換了管理員,則表中所有行的管理員ID都要修改。
把倉庫管理關系表分解為二個關系表:
倉庫管理:StorehouseManage(倉庫ID, 管理員ID);
倉庫:Storehouse(倉庫ID, 存儲物品ID, 數量)。
這樣的數據庫表是符合BCNF范式的,消除了刪除異常、插入異常和更新異常。
范式應用
我們來逐步搞定一個論壇的數據庫,有如下信息:
(1) 用戶:用戶名,email,主頁,電話,聯系地址
(2) 帖子:發帖標題,發帖內容,回復標題,回復內容
第一次我們將數據庫設計為僅僅存在表:
用戶名 | email | 主頁 | 電話 | 聯系地址 | 發帖標題 | 發帖內容 | 回復標題 | 回復內容 |
這個數據庫表符合第一范式,但是沒有任何一組候選關鍵字能決定數據庫表的整行,唯一的關鍵字段用戶名也不能完全決定整個元組。我們需要增加"發帖ID"、"回復ID"字段,即將表修改為:
用戶名 | email | 主頁 | 電話 | 聯系地址 | 發帖ID | 發帖標題 | 發帖內容 | 回復ID | 回復標題 | 回復內容 |
這樣數據表中的關鍵字(用戶名,發帖ID,回復ID)能決定整行:
(用戶名,發帖ID,回復ID) → (email,主頁,電話,聯系地址,發帖標題,發帖內容,回復標題,回復內容)
但是,這樣的設計不符合第二范式,因為存在如下決定關系:
(用戶名) → (email,主頁,電話,聯系地址)
(發帖ID) → (發帖標題,發帖內容)
(回復ID) → (回復標題,回復內容)
即非關鍵字段部分函數依賴于候選關鍵字段,很明顯,這個設計會導致大量的數據冗余和操作異常。
我們將數據庫表分解為(帶下劃線的為關鍵字):
?。?) 用戶信息:用戶名,email,主頁,電話,聯系地址
?。?) 帖子信息:發帖ID,標題,內容
?。?) 回復信息:回復ID,標題,內容
?。?) 發貼:用戶名,發帖ID
?。?) 回復:發帖ID,回復ID
這樣的設計是滿足第1、2、3范式和BCNF范式要求的,但是這樣的設計是不是最好的呢?
不一定。
觀察可知,第4項"發帖"中的"用戶名"和"發帖ID"之間是1:N的關系,因此我們可以把"發帖"合并到第2項的"帖子信息"中;第5項"回復"中的 "發帖ID"和"回復ID"之間也是1:N的關系,因此我們可以把"回復"合并到第3項的"回復信息"中。這樣可以一定量地減少數據冗余,新的設計為:
?。?) 用戶信息:用戶名,email,主頁,電話,聯系地址
?。?) 帖子信息:用戶名,發帖ID,標題,內容
?。?) 回復信息:發帖ID,回復ID,標題,內容
數據庫表1顯然滿足所有范式的要求;
數據庫表2中存在非關鍵字段"標題"、"內容"對關鍵字段"發帖ID"的部分函數依賴,即不滿足第二范式的要求,但是這一設計并不會導致數據冗余和操作異常;
數據庫表3中也存在非關鍵字段"標題"、"內容"對關鍵字段"回復ID"的部分函數依賴,也不滿足第二范式的要求,但是與數據庫表2相似,這一設計也不會導致數據冗余和操作異常。
由此可以看出,并不一定要強行滿足范式的要求,對于1:N關系,當1的一邊合并到N的那邊后,N的那邊就不再滿足第二范式了,但是這種設計反而比較好!
對于M:N的關系,不能將M一邊或N一邊合并到另一邊去,這樣會導致不符合范式要求,同時導致操作異常和數據冗余。
對于1:1的關系,我們可以將左邊的1或者右邊的1合并到另一邊去,設計導致不符合范式要求,但是并不會導致操作異常和數據冗余。
結論
滿足范式要求的數據庫設計是結構清晰的,同時可避免數據冗余和操作異常。這并意味著不符合范式要求的設計一定是錯誤的,在數據庫表中存在1:1或1:N關系這種較特殊的情況下,合并導致的不符合范式要求反而是合理的。
在我們設計數據庫的時候,一定要時刻考慮范式的要求。
一、數據庫設計過程
數據庫技術是信息資源管理最有效的手段。數據庫設計是指對于一個給定的應用環境,構造最優的數據庫模式,建立數據庫及其應用系統,有效存儲數據,滿足用戶信息要求和處理要求。
數據庫設計中需求分析階段綜合各個用戶的應用需求(現實世界的需求),在概念設計階段形成獨立于機器特點、獨立于各個DBMS產品的概念模式(信息世界模型),用E-R圖來描述。在邏輯設計階段將E-R圖轉換成具體的數據庫產品支持的數據模型如關系模型,形成數據庫邏輯模式。然后根據用戶處理的要求,安全性的考慮,在基本表的基礎上再建立必要的視圖(VIEW)形成數據的外模式。在物理設計階段根據DBMS特點和處理的需要,進行物理存儲安排,設計索引,形成數據庫內模式。
1.?需求分析階段?
需求收集和分析,結果得到數據字典描述的數據需求(和數據流圖描述的處理需求)。?
需求分析的重點是調查、收集與分析用戶在數據管理中的信息要求、處理要求、安全性與完整性要求。
需求分析的方法:調查組織機構情況、調查各部門的業務活動情況、協助用戶明確對新系統的各種要求、確定新系統的邊界。?
常用的調查方法有:?跟班作業、開調查會、請專人介紹、詢問、設計調查表請用戶填寫、查閱記錄。
分析和表達用戶需求的方法主要包括自頂向下和自底向上兩類方法。自頂向下的結構化分析方法(Structured?Analysis,簡稱SA方法)從最上層的系統組織機構入手,采用逐層分解的方式分析系統,并把每一層用數據流圖和數據字典描述。
數據流圖表達了數據和處理過程的關系。系統中的數據則借助數據字典(Data?Dictionary,簡稱DD)來描述。
數據字典是各類數據描述的集合,它是關于數據庫中數據的描述,即元數據,而不是數據本身。數據字典通常包括數據項、數據結構、數據流、數據存儲和處理過程五個部分(至少應該包含每個字段的數據類型和在每個表內的主外鍵)。
數據項描述={數據項名,數據項含義說明,別名,數據類型,長度,?
取值范圍,取值含義,與其他數據項的邏輯關系}?
數據結構描述={數據結構名,含義說明,組成:{數據項或數據結構}}?
數據流描述={數據流名,說明,數據流來源,數據流去向,?
組成:{數據結構},平均流量,高峰期流量}?
數據存儲描述={數據存儲名,說明,編號,流入的數據流,流出的數據流,?
組成:{數據結構},數據量,存取方式}?
處理過程描述={處理過程名,說明,輸入:{數據流},輸出:{數據流},?
處理:{簡要說明}}?
2.?概念結構設計階段?
通過對用戶需求進行綜合、歸納與抽象,形成一個獨立于具體DBMS的概念模型,可以用E-R圖表示。?
概念模型用于信息世界的建模。概念模型不依賴于某一個DBMS支持的數據模型。概念模型可以轉換為計算機上某一DBMS支持的特定數據模型。?
概念模型特點:
(1)?具有較強的語義表達能力,能夠方便、直接地表達應用中的各種語義知識。?
(2)?應該簡單、清晰、易于用戶理解,是用戶與數據庫設計人員之間進行交流的語言。
概念模型設計的一種常用方法為IDEF1X方法,它就是把實體-聯系方法應用到語義數據模型中的一種語義模型化技術,用于建立系統信息模型。
????使用IDEF1X方法創建E-R模型的步驟如下所示:
2.1?第零步——初始化工程
這個階段的任務是從目的描述和范圍描述開始,確定建模目標,開發建模計劃,組織建模隊伍,收集源材料,制定約束和規范。收集源材料是這階段的重點。通過調查和觀察結果,業務流程,原有系統的輸入輸出,各種報表,收集原始數據,形成了基本數據資料表。
2.2?第一步——定義實體
實體集成員都有一個共同的特征和屬性集,可以從收集的源材料——基本數據資料表中直接或間接標識出大部分實體。根據源材料名字表中表示物的術語以及具有“代碼”結尾的術語,如客戶代碼、代理商代碼、產品代碼等將其名詞部分代表的實體標識出來,從而初步找出潛在的實體,形成初步實體表。
2.3?第二步——定義聯系
IDEF1X模型中只允許二元聯系,n元聯系必須定義為n個二元聯系。根據實際的業務需求和規則,使用實體聯系矩陣來標識實體間的二元關系,然后根據實際情況確定出連接關系的勢、關系名和說明,確定關系類型,是標識關系、非標識關系(強制的或可選的)還是非確定關系、分類關系。如果子實體的每個實例都需要通過和父實體的關系來標識,則為標識關系,否則為非標識關系。非標識關系中,如果每個子實體的實例都與而且只與一個父實體關聯,則為強制的,否則為非強制的。如果父實體與子實體代表的是同一現實對象,那么它們為分類關系。
2.4?第三步——定義碼
通過引入交叉實體除去上一階段產生的非確定關系,然后從非交叉實體和獨立實體開始標識侯選碼屬性,以便唯一識別每個實體的實例,再從侯選碼中確定主碼。為了確定主碼和關系的有效性,通過非空規則和非多值規則來保證,即一個實體實例的一個屬性不能是空值,也不能在同一個時刻有一個以上的值。找出誤認的確定關系,將實體進一步分解,最后構造出IDEF1X模型的鍵基視圖(KB圖)。
2.5?第四步——定義屬性
從源數據表中抽取說明性的名詞開發出屬性表,確定屬性的所有者。定義非主碼屬性,檢查屬性的非空及非多值規則。此外,還要檢查完全依賴函數規則和非傳遞依賴規則,保證一個非主碼屬性必須依賴于主碼、整個主碼、僅僅是主碼。以此得到了至少符合關系理論第三范式的改進的IDEF1X模型的全屬性視圖。
2.6?第五步——定義其他對象和規則
????定義屬性的數據類型、長度、精度、非空、缺省值、約束規則等。定義觸發器、存儲過程、視圖、角色、同義詞、序列等對象信息。
3.?邏輯結構設計階段?
????將概念結構轉換為某個DBMS所支持的數據模型(例如關系模型),并對其進行優化。設計邏輯結構應該選擇最適于描述與表達相應概念結構的數據模型,然后選擇最合適的DBMS。
將E-R圖轉換為關系模型實際上就是要將實體、實體的屬性和實體之間的聯系轉化為關系模式,這種轉換一般遵循如下原則:?
1)一個實體型轉換為一個關系模式。實體的屬性就是關系的屬性。實體的碼就是關系的碼。?
2)一個m:n聯系轉換為一個關系模式。與該聯系相連的各實體的碼以及聯系本身的屬性均轉換為關系的屬性。而關系的碼為各實體碼的組合。?
3)一個1:n聯系可以轉換為一個獨立的關系模式,也可以與n端對應的關系模式合并。如果轉換為一個獨立的關系模式,則與該聯系相連的各實體的碼以及聯系本身的屬性均轉換為關系的屬性,而關系的碼為n端實體的碼。?
4)一個1:1聯系可以轉換為一個獨立的關系模式,也可以與任意一端對應的關系模式合并。
5)三個或三個以上實體間的一個多元聯系轉換為一個關系模式。與該多元聯系相連的各實體的碼以及聯系本身的屬性均轉換為關系的屬性。而關系的碼為各實體碼的組合。??
6)同一實體集的實體間的聯系,即自聯系,也可按上述1:1、1:n和m:n三種情況分別處理。?
7)具有相同碼的關系模式可合并。?
為了進一步提高數據庫應用系統的性能,通常以規范化理論為指導,還應該適當地修改、調整數據模型的結構,這就是數據模型的優化。確定數據依賴。消除冗余的聯系。確定各關系模式分別屬于第幾范式。確定是否要對它們進行合并或分解。一般來說將關系分解為3NF的標準,即:
表內的每一個值都只能被表達一次。
??表內的每一行都應該被唯一的標識(有唯一鍵)。
表內不應該存儲依賴于其他鍵的非鍵信息。???
4.?數據庫物理設計階段?
為邏輯數據模型選取一個最適合應用環境的物理結構(包括存儲結構和存取方法)。根據DBMS特點和處理的需要,進行物理存儲安排,設計索引,形成數據庫內模式。
5.?數據庫實施階段?
運用DBMS提供的數據語言(例如SQL)及其宿主語言(例如C),根據邏輯設計和物理設計的結果建立數據庫,編制與調試應用程序,組織數據入庫,并進行試運行。?數據庫實施主要包括以下工作:用DDL定義數據庫結構、組織數據入庫?、編制與調試應用程序、數據庫試運行??
6.?數據庫運行和維護階段?
數據庫應用系統經過試運行后即可投入正式運行。在數據庫系統運行過程中必須不斷地對其進行評價、調整與修改。包括:數據庫的轉儲和恢復、數據庫的安全性、完整性控制、數據庫性能的監督、分析和改進、數據庫的重組織和重構造。
建模工具的使用
為加快數據庫設計速度,目前有很多數據庫輔助工具(CASE工具),如Rational公司的Rational?Rose,CA公司的Erwin和Bpwin,Sybase公司的PowerDesigner以及Oracle公司的Oracle?Designer等。
ERwin主要用來建立數據庫的概念模型和物理模型。它能用圖形化的方式,描述出實體、聯系及實體的屬性。ERwin支持IDEF1X方法。通過使用ERwin建模工具自動生成、更改和分析IDEF1X模型,不僅能得到優秀的業務功能和數據需求模型,而且可以實現從IDEF1X模型到數據庫物理設計的轉變。ERwin工具繪制的模型對應于邏輯模型和物理模型兩種。在邏輯模型中,IDEF1X工具箱可以方便地用圖形化的方式構建和繪制實體聯系及實體的屬性。在物理模型中,ERwin可以定義對應的表、列,并可針對各種數據庫管理系統自動轉換為適當的類型。
設計人員可根據需要選用相應的數據庫設計建模工具。例如需求分析完成之后,設計人員可以使用Erwin畫ER圖,將ER圖轉換為關系數據模型,生成數據庫結構;畫數據流圖,生成應用程序。
二、數據庫設計技巧
1.?設計數據庫之前(需求分析階段)
1)?理解客戶需求,詢問用戶如何看待未來需求變化。讓客戶解釋其需求,而且隨著開發的繼續,還要經常詢問客戶保證其需求仍然在開發的目的之中。
2)?了解企業業務可以在以后的開發階段節約大量的時間。
3)?重視輸入輸出。
在定義數據庫表和字段需求(輸入)時,首先應檢查現有的或者已經設計出的報表、查詢和視圖(輸出)以決定為了支持這些輸出哪些是必要的表和字段。
舉例:假如客戶需要一個報表按照郵政編碼排序、分段和求和,你要保證其中包括了單獨的郵政編碼字段而不要把郵政編碼糅進地址字段里。
4)?創建數據字典和ER?圖表
ER?圖表和數據字典可以讓任何了解數據庫的人都明確如何從數據庫中獲得數據。ER圖對表明表之間關系很有用,而數據字典則說明了每個字段的用途以及任何可能存在的別名。對SQL?表達式的文檔化來說這是完全必要的。
5)?定義標準的對象命名規范
數據庫各種對象的命名必須規范。
2.?表和字段的設計(數據庫邏輯設計)
表設計原則
1)?標準化和規范化
數據的標準化有助于消除數據庫中的數據冗余。標準化有好幾種形式,但Third?Normal?Form(3NF)通常被認為在性能、擴展性和數據完整性方面達到了最好平衡。簡單來說,遵守3NF?標準的數據庫的表設計原則是:“One?Fact?in?One?Place”即某個表只包括其本身基本的屬性,當不是它們本身所具有的屬性時需進行分解。表之間的關系通過外鍵相連接。它具有以下特點:有一組表專門存放通過鍵連接起來的關聯數據。
舉例:某個存放客戶及其有關定單的3NF?數據庫就可能有兩個表:Customer?和Order。Order?表不包含定單關聯客戶的任何信息,但表內會存放一個鍵值,該鍵指向Customer?表里包含該客戶信息的那一行。
事實上,為了效率的緣故,對表不進行標準化有時也是必要的。
2)?數據驅動
采用數據驅動而非硬編碼的方式,許多策略變更和維護都會方便得多,大大增強系統的靈活性和擴展性。
舉例,假如用戶界面要訪問外部數據源(文件、XML?文檔、其他數據庫等),不妨把相應的連接和路徑信息存儲在用戶界面支持表里。還有,如果用戶界面執行工作流之類的任務(發送郵件、打印信箋、修改記錄狀態等),那么產生工作流的數據也可以存放在數據庫里。角色權限管理也可以通過數據驅動來完成。事實上,如果過程是數據驅動的,你就可以把相當大的責任推給用戶,由用戶來維護自己的工作流過程。
3)?考慮各種變化
在設計數據庫的時候考慮到哪些數據字段將來可能會發生變更。
舉例,姓氏就是如此(注意是西方人的姓氏,比如女性結婚后從夫姓等)。所以,在建立系統存儲客戶信息時,在單獨的一個數據表里存儲姓氏字段,而且還附加起始日和終止日等字段,這樣就可以跟蹤這一數據條目的變化。
字段設計原則
4)?每個表中都應該添加的3?個有用的字段
??dRecordCreationDate,在VB?下默認是Now(),而在SQL?Server?下默認為GETDATE()
??sRecordCreator,在SQL?Server?下默認為NOT?NULL?DEFAULT?USER
??nRecordVersion,記錄的版本標記;有助于準確說明記錄中出現null?數據或者丟失數據的原因
5)?對地址和電話采用多個字段
描述街道地址就短短一行記錄是不夠的。Address_Line1、Address_Line2?和Address_Line3?可以提供更大的靈活性。還有,電話號碼和郵件地址最好擁有自己的數據表,其間具有自身的類型和標記類別。
6)?使用角色實體定義屬于某類別的列
在需要對屬于特定類別或者具有特定角色的事物做定義時,可以用角色實體來創建特定的時間關聯關系,從而可以實現自我文檔化。
舉例:用PERSON?實體和PERSON_TYPE?實體來描述人員。比方說,當John?Smith,?Engineer?提升為John?Smith,?Director?乃至最后爬到John?Smith,?CIO?的高位,而所有你要做的不過是改變兩個表PERSON?和PERSON_TYPE?之間關系的鍵值,同時增加一個日期/時間字段來知道變化是何時發生的。這樣,你的PERSON_TYPE?表就包含了所有PERSON?的可能類型,比如Associate、Engineer、Director、CIO?或者CEO?等。還有個替代辦法就是改變PERSON?記錄來反映新頭銜的變化,不過這樣一來在時間上無法跟蹤個人所處位置的具體時間。
7)?選擇數字類型和文本類型盡量充足
在SQL?中使用smallint?和tinyint?類型要特別小心。比如,假如想看看月銷售總額,總額字段類型是smallint,那么,如果總額超過了$32,767?就不能進行計算操作了。
而ID?類型的文本字段,比如客戶ID?或定單號等等都應該設置得比一般想象更大。假設客戶ID?為10?位數長。那你應該把數據庫表字段的長度設為12?或者13?個字符長。但這額外占據的空間卻無需將來重構整個數據庫就可以實現數據庫規模的增長了。
8)?增加刪除標記字段
在表中包含一個“刪除標記”字段,這樣就可以把行標記為刪除。在關系數據庫里不要單獨刪除某一行;最好采用清除數據程序而且要仔細維護索引整體性。?
3.?選擇鍵和索引(數據庫邏輯設計)
鍵選擇原則:
1)?鍵設計4?原則
??為關聯字段創建外鍵。
??所有的鍵都必須唯一。
??避免使用復合鍵。
??外鍵總是關聯唯一的鍵字段。
2)?使用系統生成的主鍵
設計數據庫的時候采用系統生成的鍵作為主鍵,那么實際控制了數據庫的索引完整性。這樣,數據庫和非人工機制就有效地控制了對存儲數據中每一行的訪問。采用系統生成鍵作為主鍵還有一個優點:當擁有一致的鍵結構時,找到邏輯缺陷很容易。
3)?不要用用戶的鍵(不讓主鍵具有可更新性)
在確定采用什么字段作為表的鍵的時候,可一定要小心用戶將要編輯的字段。通常的情況下不要選擇用戶可編輯的字段作為鍵。
4)?可選鍵有時可做主鍵
把可選鍵進一步用做主鍵,可以擁有建立強大索引的能力。
索引使用原則:
索引是從數據庫中獲取數據的最高效方式之一。95%的數據庫性能問題都可以采用索引技術得到解決。
1) 邏輯主鍵使用唯一的成組索引,對系統鍵(作為存儲過程)采用唯一的非成組索引,對任何外鍵列采用非成組索引??紤]數據庫的空間有多大,表如何進行訪問,還有這些訪問是否主要用作讀寫。
2) 大多數數據庫都索引自動創建的主鍵字段,但是可別忘了索引外鍵,它們也是經常使用的鍵,比如運行查詢顯示主表和所有關聯表的某條記錄就用得上。
3) 不要索引memo/note?字段,不要索引大型字段(有很多字符),這樣作會讓索引占用太多的存儲空間。
4) 不要索引常用的小型表
不要為小型數據表設置任何鍵,假如它們經常有插入和刪除操作就更別這樣作了。對這些插入和刪除操作的索引維護可能比掃描表空間消耗更多的時間。
4.?數據完整性設計(數據庫邏輯設計)
1) 完整性實現機制:
實體完整性:主鍵
參照完整性:
父表中刪除數據:級聯刪除;受限刪除;置空值
父表中插入數據:受限插入;遞歸插入
父表中更新數據:級聯更新;受限更新;置空值
DBMS對參照完整性可以有兩種方法實現:外鍵實現機制(約束規則)和觸發器實現機制
用戶定義完整性:
????NOT?NULL;CHECK;觸發器
2)?用約束而非商務規則強制數據完整性
采用數據庫系統實現數據的完整性。這不但包括通過標準化實現的完整性而且還包括數據的功能性。在寫數據的時候還可以增加觸發器來保證數據的正確性。不要依賴于商務層保證數據完整性;它不能保證表之間(外鍵)的完整性所以不能強加于其他完整性規則之上。
3)?強制指示完整性
在有害數據進入數據庫之前將其剔除。激活數據庫系統的指示完整性特性。這樣可以保持數據的清潔而能迫使開發人員投入更多的時間處理錯誤條件。
4)?使用查找控制數據完整性
控制數據完整性的最佳方式就是限制用戶的選擇。只要有可能都應該提供給用戶一個清晰的價值列表供其選擇。這樣將減少鍵入代碼的錯誤和誤解同時提供數據的一致性。某些公共數據特別適合查找:國家代碼、狀態代碼等。
5)?采用視圖
為了在數據庫和應用程序代碼之間提供另一層抽象,可以為應用程序建立專門的視圖而不必非要應用程序直接訪問數據表。這樣做還等于在處理數據庫變更時給你提供了更多的自由。
5.?其他設計技巧
1)?避免使用觸發器
觸發器的功能通常可以用其他方式實現。在調試程序時觸發器可能成為干擾。假如你確實需要采用觸發器,你最好集中對它文檔化。
2)?使用常用英語(或者其他任何語言)而不要使用編碼
在創建下拉菜單、列表、報表時最好按照英語名排序。假如需要編碼,可以在編碼旁附上用戶知道的英語。
3)?保存常用信息
讓一個表專門存放一般數據庫信息非常有用。在這個表里存放數據庫當前版本、最近檢查/修復(對Access)、關聯設計文檔的名稱、客戶等信息。這樣可以實現一種簡單機制跟蹤數據庫,當客戶抱怨他們的數據庫沒有達到希望的要求而與你聯系時,這樣做對非客戶機/服務器環境特別有用。
4)?包含版本機制
在數據庫中引入版本控制機制來確定使用中的數據庫的版本。時間一長,用戶的需求總是會改變的。最終可能會要求修改數據庫結構。把版本信息直接存放到數據庫中更為方便。?
5)?編制文檔
對所有的快捷方式、命名規范、限制和函數都要編制文檔。
采用給表、列、觸發器等加注釋的數據庫工具。對開發、支持和跟蹤修改非常有用。
對數據庫文檔化,或者在數據庫自身的內部或者單獨建立文檔。這樣,當過了一年多時間后再回過頭來做第2?個版本,犯錯的機會將大大減少。
6)?測試、測試、反復測試
建立或者修訂數據庫之后,必須用用戶新輸入的數據測試數據字段。最重要的是,讓用戶進行測試并且同用戶一道保證選擇的數據類型滿足商業要求。測試需要在把新數據庫投入實際服務之前完成。
7)?檢查設計
在開發期間檢查數據庫設計的常用技術是通過其所支持的應用程序原型檢查數據庫。換句話說,針對每一種最終表達數據的原型應用,保證你檢查了數據模型并且查看如何取出數據。
三、數據庫命名規范
1.?實體(表)的命名
1)?表以名詞或名詞短語命名,確定表名是采用復數還是單數形式,此外給表的別名定義簡單規則(比方說,如果表名是一個單詞,別名就取單詞的前4?個字母;如果表名是兩個單詞,就各取兩個單詞的前兩個字母組成4?個字母長的別名;如果表的名字由3?個單詞組成,從頭兩個單詞中各取一個然后從最后一個單詞中再取出兩個字母,結果還是組成4?字母長的別名,其余依次類推)
對工作用表來說,表名可以加上前綴WORK_?后面附上采用該表的應用程序的名字。在命名過程當中,根據語義拼湊縮寫即可。注意,由于ORCLE會將字段名稱統一成大寫或者小寫中的一種,所以要求加上下劃線。
舉例:
定義的縮寫?Sales:?Sal?銷售;
Order:?Ord?訂單;
Detail:?Dtl?明細;
則銷售訂單明細表命名為:Sal_Ord_Dtl;
2)?如果表或者是字段的名稱僅有一個單詞,那么建議不使用縮寫,而是用完整的單詞。
舉例:
定義的縮寫?Material?Ma?物品;
物品表名為:Material,?而不是?Ma.
但是字段物品編碼則是:Ma_ID;而不是Material_ID
3)?所有的存儲值列表的表前面加上前綴Z
目的是將這些值列表類排序在數據庫最后。
4)?所有的冗余類的命名(主要是累計表)前面加上前綴X
冗余類是為了提高數據庫效率,非規范化數據庫的時候加入的字段或者表
5)?關聯類通過用下劃線連接兩個基本類之后,再加前綴R的方式命名,后面按照字母順序羅列兩個表名或者表名的縮寫。
關聯表用于保存多對多關系。
如果被關聯的表名大于10個字母,必須將原來的表名的進行縮寫。如果沒有其他原因,建議都使用縮寫。
舉例:表Object與自身存在多對多的關系,則保存多對多關系的表命名為:R_Object;
表?Depart和Employee;存在多對多的關系;則關聯表命名為R_Dept_Emp
2.?屬性(列)的命名
1)?采用有意義的列名,表內的列要針對鍵采用一整套設計規則。每一個表都將有一個自動ID作為主健,邏輯上的主健作為第一組候選主健來定義,如果是數據庫自動生成的編碼,統一命名為:ID;如果是自定義的邏輯上的編碼則用縮寫加“ID”的方法命名。如果鍵是數字類型,你可以用_NO?作為后綴;如果是字符類型則可以采用_CODE?后綴。對列名應該采用標準的前綴和后綴。
舉例:銷售訂單的編號字段命名:Sal_Ord_ID;如果還存在一個數據庫生成的自動編號,則命名為:ID。
2)?所有的屬性加上有關類型的后綴,注意,如果還需要其它的后綴,都放在類型后綴之前。
注:?數據類型是文本的字段,類型后綴TX可以不寫。有些類型比較明顯的字段,可以不寫類型后綴。
3)?采用前綴命名
給每個表的列名都采用統一的前綴,那么在編寫SQL表達式的時候會得到大大的簡化。這樣做也確實有缺點,比如破壞了自動表連接工具的作用,后者把公共列名同某些數據庫聯系起來。
3.?視圖的命名
1)?視圖以V作為前綴,其他命名規則和表的命名類似;
2)?命名應盡量體現各視圖的功能。
4.?觸發器的命名
觸發器以TR作為前綴,觸發器名為相應的表名加上后綴,Insert觸發器加'_I',Delete觸發器加'_D',Update觸發器加'_U',如:TR_Customer_I,TR_Customer_D,TR_Customer_U。
5.?存儲過程名
存儲過程應以'UP_'開頭,和系統的存儲過程區分,后續部分主要以動賓形式構成,并用下劃線分割各個組成部分。如增加代理商的帳戶的存儲過程為'UP_Ins_Agent_Account'。
6.?變量名
變量名采用小寫,若屬于詞組形式,用下劃線分隔每個單詞,如@my_err_no。
7.?命名中其他注意事項
1)??以上命名都不得超過30個字符的系統限制。變量名的長度限制為29(不包括標識字符@)。
2)??數據對象、變量的命名都采用英文字符,禁止使用中文命名。絕對不要在對象名的字符之間留空格。
3)?小心保留詞,要保證你的字段名沒有和保留詞、數據庫系統或者常用訪問方法沖突
5) 保持字段名和類型的一致性,在命名字段并為其指定數據類型的時候一定要保證一致性。假如數據類型在一個表里是整數,那在另一個表里可就別變成字符型了。
工具:
????Eclipse3.1、MyEclipse4.03、Tomcat5.5.9、Properties Editor插件、MySql4.1.13
?
新建工程:名稱為 login
?
創建Struts框架
?
創建 index.jsp,增加一鏈接指向 login.jsp
?
按下Ctrl + N,創建 login.jsp、LoginAction,使用MyEclipse的向導就可以了,記得選對正確的版本
?
在ActionForm配置頁中選擇類型為動態Form,并繼承于DynaValidatorForm,新增兩個屬性:username、password,在創建jsp文件打上鉤,將路徑改為/login.jsp,然后下一步,改LoginAction的Input source改為/login.jsp,點擊完成
?
按下Ctrl + N 創建一個forwards,記得選對正確的版本
name 輸入 indexGo
路徑選擇 /index.jsp
?
配置validator
先添加Struts插件,使用向導
Plugin class : org.apache.struts.validator.ValidatorPlugIn
Property : pathnames
Value : /WEB-INF/validator-rules.xml,/WEB-INF/validation.xml
這里需要兩個xml文件
現在創建“
validation.xml
”?文件
?
在這里說明一點,我使用MyEclipse創建的Struts框架中缺少了validator-rules.xml文件,需要動拷貝到WEB-INF目錄中
?
文件內容如下:
<
form-validation
>
?
<
formset
>
??
<
form?
name
="loginForm"
>
???
<
field?
property
="username"
?depends
="required"
>
????
<
arg0?
key
="prompt.username"
?
/>
???
</
field
>
???
<
field?
property
="password"
?depends
="required"
>
????
<
arg0?
key
="prompt.password"
?
/>
???
</
field
>
??
</
form
>
?
</
formset
>
</
form-validation
>
?
編輯資源文件“ApplicationResources.properties”
增加以下內容
?
prompt.username=User Name
prompt.password=User Password
errors.required={0} is required.
?
再創建中文件資源文件“ApplicationResources_zh_CN.properties”
增加以下內容
prompt.username=用戶名稱
prompt.password=登錄密碼
errors.required={0} 必需填寫!
?
修改struts-config.xml文件
在以下位置增加綠色字體部份
<action-mappings >
??? <action
????? attribute="loginForm"
????? input="/login.jsp"
????? name="loginForm"
????? path="/login"
????? scope="request"
????? validate="true"
????? type="com.test.struts.action.LoginAction" />
? </action-mappings>
?
這里說明提交的數據必需經過驗證,而驗證則是通過validator框架進行的。
?
修改LoginAction.java文件的execute方法,內容如下
public ActionForward execute(
??ActionMapping mapping,
??ActionForm form,
??HttpServletRequest request,
??HttpServletResponse response) {
??DynaValidatorForm loginForm = (DynaValidatorForm) form;
??String username=loginForm.getString("username");
??String password=loginForm.getString("password");
??if(username.equals("test")||password.equals("test")){
???return mapping.findForward("indexGo");
??}else{
???return mapping.getInputForward();
??}
?}
?
現在再修改一下login.jsp
增加以下綠色字體部份
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
?
其中charset=UTF-8 是使用UTF-8的字符編碼,這也是為了支持國際化而使用的。
?
好了,現在可以啟動Tomcat進行測試了
?
如果不輸入任何數據而直接提交表單的話就可以看到效果了。
?
好了,如果沒有什么問題的話就繼續往下看吧,如果有問題的話就得往上看了^_^
?
現在創建Spring框架了,在這里我將Spring所有的包全部加載進去,因為我還不知道具體用到哪些類,全部加進去方便點
?
單選框選第二個,這樣的話所有的類庫和標簽等都將拷貝到項目中去,這樣方便以后的布署
下一步后是創建配置文件,將文件放到“WebRoot/WEB-INF”目錄下,文件名稱為“applicationContext.xml”
?
?
配置struts-config.xml文件,添加(spring)的插件
?
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
??? <set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml" />
? </plug-in>
?
?
修改LoginAction配置
?
原:
<action
????? attribute="loginForm"
????? input="/login.jsp"
????? name="loginForm"
????? path="/login"
????? scope="request"
????? validate="true"
????? type="com.test.struts.action.LoginAction" />
?
? </action-mappings>
?
改為:
<action
????? attribute="loginForm"
????? input="/login.jsp"
????? name="loginForm"
????? path="/login"
????? scope="request"
????? validate="true"
????? type="org.springframework.web.struts.DelegatingActionProxy" />
? </action-mappings>
?
?
綠色字體部份為修改內容
這里將使用spring的代理器來對Action進行控制
?
當提交到/login.do是將控制權交給了spring,然后由spring來決定是否轉回到struts的Action
?
現在來配置spring
?
?
<beans>
?<bean name="/login" class="com.test.struts.action.LoginAction" singleton="false"></bean>
</beans>
?
綠色字體是關于轉交控制權的配置內容
?
屬性singleton="false",指明了Action 的實例獲取方式為每次重新創建。解決了Struts中令人詬病的線程安全問題(Struts中,由一個Action實例處理所有的請求,這就導致了類公用資源在并發請求中的線程同步問題。)(摘自spring開發指南)
?
這時如果你要進行測試也是可以的,不過為了省點時間就不進行測試了。
?
建立數據庫在 這里我使用的是mysql4.1.13
?
CREATE TABLE `user` (
? `ID` int(11) NOT NULL auto_increment,
? `USERNAME` varchar(50) NOT NULL default '',
? `PASSWORD` varchar(50) NOT NULL default '',
? PRIMARY KEY? (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
?
添加記錄 insert into user (USERNAME,PASSWORD) values ('test','test')
?
創建Hibernate框架
在配置界面中配置數據庫的連接部份,重要的是點擊鏈接將jdbc拷貝到lib目錄中
使用MyEclipse的數據Database Explorer工具創建User.hmb.xml、AbstractUser.java、User.java映射文件
創建完成后可以將自動生成的hibernate.cfg.xml刪除
?
創建UserDAO.java、UserDAOImp.java
UserDAO.java
public interface UserDAO {
?? public abstract boolean isValidUser(String username, String password);
}
?
UserDAOImp.java
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.test.Hibernate.SessionFactory;
public class UserDAOImp extends HibernateDaoSupport implements UserDAO {
??? private SessionFactory sessionFactory;
??? private static String hql = "from User u where u.username=? ";
??? public boolean isValidUser(String username, String password) {
?????? List userList = this.getHibernateTemplate().find(hql, username);
?????? if (userList.size() > 0) {
?????????? return true;
?????? }
?????? return false;
??? }
}
?
修改LoginAction.java文件,使用userDao的方法來進行用戶驗證
package com.test.struts.action;
?
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
?
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.validator.DynaValidatorForm;
?
import com.test.UserDAO;
public class LoginAction extends Action {
?private UserDAO userDAO;
?
?public UserDAO getUserDAO() {
??return userDAO;
?}
?
?public void setUserDAO(UserDAO userDAO) {
??this.userDAO = userDAO;
?}
?
?public ActionForward execute(ActionMapping mapping, ActionForm form,
???HttpServletRequest request, HttpServletResponse response) {
??DynaValidatorForm loginForm = (DynaValidatorForm) form;
??// TODO Auto-generated method stub
??String username = (String) loginForm.get("username");
??String password = (String) loginForm.get("password");
??loginForm.set("password", null);
??if (userDAO.isValidUser(username,password)) {
???return mapping.findForward("indexGo");
??} else {
???return mapping.getInputForward();
??}
?}
}
綠色字體為修改部份
?
現在剩下最后的spring配置了
<?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="dataSource"?class="org.apache.commons.dbcp.BasicDataSource"?destroy-method="close">
??<property?name="driverClassName">
???<value>com.mysql.jdbc.Driver</value>
??</property>
??<property?name="url">
???<value>jdbc:mysql://localhost/test</value>
??</property>
??<property?name="username">
???<value>root</value>
??</property>
??<property?name="password">
???<value>root</value>
??</property>
?</bean>

?<!--?配置sessionFactory,?注意這里引入的包的不同??-->
?<bean?id="sessionFactory"?class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
??<property?name="dataSource">
???<ref?local="dataSource"?/>
??</property>
??<property?name="mappingResources">
???<list>
????<value>com/test/Hibernate/User.hbm.xml</value>
???</list>
??</property>
??<property?name="hibernateProperties">
???<props>
????<prop?key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
????<prop?key="hibernate.show_sql">true</prop>
???</props>
??</property>
?</bean>

?<bean?id="transactionManager"?class="org.springframework.orm.hibernate3.HibernateTransactionManager">
??<property?name="sessionFactory">
???<ref?local="sessionFactory"?/>
??</property>
?</bean>

?<bean?id="userDAO"?class="com.test.UserDAOImp">
??<property?name="sessionFactory">
???<ref?local="sessionFactory"?/>
??</property>
?</bean>

?<bean?id="userDAOProxy"?class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
??<property?name="transactionManager">
???<ref?bean="transactionManager"?/>
??</property>
??<property?name="target">
???<ref?local="userDAO"?/>
??</property>
??<property?name="transactionAttributes">
???<props>
????<prop?key="insert*">PROPAGATION_REQUIRED</prop>
????<prop?key="get*">PROPAGATION_REQUIRED,readOnly</prop>
????<prop?key="is*">PROPAGATION_REQUIRED,readOnly</prop>
???</props>
??</property>
?</bean>

?<bean?name="/login"?class="com.test.struts.action.LoginAction"?singleton="false">
??<property?name="userDAO">
???<ref?bean="userDAOProxy"?/>
??</property>
?</bean>
</beans>?
現在可以進行測試了!
?
在編寫代碼有配置內容時一定要注意 hibernate 和 hibernate3 ,這兩個包的名字就只差一個字,千萬不要有錯,否則找錯誤可是很難的。
Struts應用采用兩個基于XML的配置文件來配置,分別是web.xml和struts-cofig.xml文件.web.xml文件是配置所有web應用的而struts-config.xml文件是struts專用的配置文件,事實上也是可以根據需要給這個配置文件起其他名稱的.
Web應用的發布描述文件:web應用發布描述文件可以在應用開著者,發布者和組裝者之間傳遞配置信息,Web容器在啟動的時候從該文件中讀取配置信息,根據它來裝載和配置web應用.文檔類型定義DTD對XML文檔的格式做了定義,DTD吧XML文檔劃分為元素,屬性,實體每一種XML文檔都有獨自的DTD文件.可以從網上下載.<web-app>元素是web.xml的根元素,其他元素必須嵌入在<web-app>元素之內.要注意的是子元素也是有順序的比如必須是首先<servlet>,然后<servlet-mapping>最后<taglib>.
為Struts應用配置Web.xml文件:首先最重要的一步是配置ActionServlet,這個用<servlet>標簽的servlet-name屬性起一個名字叫action,然后用servlet-class屬性指定ActionServlet的類.然后用<servlet-mapping>標簽的servlet-name屬性指定action,在用url-pattern指定接收范圍是*.do的請求.不管應用中包含了多少子應用,都只需要配置一個ActionServlet,類來出來應用中的不同的功能,其實者就是不必要的,因為Servlet本身就是多線程的,而且目前Struts只允許配置一個ActionServlet.聲明ActionServlet的初始化參數:<servlet>的<init-param>子元素用來配置Servlet的初始化參數.param-name設置config參數名.param-value設置struts-config.xml的路徑參數值.
配置歡迎使用清單:如果客戶訪問Web的時候值是訪問了WEB應用的根目錄URL.沒有具體的指定文件,Web會自動調用Web的歡迎文件.<welcome-file-list>元素來配置的.通過其中的<welcome-file>歡迎頁面</welcome-file>來配置.
配置錯誤處理:盡管Struts框架功能強大的錯誤處理機制,但是不能保證處理所有的錯誤或者異常.當錯誤發生時,如果框架不能處理這種錯誤,把錯誤拋棄給Web容器,在默認的情況下web容器會想客戶端返回錯誤信息.如果想避免讓客戶看到原始的錯誤信息,可以在Web應用發布描述文件中配置<error-page>元素.通過<error-code>404來定義錯誤的類型.然后通過<location>要處理錯誤的JSP頁面來對錯誤進行處理.還可以用<exception-type>來設置異常,然后通過<location>來處理異常的JSP頁面來處理異常.
配置Struts標簽庫:這個就和以前學到的JSP自定義標簽類似,配置元素為<taglib>來配置.<taglib-uri>這個指定標簽庫的uri,類似起一個名稱.<taglib-location>這個是標簽庫的位置也就是實際所在的路徑.通過這樣的方法引入一個標簽庫,然后在前臺JSP頁面就可以通過自己定義的URI來調用標簽.
Struts配置文件:struts-config.xml文件.首先研討一下org.apache.struts.config包,在struts應用啟動的時候會把Struts配置文件信息讀取到內存中,并把它們存放在config包中相關的JavaBean類的實例中.包中的每一個類都和struts配置文件中特定的配置元素對應,ModuleConfig在Struts框架中扮演了十分重要的角色,它是整個config包的核心,在Struts運行時來存放整個應用的配置信息.如果有多個子應用都會有一個ModuleConfig對象,它和Struts文件根元素的<struts-config>對應.根元素中包含<form-bean><action><forward>等元素.
<struts-config>元素:時Struts配置文件的根元素,和它對應的配置類ModuleConfig類,<struts-config>元素有8個子元素.他們的DTD定義是data-sources?form-bean? global-exception?global-forwards?action-mapping?controller?message-resources?plug-in*在Struts配置文件中,必須按照DTD指定的先手順序來配置<struts-config>元素的各個子元素,如果顛倒了這些子元素的順序,會產生錯誤.
<data-sources>元素:用來配置應用所需要的數據源,數據源負責創建和特定的數據庫的連接.許多數據源采用連接池的機制實現.以便提高數據庫訪問的性能.JAVA語言提供了javax.sql.DataSource接口,所有的數據源都必須實現這個接口.許多應用服務器和Web服務器都提供了數據源組件.很多數據庫廠商也提供了數據源的實現.<data-sources>元素包含多個<data-source>子元素永遠配置特定的數據源.他們可以包含多個<set-property>子元素用于設置數據源的各種屬性.配置了數據源以后,就可以在Action類中訪問數據源,在Action中定義了getDataSource(HttpRequest)方法,用于獲取數據源對象的引用.然后可以利用DataSource對象調用getConnection獲取一個連接對象對數據庫進行操作.在配置文件中聲明多個數據源的時候需要為每一個數據源分配唯一的Key值,通過這個來表示特定的數據源.獲取特定的數據源的時候可以用dataSource = getDataSource(reqeust,”A”);
<form-beans>元素:用來配置多個ActionForm,包含一個或者N個<form-bean>子元素.每個<form-bean>元素都包含多個屬性.className指定和<form-bean>匹配的類.name指定該ActionForm的唯一標識符,這個屬性是必須的以后作為引用使用.type指定ActionForm類的完整類名,這個屬性也是必須的.注意包名也要加上.<form-property>是指定動態的Form的元素,以后會深入了解.
<global-exception>元素:用于配置異常處理,元素可以包含一個或者多個<exception>元素,用來設置JAVA異常和異常處理類ExceptionHandler之間的映射.className指定和元素對應的配置類,默認的不用動.handler指定異常處理類默認是ExceptionHandler.key指定在本地資源文件中異常的消息Key,path指定當前異常發生的時候轉發的路徑.scope指定ActionMessages實例存放的范圍.type指定需要處理異常類的名字,必須的.bundle指定Resource Bundle.
<global-forwards>元素:用來聲明全局轉發,元素可以有一個或者N個<forward>元素組成,用于把一個邏輯名映射到特定的URL,通過這種方法Action類或者JSP頁面無需指定URL,只要指定邏輯名稱就可以實現請求轉發或者重定向.這樣可以減少控制組件和視圖的聚合.易于維護.className對應的配置類.contextRelative如果為true表示當path屬性以/開頭的時候,給出的是對應的上下文URL默認是false.name轉發路徑的邏輯名,必須寫.path轉發或者重定向的URL,必須寫必須是以/開頭.redirect設置為true的時候表示執行重定向操作,此項為false的時候,表示執行請求轉發操作.重定向與請求轉發的區別以后就是重定向是把請求生成應答給客戶端然后在重新發送給定向的URL,瀏覽器地址欄會有顯示.而轉發就是直接把請求轉發給本應用的另一個文件,不生成應答所以客戶端IE沒顯示.
<action-mapping>元素:包含一個或者N個<action>元素,描述了從特定的請求路徑到響應的Action的映射.在<action>元素中可以包含多個<exception>和<forward>子元素,他們分別配置局部異常處理和局部轉發.attribute設置Action關聯的ActionForm在request或者session范圍內的key.就是在request或者session共享內的名稱.className對應配置元素的類.默認的是ActionMapping.forward指定轉發URL路徑include指定包含URL路徑.input指定包含表單的URL,當表單驗證失敗的時候發送的URL.name,指定和該Action關聯的Form名字.該名字必須是在form-bean中定義過的,可寫可不寫.path必須/開頭的方位Action的路徑.parameter指定Action配置參數.在Action的execute()方法中可以調用ActionMapping的getParameter()方法來讀取匹配的參數.roles指定允許調用該Action的安全角色,多個角色之間逗號格開.scope指定Form的存在范圍.默認是session.tyep指定Action的完整類名.unknown如果是true表示可以處理用戶發出的所有的無效的ActionURL默認是false.validate指定是否調用ActionForm的validate方法.
<controller>元素:用于配置ActionServlet.buffreSize指定上載文件的輸入緩沖大小.該屬性為可選默認4096.className指定元素對應的配置類,ControllerConfig.然后是contentType指定響應結果內容類型和字符編碼,該屬性為可選,默認是text/html如果在Action或者JSP網頁也設置了類型內容,會覆蓋這個.locale指定是否把Locale對象保存到當前用戶的session中默認false.tempDir指定處理文件上載的臨時工作目錄.nochache如果是true在響應結果中加入特定的頭參數.
<message-resources>元素:用來配置Resource Bundle.用于存放本地文本消息文件.className元素對應的配置類.MessageResourcesConfig.factory指定消息的工廠類.key指定文件存放的Servlet對象中采用的屬性Key.null指定如何處理未知消息.parameter指定消息的文件名.
<plug-in>元素:用于配置Struts插件.
配置多應用模塊:所有的子應用都可以共享同一個ActionServlet實例,但是每個子應用都有單獨的配置文件.把應用劃分為多個子應用模塊.首先為每個應用創建單獨的Struts配置文件,在web.xml的ActionServlet配置代碼中添加幾個子應用信息.采用<forward>元素來實現應用之間的切換.
Digester組件:是一個Apache的另一個開源代碼項目.當Struts被初始化的時候,首先會讀取并解析配置文件,框架采用Digester組件來且西配置文件.然后創建config包中的對象.者對象用于存放配置信息.
其實配置文件不難,只要都理其中的原理就OK了.真正實際的項目開發中,采用的工具例如Eclipse系列,提供了相應的插件,在創建一個Struts工程的時候配置文件的標簽都是自動生成的,而我們只需要往里面填寫屬性就OK了. (網友們的支持,是我繼續寫技術文章的動力!)
STEP 1:下載和安裝
首先在Subversion的官方網站去下載windows安裝包,最新版是1.3.1,可惜在項目樹上只更新到了1.3.0的二進制包。
下載后安裝在本地機器上,這里注意的是最好將安裝目錄指定為純英文名目錄,安裝在中文目錄下天知道哪天會冒出一個讓你想破頭也想不出的錯誤來。
下載TortoiseSVN進行本地安裝,我安裝的是最新的1.3.2 for svn 1.3.0,這是一個將SVN集成到windows shell中的GUI管理工具,推薦使用。
STEP 2:創建儲存庫
安裝完TortoiseSVN后提示要重啟機器,其實啟不啟都可以正常使用了,首先創建SVN儲存庫(repository),可以選擇命令行方式或者通過TortoiseSVN插件進行GUI操作,命令行運行如下:
svnadmin create E:\svn\repository
e:\svn\repository就是我指定的儲存庫目錄,如果用GUI方式,可以在這個目錄下點擊右鍵選擇[TotoiseSVN]->[Create Repository href...]進行創建,版本庫模式指定為默認的即可。
repository創建完畢后會在目錄下生成若干個文件和文件夾,dav目錄是提供給Apache與mod_dav_svn使用的目錄,讓它們存儲內部 數據;db目錄就是所有版本控制的數據文件;hooks目錄放置hook腳本文件的目錄;locks用來放置Subversion文件庫鎖定數據的目錄, 用來追蹤存取文件庫的客戶端;format文件是一個文本文件,里面只放了一個整數,表示當前文件庫配置的版本號;
STEP 3:配置
打開/conf/目錄,打開svnserve.conf找到一下兩句:
# [general]
# password-db = passwd
去之每行開頭的#,其中第二行是指定身份驗證的文件名,即passwd文件
同樣打開passwd文件,將
# [users]
# harry = harryssecret
# sally = sallyssecret
這幾行的開頭#字符去掉,這是設置用戶,一行一個,存儲格式為“用戶名 = 密碼”,如可插入一行:admin = admin888,即為系統添加一個用戶名為admin,密碼為admin888的用戶
STEP 4:運行SVN服務
在命令行執行
svnserve --daemon --root E:\svn\repository
服務啟動,--daemon可簡寫為-d,--root可簡寫為-r,可以建立一個批處理文件并放在windows啟動組中便于開機就運行SVN服務,或者在這個地址
http://clanlib.org/~mbn/svnservice/下載那個
svnservice.exe文件,拷貝到E:\svn\bin目錄下,再從命令行下執行:
svnservice -install --daemon --root "E:\svn\Repository"
sc config svnservice start= auto
net start svnservice
此文件會將SVN變成windows系統的一個服務,并默認為自啟動,注意:執行第三句時確保前面以命令行方式運行的SVN服務已經停止,如果沒停止可在其窗口中按Ctrl+C中止運行。
STEP 5:創建項目版本樹
確定SVN服務(命令行或windows服務)運行后,在你需要導入儲存庫的目錄下單擊右鍵選擇[TortoiseSVN]-> [Import...],在彈開的窗口的URL框中輸入 "svn://localhost/myproject" 點擊 "OK" 執行導入,如果沒有報錯,數 據就全部加入SVN儲存庫目錄樹上了。用命令行也可以完成這些操作,這需要你在系統變量中新建一個“SVN_EDITOR”的系統變量,變量值為本地的一 個文本編輯器執行文件路徑,一般指到windows的記事本上就行了 "c:\windows\notepad.exe" ,然后新開一個CMD窗口,執行
svn mkdir svn://localhost/myproject
隨即關閉記事本打開的log文件窗口后按"c"鍵繼續后生成項目樹。一般情況,我們在創建文件根路徑后應該在創建三個目錄:branches、tags、trunk,這三個目錄是Subversion需要的三個目錄。對于check out、commit、update等操作可以通過svn命令行方式執行,也可以用TortoiseSVN的windows菜單完成,非常簡單咯。