国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
在Spring中配置Hibernate事務(wù)
在Spring中配置Hibernate事務(wù)

時間:2005-12-26
作者:Binildas Christudas
瀏覽次數(shù): 3414
本文關(guān)鍵字:Web ServicesJavatransactionsspringhibernateEJB事務(wù)
文章工具
 推薦給朋友
 打印文章

  本文主要探討如何利用Spring來裝配組件,包括其事務(wù)上下文。從J2EE應(yīng)用程序內(nèi)部連接到單個的數(shù)據(jù)庫并不是什么難事。但是,如果要裝配或者集成企業(yè)級的組件,情況就復(fù)雜了。一個組件可以有一個或多個支持它的數(shù)據(jù)庫,因此,當裝配兩個或更多的組件時,我們希望能夠保持在跨組件的多個數(shù)據(jù)庫中進行的操作的原子性。J2EE服務(wù)器為這些組件提供了一個容器來保證事務(wù)原子性和跨組件獨立性。如果使用的不是J2EE服務(wù)器,則可以利用Spring來幫助我們。Spring基于Inversion of Control(控制反轉(zhuǎn))模式(也稱為依賴注入),它不僅可以連接組件服務(wù),還可以連接關(guān)聯(lián)的事務(wù)上下文。在本文中,我們將Hibernate用作對象/關(guān)系持久性存儲和查詢服務(wù)?! ?nbsp;

裝配組件事務(wù)
  假設(shè)在企業(yè)組件庫里,我們已經(jīng)有一個審計組件,里面有可以被客戶端調(diào)用的服務(wù)方法。然后,當我們想要構(gòu)建一個訂單處理系統(tǒng)時,我們發(fā)現(xiàn)存在這樣的設(shè)計要求:OrderListManager組件服務(wù)同樣需要審計組件服務(wù)。OrderListManager創(chuàng)建和管理訂單,因此所有的OrderListManager服務(wù)都有自己的事務(wù)屬性。當我們從OrderListManager服務(wù)內(nèi)調(diào)用審計組件時,我們實際上是在把OrderListManager服務(wù)的事務(wù)上下文傳播給審計服務(wù)。也許將來新的業(yè)務(wù)服務(wù)組件同樣需要審計組件,但那時將在一個不同的事務(wù)上下文中調(diào)用它。實際結(jié)果就是,即使審計組件的功能保持不變,它也可能是由別的業(yè)務(wù)服務(wù)功能組成,包含了混搭的(mix-and-match)事務(wù)屬性來提供不同的運行時事務(wù)性行為。

  在圖1中有兩個獨立的調(diào)用上下文流程。在流程1里,如果客戶端有TX上下文,那么OrderListManager既可以參與其中,也可以啟動一個新的TX,這取決于客戶端是否在TX中,以及為OrderListManager方法指定了什么樣的TX屬性。這同樣適用于OrderListManager服務(wù)依次調(diào)用AuditManager方法的情況。

1 裝配組件事務(wù)

  EJB架構(gòu)允許組件裝配者聲明式地給出正確的事務(wù)屬性,從而為他們提供這種靈活性。我們不探討聲明式事務(wù)管理的替代方案(即所謂的編程式事務(wù)控制),因為這會牽涉到代碼更改,從而產(chǎn)生不同的運行時事務(wù)行為。幾乎所有的J2EE應(yīng)用服務(wù)器都按照X/Open XA規(guī)范提供了服從兩階段提交協(xié)議的分布式事務(wù)管理器?,F(xiàn)在的問題是,我們能不能利用EJB服務(wù)器來實現(xiàn)相同的功能?Spring就是其中的一種解決方案。讓我們來看一下Spring如何幫助我們解決事務(wù)組裝的問題:

使用Spring進行事務(wù)管理
  我們將看到一個輕量級的事務(wù)基礎(chǔ)架構(gòu),它實際上可以管理組件級的事務(wù)裝配。Spring是其中的一個解決方案。它的優(yōu)點在于,我們不會被捆綁到J2EE容器服務(wù)(如JNDI DataSource)上。最棒的一點是,如果我們想把這個輕量級事務(wù)基礎(chǔ)架構(gòu)關(guān)聯(lián)到一個已可用的J2EE容器基礎(chǔ)架構(gòu),將不會有任何問題??雌饋砦覀兛梢岳脙烧叩膬?yōu)點。

  另一方面,Spring這個輕量級事務(wù)基礎(chǔ)架構(gòu)使用了一個面向方面編程(Aspect-Oriented Programming,AOP)框架。Spring AOP框架使用了一個支持AOP的Spring bean工廠。在特定于Spring的配置文件applicationContext.xml中,通過在組件服務(wù)級指定事務(wù)特性來劃分事務(wù)。

<beans><!-- other code goes here... --><bean id="orderListManager"class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionManager"><ref local="transactionManager1"/></property><property name="target"><ref local="orderListManagerTarget"/></property><property name="transactionAttributes"><props><prop key="getAllOrderList">PROPAGATION_REQUIRED</prop><prop key="getOrderList">PROPAGATION_REQUIRED</prop><prop key="createOrderList">PROPAGATION_REQUIRED</prop><prop key="addLineItem">PROPAGATION_REQUIRED,-com.example.exception.FacadeException</prop><prop key="getAllLineItems">PROPAGATION_REQUIRED,readOnly</prop><prop key="queryNumberOfLineItems">PROPAGATION_REQUIRED,readOnly</prop></props></property></bean></beans>

  一旦我們在服務(wù)級指定了事務(wù)屬性,org.springframework.transaction.PlatformTransactionManager接口的一個特定實現(xiàn)就會截獲并解釋它們。該接口如下:

public interface PlatformTransactionManager{TransactionStatus getTransaction(TransactionDefinition definition);void commit(TransactionStatus status);void rollback(TransactionStatus status);}

Hibernate事務(wù)管理器
  由于我們已決定使用Hibernate作為ORM工具,下一步要做的就是配置一個特定于Hibernate的事務(wù)管理器實現(xiàn)。

<beans><!-- other code goes here... --><bean id="transactionManager1"class="org.springframework.orm.hibernate.HibernateTransactionManager"><property name="sessionFactory"><ref local="sessionFactory1"/></property></bean></beans>

設(shè)計多個組件中的事務(wù)的管理
  現(xiàn)在,我們來討論什么是“裝配組件事務(wù)”。您也許注意到了為域中的服務(wù)級組件OrderListManager所指定的各種TX屬性。圖2所示的業(yè)務(wù)域?qū)ο竽P停˙usiness Domain Object Model,BDOM)顯示了我們的域所確定的主要對象:

2 業(yè)務(wù)域?qū)ο竽P?/em>(BDOM)

  圖字:Order:訂單;Audit:審計

  為了更好的說明,我們來列出我們的域中的一些非功能性需求(Non-Functional Requirement,NFR):

  • 業(yè)務(wù)對象需要保存在一個數(shù)據(jù)庫中(appfuse1)。
  • 審計時要登錄到另一個數(shù)據(jù)庫中(appfuse2),出于安全的考慮,數(shù)據(jù)庫要有防火墻保護。
  • 業(yè)務(wù)組件應(yīng)該可以重用。
  • 必須盡一切努力審計業(yè)務(wù)服務(wù)層的所有活動。

  考慮了以上要求之后,我們決定,OrderListManager服務(wù)會將所有的審計日志調(diào)用委托給已經(jīng)可用的AuditManager組件。這樣就得出了詳細設(shè)計,如圖3所示:

3 組件服務(wù)的設(shè)計

  這里值得注意的一點是,由于我們的NFR,我們要將與OrderListManager相關(guān)的對象映射到appfuse1數(shù)據(jù)庫,而將與審計相關(guān)的對象映射到appfuse2。這樣,無論要審計什么,OrderListManager組件都會調(diào)用AuditManager組件。我們會看到,OrderListManager組件中的所有方法都應(yīng)該是事務(wù)性的,因為我們通過服務(wù)來創(chuàng)建訂單和線項目(line item)。那么AuditManager組件中的服務(wù)呢?因為它做的是審計跟蹤,我們關(guān)心的是盡可能維持長時間的審計跟蹤,并針對系統(tǒng)中所有可能的業(yè)務(wù)活動。這就產(chǎn)生了如下的需求:“即使主要的業(yè)務(wù)活動失敗了,也要進行審計跟蹤記錄”。AuditManager組件同樣要有自己的事務(wù),因為它也與自己的數(shù)據(jù)庫進行交互。如下所示:

<beans><!-- other code goes here... --><bean id="auditManager"class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionManager"><ref local="transactionManager2"/></property><property name="target"><ref local="auditManagerTarget"/></property><property name="transactionAttributes"><props><prop key="log">PROPAGATION_REQUIRES_NEW</prop></props></property></bean></beans>

  現(xiàn)在,為了演示,我們把注意力放到createOrderList和addLineItem這兩個業(yè)務(wù)服務(wù)上。同時請注意,我們并沒有要求最佳設(shè)計策略——你可能注意到了,addLineItem方法拋出了FacadeException異常,而createOrderList卻沒有。在生產(chǎn)設(shè)計中,您也許希望每一個服務(wù)方法都可以處理異常場景。

public class OrderListManagerImplimplements OrderListManager{private AuditManager auditManager;public Long createOrderList(OrderList orderList){Long orderId = orderListDAO.createOrderList(orderList);auditManager.log(new AuditObject(ORDER + orderId, CREATE));return orderId;}public void addLineItem(Long orderId, LineItem lineItem)throws FacadeException{Long lineItemId = orderListDAO.addLineItem(orderId, lineItem);auditManager.log(new AuditObject(LINE_ITEM + lineItemId, CREATE));int numberOfLineItems = orderListDAO.queryNumberOfLineItems(orderId);if(numberOfLineItems > 2){log("Added LineItem " + lineItemId +" to Order " + orderId + ";But rolling back *** !");throw new FacadeException("Make a new Order for this line item");}else{log("Added LineItem " + lineItemId +" to Order " + orderId + ".");}}//Other code goes here...}

  為了創(chuàng)建一個異常場景來進行演示,我們引入了另一種業(yè)務(wù)規(guī)則,它規(guī)定一個特定的訂單不能包含多于兩個的線項目?,F(xiàn)在應(yīng)該注意,我們是從createOrderList和addLineItem中調(diào)用auditManager.log()方法的。您應(yīng)該也注意到了為上述方法所指定的事務(wù)屬性。

<bean id="orderListManager"class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionAttributes"><props><prop key="createOrderList">PROPAGATION_REQUIRED</prop><prop key="addLineItem">PROPAGATION_REQUIRED,-com.example.exception.FacadeException</prop></props></property></bean><bean id="auditManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionAttributes"><props><prop key="log">PROPAGATION_REQUIRES_NEW</prop></props></property></bean>

  PROPAGATION_REQUIRED等效于TX_REQUIRED,而PROPAGATION_REQUIRES_NEW等效于EJB中的TX_REQUIRES_NEW。如果我們想讓服務(wù)方法始終在事務(wù)中運行,我們可以使用PROPAGATION_REQUIRED。當使用PROPAGATION_REQUIRED時,如果已經(jīng)運行了一個TX,bean方法就會加入到該TX中;否則的話,Spring的輕量級TX管理器就會啟動一個TX。如果在調(diào)用組件服務(wù)時我們總是希望開始新的事務(wù),那么可以利用PROPAGATION_REQUIRES_NEW屬性。

  我們還指定,當方法拋出FacadeException類型的異常時,addLineItem就總是回滾事務(wù)。這就達到了另一個粒度級別:在異常場景中,我們的控制可以精細到TX的具體結(jié)束方式。前綴符號“-”指定回滾TX,而前綴符號“+”指定提交TX。

  接下來的問題是,為什么我們要為log方法設(shè)置PROPAGATION_REQUIRES_NEW屬性呢?這是由我們的以下需求決定的:無論主服務(wù)方法發(fā)生什么情況,對所有創(chuàng)建訂單以及向系統(tǒng)添加線項目的嘗試都要記錄審計跟蹤。也就是說,即使在createOrderList和addLineItem的實現(xiàn)過程中出現(xiàn)了異常也要記錄審計跟蹤。這僅在啟動一個新的TX并在這個新的TX上下文中調(diào)用log的時候起作用。這就是為什么要為log設(shè)置PROPAGATION_REQUIRES_NEW TX屬性的原因:如果對下述方法的調(diào)用成功了

auditManager.log(new AuditObject(LINE_ITEM +lineItemId, CREATE));

  ,auditManager.log()就將在新的TX上下文中執(zhí)行,而且只要auditManager.log()本身成功(即,沒有拋出異常),新的上下文就會被提交。

  設(shè)置演示環(huán)境

  準備演示環(huán)境時,我參考了Spring Live這本書的流程:

  1. 下載并安裝以下組件,這時請注意使用準確的版本,不然就會引起版本不兼容問題。
  2. 在系統(tǒng)中設(shè)置以下環(huán)境變量:
    • JAVA_HOME
    • CATALINA_HOME
    • ANT_HOME
  3. 把下列目錄添加到您的PATH環(huán)境變量中,或者使用完全路徑來執(zhí)行腳本:
    • JAVA_HOMEin
    • CATALINA_HOMEin
    • ANT_HOMEin
  4. 要設(shè)置Tomcat,在文本編輯器中打開/conf/tomcat-users.xml文件,驗證以下各行是否存在。如果不存在,必須手動添加進去:
<role rolename="manager"/><user username="admin"
password="admin" roles="manager"/> 
  1. 要創(chuàng)建基于Struts、Spring和Hibernate的Web應(yīng)用程序,必須用Equinox來構(gòu)建一個基本的框架程序(bare-bones starter application),它將包含預(yù)定義的文件夾結(jié)構(gòu)、所有需要用到的.jar文件以及Ant構(gòu)建腳本。把Equinox解壓到一個文件夾中,它將創(chuàng)建一個equinox文件夾。將目錄更改為equinox文件夾,輸入命令A(yù)NT_HOMEinnt new -Dapp.name=myusers。這樣就會創(chuàng)建一個與equinox同級的文件夾myusers。該文件夾的具體內(nèi)容如下:

4 Equinoxmyusers應(yīng)用程序文件夾模板

  1. 刪除myuserswebWEB-INF文件夾下的所有.xml文件。
  2. 復(fù)制equinoxxtrasstrutswebWEB-INFibstruts*.jar文件至myuserswebWEB-INFib文件夾下,這樣,這個示例應(yīng)用程序就可以利用struts了。
  3. 參考資料小節(jié)的示例代碼中,解壓myusersextra.zip到一個合適的位置。將目錄更改為新創(chuàng)建的myusersextra文件夾,復(fù)制myusersextra文件夾中的所有內(nèi)容,并將它們粘貼到myusers文件夾。
  4. 打開命令提示符,將目錄轉(zhuǎn)至myusers目錄下。執(zhí)行CATALINA_HOMEinstartup。要從myusers文件夾啟動Tomcat,這一點非常重要,否則數(shù)據(jù)庫將不會創(chuàng)建在myusers文件夾中,從而導致在執(zhí)行一些定義在build.xml中的任務(wù)時出現(xiàn)錯誤。
  5. 再次打開命令提示符并將目錄轉(zhuǎn)至myusers目錄下。執(zhí)行ANT_HOMEinnt install。這將構(gòu)建應(yīng)用程序并把它部署到Tomcat中。這時,我們可以看到myusers中多了一個db目錄,以便存放數(shù)據(jù)庫appfuse1和appfuse2。
  6. 打開瀏覽器并驗證myusers應(yīng)用程序已經(jīng)部署在http://localhost:8080/myusers/上了。
  7. 要重新安裝應(yīng)用程序,執(zhí)行ANT_HOMEinnt remove,然后執(zhí)行CATALINA_HOMEinshutdown關(guān)閉Tomcat。現(xiàn)在,從CATALINA_HOMEwebapps文件夾刪除所有的myusers文件夾。然后執(zhí)行CATALINA_HOMEinstartup重新啟動Tomcat,并通過執(zhí)行ANT_HOMEinnt install重新安裝應(yīng)用程序。

運行演示
  為了運行測試用例,myusers estmxampleservice中提供了一個JUnit測試類,OrderListManagerTest。要執(zhí)行它,可以在構(gòu)建應(yīng)用程序的命令提示符中輸入以下命令:

CATALINA_HOMEinnt test -Dtestcase=OrderListManager

  測試用例分為兩個主要部分:第一部分創(chuàng)建一個由兩個線項目組成的訂單,然后把這兩個線項目鏈接到訂單中。它可以成功運行,如下所示:

OrderList orderList1 = new OrderList();Long orderId1 = orderListManager.createOrderList(orderList1);log("Created OrderList with id ‘"+ orderId1 + "‘...");orderListManager.addLineItem(orderId1,lineItem1);orderListManager.addLineItem(orderId1,lineItem2);

  第二部分執(zhí)行類似的操作,但是這次我們試圖向訂單添加三個線項目,這將產(chǎn)生一個異常:

OrderList orderList2 = new OrderList();Long orderId2 = orderListManager.createOrderList(orderList2);log("Created OrderList with id ‘" + orderId2 + "‘...");orderListManager.addLineItem(orderId2,lineItem3);orderListManager.addLineItem(orderId2,lineItem4);//We know, we will have an exception here,still want to proceedtry{orderListManager.addLineItem(orderId2,lineItem5);}catch(FacadeException facadeException){log("ERROR : " + facadeException.getMessage());}

  控制臺的輸出如圖5所示:


5 客戶端控制臺輸出

  我們創(chuàng)建了Order1,并向其添加了兩個ID為1和2的線項目。然后我們創(chuàng)建Order2,并嘗試添加3個項目,前兩個(ID為3和4)添加成功,但是圖5顯示,添加第三個項目(ID為5)時業(yè)務(wù)方法遇到了異常。因此,業(yè)務(wù)方法TX被回滾,數(shù)據(jù)庫中沒有ID為5的線項目。從控制臺執(zhí)行以下命令,就可以通過圖6和圖7進行驗證:

CATALINA_HOMEinnt browse1

6 appfuse1數(shù)據(jù)庫中創(chuàng)建的訂單

7 appfuse1數(shù)據(jù)庫中創(chuàng)建的線項目

  在接下來的也是最重要的演示部分中可以看出,訂單和線項目保存在appfuse1數(shù)據(jù)庫中,而審計對象保存在appfuse2數(shù)據(jù)庫中。實際上,OrderListManager中的服務(wù)方法可以與多個數(shù)據(jù)庫交互。啟動appfuse2數(shù)據(jù)庫,查看審計跟蹤,如下所示:

CATALINA_HOMEinnt browse2

8 創(chuàng)建到appfuse2數(shù)據(jù)庫中的審計跟蹤,包括失敗TX的記錄項

  表8最后一行尤其值得注意,RESOURCE列顯示這一行對應(yīng)的是LineItem5。但是當我們回過來看圖7時,卻發(fā)現(xiàn)并沒有對應(yīng)于LineItem5的線項目。哪里出錯了呢?事實上并沒有出錯,圖7沒有的那一行其實正是這篇文章的關(guān)鍵所在,讓我們來看看是怎么回事。

  我們知道,addLineItem()方法包含PROPAGATION_REQUIRED屬性,而log()方法有PROPAGATION_REQUIRES_NEW屬性。而且addLineItem()在內(nèi)部調(diào)用了log()方法。因此,當我們試圖向Order2添加第三個線項目時,就(按照我們的業(yè)務(wù)規(guī)則)引發(fā)了異常,于是這個線項目的創(chuàng)建以及將其鏈接到Order2的操作都被回滾了。但是,因為還從addLineItem()中調(diào)用了log(),還因為log()具有PROPAGATION_REQUIRES_NEW TX屬性,回滾addLineItem()將不會造成回滾log(),因為log()是在一個新的TX中執(zhí)行。

  讓我們對log()的TX屬性做一下改動,把PROPAGATION_REQUIRES_NEW替換為PROPAGATION_SUPPORTS。PROPAGATION_SUPPORTS屬性允許服務(wù)方法在客戶端有TX上下文時在客戶端TX中運行;如果客戶端沒有TX,就不用TX而直接運行。您可能必須重新安裝應(yīng)用程序,以便數(shù)據(jù)庫中已經(jīng)可用的數(shù)據(jù)可以自動刷新。重新安裝請按照“設(shè)置演示環(huán)境”中的步驟12進行。

  如果再次運行,我們會發(fā)現(xiàn)一點不同。這次,在試圖向Order2添加第三個線項目時依然有異常,這將回滾試圖添加第三個線項目的事務(wù)。而這個方法又調(diào)用了log()方法。但是由于它的PROPAGATION_SUPPORTS TX屬性,log()將在與addLineItem()方法相同的TX上下文中調(diào)用。由于addLineItem()回滾,log()也回滾了,沒有留下回滾的TX的審計跟蹤。所以在圖9中沒有對應(yīng)于失敗TX的審計跟蹤記錄項!

9 創(chuàng)建在appfuse2數(shù)據(jù)庫中的審計跟蹤,沒有失敗TX記錄項

  我們改動的僅僅是Spring配置文件中的TX屬性,就產(chǎn)生了這樣不同的事務(wù)行為,如下所示:

<bean id="auditManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionAttributes"><props><!-- prop key="log">PROPAGATION_REQUIRES_NEW</prop --><prop key="log">PROPAGATION_SUPPORTS</prop></props></property></bean>

  這就是聲明式事務(wù)管理的效果。自從EJB出現(xiàn)以來我們就一直在使用這種方法,但是我們需要一個高端的應(yīng)用服務(wù)器來駐留EJB組件?,F(xiàn)在,我們可以看到,利用Spring,沒有EJB服務(wù)器也可以達到類似的結(jié)果。

結(jié)束語
  這篇文章重點介紹了J2EE領(lǐng)域的強大組合之一:Spring和Hibernate。利用二者的功能,現(xiàn)在對于容器管理持久性(Container-Managed Persistence,CMP)、容器管理關(guān)系(Container-Managed Relationships,CMR)和聲明式事務(wù)管理,我們多了一種技術(shù)選擇。雖然Spring不能視為EJB的替代方案,但是它提供的許多功能,例如普通Java對象的聲明式事務(wù)管理,使得在許多項目中沒有EJB也完全可以。

  本文的目的不是要尋找EJB的替代方案,而是為當前的問題找出一個最可行的技術(shù)方案。至于Spring和Hibernate的輕量級組合的更多功能,就留給我們的讀者去探索了。

  參考資料

  原文出處 Wire Hibernate Transactions in Spring http://www.onjava.com/pub/a/onjava/2005/05/18/swingxactions.html

 作者簡介
Binildas Christudas是工程學學士和系統(tǒng)學碩士,他還是Sun公司認證的Java平臺程序員、軟件開發(fā)員和企業(yè)級的系統(tǒng)架構(gòu)師。他有6年IT行業(yè)的從業(yè)經(jīng)驗,目前供職于一家印度IT公司 —— 塔塔咨詢服務(wù)公司,為瑞士航空、Sabena航空、新加坡航空、荷蘭皇家航空公司等提供咨詢服務(wù)。對于許多新一代航空系統(tǒng)的設(shè)計和開發(fā),他的經(jīng)驗非常豐富。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
生活服務(wù)
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服