隨筆-4  評(píng)論-1  文章-0  trackbacks-0
            2008年7月16日
               摘要: 原文:http://www.hibernate.org/43.html  問(wèn)題  在一個(gè)典型的(Web)應(yīng)用中常見(jiàn)的一個(gè)問(wèn)題是在主要的邏輯動(dòng)作完成之后渲染頁(yè)面,同時(shí),因?yàn)檫壿媱?dòng)作的完成,Hibernate Session 已被關(guān)閉,數(shù)據(jù)庫(kù)事務(wù)也已結(jié)束。如果你在你的 JSP 中(或者其它視圖渲染機(jī)制)訪問(wèn)已被 Session 加載的 Detached Object 的...  閱讀全文
          posted @ 2009-04-04 21:45 阿里 閱讀(925) | 評(píng)論 (0)編輯 收藏

          本文的主要內(nèi)容是對(duì) Grails 1.1 中 PetClinic-MVC 實(shí)例的代碼分析。這個(gè)實(shí)例主要驗(yàn)證了如何在非 Grails 的項(xiàng)目中使用 GORM。此外也演示了 Spring MVC 2.5 的一些用法。本文假設(shè)大家對(duì) Spring、Spring MVC、Groovy 和 Grails 有著初步的了解。OK, let's go!

          Domain 層

          目前 Grails 只提供了在普通 Spring 應(yīng)用中獨(dú)立使用 GORM 的方法(現(xiàn)在 Groovy 和 Grails 已是 SpringSource 旗下的產(chǎn)品了,自然要首先對(duì)自家產(chǎn)品提供支持)。不過(guò)相信以后在其它 Java EE 框架中也可獨(dú)立使用 GORM。

          在 Grails 以外使用 GORM,只需在 Domain 類上使用 grails.persistence.Entity 注釋即可。其它的就像在 Grails 中的一樣。

          在配置方面也很簡(jiǎn)單,只要使用 <gorm:sessionFactory ... /> 即可。詳細(xì)請(qǐng)見(jiàn) Grails 的 PetClinic,這里就不拗述了。

          Controller 層

          同 Domain 層一樣,Controller 層也全部使用 Groovy 實(shí)現(xiàn)。主要是因?yàn)?GORM 的大部分方法都是動(dòng)態(tài)的。

          1. ClinicController: ClinicController 實(shí)現(xiàn)了 InitializingBean 接口。這個(gè)接口只有一個(gè)方法 afterPropertiesSet。這個(gè)方法會(huì)在 Bean 完成屬性設(shè)置之后執(zhí)行。PetClinic 用這個(gè)方式實(shí)現(xiàn)了類似 Grails 中 BootStrap 的部分功能。在這個(gè)方法中我們可以看到 GORM 中的 save() 方法和 addTo* 動(dòng)態(tài)方法都是可用的。

             1public 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. } 


          可能有人會(huì)問(wèn),afterPropertiesSet() 這個(gè)方法的作用就是增加一些預(yù)定義的數(shù)據(jù),為什么不使用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)。實(shí)際上,在本例中,afterPropertiesSet() 的作用的確可以用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)(經(jīng)過(guò)測(cè)試)。至于作者是出于什么目的而選擇了實(shí)現(xiàn) InitializingBean 接口我也說(shuō)不好。

          ClinicController 的其它部分就是一些簡(jiǎn)單的不能再簡(jiǎn)單的 Http 請(qǐng)求處理函數(shù)了。需要說(shuō)明的一點(diǎn)是 @RequestMapping 中的值代表了請(qǐng)求的 URL,這個(gè)是不用帶后綴的,也就是說(shuō) ".do" 是多余的。

          2. AddPetForm:

                 在 AddPetForm 類的級(jí)別上有三個(gè)注釋,分別是 @Controller, @RequestMapping 和 @SessionAttributes。@Controller 不用多說(shuō),作用是這個(gè)類聲明為一個(gè) Spring Controller,并添加到 Context 中。下面介紹一下 @RequestMapping 和 @SessionAttributes 的功能:

          • @RequestMapping 可以定義處理請(qǐng)求的路徑和方法類型,即可放在類聲明上,也可放在方法聲明上。在 AddPetForm 中,類和方法上都有 @RequestMapping,類上的 @RequestMapping 用來(lái)定義請(qǐng)求路徑。兩個(gè) @RequestMapping 的方法分別負(fù)責(zé)處理 get 請(qǐng)求和 post 請(qǐng)求。但我認(rèn)為更好的方式是在一個(gè) Controller 類中定義多個(gè)處理請(qǐng)求的方法,就像 Grails 的 Controller 那樣。需要注意的是方法上面的 @RequestMapping 上面的值并不會(huì)覆蓋類上的 @RequestMapping 中的值。
          • @SessionAttributes 中的值代表了 org.springframework.ui.Model 中應(yīng)該被設(shè)置為 Session 范圍的變量。在 Spring MVC 應(yīng)用中,這些變量往往是起到 ActionForm(Struts) 或 CommandObject(Spring MVC) 的作用。之所以要將表單對(duì)象放在 Session 中,是為了使其能夠在表單顯示和之后的表單提交這樣過(guò)程中不丟失。@SessionAttributes 的這種用法是源于 Spring MVC 2.0 AbstractFormController 的 SessionForm 模式。

            實(shí)際上,使用 @SessionAttributes 是很成問(wèn)題的,有時(shí)是不可接受的。考慮以下場(chǎng)景:在 PetClinic 應(yīng)用中,新建兩個(gè) Owner,然后為這兩個(gè) Owner 都添加新的 Pet。注意,只要顯示出來(lái) Add Pet 的頁(yè)面即可。這時(shí),這兩個(gè)頁(yè)面的 URL 應(yīng)該是 addPet.do?ownerId=num1 和 addPet.do?ownerId=num2 。這時(shí)將先打開(kāi)的表單 提交 ,假設(shè)是 num1 的那個(gè)。你會(huì)發(fā)現(xiàn)你 實(shí)際上是為 num2 Owner 添加了一個(gè) Pet。

            這個(gè)問(wèn)題的原因其實(shí)是很簡(jiǎn)單的,我就不解釋了。從上面這個(gè)事實(shí)來(lái)看,@SessionAttributes 的缺點(diǎn)還是比較明顯。是否采用這種方式還要看這個(gè)問(wèn)題對(duì)你的應(yīng)用的影響是否是致命的了。Spring MVC 有必要改進(jìn)一下,像 Seam 那樣可以很方便的使用 Conversation 是解決問(wèn)題的一個(gè)好辦法。(當(dāng)然也可以使用 hidden 域來(lái)保存 id)。

            Spring MVC 之所以要使用這種有較明顯缺陷的方式的原因是要使用 Spring MVC 的表單數(shù)據(jù)綁定功能。由于目前還沒(méi)有找到更好的方式,所以只能使用 @SessionAttribute 了。這一塊還是 Spring MVC 的一個(gè)短板,需要在新版本中改進(jìn)。

          除此以外,AddPetForm 中還有兩個(gè)特殊的方法,一個(gè)是注釋了 @ModelAttribute 的方法;另一個(gè)是注釋了 @InitBinder 的方法。下面分別介紹一下:

          • @ModelAttribute 可以用在方法上,也可以用在形參上。如果放在方法上,那它會(huì)在進(jìn)入請(qǐng)求處理方法是準(zhǔn)備一些數(shù)據(jù),作用和 SimpleFormController 的 referenceData() 一樣。@ModelAttribute 的這種用法可能會(huì)讓人聯(lián)想到 Seam 中的 @Factory。但被 @ModelAttribute 注釋的方法只會(huì)在同一個(gè) Controller 中被注釋了 @RequestMapping 的方法調(diào)用之前被調(diào)用,而 @Factory 方法則是全局的。

            你可以在 setupForm 方法中向 request 或 session 中設(shè)置一些需要顯示在頁(yè)面上的數(shù)據(jù),但是如果只放在 request 里的話,在表單驗(yàn)證發(fā)生時(shí),數(shù)據(jù)便會(huì)丟失;放在 session 缺點(diǎn)自不必多說(shuō)。在 Spring Web Flow 新加入的 flow 和 conversation 等新的 scope 無(wú)法很容易地在 Spring MVC 中使用的情況下,使用 @ModelAttribute 無(wú)疑是更好的選擇。并且使用 @ModelAttribute 可以在多個(gè) @RequestMapping 方法之間實(shí)現(xiàn)代碼的重用。

          • @InitBinder 適用于注冊(cè)只屬于本 Controller 的屬性編輯器。但在 AddPetForm 中,@InitBinder 方法只是告訴 Spring MVC,路徑為(<form: input path="id" />) id 的值不要使用屬性編輯器。不過(guò)頁(yè)面里也壓根沒(méi)有 路徑為 id 的值。所以,我覺(jué)得這個(gè)又是一個(gè)多余的代碼。(我試過(guò)將 @InitBinder 方法注釋掉,對(duì)程序沒(méi)有影響)

          Controller 層中兩個(gè)特殊的類:ClinicBindingInitializer 和 PetTypeEditor

          • ClinicBindingInitializer 的作用是注冊(cè)全句的屬性編輯器
          • PetTypeEditor 是用于處理 PetType 類型數(shù)據(jù)綁定的屬性編輯器。對(duì)于像 PetType 這樣的實(shí)體之間的關(guān)聯(lián)屬性,恰當(dāng)?shù)厥褂脤傩跃庉嬈骺梢院?jiǎn)化程序的開(kāi)發(fā)。 (不知 Grails 是否實(shí)現(xiàn)了一個(gè)動(dòng)態(tài)的屬性編輯器,用于關(guān)聯(lián)屬性的數(shù)據(jù)綁定)

           

          配置

          1. applicationContext.xml

                  applicationContext 中大部分都是常見(jiàn)的 Spring 配置。特殊一點(diǎn)的配置除了 <gorm:sessionFactory ... /> 以外,還有 Hibernate StatisticsService JMX 的配置。

          2. dispatcher-servlet.xml

                  Spring MVC 配置文件(也是一個(gè) Spring 的配置文件)的默認(rèn)名是 dispatcher-servlet.xml。

                  從上至下,第一個(gè)配置表明哪個(gè) Package 下的 Class 使用了 Spring 的 @Controller 注釋。這樣 Spring 容器在啟動(dòng)的時(shí)候會(huì)自動(dòng)掃描其下的 Class,將加注了 @Controller 注釋的類加入到 Spring 的容器中。

                  第二個(gè)配置 DefaultAnnotationHandlerMapping HandlerMapping 接口的一個(gè)最常用的實(shí)現(xiàn)類。用于映射 Handler 和請(qǐng)求路徑之間的關(guān)系。通過(guò)這樣的配置,Controller 中的 @RequestMapping 中的路徑值就可以真正地和 Http 請(qǐng)求對(duì)應(yīng)起來(lái)。HandlerMapping 的另外一個(gè)常用的實(shí)現(xiàn)類是 ControllerClassNameHandlerMapping

                  第三個(gè)配置 AnnotationMethodHandlerAdapter 的作用是使處理請(qǐng)求的 Handler 能夠真正的具有處理請(qǐng)求的能力而提供一些服務(wù),包括為請(qǐng)求參數(shù)綁定應(yīng)用相應(yīng)的屬性編輯器(通過(guò)注冊(cè) webBindingInitializer 實(shí)現(xiàn))。

           

          ==============================結(jié)束的分割線==============================

          寫到這里,PetClinic-MVC 中值得分析學(xué)習(xí)的代碼都提到了。PetClinic-MVC 是一個(gè)很小的應(yīng)用,但是其中演示了 GORM 在普通 Spring 中的應(yīng)用和 Spring MVC 2.5 的使用。細(xì)看的話還是能學(xué)到不少東西的。我所寫的只是我的理解,難免會(huì)有一些不足和錯(cuò)誤,歡迎糾正。

          posted @ 2009-03-14 12:33 阿里 閱讀(1622) | 評(píng)論 (1)編輯 收藏
          SessionFactory.getCurrentSession() 是 Hibernate 應(yīng)用獲取 Session 的常用方法。在調(diào)用該方法時(shí),Hibernate 會(huì)從 interface CurrentSessionContext 獲取當(dāng)前的 Session,這是Hibernate 在不同組件中傳遞 Session 的方法。

          CurrentSessionContext 有三個(gè)實(shí)現(xiàn),分別是 ThreadLocalSessionContext、JTASessionContext 和 ManagedSessionContext。ThreadLocalSessionContext 將 Session 與當(dāng)前線程綁定,是使用較多的一種方案;JTASessionContext 將 Session 與 JTA 事務(wù)綁定,在 JTA 環(huán)境中使用;ManagedSessionContext 使應(yīng)用可以通過(guò) bind() 和 unbind() 方法控制 Session 的綁定,主要在有 Conversation 的應(yīng)用中使用(如果使用 ManagedSessionContext,開(kāi)發(fā)人員要做的事情還是很多的)。CurrentSessionContext 實(shí)現(xiàn)的選擇可以通過(guò) hibernate.current_session_context_class 來(lái)配置。

          另一種更常見(jiàn)的創(chuàng)建 Session 的方法是 openSession()。openSession() 與 getCurrentSession() 有何不同和關(guān)聯(lián)呢?在 SessionFactory 啟動(dòng)的時(shí)候,Hibernate 會(huì)根據(jù)配置創(chuàng)建相應(yīng)的 CurrentSessionContext,在 getCurrentSession() 被調(diào)用的時(shí)候,實(shí)際被執(zhí)行的方法是 CurrentSessionContext.currentSession() 。在 currentSession() 執(zhí)行時(shí),如果當(dāng)前 Session 為空,currentSession 會(huì)調(diào)用 SessionFactory 的 openSession。所以 getCurrentSession() 對(duì)于 Java EE 來(lái)說(shuō)是更好的獲取 Session 的方法。

          再說(shuō) ManagedSessionContext,它提供了更靈活的綁定 Session 的方式,但是使用起來(lái)去不簡(jiǎn)單。在 Hibernate 的 CaveatEmptor 實(shí)例中有關(guān)于使用 ManagedSessionContext 的例子,但更好的選擇是使用 Seam Framework。
          posted @ 2008-07-16 15:48 阿里 閱讀(1545) | 評(píng)論 (0)編輯 收藏
          主站蜘蛛池模板: 平乡县| 九江县| 永宁县| 万安县| 尉氏县| 霍邱县| 金昌市| 三门县| 曲松县| 安远县| 镇远县| 通山县| 阳曲县| 滨海县| 册亨县| 定日县| 遵化市| 正镶白旗| 大港区| 策勒县| 灵武市| 合川市| 永川市| 龙山县| 金湖县| 临汾市| 宁陕县| 贵州省| 剑川县| 潮安县| 郓城县| 安阳县| 宁晋县| 电白县| 松阳县| 通城县| 温宿县| 大港区| 淮安市| 宜春市| 莲花县|