隨著Hibernate在Java開發(fā)中的廣泛應(yīng)用,我們在使用Hibernate進行對象持久化操作中也遇到了各種各樣的問題。這些問題往往都是我們對Hibernate缺乏了解所致,這里我講個我從前遇到的問題及一些想法,希望能給大家一點借鑒。 ? ???????這是在一次事務(wù)提交時遇到的異常。 ???????an?assertion?failure?occured?(this?may?indicate?a?bug?in?Hibernate,?but?is?more?likely?due?to?unsafe?use?of?the?session) net.sf.hibernate.AssertionFailure:?possible?nonthreadsafe?access?to?session 注:非possible?non-threadsafe?access?to?the?session?(那是另外的錯誤,類似但不一樣) ? ???????這個異常應(yīng)該很多的朋友都遇到過,原因可能各不相同。但所有的異常都應(yīng)該是在flush或者事務(wù)提交的過程中發(fā)生的。這一般由我們在事務(wù)開始至事務(wù)提交的過程中進行了不正確的操作導致,也會在多線程同時操作一個Session時發(fā)生,這里我們僅僅討論單線程的情況,多線程除了線程同步外基本與此相同。 ? ???????至于具體是什么樣的錯誤操作那?我給大家看一個例子(假設(shè)Hibernate配置正確,為保持代碼簡潔,不引入包及處理任何異常) ?? SessionFactory?sf?=?new?Configuration().configure().buildSessionFactory()?; Session?s?=?sf.openSession(); Cat?cat?=?new?Cat(); Transaction?tran?=?s.beginTransaction();?(1) s.save(cat);?(2)(此處同樣可以為update?delete) s.evict(cat);?(3) tran.commit();?(4) s.close();(5) ???????這就是引起此異常的典型錯誤。我當時就遇到了這個異常,檢查代碼時根本沒感覺到這段代碼出了問題,想當然的認為在Session上開始一個事務(wù),通過Session將對象存入數(shù)據(jù)庫,再將這個對象從Session上拆離,提交事務(wù),這是一個很正常的流程。如果這里正常的話,那問題一定在別處。 ? ????????問題恰恰就在這里,我的想法也許沒有錯,但是一個錯誤的論據(jù)所引出的觀點永遠都不可能是正確的。因為我一直以為直接在對數(shù)據(jù)庫進行操作,忘記了在我與數(shù)據(jù)庫之間隔了一個Hibernate,Hibernate在為我們提供持久化服務(wù)的同時,也改變了我們對數(shù)據(jù)庫的操作方式,這種方式與我們直接的數(shù)據(jù)庫操作有著很多的不同,正因為我們對這種方式?jīng)]有一個大致的了解造成了我們的應(yīng)用并未得到預(yù)先設(shè)想的結(jié)果。 ? 那Hibernate的持久化機制到底有什么不同那?簡單的說,Hibernate在數(shù)據(jù)庫層之上實現(xiàn)了一個緩存區(qū),當應(yīng)用save或者update一個對象時,Hibernate并未將這個對象實際的寫入數(shù)據(jù)庫中,而僅僅是在緩存中根據(jù)應(yīng)用的行為做了登記,在真正需要將緩存中的數(shù)據(jù)flush入數(shù)據(jù)庫時才執(zhí)行先前登記的所有行為。 ? 在實際執(zhí)行的過程中,每個Session是通過幾個映射和集合來維護所有與該Session建立了關(guān)聯(lián)的對象以及應(yīng)用對這些對象所進行的操作的,與我們這次討論有關(guān)的有entityEntries(與Session相關(guān)聯(lián)的對象的映射)、insertions(所有的插入操作集合)、deletions(刪除操作集合)、updates(更新操作集合)。下面我就開始解釋在最開始的例子中,Hibernate到底是怎樣運作的。 (1)生成一個事務(wù)的對象,并標記當前的Session處于事務(wù)狀態(tài)(注:此時并未啟動數(shù)據(jù)庫級事務(wù))。 (2)應(yīng)用使用s.save保存cat對象,這個時候Session將cat這個對象放入entityEntries,用來標記cat已經(jīng)和當前的會話建立了關(guān)聯(lián),由于應(yīng)用對cat做了保存的操作,Session還要在insertions中登記應(yīng)用的這個插入行為(行為包括:對象引用、對象id、Session、持久化處理類)。 (3)s.evict(cat)將cat對象從s會話中拆離,這時s會從entityEntries中將cat這個對象移出。 (4)事務(wù)提交,需要將所有緩存flush入數(shù)據(jù)庫,Session啟動一個事務(wù),并按照insert,update,……,delete的順序提交所有之前登記的操作(注意:所有insert執(zhí)行完畢后才會執(zhí)行update,這里的特殊處理也可能會將你的程序搞得一團糟,如需要控制操作的執(zhí)行順序,要善于使用flush),現(xiàn)在cat不在entityEntries中,但在執(zhí)行insert的行為時只需要訪問insertions就足夠了,所以此時不會有任何的異常。異常出現(xiàn)在插入后通知Session該對象已經(jīng)插入完畢這個步驟上,這個步驟中需要將entityEntries中cat的existsInDatabase標志置為true,由于cat并不存在于entityEntries中,此時Hibernate就認為insertions和entityEntries可能因為線程安全的問題產(chǎn)生了不同步(也不知道Hibernate的開發(fā)者是否考慮到例子中的處理方式,如果沒有的話,這也許算是一個bug吧),于是一個net.sf.hibernate.AssertionFailure就被拋出,程序終止。 ? 我想現(xiàn)在大家應(yīng)該明白例子中的程序到底哪里有問題了吧,我們的錯誤的認為s.save會立即的執(zhí)行,而將cat對象過早的與Session拆離,造成了Session的insertions和entityEntries中內(nèi)容的不同步。所以我們在做此類操作時一定要清楚Hibernate什么時候會將數(shù)據(jù)flush入數(shù)據(jù)庫,在未flush之前不要將已進行操作的對象從Session上拆離。 ? 對于這個錯誤的解決方法是,我們可以在(2)和(3)之間插入一個s.flush()強制Session將緩存中的數(shù)據(jù)flush入數(shù)據(jù)庫(此時Hibernate會提前啟動事務(wù),將(2)中的save登記的insert語句登記在數(shù)據(jù)庫事務(wù)中,并將所有操作集合清空),這樣在(4)事務(wù)提交時insertions集合就已經(jīng)是空的了,即使我們拆離了cat也不會有任何的異常了。 前面簡單的介紹了一下Hibernate的flush機制和對我們程序可能帶來的影響以及相應(yīng)的解決方法,Hibernate的緩存機制還會在其他的方面給我們的程序帶來一些意想不到的影響。看下面的例子: ?? (name為cat表的主鍵) Cat?cat?=?new?Cat(); cat.setName(“tom”); s.save(cat); cat.setName(“mary”); s.update(cat);(6) Cat?littleCat?=?new?Cat(); littleCat.setName(“tom”); s.save(littleCat); s.flush(); 這個例子看起來有什么問題?估計不了解Hibernate緩存機制的人多半會說沒有問題,但它也一樣不能按照我們的思路正常運行,在flush過程中會產(chǎn)生主鍵沖突,可能你想問:“在save(littleCat)之前不是已經(jīng)更改cat.name并已經(jīng)更新了么?為什么還會發(fā)生主鍵沖突那?”這里的原因就是我在解釋第一個例子時所提到的緩存flush順序的問題,Hibernate按照insert,update,……,delete的順序提交所有登記的操作,所以你的s.update(cat)雖然在程序中出現(xiàn)在s.save(littleCat)之前,但是在flush的過程中,所有的save都將在update之前執(zhí)行,這就造成了主鍵沖突的發(fā)生。 ? 這個例子中的更改方法一樣是在(6)之后加入s.flush()強制Session在保存littleCat之前更新cat的name。這樣在第二次flush時就只會執(zhí)行s.save(littleCat)這次登記的動作,這樣就不會出現(xiàn)主鍵沖突的狀況。 ? 再看一個例子(很奇怪的例子,但是能夠說明問題) Cat?cat?=?new?Cat(); cat.setName(“tom”); s.save(cat);?(7) s.delete(cat);(8) cat.id=null;(9) s.save(cat);(10) s.flush(); ? 這個例子在運行時會產(chǎn)生異常net.sf.hibernate.HibernateException:?identifier?of?an?instance?of?Cat?altered?from?8b818e920a86f038010a86f03a9d0001?to?null ? 這里例子也是有關(guān)于緩存的問題,但是原因稍有不同: (7)和(2)的處理相同。 (8)Session會在deletions中登記這個刪除動作,同時更新entityEntries中該對象的登記狀態(tài)為DELETED。 (9)Cat類的標識符字段為id,將其置為null便于重新分配id并保存進數(shù)據(jù)庫。 (10)此時Session會首先在entityEntries查找cat對象是否曾經(jīng)與Session做過關(guān)聯(lián),因為cat只改變了屬性值,引用并未改變,所以會取得狀態(tài)為DELETED的那個登記對象。由于第二次保存的對象已經(jīng)在當前Session中刪除,save會強制Session將緩存flush才會繼續(xù),flush的過程中首先要執(zhí)行最開始的save動作,在這個save中檢查了cat這個對象的id是否與原來執(zhí)行動作時的id相同。不幸的是,此時cat的id被賦為null,異常被拋出,程序終止(此處要注意,我們在以后的開發(fā)過程盡量不要在flush之前改變已經(jīng)進行了操作的對象的id)。 ? 這個例子中的錯誤也是由于緩存的延時更新造成的(當然,與不正規(guī)的使用Hibernate也有關(guān)系),處理方法有兩種: 1、在(8)之后flush,這樣就可以保證(10)處save將cat作為一個全新的對象進行保存。 2、刪除(9),這樣第二次save所引起的強制flush可以正常的執(zhí)行,在數(shù)據(jù)庫中插入cat對象后將其刪除,然后繼續(xù)第二次save重新插入cat對象,此時cat的id仍與從前一致。 ? 這兩種方法可以根據(jù)不同的需要來使用,呵呵,總覺得好像是很不正規(guī)的方式來解決問題,但是問題本身也不夠正規(guī),只希望能夠在應(yīng)用開發(fā)中給大家一些幫助,不對的地方也希望各位給與指正。 ? 總的來說,由于Hibernate的flush處理機制,我們在一些復雜的對象更新和保存的過程中就要考慮數(shù)據(jù)庫操作順序的改變以及延時flush是否對程序的結(jié)果有影響。如果確實存在著影響,那就可以在需要保持這種操作順序的位置加入flush強制Hibernate將緩存中記錄的操作flush入數(shù)據(jù)庫,這樣看起來也許不太美觀,但很有效。 |
?
??
JList組件有一個單獨的顯示模式ListModel來表示JList的顯示數(shù)據(jù).??
JList創(chuàng)建以后,JList數(shù)據(jù)元素的值及數(shù)據(jù)元素的數(shù)量可以動態(tài)地改變.??
JList在它的數(shù)據(jù)模式ListModel中觀察數(shù)據(jù)的改變.因此,一個ListModel 的正確實現(xiàn)應(yīng)當在每次數(shù)據(jù)發(fā)生改變時,通知事件的監(jiān)聽者.??
當使用構(gòu)造函數(shù)JList(Object[])創(chuàng)建一個JList的實例時,系統(tǒng)將自動 創(chuàng)建一個DefaultListModel的實例來存儲JList的顯示數(shù)據(jù), 可以調(diào)用 DefaultListModel中定義的簡便方法來動態(tài)地修改JList的數(shù)據(jù),如 removeElementAt(index),addElement(Object)等. DefaultListModel 在修改數(shù)據(jù)的同時,將通知JList關(guān)于數(shù)據(jù)的改變.??
?
?
| |||||||||
日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
---|---|---|---|---|---|---|---|---|---|
26 | 27 | 28 | 29 | 30 | 31 | 1 | |||
2 | 3 | 4 | 5 | 6 | 7 | 8 | |||
9 | 10 | 11 | 12 | 13 | 14 | 15 | |||
16 | 17 | 18 | 19 | 20 | 21 | 22 | |||
23 | 24 | 25 | 26 | 27 | 28 | 29 | |||
30 | 1 | 2 | 3 | 4 | 5 | 6 |