
2008年7月16日
摘要: 原文:http://www.hibernate.org/43.html
問題
在一個典型的(Web)應(yīng)用中常見的一個問題是在主要的邏輯動作完成之后渲染頁面,同時,因為邏輯動作的完成,Hibernate Session 已被關(guān)閉,數(shù)據(jù)庫事務(wù)也已結(jié)束。如果你在你的 JSP 中(或者其它視圖渲染機制)訪問已被 Session 加載的 Detached Object 的...
閱讀全文
posted @
2009-04-04 21:45 阿里 閱讀(923) |
評論 (0) |
編輯 收藏
本文的主要內(nèi)容是對 Grails 1.1 中 PetClinic-MVC 實例的代碼分析。這個實例主要驗證了如何在非 Grails
的項目中使用 GORM。此外也演示了 Spring MVC 2.5 的一些用法。本文假設(shè)大家對 Spring、Spring
MVC、Groovy 和 Grails 有著初步的了解。OK, let's go!
Domain 層
目前 Grails 只提供了在普通 Spring 應(yīng)用中獨立使用 GORM 的方法(現(xiàn)在 Groovy 和 Grails 已是
SpringSource 旗下的產(chǎn)品了,自然要首先對自家產(chǎn)品提供支持)。不過相信以后在其它 Java EE 框架中也可獨立使用 GORM。
在 Grails 以外使用 GORM,只需在 Domain 類上使用 grails.persistence.Entity 注釋即可。其它的就像在 Grails 中的一樣。
在配置方面也很簡單,只要使用 <gorm:sessionFactory ... /> 即可。詳細(xì)請見 Grails 的 PetClinic,這里就不拗述了。
Controller 層
同 Domain 層一樣,Controller 層也全部使用 Groovy 實現(xiàn)。主要是因為 GORM 的大部分方法都是動態(tài)的。
1. ClinicController: ClinicController 實現(xiàn)了 InitializingBean
接口。這個接口只有一個方法 afterPropertiesSet。這個方法會在 Bean 完成屬性設(shè)置之后執(zhí)行。PetClinic
用這個方式實現(xiàn)了類似 Grails 中 BootStrap 的部分功能。在這個方法中我們可以看到 GORM 中的 save() 方法和
addTo* 動態(tài)方法都是可用的。
1. public void afterPropertiesSet() {
2. if (!Speciality.count()) {
3. def rad = new Speciality(name: "radiology").save()
4.
5. new Vet(firstName: "Linda", lastName: "Douglas").
6. addToSpecialities(sur).save()
7.
8.
9. ['dog', 'lizard', 'cat', 'snake', 'bird', 'hamster'].each {
10. new PetType(name: it).save()
11. }
12. }
13. }
可能有人會問,afterPropertiesSet()
這個方法的作用就是增加一些預(yù)定義的數(shù)據(jù),為什么不使用構(gòu)造函數(shù)來實現(xiàn)。實際上,在本例中,afterPropertiesSet()
的作用的確可以用構(gòu)造函數(shù)來實現(xiàn)(經(jīng)過測試)。至于作者是出于什么目的而選擇了實現(xiàn) InitializingBean 接口我也說不好。
ClinicController 的其它部分就是一些簡單的不能再簡單的 Http 請求處理函數(shù)了。需要說明的一點是 @RequestMapping 中的值代表了請求的 URL,這個是不用帶后綴的,也就是說 ".do" 是多余的。
2. AddPetForm:
在 AddPetForm 類的級別上有三個注釋,分別是 @Controller, @RequestMapping 和
@SessionAttributes。@Controller 不用多說,作用是這個類聲明為一個 Spring Controller,并添加到
Context 中。下面介紹一下 @RequestMapping 和 @SessionAttributes 的功能:
- @RequestMapping 可以定義處理請求的路徑和方法類型,即可放在類聲明上,也可放在方法聲明上。在 AddPetForm
中,類和方法上都有 @RequestMapping,類上的 @RequestMapping 用來定義請求路徑。兩個
@RequestMapping 的方法分別負(fù)責(zé)處理 get 請求和 post 請求。但我認(rèn)為更好的方式是在一個 Controller
類中定義多個處理請求的方法,就像 Grails 的 Controller 那樣。需要注意的是方法上面的 @RequestMapping
上面的值并不會覆蓋類上的 @RequestMapping 中的值。
- @SessionAttributes 中的值代表了 org.springframework.ui.Model 中應(yīng)該被設(shè)置為
Session 范圍的變量。在 Spring MVC 應(yīng)用中,這些變量往往是起到 ActionForm(Struts) 或
CommandObject(Spring MVC) 的作用。之所以要將表單對象放在 Session
中,是為了使其能夠在表單顯示和之后的表單提交這樣過程中不丟失。@SessionAttributes 的這種用法是源于 Spring MVC
2.0 AbstractFormController 的 SessionForm 模式。
實際上,使用 @SessionAttributes
是很成問題的,有時是不可接受的。考慮以下場景:在 PetClinic 應(yīng)用中,新建兩個 Owner,然后為這兩個 Owner 都添加新的
Pet。注意,只要顯示出來 Add Pet 的頁面即可。這時,這兩個頁面的 URL 應(yīng)該是 addPet.do?ownerId=num1 和
addPet.do?ownerId=num2 。這時將先打開的表單
提交
,假設(shè)是 num1 的那個。你會發(fā)現(xiàn)你
實際上是為 num2 Owner
添加了一個 Pet。
這個問題的原因其實是很簡單的,我就不解釋了。從上面這個事實來看,@SessionAttributes
的缺點還是比較明顯。是否采用這種方式還要看這個問題對你的應(yīng)用的影響是否是致命的了。Spring MVC 有必要改進一下,像 Seam
那樣可以很方便的使用 Conversation 是解決問題的一個好辦法。(當(dāng)然也可以使用 hidden 域來保存 id)。
Spring MVC 之所以要使用這種有較明顯缺陷的方式的原因是要使用 Spring MVC
的表單數(shù)據(jù)綁定功能。由于目前還沒有找到更好的方式,所以只能使用 @SessionAttribute 了。這一塊還是 Spring MVC
的一個短板,需要在新版本中改進。
除此以外,AddPetForm 中還有兩個特殊的方法,一個是注釋了 @ModelAttribute 的方法;另一個是注釋了 @InitBinder 的方法。下面分別介紹一下:
- @ModelAttribute 可以用在方法上,也可以用在形參上。如果放在方法上,那它會在進入請求處理方法是準(zhǔn)備一些數(shù)據(jù),作用和
SimpleFormController 的 referenceData() 一樣。@ModelAttribute 的這種用法可能會讓人聯(lián)想到
Seam 中的 @Factory。但被 @ModelAttribute 注釋的方法只會在同一個 Controller 中被注釋了
@RequestMapping 的方法調(diào)用之前被調(diào)用,而 @Factory 方法則是全局的。
你可以在 setupForm 方法中向 request 或 session 中設(shè)置一些需要顯示在頁面上的數(shù)據(jù),但是如果只放在 request
里的話,在表單驗證發(fā)生時,數(shù)據(jù)便會丟失;放在 session 缺點自不必多說。在 Spring Web Flow 新加入的 flow 和 conversation 等新的 scope
無法很容易地在 Spring MVC 中使用的情況下,使用 @ModelAttribute 無疑是更好的選擇。并且使用 @ModelAttribute 可以在多個
@RequestMapping 方法之間實現(xiàn)代碼的重用。
- @InitBinder 適用于注冊只屬于本 Controller 的屬性編輯器。但在 AddPetForm 中,@InitBinder
方法只是告訴 Spring MVC,路徑為(<form: input path="id" />) id
的值不要使用屬性編輯器。不過頁面里也壓根沒有 路徑為 id 的值。所以,我覺得這個又是一個多余的代碼。(我試過將 @InitBinder
方法注釋掉,對程序沒有影響)
Controller 層中兩個特殊的類:ClinicBindingInitializer 和 PetTypeEditor
- ClinicBindingInitializer 的作用是注冊全句的屬性編輯器
- PetTypeEditor 是用于處理 PetType 類型數(shù)據(jù)綁定的屬性編輯器。對于像 PetType 這樣的實體之間的關(guān)聯(lián)屬性,恰當(dāng)?shù)厥褂脤傩跃庉嬈骺梢院喕绦虻拈_發(fā)。
(不知 Grails 是否實現(xiàn)了一個動態(tài)的屬性編輯器,用于關(guān)聯(lián)屬性的數(shù)據(jù)綁定)
配置
1. applicationContext.xml
applicationContext 中大部分都是常見的 Spring 配置。特殊一點的配置除了
<gorm:sessionFactory ... /> 以外,還有 Hibernate StatisticsService JMX
的配置。
2. dispatcher-servlet.xml
Spring MVC 配置文件(也是一個 Spring 的配置文件)的默認(rèn)名是 dispatcher-servlet.xml。
從上至下,第一個配置表明哪個 Package 下的 Class 使用了 Spring 的 @Controller
注釋。這樣 Spring 容器在啟動的時候會自動掃描其下的 Class,將加注了 @Controller 注釋的類加入到 Spring
的容器中。
第二個配置 DefaultAnnotationHandlerMapping
是 HandlerMapping
接口的一個最常用的實現(xiàn)類。用于映射 Handler 和請求路徑之間的關(guān)系。通過這樣的配置,Controller 中的 @RequestMapping 中的路徑值就可以真正地和 Http 請求對應(yīng)起來。HandlerMapping 的另外一個常用的實現(xiàn)類是 ControllerClassNameHandlerMapping
。
第三個配置 AnnotationMethodHandlerAdapter
的作用是使處理請求的 Handler 能夠真正的具有處理請求的能力而提供一些服務(wù),包括為請求參數(shù)綁定應(yīng)用相應(yīng)的屬性編輯器(通過注冊 webBindingInitializer 實現(xiàn))。
==============================結(jié)束的分割線==============================
寫到這里,PetClinic-MVC
中值得分析學(xué)習(xí)的代碼都提到了。PetClinic-MVC 是一個很小的應(yīng)用,但是其中演示了 GORM 在普通 Spring 中的應(yīng)用和
Spring MVC 2.5 的使用。細(xì)看的話還是能學(xué)到不少東西的。我所寫的只是我的理解,難免會有一些不足和錯誤,歡迎糾正。
posted @
2009-03-14 12:33 阿里 閱讀(1616) |
評論 (1) |
編輯 收藏
SessionFactory.getCurrentSession() 是 Hibernate 應(yīng)用獲取 Session
的常用方法。在調(diào)用該方法時,Hibernate 會從 interface CurrentSessionContext 獲取當(dāng)前的
Session,這是Hibernate 在不同組件中傳遞 Session 的方法。
CurrentSessionContext 有三個實現(xiàn),分別是
ThreadLocalSessionContext、JTASessionContext 和
ManagedSessionContext。ThreadLocalSessionContext 將 Session
與當(dāng)前線程綁定,是使用較多的一種方案;JTASessionContext 將 Session 與 JTA 事務(wù)綁定,在 JTA
環(huán)境中使用;ManagedSessionContext 使應(yīng)用可以通過 bind() 和 unbind() 方法控制 Session
的綁定,主要在有 Conversation 的應(yīng)用中使用(如果使用
ManagedSessionContext,開發(fā)人員要做的事情還是很多的)。CurrentSessionContext 實現(xiàn)的選擇可以通過
hibernate.current_session_context_class 來配置。
另一種更常見的創(chuàng)建 Session 的方法是 openSession()。openSession() 與
getCurrentSession() 有何不同和關(guān)聯(lián)呢?在 SessionFactory 啟動的時候,Hibernate
會根據(jù)配置創(chuàng)建相應(yīng)的 CurrentSessionContext,在 getCurrentSession() 被調(diào)用的時候,實際被執(zhí)行的方法是
CurrentSessionContext.currentSession() 。在 currentSession() 執(zhí)行時,如果當(dāng)前
Session 為空,currentSession 會調(diào)用 SessionFactory 的 openSession。所以
getCurrentSession() 對于 Java EE 來說是更好的獲取 Session 的方法。
再說 ManagedSessionContext,它提供了更靈活的綁定 Session 的方式,但是使用起來去不簡單。在 Hibernate
的 CaveatEmptor 實例中有關(guān)于使用 ManagedSessionContext 的例子,但更好的選擇是使用 Seam
Framework。
posted @
2008-07-16 15:48 阿里 閱讀(1544) |
評論 (0) |
編輯 收藏