??????? 切面(Aspect): 一個關注點的模塊化,這個關注點可能會橫切多個對象。事務管理是J2EE應用中一個關于橫切關注點的很好的例子。 在Spring AOP中,切面可以使用通用類(基于模式的風格) 或者在普通類中以 @Aspect 注解(@AspectJ風格)來實現。
??????? 連接點(Joinpoint): 在程序執行過程中某個特定的點,比如某方法調用的時候或者處理異常的時候。 在Spring AOP中,一個連接點 總是 代表一個方法的執行。 通過聲明一個org.aspectj.lang.JoinPoint類型的參數可以使通知(Advice)的主體部分獲得連接點信息。
??????? 通知(Advice): 在切面的某個特定的連接點(Joinpoint)上執行的動作。通知有各種類型,其中包括“around”、“before”和“after”等通知。 通知的類型將在后面部分進行討論。許多AOP框架,包括Spring,都是以攔截器做通知模型, 并維護一個以連接點為中心的攔截器鏈。
??????? 切入點(Pointcut): 匹配連接點(Joinpoint)的斷言。通知和一個切入點表達式關聯,并在滿足這個切入點的連接點上運行(例如,當執行某個特定名稱的方法時)。 切入點表達式如何和連接點匹配是AOP的核心:Spring缺省使用AspectJ切入點語法。
??????? 引入(Introduction): (也被稱為內部類型聲明(inter-type declaration))。聲明額外的方法或者某個類型的字段。 Spring允許引入新的接口(以及一個對應的實現)到任何被代理的對象。 例如,你可以使用一個引入來使bean實現 IsModified 接口,以便簡化緩存機制。
??????? 目標對象(Target Object): 被一個或者多個切面(aspect)所通知(advise)的對象。也有人把它叫做 被通知(advised) 對象。 既然Spring AOP是通過運行時代理實現的,這個對象永遠是一個 被代理(proxied) 對象。
??????? AOP代理(AOP Proxy): AOP框架創建的對象,用來實現切面契約(aspect contract)(包括通知方法執行等功能)。 在Spring中,AOP代理可以是JDK動態代理或者CGLIB代理。 注意:Spring 2.0最新引入的基于模式(schema-based)風格和@AspectJ注解風格的切面聲明,對于使用這些風格的用戶來說,代理的創建是透明的。
??????? 織入(Weaving): 把切面(aspect)連接到其它的應用程序類型或者對象上,并創建一個被通知(advised)的對象。 這些可以在編譯時(例如使用AspectJ編譯器),類加載時和運行時完成。 Spring和其他純JavaAOP框架一樣,在運行時完成織入。
??????? 通知的類型:
??????? 前置通知(Before advice): 在某連接點(join point)之前執行的通知,但這個通知不能阻止連接點前的執行(除非它拋出一個異常)。
??????? 返回后通知(After returning advice): 在某連接點(join point)正常完成后執行的通知:例如,一個方法沒有拋出任何異常,正常返回。
??????? 拋出異常后通知(After throwing advice): 在方法拋出異常退出時執行的通知。
??????? 后通知(After (finally) advice): 當某連接點退出的時候執行的通知(不論是正常返回還是異常退出)。
??????? 環繞通知(Around Advice): 包圍一個連接點(join point)的通知,如方法調用。這是最強大的一種通知類型。 環繞通知可以在方法調用前后完成自定義的行為。它也會選擇是否繼續執行連接點或直接返回它們自己的返回值或拋出異常來結束執行。
===============================================
1.切面(aspect):要實現的交叉功能,是系統模塊化的一個切面或領域。如日志記錄。
2.連接點:應用程序執行過程中插入切面的地點,可以是方法調用,異常拋出,或者要修改的字段。
3.通知:切面的實際實現,他通知系統新的行為。如在日志通知包含了實現日志功能的代碼,如向日志文件寫日志。通知在連接點插入到應用系統中。
4.切入點:定義了通知應該應用在哪些連接點,通知可以應用到AOP框架支持的任何連接點。
5.引入:為類添加新方法和屬性。
6.目標對象:被通知的對象。既可以是你編寫的類也可以是第三方類。
7.代理:將通知應用到目標對象后創建的對象,應用系統的其他部分不用為了支持代理對象而改變。
8.織入:將切面應用到目標對象從而創建一個新代理對象的過程。織入發生在目標對象生命周期的多個點上:
編譯期:切面在目標對象編譯時織入.這需要一個特殊的編譯器.
類裝載期:切面在目標對象被載入JVM時織入.這需要一個特殊的類載入器.
運行期:切面在應用系統運行時織入.
posted @
2009-07-20 00:41 jadmin 閱讀(83) |
評論 (0) |
編輯 收藏
?????? 數據庫查詢性能的提升也是涉及到開發中的各個階段,在開發中選用正確的查詢方法無疑是最基礎也最簡單的。
SQL語句的優化
?????? 使用正確的SQL語句可以在很大程度上提高系統的查詢性能。獲得同樣數據而采用不同方式的SQL語句在性能上的差距可能是十分巨大的。
?????? 由于Hibernate是對JDBC的封裝,SQL語句的產生都是動態由Hibernate自動完成的。Hibernate產生SQL語句的方式有兩種:一種是通過開發人員編寫的HQL語句來生成,另一種是依據開發人員對關聯對象的訪問來自動生成相應的SQL語句。
?????? 至于使用什么樣的SQL語句可以獲得更好的性能要依據數據庫的結構以及所要獲取數據的具體情況來進行處理。在確定了所要執行的SQL語句后,可以通過以下三個方面來影響Hibernate所生成的SQL語句:
●?? HQL語句的書寫方法。
●?? 查詢時所使用的查詢方法。
●?? 對象關聯時所使用的抓取策略。
使用正確的查詢方法
?????? 在前面已經介紹過,執行數據查詢功能的基本方法有兩種:一種是得到單個持久化對象的get()方法和load()方法,另一種是Query對象的list()方法和iterator()方法。在開發中應該依據不同的情況選用正確的方法。
?????? get()方法和load()方法的區別在于對二級緩存的使用上。load()方法會使用二級緩存,而get()方法在一級緩存沒有找到的情況下會直接查詢數據庫,不會去二級緩存中查找。在使用中,對使用了二級緩存的對象進行查詢時最好使用load()方法,以充分利用二級緩存來提高檢索的效率。
?????? list()方法和iterator()方法之間的區別可以從以下幾個方面來進行比較。
●?? 執行的查詢不同
?????? list()方法在執行時,是直接運行查詢結果所需要的查詢語句,而iterator()方法則是先執行得到對象ID的查詢,然后再根據每個ID值去取得所要查詢的對象。因此,對于list()方式的查詢通常只會執行一個SQL語句,而對于iterator()方法的查詢則可能需要執行N+1條SQL語句(N為結果集中的記錄數)。
?????? iterator()方法只是可能執行N+1條數據,具體執行SQL語句的數量取決于緩存的情況以及對結果集的訪問情況。
●?? 緩存的使用
?????? list()方法只能使用二級緩存中的查詢緩存,而無法使用二級緩存對單個對象的緩存(但是會把查詢出的對象放入二級緩存中)。所以,除非重復執行相同的查詢操作,否則無法利用緩存的機制來提高查詢的效率。
?????? iterator()方法則可以充分利用二級緩存,在根據ID檢索對象的時候會首先到緩存中查找,只有在找不到的情況下才會執行相應的查詢語句。所以,緩存中對象的存在與否會影響到SQL語句的執行數量。
●?? 對于結果集的處理方法不同
?????? list()方法會一次獲得所有的結果集對象,而且它會依據查詢的結果初始化所有的結果集對象。這在結果集非常大的時候必然會占據非常多的內存,甚至會造成內存溢出情況的發生。
?????? iterator()方法在執行時不會一次初始化所有的對象,而是根據對結果集的訪問情況來初始化對象。因此在訪問中可以控制緩存中對象的數量,以避免占用過多緩存,導致內存溢出情況的發生。使用iterator()方法的另外一個好處是,如果只需要結果集中的部分記錄,那么沒有被用到的結果對象根本不會被初始化。所以,對結果集的訪問情況也是調用iterator()方法時執行數據庫SQL語句多少的一個因素。
?????? 所以,在使用Query對象執行數據查詢時應該從以上幾個方面去考慮使用何種方法來執行數據庫的查詢操作。
使用正確的抓取策略
?????? 所謂抓取策略(fetching strategy)是指當應用程序需要利用關聯關系進行對象獲取的時候,Hibernate獲取關聯對象的策略。抓取策略可以在O/R映射的元數據中聲明,也可以在特定的HQL或條件查詢中聲明。
?????? Hibernate 3定義了以下幾種抓取策略。
●?? 連接抓取(Join fetching)
?????? 連接抓取是指Hibernate在獲得關聯對象時會在SELECT語句中使用外連接的方式來獲得關聯對象。
●?? 查詢抓取(Select fetching)
?????? 查詢抓取是指Hibernate通過另外一條SELECT語句來抓取當前對象的關聯對象的方式。這也是通過外鍵的方式來執行數據庫的查詢。與連接抓取的區別在于,通常情況下這個SELECT語句不是立即執行的,而是在訪問到關聯對象的時候才會執行。
●?? 子查詢抓取(Subselect fetching)
?????? 子查詢抓取也是指Hibernate通過另外一條SELECT語句來抓取當前對象的關聯對象的方式。與查詢抓取的區別在于它所采用的SELECT語句的方式為子查詢,而不是通過外連接。
●?? 批量抓取(Batch fetching)
?????? 批量抓取是對查詢抓取的優化,它會依據主鍵或者外鍵的列表來通過單條SELECT語句實現管理對象的批量抓取。
以上介紹的是Hibernate 3所提供的抓取策略,也就是抓取關聯對象的手段。為了提升系統的性能,在抓取關聯對象的時機上,還有以下一些選擇。
●?? 立即抓取(Immediate fetching)
?????? 立即抓取是指宿主對象被加載時,它所關聯的對象也會被立即加載。
●?? 延遲集合抓取(Lazy collection fetching)
?????? 延遲集合抓取是指在加載宿主對象時,并不立即加載它所關聯的對象,而是到應用程序訪問關聯對象的時候才抓取關聯對象。這是集合關聯對象的默認行為。
●?? 延遲代理抓取(Lazy proxy fetching)
?????? 延遲代理抓取是指在返回單值關聯對象的情況下,并不在對其進行get操作時抓取,而是直到調用其某個方法的時候才會抓取這個對象。
●?? 延遲屬性加載(Lazy attribute fetching)
?????? 延遲屬性加載是指在關聯對象被訪問的時候才進行關聯對象的抓取。
?????? 介紹了Hibernate所提供的關聯對象的抓取方法和抓取時機,這兩個方面的因素都會影響Hibernate的抓取行為,最重要的是要清楚這兩方面的影響是不同的,不要將這兩個因素混淆,在開發中要結合實際情況選用正確的抓取策略和合適的抓取時機。
?????? 抓取時機的選擇
?????? 在Hibernate 3中,對于集合類型的關聯在默認情況下會使用延遲集合加載的抓取時機,而對于返回單值類型的關聯在默認情況下會使用延遲代理抓取的抓取時機。
?????? 對于立即抓取在開發中很少被用到,因為這很可能會造成不必要的數據庫操作,從而影響系統的性能。當宿主對象和關聯對象總是被同時訪問的時候才有可能會用到這種抓取時機。另外,使用立即連接抓取可以通過外連接來減少查詢SQL語句的數量,所以,也會在某些特殊的情況下使用。
?????? 然而,延遲加載又會面臨另外一個問題,如果在Session關閉前關聯對象沒有被實例化,那么在訪問關聯對象的時候就會拋出異常。處理的方法就是在事務提交之前就完成對關聯對象的訪問。
?????? 所以,在通常情況下都會使用延遲的方式來抓取關聯的對象。因為每個立即抓取都會導致關聯對象的立即實例化,太多的立即抓取關聯會導致大量的對象被實例化,從而占用過多的內存資源。
?????? 抓取策略的選取
?????? 對于抓取策略的選取將影響到抓取關聯對象的方式,也就是抓取關聯對象時所執行的SQL語句。這就要根據實際的業務需求、數據的數量以及數據庫的結構來進行選擇了。
在這里需要注意的是,通常情況下都會在執行查詢的時候針對每個查詢來指定對其合適的抓取策略。指定抓取策略的方法如下所示:
?????? User user = (User) session.createCriteria(User.class)
??????????? ?????? .setFetchMode("permissions", FetchMode.JOIN)
??????????? ?????? .add( Restrictions.idEq(userId) )
??????????? ?????? .uniqueResult();
?????? 本文介紹了查詢性能提升的方法,關鍵是如何通過優化SQL語句來提升系統的查詢性能。查詢方法和抓取策略的影響也是通過執行查詢方式和SQL語句的多少來改變系統的性能的。這些都屬于開發人員所應該掌握的基本技能,避免由于開發不當而導致系統性能的低下。
?????? 在性能調整中,除了前面介紹的執行SQL語句的因素外,對于緩存的使用也會影響系統的性能。通常來說,緩存的使用會增加系統查詢的性能,而降低系統增加、修改和刪除操作的性能(因為要進行緩存的同步處理)。所以,開發人員應該能夠正確地使用有效的緩存來提高數據查詢的性能,而要避免濫用緩存而導致的系統性能變低。在采用緩存的時候也應該注意調整自己的檢索策略和查詢方法,這三者配合起來才可以達到最優的性能。
?????? 另外,事務的使用策略也會影響到系統的性能。選取正確的事務隔離級別以及使用正確的鎖機制來控制數據的并發訪問都會影響到系統的性能。
posted @
2009-07-19 21:36 jadmin 閱讀(78) |
評論 (0) |
編輯 收藏
?????? Hibernate是對JDBC的輕量級封裝,因此在很多情況下Hibernate的性能比直接使用JDBC存取數據庫要低。然而,通過正確的方法和策略,在使用Hibernate的時候還是可以非常接近直接使用JDBC時的效率的,并且,在有些情況下還有可能高于使用JDBC時的執行效率。
?????? 在進行Hibernate性能優化時,需要從以下幾個方面進行考慮:
●?? 數據庫設計調整。
●?? HQL優化。
●?? API的正確使用(如根據不同的業務類型選用不同的集合及查詢API)。
●?? 主配置參數(日志、查詢緩存、fetch_size、batch_size等)。
●?? 映射文件優化(ID生成策略、二級緩存、延遲加載、關聯優化)。
●?? 一級緩存的管理。
●?? 針對二級緩存,還有許多特有的策略。
●?? 事務控制策略。
?????? 數據的查詢性能往往是影響一個應用系統性能的主要因素。對查詢性能的影響會涉及到系統軟件開發的各個階段,例如,良好的設計、正確的查詢方法、適當的緩存都有利于系統性能的提升。
?????? 系統性能的提升設計到系統中的各個方面,是一個相互平衡的過程,需要在應用的各個階段都要考慮。并且在開發、運行的過程中要不斷地調整和優化才能逐步提升系統的性能。
posted @
2009-07-19 21:30 jadmin 閱讀(51) |
評論 (0) |
編輯 收藏
?????? 在前面介紹了Hibernate的緩存技術以及基本的用法,在這里就具體的Hibernate所提供的查詢方法與Hibernate緩存之間的關系做一個簡單的總結。
?????? 在開發中,通常是通過兩種方式來執行對數據庫的查詢操作的。一種方式是通過ID來獲得單獨的Java對象,另一種方式是通過HQL語句來執行對數據庫的查詢操作。下面就分別結合這兩種查詢方式來說明一下緩存的作用。
?????? 通過ID來獲得Java對象可以直接使用Session對象的load()或者get()方法,這兩種方式的區別就在于對緩存的使用上。
●?? load()方法
?????? 在使用了二級緩存的情況下,使用load()方法會在二級緩存中查找指定的對象是否存在。
在執行load()方法時,Hibernate首先從當前Session的一級緩存中獲取ID對應的值,在獲取不到的情況下,將根據該對象是否配置了二級緩存來做相應的處理。
?????? 如配置了二級緩存,則從二級緩存中獲取ID對應的值,如仍然獲取不到則還需要根據是否配置了延遲加載來決定如何執行,如未配置延遲加載則從數據庫中直接獲取。在從數據庫獲取到數據的情況下,Hibernate會相應地填充一級緩存和二級緩存,如配置了延遲加載則直接返回一個代理類,只有在觸發代理類的調用時才進行數據庫的查詢操作。
?????? 在Session一直打開的情況下,并在該對象具有單向關聯維護的時候,需要使用類似Session.clear(),Session.evict()的方法來強制刷新一級緩存。
●?? get()方法
?????? get()方法與load()方法的區別就在于不會查找二級緩存。在當前Session的一級緩存中獲取不到指定的對象時,會直接執行查詢語句從數據庫中獲得所需要的數據。
?????? 在Hibernate中,可以通過HQL來執行對數據庫的查詢操作。具體的查詢是由Query對象的list()和iterator()方法來執行的。這兩個方法在執行查詢時的處理方法存在著一定的差別,在開發中應該依據具體的情況來選擇合適的方法。
●?? list()方法
?????? 在執行Query的list()方法時,Hibernate的做法是首先檢查是否配置了查詢緩存,如配置了則從查詢緩存中尋找是否已經對該查詢進行了緩存,如獲取不到則從數據庫中進行獲取。從數據庫中獲取到后,Hibernate將會相應地填充一級、二級和查詢緩存。如獲取到的為直接的結果集,則直接返回,如獲取到的為一些ID的值,則再根據ID獲取相應的值(Session.load()),最后形成結果集返回。可以看到,在這樣的情況下,list()方法也是有可能造成N次查詢的。
?????? 查詢緩存在數據發生任何變化的情況下都會被自動清空。
●?? iterator()方法
?????? Query的iterator()方法處理查詢的方式與list()方法是不同的,它首先會使用查詢語句得到ID值的列表,然后再使用Session的load()方法得到所需要的對象的值。
?????? 在獲取數據的時候,應該依據這4種獲取數據方式的特點來選擇合適的方法。在開發中可以通過設置show_sql選項來輸出Hibernate所執行的SQL語句,以此來了解Hibernate是如何操作數據庫的。
posted @
2009-07-19 21:29 jadmin 閱讀(55) |
評論 (0) |
編輯 收藏
查詢緩存
?????? 查詢緩存是專門針對各種查詢操作進行緩存。查詢緩存會在整個SessionFactory的生命周期中起作用,存儲的方式也是采用key-value的形式來進行存儲的。
?????? 查詢緩存中的key是根據查詢的語句、查詢的條件、查詢的參數和查詢的頁數等信息組成的。而數據的存儲則會使用兩種方式,使用SELECT語句只查詢實體對象的某些列或者某些實體對象列的組合時,會直接緩存整個結果集。而對于查詢結果為某個實體對象集合的情況則只會緩存實體對象的ID值,以達到緩存空間可以共用,節省空間的目的。
?????? 在使用查詢緩存時,除了需要設置hibernate.cache.provider_class參數來啟動二級緩存外,還需要通過hibernate.cache.use_query_cache參數來啟動對查詢緩存的支持。
?????? 另外需要注意的是,查詢緩存是在執行查詢語句的時候指定緩存的方式以及是否需要對查詢的結果進行緩存。
?????? 下面就來了解一下查詢緩存的使用方法及作用。
?????? 修改Hibernate配置文件
?????? 首先需要修改Hibernate的配置文件,增加hibernate.cache.use_query_cache參數的配置。配置方法如下:
?????? <property name="hibernate.cache.use_query_cache">true</property>
?????? Hibernate配置文件的詳細內容請參考配套光盤中的hibernate\src\cn\hxex\ hibernate\cache\hibernate.cfg.xml文件。
?????? 編寫主測試程序
?????? 由于這是在前面二級緩存例子的基礎上來開發的,所以,對于EHCache的配置以及視圖對象的開發和映射文件的配置工作就都不需要再重新進行了。下面就來看一下主測試程序的實現方法,如清單14.11所示。
?????? 清單14.11??? 主程序的實現
……
??? 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();
?????? }
}
?????? 主程序在實現的時候采用了多線程的方式來運行。首先將“from User”查詢結果進行緩存,然后再通過ID取得對象來檢查是否對對象進行了緩存。另外,多個線程的執行可以看出對于進行了緩存的查詢是不會執行第二次的。
?????? 運行測試主程序
?????? 接著就來運行測試主程序,其輸出結果應該如下所示:
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
?????? 通過上面的執行結果可以看到,在兩個線程執行中,只執行了一個SQL查詢語句。這是因為根據ID所要獲取的對象在前面的查詢中已經得到了,并進行了緩存,所以沒有再次執行查詢語句。
posted @
2009-07-19 21:25 jadmin 閱讀(66) |
評論 (0) |
編輯 收藏
二級緩存
?????? 與Session相對的是,SessionFactory也提供了相應的緩存機制。SessionFactory緩存可以依據功能和目的的不同而劃分為內置緩存和外置緩存。
?????? SessionFactory的內置緩存中存放了映射元數據和預定義SQL語句,映射元數據是映射文件中數據的副本,而預定義SQL語句是在Hibernate初始化階段根據映射元數據推導出來的。SessionFactory的內置緩存是只讀的,應用程序不能修改緩存中的映射元數據和預定義SQL語句,因此SessionFactory不需要進行內置緩存與映射文件的同步。
?????? SessionFactory的外置緩存是一個可配置的插件。在默認情況下,SessionFactory不會啟用這個插件。外置緩存的數據是數據庫數據的副本,外置緩存的介質可以是內存或者硬盤。SessionFactory的外置緩存也被稱為Hibernate的二級緩存。
?????? Hibernate的二級緩存的實現原理與一級緩存是一樣的,也是通過以ID為key的Map來實現對對象的緩存。
?????? 由于Hibernate的二級緩存是作用在SessionFactory范圍內的,因而它比一級緩存的范圍更廣,可以被所有的Session對象所共享。
二級緩存的工作內容
?????? Hibernate的二級緩存同一級緩存一樣,也是針對對象ID來進行緩存。所以說,二級緩存的作用范圍是針對根據ID獲得對象的查詢。
?????? 二級緩存的工作可以概括為以下幾個部分:
●?? 在執行各種條件查詢時,如果所獲得的結果集為實體對象的集合,那么就會把所有的數據對象根據ID放入到二級緩存中。
●?? 當Hibernate根據ID訪問數據對象的時候,首先會從Session一級緩存中查找,如果查不到并且配置了二級緩存,那么會從二級緩存中查找,如果還查不到,就會查詢數據庫,把結果按照ID放入到緩存中。
●?? 刪除、更新、增加數據的時候,同時更新緩存。
二級緩存的適用范圍
?????? Hibernate的二級緩存作為一個可插入的組件在使用的時候也是可以進行配置的,但并不是所有的對象都適合放在二級緩存中。
?????? 在通常情況下會將具有以下特征的數據放入到二級緩存中:
●?? 很少被修改的數據。
●?? 不是很重要的數據,允許出現偶爾并發的數據。
●?? 不會被并發訪問的數據。
●?? 參考數據。
?????? 而對于具有以下特征的數據則不適合放在二級緩存中:
●?? 經常被修改的數據。
●?? 財務數據,絕對不允許出現并發。
●?? 與其他應用共享的數據。
?????? 在這里特別要注意的是對放入緩存中的數據不能有第三方的應用對數據進行更改(其中也包括在自己程序中使用其他方式進行數據的修改,例如,JDBC),因為那樣Hibernate將不會知道數據已經被修改,也就無法保證緩存中的數據與數據庫中數據的一致性。
二級緩存組件
?????? 在默認情況下,Hibernate會使用EHCache作為二級緩存組件。但是,可以通過設置hibernate.cache.provider_class屬性,指定其他的緩存策略,該緩存策略必須實現org.hibernate.cache.CacheProvider接口。
?????? 通過實現org.hibernate.cache.CacheProvider接口可以提供對不同二級緩存組件的支持。
?????? Hibernate內置支持的二級緩存組件如表14.1所示。
表14.1??? Hibernate所支持的二級緩存組件
posted @
2009-07-19 21:23 jadmin 閱讀(50) |
評論 (0) |
編輯 收藏
?????? 大家都知道,Hibernate是以JDBC為基礎實現的持久層組件,因而其性能肯定會低于直接使用JDBC來訪問數據庫。因此,為了提高Hibernate的性能,在Hibernate組件中提供了完善的緩存機制來提高數據庫訪問的性能。
?????? 什么是緩存
?????? 緩存是介于應用程序和物理數據之間的,其作用是為了降低應用程序對物理數據訪問的頻次從而提高應用系統的性能。緩存思想的提出主要是因為對物理數據的訪問效率要遠遠低于對內存的訪問速度,因而采用了將部分物理數據存放于內存當中,這樣可以有效地減少對物理數據的訪問次數,從而提高系統的性能。
?????? 緩存廣泛地存在于我們所接觸的各種應用系統中,例如數據庫系統、Windows操作系統等,在進行物理數據的訪問時無一例外地都使用了緩存機制來提高操作的性能。
?????? 緩存內的數據是對物理數據的復制,因此一個緩存系統所應該包括的最基本的功能是數據的緩存和讀取,同時在使用緩存的時候還要考慮緩存中的數據與物理數據的同步,也就是要保持兩者是一致的。
?????? 緩存要求對數據的讀寫速度很高,因此,一般情況下會選用內存作為存儲的介質。但如果內存有限,并且緩存中存放的數據量非常大時,也會用硬盤作為緩存介質。緩存的實現不僅僅要考慮存儲的介質,還要考慮到管理緩存的并發訪問和緩存數據的生命周期。
?????? 為了提高系統的性能,Hibernate也使用了緩存的機制。在Hibernate框架中,主要包括以下兩個方面的緩存:一級緩存和二級緩存(包含查詢緩存)。Hibernate中緩存的作用主要表現在以下兩個方面:
●?? 通過主鍵(ID)加載數據的時候
●?? 延遲加載中
一級緩存
?????? Hibernate的一級緩存是由Session提供的,因此它只存在于Session的生命周期中,也就是當Session關閉的時候該Session所管理的一級緩存也會立即被清除。
?????? Hibernate的一級緩存是Session所內置的,不能被卸載,也不能進行任何配置。
?????? 一級緩存采用的是key-value的Map方式來實現的,在緩存實體對象時,對象的主關鍵字ID是Map的key,實體對象就是對應的值。所以說,一級緩存是以實體對象為單位進行存儲的,在訪問的時候使用的是主關鍵字ID。
?????? 雖然,Hibernate對一級緩存使用的是自動維護的功能,沒有提供任何配置功能,但是可以通過Session中所提供的方法來對一級緩存的管理進行手工干預。Session中所提供的干預方法包括以下兩種。
●?? evict() :用于將某個對象從Session的一級緩存中清除。
●?? clear() :用于將一級緩存中的對象全部清除。
?????? 在進行大批量數據一次性更新的時候,會占用非常多的內存來緩存被更新的對象。這時就應該階段性地調用clear()方法來清空一級緩存中的對象,控制一級緩存的大小,以避免產生內存溢出的情況。具體的實現方法如清單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 ) {
??????? //將本批插入的對象立即寫入數據庫并釋放內存
??????? session.flush();
??????? session.clear();
??? }
}
??
tx.commit();
session.close();
posted @
2009-07-19 21:18 jadmin 閱讀(92) |
評論 (0) |
編輯 收藏
并發控制
?????? 當數據庫系統采用Read Committed隔離級別時,會導致不可重復讀取和兩次更新丟失的并發問題,可以在應用程序中采用鎖機制來避免這類問題的產生。
?????? 從應用程序的角度上看,鎖可以分為樂觀鎖和悲觀鎖兩大類。
悲觀鎖
?????? 在多個客戶端可能讀取同一筆數據或同時更新一筆數據的情況下,必須要有訪問控制的手段,防止同一個數據被修改而造成混亂,最簡單的手段就是對數據進行鎖定。在自己進行數據讀取或更新等動作時,鎖定其他客戶端不能對同一筆數據進行任何的動作。
?????? 悲觀鎖(Pessimistic Locking),如其名稱所示,悲觀地認定每次資料存取時,其他的客戶端也會存取同一筆數據,因此將會鎖住該筆數據,直到自己操作完成后再解除鎖。
?????? 悲觀鎖假定任何時刻存取數據時,都可能有另一個客戶也正在存取同一筆數據,因而對數據采取了數據庫層次的鎖定狀態,在鎖定的時間內其他的客戶不能對數據進行存取。對于單機或小系統而言,這并不成問題,然而如果是在網絡上的系統,同時間會有許多訪問的機器,如果每一次讀取數據都造成鎖定,其后繼的存取就必須等待,這將造成效能上的問題,造成后繼使用者的長時間等待。
?????? 悲觀鎖通常透過系統或數據庫本身的功能來實現,依賴系統或數據庫本身提供的鎖機制。Hibernate即是如此,可以利用Query或Criteria的setLockMode()方法來設定要鎖定的表或列及其鎖模式,可設定的鎖模式有以下幾個。
?????? LockMode.UPGRADE:利用數據庫的for update子句進行鎖定。
?????? LockMode.UPGRADE_NOWAIT:使用for update nowait子句進行鎖定,在Oracle數據庫中使用。
?????? 下面來實現一個簡單的例子,測試一下采用悲觀鎖時數據庫是如何進行操作的。
?????? 首先來完成一個實體對象——User,該對象包含了id,name和age三個屬性,實現的方法如清單14.1所示。
?????? 清單14.1??? User對象的實現
package cn.hxex.hibernate.lock;
public class User {
?????? private String id;
?????? private String name;
?????? private Integer age;
??????
?????? // 省略了getter和setter方法
?????? ……
}
?????? 接下來就是映射文件的配置,由于該映射文件沒有涉及到任何與其他對象的關聯配置,所以實現的方法也非常簡單,代碼如清單14.2所示。
?????? 清單14.2??? User映射文件的實現
<?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的配置文件了,在這個配置文件中包含了連接數據庫的參數以及其他一些重要的參數,實現的方法如清單14.3所示。
?????? 清單14.3??? Hibernate配置文件的實現
<?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>
????????????? <!-- 數據庫的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&characterEncoding=utf8&autoReconnect=true&autoReconnectForPools=true
??????? </property>
?????????????
????????????? <!-- 數據庫的驅動程序 -->
????????????? <!-- property name="hibernate.connection.driver_class">
????????????? oracle.jdbc.driver.OracleDriver</property-->
????????????? <property name="hibernate.connection.driver_class">
?????????? com.mysql.jdbc.Driver
??????? </property>
????????????? <!-- 數據庫的用戶名 -->
????????????? <property name="hibernate.connection.username">lockdb</property>
????????????? <!-- 數據庫的密碼 -->
????????????? <property name="hibernate.connection.password">lockdb</property>
????????????? <!-- 數據庫的Dialect -->
????????????? <!-- property name="hibernate.dialect">
????????????? org.hibernate.dialect.Oracle9Dialect</property -->
????????????? <property name="hibernate.dialect">
????????????? org.hibernate.dialect.MySQLDialect</property>
????????????? <!-- 輸出執行的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>
?????? 最后要實現的就是測試主程序了,在測試主程序中包含了Hibernate的初始化代碼以及悲觀鎖的測試方法。測試主程序的實現方法如清單14.4所示。
?????? 清單14.4??? 測試主程序的實現
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 );
??? // 靜態Configuration和SessionFactory對象的實例(全局唯一的)
??? private static Configuration configuration;
??? private static SessionFactory sessionFactory;
??? static
??? {
??????? // 從默認的配置文件創建SessionFactory
??????? try
??????? {
?????????????? ?????????? URL configURL = ClassLoader.getSystemResource(
?????????????? ??????????????? "cn/hxex/hibernate/lock/hibernate.cfg.xml" );
?????????????? ?????????? // 創建默認的Configuration對象的實例
??????????? configuration = new Configuration();
??????????? // 讀取hibernate.properties或者hibernate.cfg.xml文件
??????????? configuration.configure( configURL );
??????????? // 使用靜態變量來保持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()方法就是測試悲觀鎖的方法,該方法在執行查詢之前通過Query對象的setLockMode()方法設置了訪問User對象的模式,這樣,這個程序在執行的時候就會使用以下的SQL語句:
?????? select user0_.userId as userId0_, user0_.name as name0_, user0_.age as age0_
?????? from USERINFO user0_ for update
?????? 除了Query對象外,也可以在使用Session的load()或是lock()時指定鎖模式。
?????? 除了前面所提及的兩種鎖模式外,還有三種Hibernate內部自動對數據進行加鎖的模式,但它的處理是與數據庫無關的。
?????? LockMode.WRITE:在insert或update時進行鎖定,Hibernate會在調用save()方法時自動獲得鎖。
?????? LockMode.READ:在讀取記錄時Hibernate會自動獲得鎖。
?????? LockMode.NONE:沒有鎖。
?????? 如果數據庫不支持所指定的鎖模式,Hibernate會選擇一個合適的鎖替換,而不是拋出一個異常。
樂觀鎖
?????? 樂觀鎖(Optimistic Locking)認為資料的存取很少發生同時存取的問題,因而不做數據庫層次上的鎖定。為了維護正確的數據,樂觀鎖是使用應用程序上的邏輯來實現版本控制的。
在使用樂觀鎖策略的情況下,數據不一致的情況一旦發生,有幾個解決方法,一種是先更新為主,一種是后更新為主,比較復雜的就是檢查發生變動的數據來實現,或是檢查所有屬性來實現樂觀鎖。
?????? Hibernate中通過檢查版本號來判斷數據是否已經被其他人所改動,這也是Hibernate所推薦的方式。在數據庫中加入一個version字段記錄,在讀取數據時連同版本號一同讀取,并在更新數據時比較版本號與數據庫中的版本號,如果等于數據庫中的版本號則予以更新,并遞增版本號,如果小于數據庫中的版本號就拋出異常。
?????? 下面就來在前面例子的基礎上進行Hibernate樂觀鎖的測試。
?????? 首先需要修改前面所實現的業務對象,在其中增加一個version屬性,用來記錄該對象所包含數據的版本信息,修改后的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屬性應該使用專門的<version>元素來進行配置,這樣才能使其發揮樂觀鎖的作用。如果還使用<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>
?????? 接下來還要進行測試主程序的修改。由于需要模擬兩個人同時修改同一個記錄的情況,所以在這里需要將主程序修改為是可以多線程執行的,然后在run()方法中,調用對User對象的修改程序。
?????? 實現后的主測試程序如清單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" );
??? ??????
??? ?????? // 等第二個進程執行
??? ?????? 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();
?????? }
}
?????? 最后,執行測試主程序,在控制臺中應該看到類似下面的輸出:
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所執行的UPDATE語句中可以看到,version字段是作為更新的條件來執行的。對于第二個進程來說,由于數據庫中的記錄已經被第一個進程更新(更新的同時會導致version自動增加),就必然會導致第二個進程操作的失敗。Hibernate正是利用這種機制來避免兩次更新問題的出現。
posted @
2009-07-19 21:11 jadmin 閱讀(162) |
評論 (0) |
編輯 收藏
?????? 在現在的B/S體系結構的軟件開發中,對于數據庫事務處理中最常使用的方式是每個用戶請求一個事務。也就是說,當服務器端接收到一個用戶請求后,會開始一個新的事務,直到對用戶請求的所有處理都進行完畢并且完成了響應用戶請求的所有輸出之后才會關閉這個事務。
?????? 對于使用Hibernate實現持久化功能的系統來說,事務的處理是這樣的:服務器端在接收到用戶的請求后,會創建一個新的Hibernate Session對象,然后通過該Session對象開始一個新的事務并且之后所有對數據庫的操作都通過該Session對象來進行。最后,完成將響應頁面發送到客戶端的工作后再提交事務并且關閉Session。
?????? Session的對象是輕型的,非線程安全的,所以在每次用戶請求時創建,請求處理完畢后丟棄。
?????? 那么,該如何實現這種方式的事務處理呢?處理的難點在于如何在業務處理之前創建Session并開始事務以及在業務處理之后提交事務并關閉Session。對于現在的Web應用來說,通常情況下是通過ServletFilter來完成事務處理的操作。這樣,就可以輕松地實現在用戶請求到達服務器端的時候創建Session并開始事務,而服務器端響應處理結束之前提交事務并關閉Session。
?????? 另外一個問題是,在ServletFilter中創建的Session是如何傳遞給業務處理方法中的呢?處理的方法是通過一個ThreadLocal變量來把創建的Session對象綁定到處理用戶請求的線程上去,這樣就可以使任何的業務處理方法可以輕松得到Session對象。
?????? Hibernate中事務處理的具體方法可以參照前面的網絡博客的實例。
?????? 但是這種事務處理的方式還是會遇到一些問題,其中最突出的就是更新沖突的問題。例如,某個操作人員進入了用戶信息的修改頁面,在經過一段時間的對用戶信息的修改后,進行提交操作,而與此同時可能會有另外一個操作人員也進行了相同的操作,這樣在處理提交的時候就會產生沖突。
?????? 產生這個沖突的原因在于在開發中需要使用多個數據庫事務來實現一個應用事務。也就是說,在應用程序層,應該將讀取用戶信息、顯示修改頁面以及用戶提交工作來作為一個事務進行處理,在處理的過程中應該避免其他操作人員進行類似的操作。
?????? 回想前面的介紹,我們對于數據庫事務所采取的策略是每個用戶請求一個事務,而上面的業務處理則至少需要兩個請求才能完成。這樣,兩者之間就存在著一定的矛盾,這也就導致了不可重復讀取和兩次更新問題的發生。
?????? 為了解決并發中數據訪問的問題,通常會采用鎖的機制來實現數據訪問的排他性,從而避免兩次更新問題的發生。
posted @
2009-07-19 21:07 jadmin 閱讀(75) |
評論 (0) |
編輯 收藏
?????? 為了避免上面出現的幾種情況,在標準SQL規范中,定義了4個事務隔離級別,不同的隔離級別對事務的處理不同。
●?? 未授權讀取(Read Uncommitted):允許臟讀取,但不允許更新丟失。如果一個事務已經開始寫數據,則另外一個數據則不允許同時進行寫操作,但允許其他事務讀此行數據。該隔離級別可以通過“排他寫鎖”實現。
●?? 授權讀取(Read Committed):允許不可重復讀取,但不允許臟讀取。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實現。讀取數據的事務允許其他事務繼續訪問該行數據,但是未提交的寫事務將會禁止其他事務訪問該行。
●?? 可重復讀取(Repeatable Read):禁止不可重復讀取和臟讀取,但是有時可能出現幻影數據。這可以通過“共享讀鎖”和“排他寫鎖”實現。讀取數據的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。
●?? 序列化(Serializable):提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接著一個地執行,但不能并發執行。如果僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的數據不會被剛執行查詢操作的事務訪問到。
?????? 隔離級別越高,越能保證數據的完整性和一致性,但是對并發性能的影響也越大。對于多數應用程序,可以優先考慮把數據庫系統的隔離級別設為Read Committed,它能夠避免臟讀取,而且具有較好的并發性能。盡管它會導致不可重復讀、虛讀和第二類丟失更新這些并發問題,在可能出現這類問題的個別場合,可以由應用程序采用悲觀鎖或樂觀鎖來控制。
?????? 通過前面的介紹已經知道,通過選用不同的隔離等級就可以在不同程度上避免前面所提及的在事務處理中所面臨的各種問題。所以,數據庫隔離級別的選取就顯得尤為重要,在選取數據庫的隔離級別時,應該注意以下幾個處理的原則:
?????? 首先,必須排除“未授權讀取”,因為在多個事務之間使用它將會是非常危險的。事務的回滾操作或失敗將會影響到其他并發事務。第一個事務的回滾將會完全將其他事務的操作清除,甚至使數據庫處在一個不一致的狀態。很可能一個已回滾為結束的事務對數據的修改最后卻修改提交了,因為“未授權讀取”允許其他事務讀取數據,最后整個錯誤狀態在其他事務之間傳播開來。
?????? 其次,絕大部分應用都無須使用“序列化”隔離(一般來說,讀取幻影數據并不是一個問題),此隔離級別也難以測量。目前使用序列化隔離的應用中,一般都使用悲觀鎖,這樣強行使所有事務都序列化執行。
?????? 剩下的也就是在“授權讀取”和“可重復讀取”之間選擇了。我們先考慮可重復讀取。如果所有的數據訪問都是在統一的原子數據庫事務中,此隔離級別將消除一個事務在另外一個并發事務過程中覆蓋數據的可能性(第二個事務更新丟失問題)。這是一個非常重要的問題,但是使用可重復讀取并不是解決問題的唯一途徑。
?????? 假設使用了“版本數據”,Hibernate會自動使用版本數據。Hibernate的一級Session緩存和版本數據已經為你提供了“可重復讀取隔離”絕大部分的特性。特別是,版本數據可以防止二次更新丟失的問題,一級Session緩存可以保證持久載入數據的狀態與其他事務對數據的修改隔離開來,因此如果使用對所有的數據庫事務采用授權讀取隔離和版本數據是行得通的。
?????? “可重復讀取”為數據庫查詢提供了更好的效率(僅對那些長時間的數據庫事務),但是由于幻影讀取依然存在,因此沒必要使用它(對于Web應用來說,一般也很少在一個數據庫事務中對同一個表查詢兩次)。
?????? 也可以同時考慮選擇使用Hibernate的二級緩存,它可以如同底層的數據庫事務一樣提供相同的事務隔離,但是它可能弱化隔離。假如在二級緩存大量使用緩存并發策略,它并不提供重復讀取語義(例如,后面章節中將要討論的讀寫,特別是非嚴格讀寫),很容易可以選擇默認的隔離級別:因為無論如何都無法實現“可重復讀取”,因此就更沒有必要拖慢數據庫了。另一方面,可能對關鍵類不采用二級緩存,或者采用一個完全的事務緩存,提供“可重復讀取隔離”。那么在業務中需要使用到“可重復讀取”嗎?如果你喜歡,當然可以那樣做,但更多的時候并沒有必要花費這個代價。
posted @
2009-07-19 21:04 jadmin 閱讀(73) |
評論 (0) |
編輯 收藏