posts - 495,  comments - 11,  trackbacks - 0
           
          ??????? 切面(Aspect): 一個關(guān)注點的模塊化,這個關(guān)注點可能會橫切多個對象。事務(wù)管理是J2EE應(yīng)用中一個關(guān)于橫切關(guān)注點的很好的例子。 在Spring AOP中,切面可以使用通用類(基于模式的風(fēng)格) 或者在普通類中以 @Aspect 注解(@AspectJ風(fēng)格)來實現(xiàn)。

          ??????? 連接點(Joinpoint): 在程序執(zhí)行過程中某個特定的點,比如某方法調(diào)用的時候或者處理異常的時候。 在Spring AOP中,一個連接點 總是 代表一個方法的執(zhí)行。 通過聲明一個org.aspectj.lang.JoinPoint類型的參數(shù)可以使通知(Advice)的主體部分獲得連接點信息。

          ??????? 通知(Advice): 在切面的某個特定的連接點(Joinpoint)上執(zhí)行的動作。通知有各種類型,其中包括“around”、“before”和“after”等通知。 通知的類型將在后面部分進行討論。許多AOP框架,包括Spring,都是以攔截器做通知模型, 并維護一個以連接點為中心的攔截器鏈。

          ??????? 切入點(Pointcut): 匹配連接點(Joinpoint)的斷言。通知和一個切入點表達式關(guān)聯(lián),并在滿足這個切入點的連接點上運行(例如,當執(zhí)行某個特定名稱的方法時)。 切入點表達式如何和連接點匹配是AOP的核心:Spring缺省使用AspectJ切入點語法。

          ??????? 引入(Introduction): (也被稱為內(nèi)部類型聲明(inter-type declaration))。聲明額外的方法或者某個類型的字段。 Spring允許引入新的接口(以及一個對應(yīng)的實現(xiàn))到任何被代理的對象。 例如,你可以使用一個引入來使bean實現(xiàn) IsModified 接口,以便簡化緩存機制。

          ??????? 目標對象(Target Object): 被一個或者多個切面(aspect)所通知(advise)的對象。也有人把它叫做 被通知(advised) 對象。 既然Spring AOP是通過運行時代理實現(xiàn)的,這個對象永遠是一個 被代理(proxied) 對象。

          ??????? AOP代理(AOP Proxy): AOP框架創(chuàng)建的對象,用來實現(xiàn)切面契約(aspect contract)(包括通知方法執(zhí)行等功能)。 在Spring中,AOP代理可以是JDK動態(tài)代理或者CGLIB代理。 注意:Spring 2.0最新引入的基于模式(schema-based)風(fēng)格和@AspectJ注解風(fēng)格的切面聲明,對于使用這些風(fēng)格的用戶來說,代理的創(chuàng)建是透明的。

          ??????? 織入(Weaving): 把切面(aspect)連接到其它的應(yīng)用程序類型或者對象上,并創(chuàng)建一個被通知(advised)的對象。 這些可以在編譯時(例如使用AspectJ編譯器),類加載時和運行時完成。 Spring和其他純JavaAOP框架一樣,在運行時完成織入。

          ??????? 通知的類型:

          ??????? 前置通知(Before advice): 在某連接點(join point)之前執(zhí)行的通知,但這個通知不能阻止連接點前的執(zhí)行(除非它拋出一個異常)。

          ??????? 返回后通知(After returning advice): 在某連接點(join point)正常完成后執(zhí)行的通知:例如,一個方法沒有拋出任何異常,正常返回。

          ??????? 拋出異常后通知(After throwing advice): 在方法拋出異常退出時執(zhí)行的通知。

          ??????? 后通知(After (finally) advice): 當某連接點退出的時候執(zhí)行的通知(不論是正常返回還是異常退出)。

          ??????? 環(huán)繞通知(Around Advice): 包圍一個連接點(join point)的通知,如方法調(diào)用。這是最強大的一種通知類型。 環(huán)繞通知可以在方法調(diào)用前后完成自定義的行為。它也會選擇是否繼續(xù)執(zhí)行連接點或直接返回它們自己的返回值或拋出異常來結(jié)束執(zhí)行。

          ===============================================

          1.切面(aspect):要實現(xiàn)的交叉功能,是系統(tǒng)模塊化的一個切面或領(lǐng)域。如日志記錄。


          2.連接點:應(yīng)用程序執(zhí)行過程中插入切面的地點,可以是方法調(diào)用,異常拋出,或者要修改的字段。


          3.通知:切面的實際實現(xiàn),他通知系統(tǒng)新的行為。如在日志通知包含了實現(xiàn)日志功能的代碼,如向日志文件寫日志。通知在連接點插入到應(yīng)用系統(tǒng)中。


          4.切入點:定義了通知應(yīng)該應(yīng)用在哪些連接點,通知可以應(yīng)用到AOP框架支持的任何連接點。


          5.引入:為類添加新方法和屬性。


          6.目標對象:被通知的對象。既可以是你編寫的類也可以是第三方類。


          7.代理:將通知應(yīng)用到目標對象后創(chuàng)建的對象,應(yīng)用系統(tǒng)的其他部分不用為了支持代理對象而改變。


          8.織入:將切面應(yīng)用到目標對象從而創(chuàng)建一個新代理對象的過程。織入發(fā)生在目標對象生命周期的多個點上:

          編譯期:切面在目標對象編譯時織入.這需要一個特殊的編譯器.
          類裝載期:切面在目標對象被載入JVM時織入.這需要一個特殊的類載入器.
          運行期:切面在應(yīng)用系統(tǒng)運行時織入.

          posted @ 2009-07-20 00:41 jadmin 閱讀(85) | 評論 (0)編輯 收藏

          ?????? 數(shù)據(jù)庫查詢性能的提升也是涉及到開發(fā)中的各個階段,在開發(fā)中選用正確的查詢方法無疑是最基礎(chǔ)也最簡單的。

          SQL語句的優(yōu)化

          ?????? 使用正確的SQL語句可以在很大程度上提高系統(tǒng)的查詢性能。獲得同樣數(shù)據(jù)而采用不同方式的SQL語句在性能上的差距可能是十分巨大的。

          ?????? 由于Hibernate是對JDBC的封裝,SQL語句的產(chǎn)生都是動態(tài)由Hibernate自動完成的。Hibernate產(chǎn)生SQL語句的方式有兩種:一種是通過開發(fā)人員編寫的HQL語句來生成,另一種是依據(jù)開發(fā)人員對關(guān)聯(lián)對象的訪問來自動生成相應(yīng)的SQL語句。

          ?????? 至于使用什么樣的SQL語句可以獲得更好的性能要依據(jù)數(shù)據(jù)庫的結(jié)構(gòu)以及所要獲取數(shù)據(jù)的具體情況來進行處理。在確定了所要執(zhí)行的SQL語句后,可以通過以下三個方面來影響Hibernate所生成的SQL語句:

          ●?? HQL語句的書寫方法。

          ●?? 查詢時所使用的查詢方法。

          ●?? 對象關(guān)聯(lián)時所使用的抓取策略。

          使用正確的查詢方法

          ?????? 在前面已經(jīng)介紹過,執(zhí)行數(shù)據(jù)查詢功能的基本方法有兩種:一種是得到單個持久化對象的get()方法和load()方法,另一種是Query對象的list()方法和iterator()方法。在開發(fā)中應(yīng)該依據(jù)不同的情況選用正確的方法。

          ?????? get()方法和load()方法的區(qū)別在于對二級緩存的使用上。load()方法會使用二級緩存,而get()方法在一級緩存沒有找到的情況下會直接查詢數(shù)據(jù)庫,不會去二級緩存中查找。在使用中,對使用了二級緩存的對象進行查詢時最好使用load()方法,以充分利用二級緩存來提高檢索的效率。

          ?????? list()方法和iterator()方法之間的區(qū)別可以從以下幾個方面來進行比較。

          ●?? 執(zhí)行的查詢不同

          ?????? list()方法在執(zhí)行時,是直接運行查詢結(jié)果所需要的查詢語句,而iterator()方法則是先執(zhí)行得到對象ID的查詢,然后再根據(jù)每個ID值去取得所要查詢的對象。因此,對于list()方式的查詢通常只會執(zhí)行一個SQL語句,而對于iterator()方法的查詢則可能需要執(zhí)行N+1條SQL語句(N為結(jié)果集中的記錄數(shù))。

          ?????? iterator()方法只是可能執(zhí)行N+1條數(shù)據(jù),具體執(zhí)行SQL語句的數(shù)量取決于緩存的情況以及對結(jié)果集的訪問情況。

          ●?? 緩存的使用

          ?????? list()方法只能使用二級緩存中的查詢緩存,而無法使用二級緩存對單個對象的緩存(但是會把查詢出的對象放入二級緩存中)。所以,除非重復(fù)執(zhí)行相同的查詢操作,否則無法利用緩存的機制來提高查詢的效率。

          ?????? iterator()方法則可以充分利用二級緩存,在根據(jù)ID檢索對象的時候會首先到緩存中查找,只有在找不到的情況下才會執(zhí)行相應(yīng)的查詢語句。所以,緩存中對象的存在與否會影響到SQL語句的執(zhí)行數(shù)量。

          ●?? 對于結(jié)果集的處理方法不同

          ?????? list()方法會一次獲得所有的結(jié)果集對象,而且它會依據(jù)查詢的結(jié)果初始化所有的結(jié)果集對象。這在結(jié)果集非常大的時候必然會占據(jù)非常多的內(nèi)存,甚至?xí)斐蓛?nèi)存溢出情況的發(fā)生。

          ?????? iterator()方法在執(zhí)行時不會一次初始化所有的對象,而是根據(jù)對結(jié)果集的訪問情況來初始化對象。因此在訪問中可以控制緩存中對象的數(shù)量,以避免占用過多緩存,導(dǎo)致內(nèi)存溢出情況的發(fā)生。使用iterator()方法的另外一個好處是,如果只需要結(jié)果集中的部分記錄,那么沒有被用到的結(jié)果對象根本不會被初始化。所以,對結(jié)果集的訪問情況也是調(diào)用iterator()方法時執(zhí)行數(shù)據(jù)庫SQL語句多少的一個因素。

          ?????? 所以,在使用Query對象執(zhí)行數(shù)據(jù)查詢時應(yīng)該從以上幾個方面去考慮使用何種方法來執(zhí)行數(shù)據(jù)庫的查詢操作。

          使用正確的抓取策略

          ?????? 所謂抓取策略(fetching strategy)是指當應(yīng)用程序需要利用關(guān)聯(lián)關(guān)系進行對象獲取的時候,Hibernate獲取關(guān)聯(lián)對象的策略。抓取策略可以在O/R映射的元數(shù)據(jù)中聲明,也可以在特定的HQL或條件查詢中聲明。

          ?????? Hibernate 3定義了以下幾種抓取策略。

          ●?? 連接抓取(Join fetching)

          ?????? 連接抓取是指Hibernate在獲得關(guān)聯(lián)對象時會在SELECT語句中使用外連接的方式來獲得關(guān)聯(lián)對象。

          ●?? 查詢抓取(Select fetching)

          ?????? 查詢抓取是指Hibernate通過另外一條SELECT語句來抓取當前對象的關(guān)聯(lián)對象的方式。這也是通過外鍵的方式來執(zhí)行數(shù)據(jù)庫的查詢。與連接抓取的區(qū)別在于,通常情況下這個SELECT語句不是立即執(zhí)行的,而是在訪問到關(guān)聯(lián)對象的時候才會執(zhí)行。

          ●?? 子查詢抓取(Subselect fetching)

          ?????? 子查詢抓取也是指Hibernate通過另外一條SELECT語句來抓取當前對象的關(guān)聯(lián)對象的方式。與查詢抓取的區(qū)別在于它所采用的SELECT語句的方式為子查詢,而不是通過外連接。

          ●?? 批量抓取(Batch fetching)

          ?????? 批量抓取是對查詢抓取的優(yōu)化,它會依據(jù)主鍵或者外鍵的列表來通過單條SELECT語句實現(xiàn)管理對象的批量抓取。

          以上介紹的是Hibernate 3所提供的抓取策略,也就是抓取關(guān)聯(lián)對象的手段。為了提升系統(tǒng)的性能,在抓取關(guān)聯(lián)對象的時機上,還有以下一些選擇。

          ●?? 立即抓取(Immediate fetching)

          ?????? 立即抓取是指宿主對象被加載時,它所關(guān)聯(lián)的對象也會被立即加載。

          ●?? 延遲集合抓取(Lazy collection fetching)

          ?????? 延遲集合抓取是指在加載宿主對象時,并不立即加載它所關(guān)聯(lián)的對象,而是到應(yīng)用程序訪問關(guān)聯(lián)對象的時候才抓取關(guān)聯(lián)對象。這是集合關(guān)聯(lián)對象的默認行為。

          ●?? 延遲代理抓取(Lazy proxy fetching)

          ?????? 延遲代理抓取是指在返回單值關(guān)聯(lián)對象的情況下,并不在對其進行g(shù)et操作時抓取,而是直到調(diào)用其某個方法的時候才會抓取這個對象。

          ●?? 延遲屬性加載(Lazy attribute fetching)

          ?????? 延遲屬性加載是指在關(guān)聯(lián)對象被訪問的時候才進行關(guān)聯(lián)對象的抓取。

          ?????? 介紹了Hibernate所提供的關(guān)聯(lián)對象的抓取方法和抓取時機,這兩個方面的因素都會影響Hibernate的抓取行為,最重要的是要清楚這兩方面的影響是不同的,不要將這兩個因素混淆,在開發(fā)中要結(jié)合實際情況選用正確的抓取策略和合適的抓取時機。

          ?????? 抓取時機的選擇

          ?????? 在Hibernate 3中,對于集合類型的關(guān)聯(lián)在默認情況下會使用延遲集合加載的抓取時機,而對于返回單值類型的關(guān)聯(lián)在默認情況下會使用延遲代理抓取的抓取時機。

          ?????? 對于立即抓取在開發(fā)中很少被用到,因為這很可能會造成不必要的數(shù)據(jù)庫操作,從而影響系統(tǒng)的性能。當宿主對象和關(guān)聯(lián)對象總是被同時訪問的時候才有可能會用到這種抓取時機。另外,使用立即連接抓取可以通過外連接來減少查詢SQL語句的數(shù)量,所以,也會在某些特殊的情況下使用。

          ?????? 然而,延遲加載又會面臨另外一個問題,如果在Session關(guān)閉前關(guān)聯(lián)對象沒有被實例化,那么在訪問關(guān)聯(lián)對象的時候就會拋出異常。處理的方法就是在事務(wù)提交之前就完成對關(guān)聯(lián)對象的訪問。

          ?????? 所以,在通常情況下都會使用延遲的方式來抓取關(guān)聯(lián)的對象。因為每個立即抓取都會導(dǎo)致關(guān)聯(lián)對象的立即實例化,太多的立即抓取關(guān)聯(lián)會導(dǎo)致大量的對象被實例化,從而占用過多的內(nèi)存資源。

          ?????? 抓取策略的選取

          ?????? 對于抓取策略的選取將影響到抓取關(guān)聯(lián)對象的方式,也就是抓取關(guān)聯(lián)對象時所執(zhí)行的SQL語句。這就要根據(jù)實際的業(yè)務(wù)需求、數(shù)據(jù)的數(shù)量以及數(shù)據(jù)庫的結(jié)構(gòu)來進行選擇了。

          在這里需要注意的是,通常情況下都會在執(zhí)行查詢的時候針對每個查詢來指定對其合適的抓取策略。指定抓取策略的方法如下所示:

          ?????? User user = (User) session.createCriteria(User.class)

          ??????????? ?????? .setFetchMode("permissions", FetchMode.JOIN)

          ??????????? ?????? .add( Restrictions.idEq(userId) )

          ??????????? ?????? .uniqueResult();

          ?????? 本文介紹了查詢性能提升的方法,關(guān)鍵是如何通過優(yōu)化SQL語句來提升系統(tǒng)的查詢性能。查詢方法和抓取策略的影響也是通過執(zhí)行查詢方式和SQL語句的多少來改變系統(tǒng)的性能的。這些都屬于開發(fā)人員所應(yīng)該掌握的基本技能,避免由于開發(fā)不當而導(dǎo)致系統(tǒng)性能的低下。

          ?????? 在性能調(diào)整中,除了前面介紹的執(zhí)行SQL語句的因素外,對于緩存的使用也會影響系統(tǒng)的性能。通常來說,緩存的使用會增加系統(tǒng)查詢的性能,而降低系統(tǒng)增加、修改和刪除操作的性能(因為要進行緩存的同步處理)。所以,開發(fā)人員應(yīng)該能夠正確地使用有效的緩存來提高數(shù)據(jù)查詢的性能,而要避免濫用緩存而導(dǎo)致的系統(tǒng)性能變低。在采用緩存的時候也應(yīng)該注意調(diào)整自己的檢索策略和查詢方法,這三者配合起來才可以達到最優(yōu)的性能。

          ?????? 另外,事務(wù)的使用策略也會影響到系統(tǒng)的性能。選取正確的事務(wù)隔離級別以及使用正確的鎖機制來控制數(shù)據(jù)的并發(fā)訪問都會影響到系統(tǒng)的性能。

          posted @ 2009-07-19 21:36 jadmin 閱讀(80) | 評論 (0)編輯 收藏

          ?????? Hibernate是對JDBC的輕量級封裝,因此在很多情況下Hibernate的性能比直接使用JDBC存取數(shù)據(jù)庫要低。然而,通過正確的方法和策略,在使用Hibernate的時候還是可以非常接近直接使用JDBC時的效率的,并且,在有些情況下還有可能高于使用JDBC時的執(zhí)行效率。

          ?????? 在進行Hibernate性能優(yōu)化時,需要從以下幾個方面進行考慮:

          ●?? 數(shù)據(jù)庫設(shè)計調(diào)整。

          ●?? HQL優(yōu)化。

          ●?? API的正確使用(如根據(jù)不同的業(yè)務(wù)類型選用不同的集合及查詢API)。

          ●?? 主配置參數(shù)(日志、查詢緩存、fetch_size、batch_size等)。

          ●?? 映射文件優(yōu)化(ID生成策略、二級緩存、延遲加載、關(guān)聯(lián)優(yōu)化)。

          ●?? 一級緩存的管理。

          ●?? 針對二級緩存,還有許多特有的策略。

          ●?? 事務(wù)控制策略。

          ?????? 數(shù)據(jù)的查詢性能往往是影響一個應(yīng)用系統(tǒng)性能的主要因素。對查詢性能的影響會涉及到系統(tǒng)軟件開發(fā)的各個階段,例如,良好的設(shè)計、正確的查詢方法、適當?shù)木彺娑加欣谙到y(tǒng)性能的提升。

          ?????? 系統(tǒng)性能的提升設(shè)計到系統(tǒng)中的各個方面,是一個相互平衡的過程,需要在應(yīng)用的各個階段都要考慮。并且在開發(fā)、運行的過程中要不斷地調(diào)整和優(yōu)化才能逐步提升系統(tǒng)的性能。

          posted @ 2009-07-19 21:30 jadmin 閱讀(52) | 評論 (0)編輯 收藏

          ?????? 在前面介紹了Hibernate的緩存技術(shù)以及基本的用法,在這里就具體的Hibernate所提供的查詢方法與Hibernate緩存之間的關(guān)系做一個簡單的總結(jié)。

          ?????? 在開發(fā)中,通常是通過兩種方式來執(zhí)行對數(shù)據(jù)庫的查詢操作的。一種方式是通過ID來獲得單獨的Java對象,另一種方式是通過HQL語句來執(zhí)行對數(shù)據(jù)庫的查詢操作。下面就分別結(jié)合這兩種查詢方式來說明一下緩存的作用。

          ?????? 通過ID來獲得Java對象可以直接使用Session對象的load()或者get()方法,這兩種方式的區(qū)別就在于對緩存的使用上。

          ●?? load()方法

          ?????? 在使用了二級緩存的情況下,使用load()方法會在二級緩存中查找指定的對象是否存在。

          在執(zhí)行l(wèi)oad()方法時,Hibernate首先從當前Session的一級緩存中獲取ID對應(yīng)的值,在獲取不到的情況下,將根據(jù)該對象是否配置了二級緩存來做相應(yīng)的處理。

          ?????? 如配置了二級緩存,則從二級緩存中獲取ID對應(yīng)的值,如仍然獲取不到則還需要根據(jù)是否配置了延遲加載來決定如何執(zhí)行,如未配置延遲加載則從數(shù)據(jù)庫中直接獲取。在從數(shù)據(jù)庫獲取到數(shù)據(jù)的情況下,Hibernate會相應(yīng)地填充一級緩存和二級緩存,如配置了延遲加載則直接返回一個代理類,只有在觸發(fā)代理類的調(diào)用時才進行數(shù)據(jù)庫的查詢操作。

          ?????? 在Session一直打開的情況下,并在該對象具有單向關(guān)聯(lián)維護的時候,需要使用類似Session.clear(),Session.evict()的方法來強制刷新一級緩存。

          ●?? get()方法

          ?????? get()方法與load()方法的區(qū)別就在于不會查找二級緩存。在當前Session的一級緩存中獲取不到指定的對象時,會直接執(zhí)行查詢語句從數(shù)據(jù)庫中獲得所需要的數(shù)據(jù)。

          ?????? 在Hibernate中,可以通過HQL來執(zhí)行對數(shù)據(jù)庫的查詢操作。具體的查詢是由Query對象的list()和iterator()方法來執(zhí)行的。這兩個方法在執(zhí)行查詢時的處理方法存在著一定的差別,在開發(fā)中應(yīng)該依據(jù)具體的情況來選擇合適的方法。

          ●?? list()方法

          ?????? 在執(zhí)行Query的list()方法時,Hibernate的做法是首先檢查是否配置了查詢緩存,如配置了則從查詢緩存中尋找是否已經(jīng)對該查詢進行了緩存,如獲取不到則從數(shù)據(jù)庫中進行獲取。從數(shù)據(jù)庫中獲取到后,Hibernate將會相應(yīng)地填充一級、二級和查詢緩存。如獲取到的為直接的結(jié)果集,則直接返回,如獲取到的為一些ID的值,則再根據(jù)ID獲取相應(yīng)的值(Session.load()),最后形成結(jié)果集返回。可以看到,在這樣的情況下,list()方法也是有可能造成N次查詢的。

          ?????? 查詢緩存在數(shù)據(jù)發(fā)生任何變化的情況下都會被自動清空。

          ●?? iterator()方法

          ?????? Query的iterator()方法處理查詢的方式與list()方法是不同的,它首先會使用查詢語句得到ID值的列表,然后再使用Session的load()方法得到所需要的對象的值。

          ?????? 在獲取數(shù)據(jù)的時候,應(yīng)該依據(jù)這4種獲取數(shù)據(jù)方式的特點來選擇合適的方法。在開發(fā)中可以通過設(shè)置show_sql選項來輸出Hibernate所執(zhí)行的SQL語句,以此來了解Hibernate是如何操作數(shù)據(jù)庫的。

          posted @ 2009-07-19 21:29 jadmin 閱讀(58) | 評論 (0)編輯 收藏

          查詢緩存

          ?????? 查詢緩存是專門針對各種查詢操作進行緩存。查詢緩存會在整個SessionFactory的生命周期中起作用,存儲的方式也是采用key-value的形式來進行存儲的。

          ?????? 查詢緩存中的key是根據(jù)查詢的語句、查詢的條件、查詢的參數(shù)和查詢的頁數(shù)等信息組成的。而數(shù)據(jù)的存儲則會使用兩種方式,使用SELECT語句只查詢實體對象的某些列或者某些實體對象列的組合時,會直接緩存整個結(jié)果集。而對于查詢結(jié)果為某個實體對象集合的情況則只會緩存實體對象的ID值,以達到緩存空間可以共用,節(jié)省空間的目的。

          ?????? 在使用查詢緩存時,除了需要設(shè)置hibernate.cache.provider_class參數(shù)來啟動二級緩存外,還需要通過hibernate.cache.use_query_cache參數(shù)來啟動對查詢緩存的支持。

          ?????? 另外需要注意的是,查詢緩存是在執(zhí)行查詢語句的時候指定緩存的方式以及是否需要對查詢的結(jié)果進行緩存。

          ?????? 下面就來了解一下查詢緩存的使用方法及作用。

          ?????? 修改Hibernate配置文件

          ?????? 首先需要修改Hibernate的配置文件,增加hibernate.cache.use_query_cache參數(shù)的配置。配置方法如下:

          ?????? <property name="hibernate.cache.use_query_cache">true</property>

          ?????? Hibernate配置文件的詳細內(nèi)容請參考配套光盤中的hibernate\src\cn\hxex\ hibernate\cache\hibernate.cfg.xml文件。

          ?????? 編寫主測試程序

          ?????? 由于這是在前面二級緩存例子的基礎(chǔ)上來開發(fā)的,所以,對于EHCache的配置以及視圖對象的開發(fā)和映射文件的配置工作就都不需要再重新進行了。下面就來看一下主測試程序的實現(xiàn)方法,如清單14.11所示。

          ?????? 清單14.11??? 主程序的實現(xiàn)

          ……

          ??? public void run() {

          ??? ?????? SessionFactory sf = QueryCacheMain.getSessionFactory();

          ??? ?????? Session session = sf.getCurrentSession();

          ??? ?????? session.beginTransaction();

          ??? ?????? Query query = session.createQuery( "from User" );

          ??? ?????? Iterator it = query.setCacheable( true ).list().iterator();

          ??? ?????? while( it.hasNext() ) {

          ??? ????????????? System.out.println( it.next() );

          ??? ?????? }

          ??? ?????? User user = (User)session.get( User.class, "1" );

          ??? ?????? System.out.println( user );

          ??? ?????? session.getTransaction().commit();

          ??? }

          ?????? public static void main(String[] args) {

          ????????????? QueryCacheMain main1 = new QueryCacheMain();

          ????????????? main1.start();

          ????????????? try {

          ???????????????????? Thread.sleep( 2000 );

          ????????????? } catch (InterruptedException e) {

          ???????????????????? e.printStackTrace();

          ????????????? }

          ????????????? QueryCacheMain main2 = new QueryCacheMain();

          ????????????? main2.start();

          ?????? }

          }

          ?????? 主程序在實現(xiàn)的時候采用了多線程的方式來運行。首先將“from User”查詢結(jié)果進行緩存,然后再通過ID取得對象來檢查是否對對象進行了緩存。另外,多個線程的執(zhí)行可以看出對于進行了緩存的查詢是不會執(zhí)行第二次的。

          ?????? 運行測試主程序

          ?????? 接著就來運行測試主程序,其輸出結(jié)果應(yīng)該如下所示:

          Hibernate: select user0_.userId as userId0_, user0_.name as name0_, user0_.age as age0_ from USERINFO user0_

          ID: 1

          Namge:?? galaxy

          Age:?????? 32

          ID: 1

          Namge:?? galaxy

          Age:?????? 32

          ID: 1

          Namge:?? galaxy

          Age:?????? 32

          ID: 1

          Namge:?? galaxy

          Age:?????? 32

          ?????? 通過上面的執(zhí)行結(jié)果可以看到,在兩個線程執(zhí)行中,只執(zhí)行了一個SQL查詢語句。這是因為根據(jù)ID所要獲取的對象在前面的查詢中已經(jīng)得到了,并進行了緩存,所以沒有再次執(zhí)行查詢語句。

          posted @ 2009-07-19 21:25 jadmin 閱讀(67) | 評論 (0)編輯 收藏

          二級緩存

          ?????? 與Session相對的是,SessionFactory也提供了相應(yīng)的緩存機制。SessionFactory緩存可以依據(jù)功能和目的的不同而劃分為內(nèi)置緩存和外置緩存。

          ?????? SessionFactory的內(nèi)置緩存中存放了映射元數(shù)據(jù)和預(yù)定義SQL語句,映射元數(shù)據(jù)是映射文件中數(shù)據(jù)的副本,而預(yù)定義SQL語句是在Hibernate初始化階段根據(jù)映射元數(shù)據(jù)推導(dǎo)出來的。SessionFactory的內(nèi)置緩存是只讀的,應(yīng)用程序不能修改緩存中的映射元數(shù)據(jù)和預(yù)定義SQL語句,因此SessionFactory不需要進行內(nèi)置緩存與映射文件的同步。

          ?????? SessionFactory的外置緩存是一個可配置的插件。在默認情況下,SessionFactory不會啟用這個插件。外置緩存的數(shù)據(jù)是數(shù)據(jù)庫數(shù)據(jù)的副本,外置緩存的介質(zhì)可以是內(nèi)存或者硬盤。SessionFactory的外置緩存也被稱為Hibernate的二級緩存。

          ?????? Hibernate的二級緩存的實現(xiàn)原理與一級緩存是一樣的,也是通過以ID為key的Map來實現(xiàn)對對象的緩存。

          ?????? 由于Hibernate的二級緩存是作用在SessionFactory范圍內(nèi)的,因而它比一級緩存的范圍更廣,可以被所有的Session對象所共享。

          二級緩存的工作內(nèi)容

          ?????? Hibernate的二級緩存同一級緩存一樣,也是針對對象ID來進行緩存。所以說,二級緩存的作用范圍是針對根據(jù)ID獲得對象的查詢。

          ?????? 二級緩存的工作可以概括為以下幾個部分:

          ●?? 在執(zhí)行各種條件查詢時,如果所獲得的結(jié)果集為實體對象的集合,那么就會把所有的數(shù)據(jù)對象根據(jù)ID放入到二級緩存中。

          ●?? 當Hibernate根據(jù)ID訪問數(shù)據(jù)對象的時候,首先會從Session一級緩存中查找,如果查不到并且配置了二級緩存,那么會從二級緩存中查找,如果還查不到,就會查詢數(shù)據(jù)庫,把結(jié)果按照ID放入到緩存中。

          ●?? 刪除、更新、增加數(shù)據(jù)的時候,同時更新緩存。

          二級緩存的適用范圍

          ?????? Hibernate的二級緩存作為一個可插入的組件在使用的時候也是可以進行配置的,但并不是所有的對象都適合放在二級緩存中。

          ?????? 在通常情況下會將具有以下特征的數(shù)據(jù)放入到二級緩存中:

          ●?? 很少被修改的數(shù)據(jù)。

          ●?? 不是很重要的數(shù)據(jù),允許出現(xiàn)偶爾并發(fā)的數(shù)據(jù)。

          ●?? 不會被并發(fā)訪問的數(shù)據(jù)。

          ●?? 參考數(shù)據(jù)。

          ?????? 而對于具有以下特征的數(shù)據(jù)則不適合放在二級緩存中:

          ●?? 經(jīng)常被修改的數(shù)據(jù)。

          ●?? 財務(wù)數(shù)據(jù),絕對不允許出現(xiàn)并發(fā)。

          ●?? 與其他應(yīng)用共享的數(shù)據(jù)。

          ?????? 在這里特別要注意的是對放入緩存中的數(shù)據(jù)不能有第三方的應(yīng)用對數(shù)據(jù)進行更改(其中也包括在自己程序中使用其他方式進行數(shù)據(jù)的修改,例如,JDBC),因為那樣Hibernate將不會知道數(shù)據(jù)已經(jīng)被修改,也就無法保證緩存中的數(shù)據(jù)與數(shù)據(jù)庫中數(shù)據(jù)的一致性。

          二級緩存組件

          ?????? 在默認情況下,Hibernate會使用EHCache作為二級緩存組件。但是,可以通過設(shè)置hibernate.cache.provider_class屬性,指定其他的緩存策略,該緩存策略必須實現(xiàn)org.hibernate.cache.CacheProvider接口。

          ?????? 通過實現(xiàn)org.hibernate.cache.CacheProvider接口可以提供對不同二級緩存組件的支持。

          ?????? Hibernate內(nèi)置支持的二級緩存組件如表14.1所示。

          表14.1??? Hibernate所支持的二級緩存組件

          posted @ 2009-07-19 21:23 jadmin 閱讀(52) | 評論 (0)編輯 收藏

          ?????? 大家都知道,Hibernate是以JDBC為基礎(chǔ)實現(xiàn)的持久層組件,因而其性能肯定會低于直接使用JDBC來訪問數(shù)據(jù)庫。因此,為了提高Hibernate的性能,在Hibernate組件中提供了完善的緩存機制來提高數(shù)據(jù)庫訪問的性能。

          ?????? 什么是緩存

          ?????? 緩存是介于應(yīng)用程序和物理數(shù)據(jù)之間的,其作用是為了降低應(yīng)用程序?qū)ξ锢頂?shù)據(jù)訪問的頻次從而提高應(yīng)用系統(tǒng)的性能。緩存思想的提出主要是因為對物理數(shù)據(jù)的訪問效率要遠遠低于對內(nèi)存的訪問速度,因而采用了將部分物理數(shù)據(jù)存放于內(nèi)存當中,這樣可以有效地減少對物理數(shù)據(jù)的訪問次數(shù),從而提高系統(tǒng)的性能。

          ?????? 緩存廣泛地存在于我們所接觸的各種應(yīng)用系統(tǒng)中,例如數(shù)據(jù)庫系統(tǒng)、Windows操作系統(tǒng)等,在進行物理數(shù)據(jù)的訪問時無一例外地都使用了緩存機制來提高操作的性能。

          ?????? 緩存內(nèi)的數(shù)據(jù)是對物理數(shù)據(jù)的復(fù)制,因此一個緩存系統(tǒng)所應(yīng)該包括的最基本的功能是數(shù)據(jù)的緩存和讀取,同時在使用緩存的時候還要考慮緩存中的數(shù)據(jù)與物理數(shù)據(jù)的同步,也就是要保持兩者是一致的。

          ?????? 緩存要求對數(shù)據(jù)的讀寫速度很高,因此,一般情況下會選用內(nèi)存作為存儲的介質(zhì)。但如果內(nèi)存有限,并且緩存中存放的數(shù)據(jù)量非常大時,也會用硬盤作為緩存介質(zhì)。緩存的實現(xiàn)不僅僅要考慮存儲的介質(zhì),還要考慮到管理緩存的并發(fā)訪問和緩存數(shù)據(jù)的生命周期。

          ?????? 為了提高系統(tǒng)的性能,Hibernate也使用了緩存的機制。在Hibernate框架中,主要包括以下兩個方面的緩存:一級緩存和二級緩存(包含查詢緩存)。Hibernate中緩存的作用主要表現(xiàn)在以下兩個方面:

          ●?? 通過主鍵(ID)加載數(shù)據(jù)的時候

          ●?? 延遲加載中

          一級緩存

          ?????? Hibernate的一級緩存是由Session提供的,因此它只存在于Session的生命周期中,也就是當Session關(guān)閉的時候該Session所管理的一級緩存也會立即被清除。

          ?????? Hibernate的一級緩存是Session所內(nèi)置的,不能被卸載,也不能進行任何配置。

          ?????? 一級緩存采用的是key-value的Map方式來實現(xiàn)的,在緩存實體對象時,對象的主關(guān)鍵字ID是Map的key,實體對象就是對應(yīng)的值。所以說,一級緩存是以實體對象為單位進行存儲的,在訪問的時候使用的是主關(guān)鍵字ID。

          ?????? 雖然,Hibernate對一級緩存使用的是自動維護的功能,沒有提供任何配置功能,但是可以通過Session中所提供的方法來對一級緩存的管理進行手工干預(yù)。Session中所提供的干預(yù)方法包括以下兩種。

          ●?? evict() :用于將某個對象從Session的一級緩存中清除。

          ●?? clear() :用于將一級緩存中的對象全部清除。

          ?????? 在進行大批量數(shù)據(jù)一次性更新的時候,會占用非常多的內(nèi)存來緩存被更新的對象。這時就應(yīng)該階段性地調(diào)用clear()方法來清空一級緩存中的對象,控制一級緩存的大小,以避免產(chǎn)生內(nèi)存溢出的情況。具體的實現(xiàn)方法如清單14.8所示。

          ?????? 清單14.8??? 大批量更新時緩存的處理方法

          Session session = sessionFactory.openSession();

          Transaction tx = session.beginTransaction();

          ??

          for ( int i=0; i<100000; i++ ) {

          ??? Customer customer = new Customer(……);

          ??? session.save(customer);

          ??? if ( i % 20 == 0 ) {

          ??????? //將本批插入的對象立即寫入數(shù)據(jù)庫并釋放內(nèi)存

          ??????? session.flush();

          ??????? session.clear();

          ??? }

          }

          ??

          tx.commit();

          session.close();

          posted @ 2009-07-19 21:18 jadmin 閱讀(93) | 評論 (0)編輯 收藏

          并發(fā)控制

          ?????? 當數(shù)據(jù)庫系統(tǒng)采用Read Committed隔離級別時,會導(dǎo)致不可重復(fù)讀取和兩次更新丟失的并發(fā)問題,可以在應(yīng)用程序中采用鎖機制來避免這類問題的產(chǎn)生。

          ?????? 從應(yīng)用程序的角度上看,鎖可以分為樂觀鎖和悲觀鎖兩大類。

          悲觀鎖

          ?????? 在多個客戶端可能讀取同一筆數(shù)據(jù)或同時更新一筆數(shù)據(jù)的情況下,必須要有訪問控制的手段,防止同一個數(shù)據(jù)被修改而造成混亂,最簡單的手段就是對數(shù)據(jù)進行鎖定。在自己進行數(shù)據(jù)讀取或更新等動作時,鎖定其他客戶端不能對同一筆數(shù)據(jù)進行任何的動作。

          ?????? 悲觀鎖(Pessimistic Locking),如其名稱所示,悲觀地認定每次資料存取時,其他的客戶端也會存取同一筆數(shù)據(jù),因此將會鎖住該筆數(shù)據(jù),直到自己操作完成后再解除鎖。

          ?????? 悲觀鎖假定任何時刻存取數(shù)據(jù)時,都可能有另一個客戶也正在存取同一筆數(shù)據(jù),因而對數(shù)據(jù)采取了數(shù)據(jù)庫層次的鎖定狀態(tài),在鎖定的時間內(nèi)其他的客戶不能對數(shù)據(jù)進行存取。對于單機或小系統(tǒng)而言,這并不成問題,然而如果是在網(wǎng)絡(luò)上的系統(tǒng),同時間會有許多訪問的機器,如果每一次讀取數(shù)據(jù)都造成鎖定,其后繼的存取就必須等待,這將造成效能上的問題,造成后繼使用者的長時間等待。

          ?????? 悲觀鎖通常透過系統(tǒng)或數(shù)據(jù)庫本身的功能來實現(xiàn),依賴系統(tǒng)或數(shù)據(jù)庫本身提供的鎖機制。Hibernate即是如此,可以利用Query或Criteria的setLockMode()方法來設(shè)定要鎖定的表或列及其鎖模式,可設(shè)定的鎖模式有以下幾個。

          ?????? LockMode.UPGRADE:利用數(shù)據(jù)庫的for update子句進行鎖定。

          ?????? LockMode.UPGRADE_NOWAIT:使用for update nowait子句進行鎖定,在Oracle數(shù)據(jù)庫中使用。

          ?????? 下面來實現(xiàn)一個簡單的例子,測試一下采用悲觀鎖時數(shù)據(jù)庫是如何進行操作的。

          ?????? 首先來完成一個實體對象——User,該對象包含了id,name和age三個屬性,實現(xiàn)的方法如清單14.1所示。

          ?????? 清單14.1??? User對象的實現(xiàn)

          package cn.hxex.hibernate.lock;

          public class User {

          ?????? private String id;

          ?????? private String name;

          ?????? private Integer age;

          ??????

          ?????? // 省略了getter和setter方法

          ?????? ……

          }

          ?????? 接下來就是映射文件的配置,由于該映射文件沒有涉及到任何與其他對象的關(guān)聯(lián)配置,所以實現(xiàn)的方法也非常簡單,代碼如清單14.2所示。

          ?????? 清單14.2??? User映射文件的實現(xiàn)

          <?xml version="1.0"?>

          <!DOCTYPE hibernate-mapping PUBLIC

          ?????? "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

          ?????? "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

          <hibernate-mapping package="cn.hxex.hibernate.lock">

          ?????? <class name="User" table="USERINFO">

          ????????????? <id name="id" column="userId">

          ????? ?????????? <generator class="uuid.hex"/>

          ??? ?????? </id>

          ?????????????

          ????????????? <property name="name" column="name" type="java.lang.String"/>

          ????????????? <property name="age" column="age" type="java.lang.Integer"/>

          ?????? </class>

          </hibernate-mapping>

          ?????? 另外一件重要的工作就是Hibernate的配置文件了,在這個配置文件中包含了連接數(shù)據(jù)庫的參數(shù)以及其他一些重要的參數(shù),實現(xiàn)的方法如清單14.3所示。

          ?????? 清單14.3??? Hibernate配置文件的實現(xiàn)

          <?xml version='1.0' encoding='UTF-8'?>

          <!DOCTYPE hibernate-configuration PUBLIC

          ????????? "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

          ????????? "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

          <hibernate-configuration>

          ?????? <session-factory>

          ????????????? <!-- 數(shù)據(jù)庫的URL -->

          ????????????? <!-- property name="hibernate.connection.url">

          ????????????? jdbc:oracle:thin:@192.168.10.121:1521:HiFinance</property-->

          ?????? ?????? <property name="hibernate.connection.url">

          ?????? jdbc:mysql://localhost:3306/lockdb?useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;autoReconnectForPools=true

          ??????? </property>

          ?????????????

          ????????????? <!-- 數(shù)據(jù)庫的驅(qū)動程序 -->

          ????????????? <!-- property name="hibernate.connection.driver_class">

          ????????????? oracle.jdbc.driver.OracleDriver</property-->

          ????????????? <property name="hibernate.connection.driver_class">

          ?????????? com.mysql.jdbc.Driver

          ??????? </property>

          ????????????? <!-- 數(shù)據(jù)庫的用戶名 -->

          ????????????? <property name="hibernate.connection.username">lockdb</property>

          ????????????? <!-- 數(shù)據(jù)庫的密碼 -->

          ????????????? <property name="hibernate.connection.password">lockdb</property>

          ????????????? <!-- 數(shù)據(jù)庫的Dialect -->

          ????????????? <!-- property name="hibernate.dialect">

          ????????????? org.hibernate.dialect.Oracle9Dialect</property -->

          ????????????? <property name="hibernate.dialect">

          ????????????? org.hibernate.dialect.MySQLDialect</property>

          ????????????? <!-- 輸出執(zhí)行的SQL語句 -->

          ????????????? <property name="hibernate.show_sql">true</property>

          ?????????????

          ????????????? <property name="hibernate.current_session_context_class">thread</property>

          ?????????????

          ????????????? <property name="hibernate.hbm2ddl.auto">update</property>

          ????????????? <!-- HBM文件列表 -->

          ????????????? <mapping resource="cn/hxex/hibernate/lock/User.hbm.xml" />

          ?????? </session-factory>

          </hibernate-configuration>

          ?????? 最后要實現(xiàn)的就是測試主程序了,在測試主程序中包含了Hibernate的初始化代碼以及悲觀鎖的測試方法。測試主程序的實現(xiàn)方法如清單14.4所示。

          ?????? 清單14.4??? 測試主程序的實現(xiàn)

          package cn.hxex.hibernate.lock;

          import java.net.URL;

          import java.util.List;

          import org.apache.commons.logging.Log;

          import org.apache.commons.logging.LogFactory;

          import org.hibernate.LockMode;

          import org.hibernate.Query;

          import org.hibernate.Session;

          import org.hibernate.SessionFactory;

          import org.hibernate.cfg.Configuration;

          public class LockMain {

          ??? private static Log log = LogFactory.getLog( LockMain.class );

          ??? // 靜態(tài)Configuration和SessionFactory對象的實例(全局唯一的)

          ??? private static Configuration configuration;

          ??? private static SessionFactory sessionFactory;

          ??? static

          ??? {

          ??????? // 從默認的配置文件創(chuàng)建SessionFactory

          ??????? try

          ??????? {

          ?????????????? ?????????? URL configURL = ClassLoader.getSystemResource(

          ?????????????? ??????????????? "cn/hxex/hibernate/lock/hibernate.cfg.xml" );

          ?????????????? ?????????? // 創(chuàng)建默認的Configuration對象的實例

          ??????????? configuration = new Configuration();

          ??????????? // 讀取hibernate.properties或者hibernate.cfg.xml文件

          ??????????? configuration.configure( configURL );

          ??????????? // 使用靜態(tài)變量來保持SessioFactory對象的實例

          ??????????? sessionFactory = configuration.buildSessionFactory();

          ??????? }

          ??????? catch (Throwable ex)

          ??????? {

          ??????????? // 輸出異常信息

          ??????????? log.error("Building SessionFactory failed.", ex);

          ??????????? ex.printStackTrace();

          ??????????? throw new ExceptionInInitializerError(ex);

          ??????? }

          ??? }

          ??????

          ??? public static SessionFactory getSessionFactory() {

          ???? ???? ??????? return sessionFactory;

          ??? }

          ???

          ??? public void testPessimisticLock() {

          ????????????? SessionFactory sf = LockMain.getSessionFactory();

          ????????????? Session session = sf.getCurrentSession();

          ????????????? session.beginTransaction();

          ?????? ??????

          ????????????? Query query = session.createQuery("from User user");

          ????????????? query.setLockMode("user", LockMode.UPGRADE);

          ????????????? List users = query.list();

          ????????????? for( int i=0; i<users.size(); i++ ) {

          ???????????????????? System.out.println( users.get( i ) );

          ????????????? }

          ????????????? session.getTransaction().commit();

          ??? }

          ???

          ?????? public static void main(String[] args) {

          ?????????????

          ????????????? LockMain main = new LockMain();

          ????????????? main.testPessimisticLock();

          ?????? }

          }

          ?????? 在上面的清單中,testPessimisticLock()方法就是測試悲觀鎖的方法,該方法在執(zhí)行查詢之前通過Query對象的setLockMode()方法設(shè)置了訪問User對象的模式,這樣,這個程序在執(zhí)行的時候就會使用以下的SQL語句:

          ?????? select user0_.userId as userId0_, user0_.name as name0_, user0_.age as age0_

          ?????? from USERINFO user0_ for update

          ?????? 除了Query對象外,也可以在使用Session的load()或是lock()時指定鎖模式。

          ?????? 除了前面所提及的兩種鎖模式外,還有三種Hibernate內(nèi)部自動對數(shù)據(jù)進行加鎖的模式,但它的處理是與數(shù)據(jù)庫無關(guān)的。

          ?????? LockMode.WRITE:在insert或update時進行鎖定,Hibernate會在調(diào)用save()方法時自動獲得鎖。

          ?????? LockMode.READ:在讀取記錄時Hibernate會自動獲得鎖。

          ?????? LockMode.NONE:沒有鎖。

          ?????? 如果數(shù)據(jù)庫不支持所指定的鎖模式,Hibernate會選擇一個合適的鎖替換,而不是拋出一個異常。

          樂觀鎖

          ?????? 樂觀鎖(Optimistic Locking)認為資料的存取很少發(fā)生同時存取的問題,因而不做數(shù)據(jù)庫層次上的鎖定。為了維護正確的數(shù)據(jù),樂觀鎖是使用應(yīng)用程序上的邏輯來實現(xiàn)版本控制的。

          在使用樂觀鎖策略的情況下,數(shù)據(jù)不一致的情況一旦發(fā)生,有幾個解決方法,一種是先更新為主,一種是后更新為主,比較復(fù)雜的就是檢查發(fā)生變動的數(shù)據(jù)來實現(xiàn),或是檢查所有屬性來實現(xiàn)樂觀鎖。

          ?????? Hibernate中通過檢查版本號來判斷數(shù)據(jù)是否已經(jīng)被其他人所改動,這也是Hibernate所推薦的方式。在數(shù)據(jù)庫中加入一個version字段記錄,在讀取數(shù)據(jù)時連同版本號一同讀取,并在更新數(shù)據(jù)時比較版本號與數(shù)據(jù)庫中的版本號,如果等于數(shù)據(jù)庫中的版本號則予以更新,并遞增版本號,如果小于數(shù)據(jù)庫中的版本號就拋出異常。

          ?????? 下面就來在前面例子的基礎(chǔ)上進行Hibernate樂觀鎖的測試。

          ?????? 首先需要修改前面所實現(xiàn)的業(yè)務(wù)對象,在其中增加一個version屬性,用來記錄該對象所包含數(shù)據(jù)的版本信息,修改后的User對象如清單14.5所示。

          ?????? 清單14.5??? 修改后的User對象

          package cn.hxex.hibernate.lock;

          public class User {

          ?????? private String id;

          ?????? private Integer version; // 增加版本屬性

          ?????? private String name;

          ?????? private Integer age;

          ??????

          ?????? // 省略了getter和setter方法

          ?????? ……

          }

          ?????? 然后是修改映射文件,增加version屬性的配置。在這里需要注意的是,這里的version屬性應(yīng)該使用專門的<version>元素來進行配置,這樣才能使其發(fā)揮樂觀鎖的作用。如果還使用<property>元素來進行配置,那么Hibernate只會將其作為一個普通的屬性來進行處理。

          修改后的映射文件如清單14.6所示。

          ?????? 清單14.6??? 修改后的映射文件

          <?xml version="1.0"?>

          <!DOCTYPE hibernate-mapping PUBLIC

          ?????? "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

          ?????? "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

          <hibernate-mapping package="cn.hxex.hibernate.lock">

          ?????? <class name="User" table="USERINFO" optimistic-lock="version">

          ????????????? <id name="id" column="userId">

          ????? ?????????? <generator class="uuid.hex"/>

          ??? ?????? </id>

          ?????????????

          ????????????? <version name="version" column="version" type="java.lang.Integer"/>

          ?????????????

          ????????????? <property name="name" column="name" type="java.lang.String"/>

          ????????????? <property name="age" column="age" type="java.lang.Integer"/>

          ?????? </class>

          </hibernate-mapping>

          ?????? 接下來還要進行測試主程序的修改。由于需要模擬兩個人同時修改同一個記錄的情況,所以在這里需要將主程序修改為是可以多線程執(zhí)行的,然后在run()方法中,調(diào)用對User對象的修改程序。

          ?????? 實現(xiàn)后的主測試程序如清單14.7所示。

          ?????? 清單14.7??? 修改后的測試主程序

          package cn.hxex.hibernate.lock;

          import java.net.URL;

          import java.util.List;

          import org.apache.commons.logging.Log;

          import org.apache.commons.logging.LogFactory;

          import org.hibernate.LockMode;

          import org.hibernate.Query;

          import org.hibernate.Session;

          import org.hibernate.SessionFactory;

          import org.hibernate.Transaction;

          import org.hibernate.cfg.Configuration;

          public class LockMain extends Thread{

          ……

          ??? public void testOptimisticLock() {

          ??? ?????? SessionFactory sf = LockMain.getSessionFactory();

          ??? ?????? Session session = sf.openSession();

          ??? ?????? Transaction tx = session.beginTransaction();

          ??? ??????

          ??? ?????? User userV1 = (User)session.load( User.class, "1" );

          ??? ??????

          ??? ?????? // 等第二個進程執(zhí)行

          ??? ?????? try {

          ???????????????????? sleep( 3000 );

          ????????????? } catch (InterruptedException e) {

          ???????????????????? e.printStackTrace();

          ????????????? }

          ??? ??????

          ????????????? userV1.setAge(new Integer(32));

          ????????????? tx.commit();

          ????????????? session.close();

          ??? }

          ???

          ??? public void run() {

          ??? ????????????? testOptimisticLock();

          ??? }

          ???

          ?????? public static void main(String[] args) {

          ?????????????

          ????????????? // LockMain main = new LockMain();

          ????????????? // main.testPessimisticLock();

          ?????????????

          ????????????? LockMain main1 = new LockMain();

          ????????????? main1.start();

          ????????????? LockMain main2 = new LockMain();

          ????????????? main2.start();

          ?????? }

          }

          ?????? 最后,執(zhí)行測試主程序,在控制臺中應(yīng)該看到類似下面的輸出:

          Hibernate: select user0_.userId as userId0_0_, user0_.version as version0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from USERINFO user0_ where user0_.userId=?

          Hibernate: select user0_.userId as userId0_0_, user0_.version as version0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from USERINFO user0_ where user0_.userId=?

          Hibernate: update USERINFO set version=?, name=?, age=? where userId=? and version=?

          Hibernate: update USERINFO set version=?, name=?, age=? where userId=? and version=?

          2006-10-3 21:27:20 org.hibernate.event.def.AbstractFlushingEventListener performExecutions

          嚴重: Could not synchronize database state with session

          org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [cn.hxex.hibernate.lock.User#1]

          ……

          ?????? 在Hibernate所執(zhí)行的UPDATE語句中可以看到,version字段是作為更新的條件來執(zhí)行的。對于第二個進程來說,由于數(shù)據(jù)庫中的記錄已經(jīng)被第一個進程更新(更新的同時會導(dǎo)致version自動增加),就必然會導(dǎo)致第二個進程操作的失敗。Hibernate正是利用這種機制來避免兩次更新問題的出現(xiàn)。

          posted @ 2009-07-19 21:11 jadmin 閱讀(164) | 評論 (0)編輯 收藏

          ?????? 在現(xiàn)在的B/S體系結(jié)構(gòu)的軟件開發(fā)中,對于數(shù)據(jù)庫事務(wù)處理中最常使用的方式是每個用戶請求一個事務(wù)。也就是說,當服務(wù)器端接收到一個用戶請求后,會開始一個新的事務(wù),直到對用戶請求的所有處理都進行完畢并且完成了響應(yīng)用戶請求的所有輸出之后才會關(guān)閉這個事務(wù)。

          ?????? 對于使用Hibernate實現(xiàn)持久化功能的系統(tǒng)來說,事務(wù)的處理是這樣的:服務(wù)器端在接收到用戶的請求后,會創(chuàng)建一個新的Hibernate Session對象,然后通過該Session對象開始一個新的事務(wù)并且之后所有對數(shù)據(jù)庫的操作都通過該Session對象來進行。最后,完成將響應(yīng)頁面發(fā)送到客戶端的工作后再提交事務(wù)并且關(guān)閉Session。

          ?????? Session的對象是輕型的,非線程安全的,所以在每次用戶請求時創(chuàng)建,請求處理完畢后丟棄。

          ?????? 那么,該如何實現(xiàn)這種方式的事務(wù)處理呢?處理的難點在于如何在業(yè)務(wù)處理之前創(chuàng)建Session并開始事務(wù)以及在業(yè)務(wù)處理之后提交事務(wù)并關(guān)閉Session。對于現(xiàn)在的Web應(yīng)用來說,通常情況下是通過ServletFilter來完成事務(wù)處理的操作。這樣,就可以輕松地實現(xiàn)在用戶請求到達服務(wù)器端的時候創(chuàng)建Session并開始事務(wù),而服務(wù)器端響應(yīng)處理結(jié)束之前提交事務(wù)并關(guān)閉Session。

          ?????? 另外一個問題是,在ServletFilter中創(chuàng)建的Session是如何傳遞給業(yè)務(wù)處理方法中的呢?處理的方法是通過一個ThreadLocal變量來把創(chuàng)建的Session對象綁定到處理用戶請求的線程上去,這樣就可以使任何的業(yè)務(wù)處理方法可以輕松得到Session對象。

          ?????? Hibernate中事務(wù)處理的具體方法可以參照前面的網(wǎng)絡(luò)博客的實例。

          ?????? 但是這種事務(wù)處理的方式還是會遇到一些問題,其中最突出的就是更新沖突的問題。例如,某個操作人員進入了用戶信息的修改頁面,在經(jīng)過一段時間的對用戶信息的修改后,進行提交操作,而與此同時可能會有另外一個操作人員也進行了相同的操作,這樣在處理提交的時候就會產(chǎn)生沖突。

          ?????? 產(chǎn)生這個沖突的原因在于在開發(fā)中需要使用多個數(shù)據(jù)庫事務(wù)來實現(xiàn)一個應(yīng)用事務(wù)。也就是說,在應(yīng)用程序?qū)樱瑧?yīng)該將讀取用戶信息、顯示修改頁面以及用戶提交工作來作為一個事務(wù)進行處理,在處理的過程中應(yīng)該避免其他操作人員進行類似的操作。

          ?????? 回想前面的介紹,我們對于數(shù)據(jù)庫事務(wù)所采取的策略是每個用戶請求一個事務(wù),而上面的業(yè)務(wù)處理則至少需要兩個請求才能完成。這樣,兩者之間就存在著一定的矛盾,這也就導(dǎo)致了不可重復(fù)讀取和兩次更新問題的發(fā)生。

          ?????? 為了解決并發(fā)中數(shù)據(jù)訪問的問題,通常會采用鎖的機制來實現(xiàn)數(shù)據(jù)訪問的排他性,從而避免兩次更新問題的發(fā)生。

          posted @ 2009-07-19 21:07 jadmin 閱讀(76) | 評論 (0)編輯 收藏

          ?????? 為了避免上面出現(xiàn)的幾種情況,在標準SQL規(guī)范中,定義了4個事務(wù)隔離級別,不同的隔離級別對事務(wù)的處理不同。

          ●?? 未授權(quán)讀取(Read Uncommitted):允許臟讀取,但不允許更新丟失。如果一個事務(wù)已經(jīng)開始寫數(shù)據(jù),則另外一個數(shù)據(jù)則不允許同時進行寫操作,但允許其他事務(wù)讀此行數(shù)據(jù)。該隔離級別可以通過“排他寫鎖”實現(xiàn)。

          ●?? 授權(quán)讀取(Read Committed):允許不可重復(fù)讀取,但不允許臟讀取。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實現(xiàn)。讀取數(shù)據(jù)的事務(wù)允許其他事務(wù)繼續(xù)訪問該行數(shù)據(jù),但是未提交的寫事務(wù)將會禁止其他事務(wù)訪問該行。

          ●?? 可重復(fù)讀取(Repeatable Read):禁止不可重復(fù)讀取和臟讀取,但是有時可能出現(xiàn)幻影數(shù)據(jù)。這可以通過“共享讀鎖”和“排他寫鎖”實現(xiàn)。讀取數(shù)據(jù)的事務(wù)將會禁止寫事務(wù)(但允許讀事務(wù)),寫事務(wù)則禁止任何其他事務(wù)。

          ●?? 序列化(Serializable):提供嚴格的事務(wù)隔離。它要求事務(wù)序列化執(zhí)行,事務(wù)只能一個接著一個地執(zhí)行,但不能并發(fā)執(zhí)行。如果僅僅通過“行級鎖”是無法實現(xiàn)事務(wù)序列化的,必須通過其他機制保證新插入的數(shù)據(jù)不會被剛執(zhí)行查詢操作的事務(wù)訪問到。

          ?????? 隔離級別越高,越能保證數(shù)據(jù)的完整性和一致性,但是對并發(fā)性能的影響也越大。對于多數(shù)應(yīng)用程序,可以優(yōu)先考慮把數(shù)據(jù)庫系統(tǒng)的隔離級別設(shè)為Read Committed,它能夠避免臟讀取,而且具有較好的并發(fā)性能。盡管它會導(dǎo)致不可重復(fù)讀、虛讀和第二類丟失更新這些并發(fā)問題,在可能出現(xiàn)這類問題的個別場合,可以由應(yīng)用程序采用悲觀鎖或樂觀鎖來控制。

          ?????? 通過前面的介紹已經(jīng)知道,通過選用不同的隔離等級就可以在不同程度上避免前面所提及的在事務(wù)處理中所面臨的各種問題。所以,數(shù)據(jù)庫隔離級別的選取就顯得尤為重要,在選取數(shù)據(jù)庫的隔離級別時,應(yīng)該注意以下幾個處理的原則:

          ?????? 首先,必須排除“未授權(quán)讀取”,因為在多個事務(wù)之間使用它將會是非常危險的。事務(wù)的回滾操作或失敗將會影響到其他并發(fā)事務(wù)。第一個事務(wù)的回滾將會完全將其他事務(wù)的操作清除,甚至使數(shù)據(jù)庫處在一個不一致的狀態(tài)。很可能一個已回滾為結(jié)束的事務(wù)對數(shù)據(jù)的修改最后卻修改提交了,因為“未授權(quán)讀取”允許其他事務(wù)讀取數(shù)據(jù),最后整個錯誤狀態(tài)在其他事務(wù)之間傳播開來。

          ?????? 其次,絕大部分應(yīng)用都無須使用“序列化”隔離(一般來說,讀取幻影數(shù)據(jù)并不是一個問題),此隔離級別也難以測量。目前使用序列化隔離的應(yīng)用中,一般都使用悲觀鎖,這樣強行使所有事務(wù)都序列化執(zhí)行。

          ?????? 剩下的也就是在“授權(quán)讀取”和“可重復(fù)讀取”之間選擇了。我們先考慮可重復(fù)讀取。如果所有的數(shù)據(jù)訪問都是在統(tǒng)一的原子數(shù)據(jù)庫事務(wù)中,此隔離級別將消除一個事務(wù)在另外一個并發(fā)事務(wù)過程中覆蓋數(shù)據(jù)的可能性(第二個事務(wù)更新丟失問題)。這是一個非常重要的問題,但是使用可重復(fù)讀取并不是解決問題的唯一途徑。

          ?????? 假設(shè)使用了“版本數(shù)據(jù)”,Hibernate會自動使用版本數(shù)據(jù)。Hibernate的一級Session緩存和版本數(shù)據(jù)已經(jīng)為你提供了“可重復(fù)讀取隔離”絕大部分的特性。特別是,版本數(shù)據(jù)可以防止二次更新丟失的問題,一級Session緩存可以保證持久載入數(shù)據(jù)的狀態(tài)與其他事務(wù)對數(shù)據(jù)的修改隔離開來,因此如果使用對所有的數(shù)據(jù)庫事務(wù)采用授權(quán)讀取隔離和版本數(shù)據(jù)是行得通的。

          ?????? “可重復(fù)讀取”為數(shù)據(jù)庫查詢提供了更好的效率(僅對那些長時間的數(shù)據(jù)庫事務(wù)),但是由于幻影讀取依然存在,因此沒必要使用它(對于Web應(yīng)用來說,一般也很少在一個數(shù)據(jù)庫事務(wù)中對同一個表查詢兩次)。

          ?????? 也可以同時考慮選擇使用Hibernate的二級緩存,它可以如同底層的數(shù)據(jù)庫事務(wù)一樣提供相同的事務(wù)隔離,但是它可能弱化隔離。假如在二級緩存大量使用緩存并發(fā)策略,它并不提供重復(fù)讀取語義(例如,后面章節(jié)中將要討論的讀寫,特別是非嚴格讀寫),很容易可以選擇默認的隔離級別:因為無論如何都無法實現(xiàn)“可重復(fù)讀取”,因此就更沒有必要拖慢數(shù)據(jù)庫了。另一方面,可能對關(guān)鍵類不采用二級緩存,或者采用一個完全的事務(wù)緩存,提供“可重復(fù)讀取隔離”。那么在業(yè)務(wù)中需要使用到“可重復(fù)讀取”嗎?如果你喜歡,當然可以那樣做,但更多的時候并沒有必要花費這個代價。

          posted @ 2009-07-19 21:04 jadmin 閱讀(76) | 評論 (0)編輯 收藏
          僅列出標題
          共50頁: First 上一頁 11 12 13 14 15 16 17 18 19 下一頁 Last 
          主站蜘蛛池模板: 邵阳市| 浪卡子县| 朝阳县| 平阳县| 广安市| 清原| 图们市| 南和县| 博客| 英超| 根河市| 呼伦贝尔市| 梅州市| 和静县| 望谟县| 黄龙县| 青铜峡市| 静宁县| 蒲城县| 商水县| 绥化市| 洛南县| 古丈县| 三明市| 莫力| 洛阳市| 拉孜县| 呼玛县| 浏阳市| 龙里县| 临沭县| 左贡县| 南开区| 苍南县| 长沙县| 德保县| 勃利县| 平舆县| 夏邑县| 宁乡县| 衡阳市|