31 構造器Constructor是否可被override?
構造器Constructor不能被繼承,因此不能重寫Overriding,但可以被重載Overloading。
32 是否可以繼承String類?
String類是final類故不可以繼承。
33 當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法?
不能,一個對象的一個synchronized方法只能由一個線程訪問。
33 try {}里有一個return語句,那么緊跟在這個try后的finally {}里的code會不
會被執行,什么時候被執行,在return前還是后?
會執行,在return前執行。
34 編程題: 用最有效率的方法算出2乘以8等於幾?
2 << 3
35 兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?
不對,有相同的hash code。
36 當一個對象被當作參數傳遞到一個方法后,此方法可改變這個對象的屬性,并可返回變化后的結果,那么這里到底是值傳遞還是引用傳遞?
是值傳遞。Java編程語言只由值傳遞參數。當一個對象實例作為一個參數被傳遞到方法中時,參數的值就是對該對象的引用。對象的內容可以在被調用的方法中改變,但對象的引用是永遠不會改變的。
37 swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
switch(expr1)中,expr1是一個整數表達式。因此傳遞給 switch 和 case 語句的參數應該是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。
38 Hashtable和HashMap
Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map interface的一個實現
HashMap允許將null作為一個entry的key或者value,而Hashtable不允許
還有就是,HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因為contains方法容易讓人引起誤解。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在
多個線程訪問Hashtable時,不需要自己為它的方法實現同步,而HashMap就必須為之提供外同步。
Hashtable和HashMap采用的hash/rehash算法都大概一樣,所以性能不會有很大的差異。
21 數組有沒有length()這個方法? String有沒有length()這個方法?
數組沒有length()這個方法,有length的屬性。
String有有length()這個方法。
22 Overload和Override的區別。Overloaded的方法是否可以改變返回值的類型?
方法的重寫Overriding和重載Overloading是Java多態性的不同表現。重寫Overriding是父類與子類之間多態性的一種表現,重載Overloading是一個類中多態性的一種表現。如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫(Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被“屏蔽”了。如果在一個類中定義了多個同名的方法,它們或有不同的參數個數或有不同的參數類型,則稱為方法的重載(Overloading)。
Overloaded的方法是可以改變返回值的類型。
23 Set里的元素是不能重復的,那么用什么方法來區分重復與否呢? 是用==還是equals()? 它們有何區別?
Set里的元素是不能重復的,那么用iterator()方法來區分重復與否。equals()是判讀兩個Set是否相等。 equals()和==方法決定引用值是否指向同一對象equals()在類中被覆蓋,為的是當兩個分離的對象的內容和類型相配的話,返回真值。
24最常見到的runtime exception。
ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DOMException,EmptyStackException, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStateException, IllegalStateException,ImagingOpException, IndexOutOfBoundsException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NullPointerException, ProfileDataException, ProviderException, RasterFORMatException, Secur
ityException, SystemException, UndeclaredThrowableException, UnmodifiableSetException, UnsupportedOperationException
25 error和exception有什么區別?
error 表示恢復不是不可能但很困難的情況下的一種嚴重問題。比如說內存溢出。不可能指望程序能處理這樣的情況。
exception 表示一種設計或實現問題。也就是說,它表示如果程序運行正常,從不會發生的情況。
26 List, Set, Map是否繼承自Collection接口?
List,Set是
Map不是
27 abstract class和interface有什么區別?
聲明方法的存在而不去實現它的類被叫做抽象類(abstract class),它用于要創建一個體現某些基本行為的類,并為該類聲明方法,但不能在該類中實現該類的情況。不能創建abstract 類的實例。然而可以創建一個變量,其類型是一個抽象類,并讓它指向具體子類的一個實例。不能有抽象構造函數或抽象靜態方法。Abstract 類的子類為它們父類中的所有抽象方法提供實現,否則它們也是抽象類為。取而代之,在子類中實現該方法。知道其行為的其它類可以在類中實現這些方法。
接口(interface)是抽象類的變體。在接口中,所有方法都是抽象的。多繼承性可通過實現這樣的接口而獲得。接口中的所有方法都是抽象的,沒有一個有程序體。接口只可以定義static final成員變量。接口的實現與子類相似,除了該實現類不能從接口定義中繼承行為。當類實現特殊接口時,它定義(即將程序體給予)所有這種接口的方法。然后,它可以在實現了該接口的類的任何對象上調用接口的方法。由于有抽象類,它允許使用接口名作為引用變量的類型。通常的動態聯編將生效。引用可以轉換到接口類型或從接口類型轉換,instanceof 運算符可以用來決定某對象的類是否實現了接口。
28 abstract的method是否可同時是static,是否可同時是native,是否可同時是synchronized?
都不能
29 接口是否可繼承接口? 抽象類是否可實現(implements)接口? 抽象類是否可繼承實體類(concrete class)?
接口可以繼承接口。抽象類可以實現(implements)接口,抽象類可繼承實體類,但前提是實體類必須有明確的構造函數。
30 啟動一個線程是用run()還是start()?
啟動一個線程是調用start()方法,使線程所代表的虛擬處理機處于可運行狀態,這意味著它可以由JVM調度并執行。這并不意味著線程就會立即運行。run()方法可以產生必須退出的標志來停止一個線程。
11 &和&&的區別。
&是位運算符。&&是布爾邏輯運算符。
12 HashMap和Hashtable的區別。
都屬于Map接口的類,實現了將惟一鍵映射到特定的值上。
HashMap 類沒有分類或者排序。它允許一個 null 鍵和多個 null 值。
Hashtable 類似于 HashMap,但是不允許 null 鍵和 null 值。它也比 HashMap 慢,因為它是同步的。
13 Collection 和 Collections的區別。
Collection是個java.util下的接口,它是各種集合結構的父接口。
Collections是個java.util下的類,它包含有各種有關集合操作的靜態方法。
14 什么時候用assert。
斷言是一個包含布爾表達式的語句,在執行這個語句時假定該表達式為 true。
如果表達式計算為 false,那么系統會報告一個 Assertionerror。它用于調試目的:
assert(a > 0); // throws an Assertionerror if a <= 0
斷言可以有兩種形式:
assert Expression1 ;
assert Expression1 : Expression2 ;
Expression1 應該總是產生一個布爾值。
Expression2 可以是得出一個值的任意表達式。這個值用于生成顯示更多調試
信息的 String 消息。
斷言在默認情況下是禁用的。要在編譯時啟用斷言,需要使用 source 1.4 標記:
javac -source 1.4 Test.java
要在運行時啟用斷言,可使用 -enableassertions 或者 -ea 標記。
要在運行時選擇禁用斷言,可使用 -da 或者 -disableassertions 標記。
要系統類中啟用斷言,可使用 -esa 或者 -dsa 標記。還可以在包的基礎上啟用或者禁用斷言。
可以在預計正常情況下不會到達的任何位置上放置斷言。斷言可以用于驗證傳遞給私有方法的參數。不過,斷言不應該用于驗證傳遞給公有方法的參數,因為不管是否啟用了斷言,公有方法都必須檢查其參數。不過,既可以在公有方法中,也可以在非公有方法中利用斷言測試后置條件。另外,斷言不應該以任何方式改變程序的狀態。
15 GC是什么? 為什么要有GC? (基礎)。
GC是垃圾收集器。Java 程序員不用擔心內存管理,因為垃圾收集器會自動進行管理。要請求垃圾收集,可以調用下面的方法之一:
System.gc()
Runtime.getRuntime().gc()
16 String s = new String("xyz");創建了幾個String Object?
兩個對象,一個是“xyz”,一個是指向“xyz”的引用對象s。
17 Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
Math.round(11.5)返回(long)12,Math.round(-11.5)返回(long)-11;
18 short s1 = 1; s1 = s1 + 1;有什么錯? short s1 = 1; s1 += 1;有什么錯?
short s1 = 1; s1 = s1 + 1;有錯,s1是short型,s1+1是int型,不能顯式轉化為short型。可修改為s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正確。
19 sleep() 和 wait() 有什么區別? 搞線程的最愛
sleep()方法是使線程停止一段時間的方法。在sleep 時間間隔期滿后,線程不一定立即恢復執行。這是因為在那個時刻,其它線程可能正在運行而且沒有被調度為放棄執行,除非(a)“醒來”的線程具有更高的優先級 (b)正在運行的線程因為其它原因而阻塞。
wait()是線程交互時,如果線程對一個同步對象x 發出一個wait()調用,該線程會暫停執行,被調對象進入等待狀態,直到被喚醒或等待時間到。
20 Java有沒有goto?
Goto—java中的保留字,現在沒有在java中使用。
1. Java中的異常處理機制的簡單原理和應用。
當Java程序違反了Java的語義規則時,Java虛擬機就會將發生的錯誤表示為一個異常。違反語義規則包括2種情況。一種是Java類庫內置的語義檢查。例如數組下標越界,會引發IndexOutOfBoundsException;訪問null的對象時會引發NullPointerException。另一種情況就是Java允許程序員擴展這種語義檢查,程序員可以創建自己的異常,并自由選擇在何時用throw關鍵字引發異常。所有的異常都是java.lang.Thowable的子類。
2. Java的接口和C++的虛類的相同和不同處。
由于Java不支持多繼承,而有可能某個類或對象要使用分別在幾個類或對象里面的方法或屬性,現有的單繼承機制就不能滿足要求。與繼承相比,接口有更高的靈活性,因為接口中沒有任何實現代碼。當一個類實現了接口以后,該類要實現接口里面所有的方法和屬性,并且接口里面的屬性在默認狀態下面都是public static,所有方法默認情況下是public.一個類可以實現多個接口。
3. 垃圾回收的優點和原理。并考慮2種回收機制。
Java語言中一個顯著的特點就是引入了垃圾回收機制,使c++程序員最頭疼的內存管理的問題迎刃而解,它使得Java程序員在編寫程序的時候不再需要考慮內存管理。由于有個垃圾回收機制,Java中的對象不再有“作用域”的概念,只有對象的引用才有“作用域”。垃圾回收可以有效的防止內存泄露,有效的使用可以使用的內存。垃圾回收器通常是作為一個單獨的低級別的線程運行,不可預知的情況下對內存堆中已經死亡的或者長時間沒有使用的對象進行清除和回收,程序員不能實時的調用垃圾回收器對某個對象或所有對象進行垃圾回收。回收機制有分代復制垃圾回收和標記垃圾回收,增量垃圾回收。
4.線程同步的方法。
wait():使一個線程處于等待狀態,并且釋放所持有的對象的lock。
sleep():使一個正在運行的線程處于睡眠狀態,是一個靜態方法,調用此方法要捕捉InterruptedException異常。
notify():喚醒一個處于等待狀態的線程,注意的是在調用此方法的時候,并不能確切地喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且不是按優先級。
Allnotity():喚醒所有處入等待狀態的線程,注意并不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。
5. Error與Exception有什么區別?
Error表示系統級的錯誤和程序不必處理的異常,
Exception表示需要捕捉或者需要程序進行處理的異常。
6. 在java中一個類被聲明為final類型,表示了什么意思?
表示該類不能被繼承,是頂級類。
7 heap和stack有什么區別。
棧是一種線形集合,其添加和刪除元素的操作應在同一段完成。棧按照后進先出的方式進行處理。堆是棧的一個組成元素。
8談談final, finally, finalize的區別。
final—修飾符(關鍵字)如果一個類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被聲明為 abstract的,又被聲明為final的。將變量或方法聲明為final,可以保證它們在使用中不被改變。被聲明為final的變量必須在聲明時給定初值,而在以后的引用中只能讀取,不可修改。被聲明為final的方法也同樣只能使用,不能重載。
finally—異常處理時提供 finally 塊來執行任何清除操作。如果拋出一個異常,那么相匹配的 catch 子句就會執行,然后控制就會進入 finally 塊(如果有的話)。
finalize—方法名。Java 技術允許使用 finalize() 方法在垃圾收集器將對象從內存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在確定這個對象沒有被引用時對這個對象調用的。它是在 Object 類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統資源或者執行其他清理工作。finalize() 方法是在垃圾收集器刪除對象之前對這個對象調用的。
9 Anonymous Inner Class (匿名內部類) 是否可以extends(繼承)其它類,是否可以implements(實現)interface(接口)?
匿名的內部類是沒有名字的內部類。不能extends(繼承) 其它類,但一個內部類可以作為一個接口,由另一個內部類實現。
10 Static Nested Class 和 Inner Class的不同
Nested Class (一般是C++的說法),Inner Class (一般是JAVA的說法)。Java內部類與C++嵌套類最大的不同就在于是否有指向外部的引用上。
注: 靜態內部類(Inner Class)意味著1創建一個static內部類的對象,不需要一個外部類對象,2不能從一個static內部類的一個對象訪問一個外部類對象 .
漢字編碼轉換
相關:
UNICODE 是為了處理包括中文,日文等字符而提出的一種通用的字符集。最初的UNICODE為雙字節字符集,即16位編碼,能夠包括65,536個字符。但這樣的容量并不能滿足所有需要,因此,現在的UNICODE已經擴展到4個字節,能夠容納1,112,064 個字符,而這些在16位之后的擴展背稱為增補字符。
UTF-32 、 UTF-16 和 UTF-8 是 Unicode 標準的編碼字符集的字符編碼方案。
UTF-8 使用一至四個字節的序列對編碼 Unicode 代碼點進行編碼
UTF-8 使用一至四個字節的序列對編碼 Unicode 代碼點進行編碼。U+0000 至 U+007F 使用一個字節編碼,U+0080 至 U+07FF 使用兩個字節,U+0800 至 U+FFFF 使用三個字節,而 U+10000 至 U+10FFFF 使用四個字節。UTF-8 設計原理為:字節值 0x00 至 0x7F 始終表示代碼點 U+0000 至 U+007F(Basic Latin 字符子集,它對應 ASCII 字符集)。這些字節值永遠不會表示其他代碼點,這一特性使 UTF-8 可以很方便地在軟件中將特殊的含義賦予某些 ASCII 字符。
GB2312(1980 年 ) 一共收錄了 7445 個字符,包括 6763 個漢字和 682 個其它符號。漢字區的內碼范圍高字節從 B0-F7 ,低字節從 A1-FE ,占用的碼位是 72*94=6768 。其中有 5 個空位是 D7FA-D7FE 。當然也可以表示數字和字符(一個字節,與 ASCII 表示相同)。
要讀取一個以 GB2312 編碼的包含漢字、數字、字母的二進制文件。
String strName =Encoding.GetEncoding("gb2312").GetString(name,0,i) ;
// name 是讀取的二進制數組。
這樣就能將二進制數組轉換為 漢字、數字或字母
同樣:也可以將包含漢字、數字、字母的字符串轉換為 二進制數組保存到 二進制文件。
String unicodeString = " 備用43E";
Byte[] encodedBytes = Encoding.GetEncoding("gb2312").GetBytes(unicodeString);
當然也可以進行二進制數組與UNICODE,UTF-8等編碼方式的轉換
Byte[] encodedBytes = utf8.GetBytes(unicodeString);
String decodedString = utf8.GetString(encodedBytes);
UnicodeEncoding unicode = new UnicodeEncoding();
Byte[] encodedBytes = unicode.GetBytes(unicodeString);
String decodedString = unicode.GetString(encodedBytes);
F1 Help
F3 查找下一個
shift + F3 反向查找下一個
ctrl + F 查找
ctrl + p 路徑查找
ctrl + F4 運行到當前位置
ctrl+F6在jbuilder中,切換不同的文件
Ctrl+F4關閉正在編輯的文件,
Ctrl + F5切換工程
F5 設置斷點
F7 跟入
F8 單步
F9 運行
ctrl + F9 編譯工程
shift + F9 調試模式運行
ctrl + shift + F9 編譯當前類
ctrl + H 顯示本類成員
ctrl + J 顯示模板
ctrl + shift + J 模板編輯
ctrl + shift + c 自動完成錯誤捕捉代碼
ctrl + Enter /mouseClick 當前關鍵字追蹤
ctrl + shit + 數字(0-9) 設置/去除標簽
ctrl + 數字(0-9) 返回標簽位置
ctrl + alt + -> / <- 返回最近訪問點
ctrl + -> / <- 光標跳過當前單位詞
ctrl + shift + -> / <- 選擇單位詞
ctrl + 上下方向鍵 滾動屏幕
ctrl + home/end/pageup/pagedown
shift +盤方向鍵/home/end/pageup/pagedown 選擇
ctrl + e 增量式查找
ctrl + w 捕捉離光標最近的單詞
ctrl + shift + h 參數查找
ctrl + alt + space class insight
ctrl + F4 關閉當前類
ctrl + shift + F4 關閉(顯示選擇)
ctrl + B 切換窗體
ctrl + F6 切換窗體
ctrl + alt + p/c/m/z/s 視圖開關
ctrl + o/n/c/v/x/p/a
ctrl + g 到指定行
Tab 格式化宿進
ctrl + / 注釋/去除注釋選擇行
ctrl + shift + I 宿進
ctrl + shift + U 反向宿進
常用到的Eclipse快捷鍵
Ctrl+s 存盤
Ctrl+/ 注釋(取消)代碼
Ctrl+shift+/ 注釋代碼塊
Ctrl+shift+\ 取消代碼塊
Alt+/ 代碼輔助/調出IF語句等程序模板:使用方法:打出if,按ALT+/
Ctrl+D 刪除一行
Ctrl+Shift+D 在 debug 模式里顯示變量值
Ctrl+1 快速修復
Ctrl+Shift+f 代碼格式化
Ctrl+Shift+o 整理導入
Ctrl+f6 切換窗口
ctrl+shift+M 導入未引用的包
ctrl+w 關閉單個窗口
F3 跳轉到類、變量的聲明
F11 運行上次程序
Ctrl + F11 調試上次程序
Alt + 回下一個編輯點
ctrl+shift+T 查找工程中的類
Alt-left arrow : 在導航歷史記錄(Navigation History)中后退。就像Web瀏覽器的后退按鈕一樣,在利用F3跳轉之后,特別有用。(用來返回原先編譯的地方)
Alt+right arrow : 導航歷史記錄中向前。
Control+Q : 回到最后依次編輯的地方。這個快捷鍵也是當你在代碼中跳轉后用的。特別是當你鉆的過深,忘記你最初在做什么的時候。
ctrl+Alt+down 復制鼠標所在行到下一行
Alt+down arrow : 將一行或多行向下移動。
Alt+up arrow 將一行或多行 向上移動。
迭代這個名詞對于熟悉Java的人來說絕對不陌生。我們常常使用JDK提供的迭代接口進行java collection的遍歷:
Iterator it = list.iterator();
while(it.hasNext()){
//using “it.next();”do some businesss logic
} |
而這就是關于迭代器模式
應用很好的例子。
二、 定義與結構
迭代器(Iterator)模式,又叫做游標(Cursor)模式。GOF給出的定義為:提供一種方法訪問一個容器(container)對象中各個元素,而又不需暴露該對象的內部細節。
從定義可見,迭代器模式是為容器而生。很明顯,對容器對象的訪問必然涉及到遍歷算法。你可以一股腦的將遍歷方法塞到容器對象中去;或者根本不去提供什么遍歷算法,讓使用容器的人自己去實現去吧。這兩種情況好像都能夠解決問題。
然而在前一種情況,容器承受了過多的功能,它不僅要負責自己“容器”內的元素維護(添加、刪除等等),而且還要提供遍歷自身的接口;而且由于遍歷狀態保存的問題,不能對同一個容器對象同時進行多個遍歷。第二種方式倒是省事,卻又將容器的內部細節暴露無遺。
而迭代器模式的出現,很好的解決了上面兩種情況的弊端。先來看下迭代器模式的真面目吧。
迭代器模式由以下角色組成:
1) 迭代器角色(Iterator):迭代器角色負責定義訪問和遍歷元素的接口。
2) 具體迭代器角色(Concrete Iterator):具體迭代器角色要實現迭代器接口,并要記錄遍歷中的當前位置。
3) 容器角色(Container):容器角色負責提供創建具體迭代器角色的接口。
4) 具體容器角色(Concrete Container):具體容器角色實現創建具體迭代器角色的接口——這個具體迭代器角色于該容器的結構相關。
迭代器模式的類圖如下:
從結構上可以看出,迭代器模式在客戶與容器之間加入了迭代器角色。迭代器角色的加入,就可以很好的避免容器內部細節的暴露,而且也使得設計符號“單一職責原則”。
注意,在迭代器模式中,具體迭代器角色和具體容器角色是耦合在一起的——遍歷算法是與容器的內部細節緊密相關的。為了使客戶程序從與具體迭代器角色耦合的困境中脫離出來,避免具體迭代器角色的更換給客戶程序帶來的修改,迭代器模式抽象了具體迭代器角色,使得客戶程序更具一般性和重用性。這被稱為多態迭代。
三、 舉例
由于迭代器模式本身的規定比較松散,所以具體實現也就五花八門。我們在此僅舉一例,根本不能將實現方式一一呈現。因此在舉例前,我們先來列舉下迭代器模式的實現方式。
1.迭代器角色定義了遍歷的接口,但是沒有規定由誰來控制迭代。在Java collection的應用中,是由客戶程序來控制遍歷的進程,被稱為外部迭代器;還有一種實現方式便是由迭代器自身來控制迭代,被稱為內部迭代器。外部迭代器要比內部迭代器靈活、強大,而且內部迭代器在java語言環境中,可用性很弱。
2.在迭代器模式中沒有規定誰來實現遍歷算法。好像理所當然的要在迭代器角色中實現。因為既便于一個容器上使用不同的遍歷算法,也便于將一種遍歷算法應用于不同的容器。但是這樣就破壞掉了容器的封裝——容器角色就要公開自己的私有屬性,在java中便意味著向其他類公開了自己的私有屬性。
那我們把它放到容器角色里來實現好了。這樣迭代器角色就被架空為僅僅存放一個遍歷當前位置的功能。但是遍歷算法便和特定的容器緊緊綁在一起了。
而在Java Collection的應用中,提供的具體迭代器角色是定義在容器角色中的內部類。這樣便保護了容器的封裝。但是同時容器也提供了遍歷算法接口,你可以擴展自己的迭代器。
好了,我們來看下Java Collection中的迭代器是怎么實現的吧。
//迭代器角色,僅僅定義了遍歷接口
public interface Iterator {
boolean hasNext();
Object next();
void remove();
}
//容器角色,這里以List為例。它也僅僅是一個接口,就不羅列出來了
//具體容器角色,便是實現了List接口的ArrayList等類。為了突出重點這里指羅列和迭代器相關的內容
//具體迭代器角色,它是以內部類的形式出來的。AbstractList是為了將各個具體容器角色的公共部分提取出來而存在的。
public abstract class AbstractList extends AbstractCollection implements List {
……
//這個便是負責創建具體迭代器角色的工廠方法
public Iterator iterator() {
return new Itr();
}
//作為內部類的具體迭代器角色
private class Itr implements Iterator {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public Object next() {
checkForComodification();
try {
Object next = get(cursor);
lastRet = cursor++;
return next;
} catch(IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch(IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
} |
至于迭代器模式的使用。正如引言中所列那樣,客戶程序要先得到具體容器角色,然后再通過具體容器角色得到具體迭代器角色。這樣便可以使用具體迭代器角色來遍歷容器了……
四、 實現自己的迭代器
在實現自己的迭代器的時候,一般要操作的容器有支持的接口才可以。而且我們還要注意以下問題:
在迭代器遍歷的過程中,通過該迭代器進行容器元素的增減操作是否安全呢?
在容器中存在復合對象的情況,迭代器怎樣才能支持深層遍歷和多種遍歷呢?
以上兩個問題對于不同結構的容器角色,各不相同,值得考慮。
五、 適用情況
由上面的講述,我們可以看出迭代器模式給容器的應用帶來以下好處:
1) 支持以不同的方式遍歷一個容器角色。根據實現方式的不同,效果上會有差別。
2) 簡化了容器的接口。但是在java Collection中為了提高可擴展性,容器還是提供了遍歷的接口。
3) 對同一個容器對象,可以同時進行多個遍歷。因為遍歷狀態是保存在每一個迭代器對象中的。
由此也能得出迭代器模式的適用范圍:
1) 訪問一個容器對象的內容而無需暴露它的內部表示。
2) 支持對容器對象的多種遍歷。
3) 為遍歷不同的容器結構提供一個統一的接口(多態迭代)。
六、 總結
迭代器模式在我們的應用中很廣泛,希望本文能幫助你理解它。如有不對之處,還請不吝指正。
非常感謝最近發布的Hibernate 3中的XML持久性特性,Java開發者現在擁有了一個框架組件,它為易于實現的對象關系(OR)和XML持久性提供了高效的和一致的方法。
Hibernate的易用性、高性能和對象關系持久性等高級特性給IT界帶來了很大的驚喜。 Hibernate的最新版本(版本3,3月29日發布的)給產品API帶來了一個重要的新特性:XML持久性。有了Hibernate 3之后,Java應用程序開發者可以輕易地把XML文檔合并到關系型數據庫中。
這個新特性應該明確地告訴已有的Hibernate開發者,因為它也遵循POJO(純的舊Java對象)相同的一致性方法,需要學習的知識最少。XML持久性的優點也應該介紹給新用戶。本文講解的是Hibernate 3持久性方法。
XML持久性為什么重要
大多數大型商業數據庫都支持某種形式的本地XML持久性。由于XML持久性是一個相對較新的機制--即使對大型廠商也是如此,這個領域中的標準還在不斷地浮現。其結果是,為了把無處不在的關系型持久性機制與日益增長的XML解決方案集成在一起,架構師必須依賴廠商特定的特性或者實現定制的XML持久性框架組件。這兩個選擇都沒有太大的吸引力。廠商特定的特性不是普及的,因為可能產生廠商封鎖(lock-in),而定制的框架組件實現可能耗費大量的時間和財力,導致代碼難于維護。
在OR(對象關系)持久性方面,Hibernate XML持久性是一個自然而然的解決方案。它可以跨越Hibernate支持的所有關系型平臺(如虛擬的或真實的關系型平臺)移動,允許自由的遷移對象、基于XML的應用程序和集成解決方案而不用擔心下層的關系型實現方法。
體系結構的細節信息
Hibernate是一個良好架構的框架組件,它無縫地利用了本地的環境,不需要用戶進行任何特殊的干涉或安裝操作。從一個數據庫切換到另外一個數據庫通常只需要改變驅動程序,并配置Hibernate(在線配置設置信息)來使用另外一種數據庫語言。
Hibernate利用dom4j框架組件進行XML的分析和維護。如果需要完全利用Hibernate的XML特性,你就必須對dom4j非常熟悉。一般來說,你會發現dom4j比Java提供的JAXP或與JAXP兼容的XML分析器要容易使用一些。它要求我們學習的相關知識較少,并且利用最少的dom4j知識你就能夠高效率地使用Hibernate XML持久性。
實際例子:價格目錄同步
通用的電子商務案例可以演示XML關系持久性機制的作用。我們來考慮一個示例,在這個例子中XML集成了在線零售商和供應商之間的產品標價目錄。
該電子目錄包含了已標價的產品列表。在線商店銷售產品,通過自己的存貨清單來管理(類似于Amazon與Toys-R-Us和運動產品商店之間的關系)。為了精確地和有效地反映價格的變化,在線零售商必須頻繁地接收產品價格信息。它把這些信息存放為XML文檔,如下所示:
<products>
<product prod_id="3" sku="100101">
<description>Athlete mode body fat scale</description>
<list_price>100.00</list_price>
<drop_price>60.00</drop_price>
</product>
<product prod_id="4" sku="100102">
<description>Thermometer</description>
<list_price>20.00</list_price>
<drop_price>11.00</drop_price>
</product>
</products> |
全面的主要的產品價格列表存儲在數據庫中,如下所示:
CREATE TABLE PRODUCT
(
id INT UNIQUE NOT NULL,
description VARCHAR(45) NOT NULL,
sku VARCHAR(45) UNIQUE NOT NULL,
list_price FLOAT,
base_price FLOAT,
order_price FLOAT,
CONSTRAINT PK_PRODUCT PRIMARY KEY (id )
) |
在線零售商通過已有的OR映射提供定價目錄的Web表現形式,定價產品都表現為demo.Product Java對象:
/** Product對象表現了定價目錄項*/
public class Product {
int id;
String sku;
String description;
Double listPrice;
Double basePrice;
Double orderPrice; |
這些對象按照下面的方式映射(為了清楚,我們列出了列名,盡管在屬性和列名相匹配的時候Hibernate可以自動地把屬性映射為列名):
<?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="demo">
<class name="Product"
table="product"
node="product">
<id name="id"
type="int"
node="@prod_id"
column="id">
</id>
<property name="sku" node="@sku" column="sku" not-null="true"/>
<property name="description" node="description" column="description" not-null="true"/>
<property name="listPrice" node="list_price" column="list_price" />
<property name="basePrice" node="drop_price" column="base_price"/>
<property name="orderPrice" column="order_price"/>
</class>
</hibernate-mapping> |
在這種情況下,Hibernate的XML關系持久性就非常方便了。由于該電子商務應用程序接收了包含產品價格更新的XML,它就利用Hibernate的XML持久性機制把這些XML寫入到產品數據庫中。Hibernate提供了幾種XML持久性選擇,包括Hibernate的saveOrUpdate方法:
document = saxReader.read(inputXML);
List users = document.selectNodes("http://product");
try {
Session session = ibernateUtil.sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
Session dom4jSession = session.openSession(EntityMode.DOM4J);
Iterator iter = users.iterator();
while (iter.hasNext()) {
Object next = iter.next();
dom4jSession.saveOrUpdate("demo.Product", next );
}// end while
transaction.commit();
session.close(); |
XML映射語法
上面的例子中使用的映射文件不用于Hibernate 2的映射文件。Hibernate 3引入了幾種專門用于XML持久性的新映射類型。
主要的新映射屬性是節點(node),它與被映射的XML文檔中的一個元素或文檔中的屬性相關聯。
一個"節點"可能表現為下面的某種映射:
· "element-name(元素名)":在例子中,<product></product>元素會被表示為node="product"。
· "@attribute-name(屬性名)":在例子中,node="@sku"會被映射為XML屬性<product sku="1001001">。
· ".(句點)":映射為元素的父元素(例如<products>就<product>是的父元素)。
· "element-name/@attribute-name(元素名/屬性名)":映射為命名元素的屬性(product/@sku)。
XML持久性并非Hibernate的主要任務
Hibernate 3框架組件高效率地實現了目前最通用的一些方法(除了LDAP之外)。Java社團現在擁有了一套框架組件,它為易于實現的OR和XML持久性提供了高效率的和一致性的方法。
在我們知道上面一些內容之后,了解Hibernate項目的任務是很重要的。盡管Hibernate 3的XML特性非常有用、有吸引力,但是它們并不是用來代替最流行的XML編組(marshalling)和轉換(transformation)框架組件的。不管它的OR映射解決方案多么完善,我們都不應該期待Hibernate成為主流的XML維護框架組件(根據Hibernate的作者Gavin King在TheServerSide Java Symposium 2005上的發言)。
由于這個原因,你應該把XML持久性特性看作是已有的強大的Hibernate框架組件的有用的擴展,它允許你輕易地把現在流行的其它的數據表現機制合并到自己的應用程序中。但是,如果你必須處理復雜的集成和轉換情況,最好去尋找其它的XML專用的框架組件。
一、首先學習hibernate.cfg.xml配置文件的具體配置
<?xml version="1.0" encoding="UTF-8"?>
<!--指定該文件的官方dtd-->
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" >
<hibernate-configuration>
<session-factory>
<!-- 顯示sql語言 -->
<property name="show_sql">true</property>
<!-- sql語言 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- jdbc驅動程式 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- jdbc url -->
<property name="connection.url">jdbc:mysql://localhost:3306/test</property>
<!-- 數據庫用戶名 -->
<property name="connection.username">root</property>
<!-- 數據庫密碼 -->
<property name="connection.password">wyq</property>
<!-- C3P0連接池設定 -->
<!--最小連接數-->
<property name="c3p0.min_size">5</property>
<!--最大連接數-->
<property name="c3p0.max_size">20</property>
<!--延遲所允許的時間-->
<property name="c3p0.timeout">1800</property>
<!--緩存所允許的最大連接數-->
<property name="c3p0.max_statements">50</property>
<!-- 每隔100筆資料送入資料庫,清除緩存(定期清除緩存,減小壓力) -->
<property name="hibernate.jdbc.batch_size">100</property>
<!-- 設定事務管理的工廠類 -->
<property name="hibernate.transaction.factiory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<mapping resource="com/wyq/hibernate/pojo/User.hbm.xml"/>
<mapping resource="com/wyq/hibernate/pojo/TUser.hbm.xml"/>
<mapping resource="com/wyq/hibernate/pojo/Room.hbm.xml"/>
</session-factory>
</hibernate-configuration>
需要的jar包有c3p0.jar,hibernate3.jar,數據庫.jar,log4j.jar
------------------------------------------------------------------------------------------------------------------------
1、讀取配置文件獲得連接
讀取hibernate.cfg.xml配置文件,hibernate.cfg.xml文件放在Classpath下,使用下面的方式讀入該文件
//Configuration 負責管理hibernate配置信息
Configuration config=new Configuration().configure();
//根據config建立SessionFactory
//SessionFactory用于建立Session
SessionFactory sessionFactory=config.buildSessionFactory();
//開啟session,相當于jdbc的Connection
session = sessionFactory.openSession();
2、Criteria 基本資料查詢
(1)標準查詢:
//創建查詢標準
Criteria criteria=session.creteCriteria(User.class);
//查詢條件
criteria.add(Expression.eq("name","caterpillar"));
************************************************************************************
Expression.eq(String s1,String s2)---------->相等s1=s2
Expression.allEq(Map map) --------------->多個屬性-值對應關系,多個Expression.eq疊加
Expression.gt(String s1,String s2)----------->大于s1>s2
Expression.ge(String s1,String s2)----------->大于等于s1>=s2
Expression.lt(String s1,String s2)------------>小于s1<s2
Expression.le(String s1,String s2)------------>小于等于s1<=s2
Expression.between(String s1,int s2,int s3)--->s2<s1<s3
Expression.like(String s1,String s2)------------>s1 like s2
比較2個屬性
Expression.eqProperty(String s1,String s2)--->s1=s2
Expression.gtProperty(String s1,String s2)---->s1>s2
Expression.geProperty(String s1,String s2)---->s1>=s2
Expression.ltProperty(String s1,String s2)----->s1<s2
Expression.leProperty(String s1,String s2)----->s1<=s2
Expression.and()----->Expression.and(Expression.eq("String s1,String s2"),Expression.eq(String s3,String s4))
Expression.or()
************************************************************************************
(2)高級查詢
一、可以使用Criteria進行查詢,并用order對結果進行排序。
//設置從第幾條開始取的記錄
criteria.setFirstResult(100);
//最多取的幾條記錄
criteria.setMaxResults(20);
//對結果進行排序
criteria.addOrder(Order.asc(String s1));
criteria.addOrder(Order.desc(String s2));
二、可以對查詢結果進行統計操作,使用Projections的rowCount(),count(),max(),min(),countDistinct()等方法:
例如:criteria.setProjection(Projections.max("age"));
三、還可以用Projections的groupProperty()來對結果進行分組
例如:criteria.setProjection(Projections.groupProperty("age"));
(***)四、結合統計與分組的功能,可以用ProjectionList
例如:ProjectionList projectionList =Projections.projectionList();
projectionList.add(Projections.groupProperty("age"));
projectionList.add(Projections.rowCount());
criteria.setProjection(projectionList);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//查詢所有記錄
List users=criteria.list();
Iterator iterator=users.iterator();
while(iterator.hasNext()){
User user=(User)iterator.next();
System.out.println(user.getId()+"\t"+user.getName()+"/"+user.getAge());
}
3、criteria的增、刪、改(還不完善)
在用到增、刪、改時,必須先聲明事務
增加:
Transaction tx = session.beginTransaction();//Transaction表示一組會話操作
session.save(user);//將事物映射到數據庫進行存儲
tx.commit();
session.close();
刪除:
Session session=this.getSession();
User user=(User)session.get(User.class, new Integer(1));
Transaction tx = session.beginTransaction();//Transaction表示一組會話操作
session.delete(user);//將事物映射到數據庫進行存儲
tx.commit();
session.close();
修改:
Session session=this.getSession();
User user =(User)session.get(User.class,new Integer(2));//創建持久化的事物
user.setName("wyqqqqqqqqqq");
user.setAge(new Integer(30));
Transaction tx = session.beginTransaction();//Transaction表示一組會話操作
session.update(user);//將事物映射到數據庫進行存儲
tx.commit();
session.close();
----------------------------------------------------------------------------------------------------------------------
一、Query查詢可以先設定查詢參數,之后通過set等方法,將指定的參數值添入.還可以使用命名參數
Session session = sessionFactory.openSession();
Query query = session.createQuery("select user.name from User as user where user.age>?( :minAge )");
query.setInteger(0,25);
query.setInteger("minAge",25);
List names=query.list();
Iterator iterator = names.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
session.close();
二、如果查詢整個表直接使用from User如果針對某個屬性使用select user.name from User as user
使用hql可以更接近我們平時的jdbc編程,和把sql語句寫在程序中差不多,另外,也可以將sql語句寫在配置文件中。
--------------------------------------------------------------------------------------------------------------------------
多表關聯
一、多對一進行關聯(多個學生對應同一間宿舍)---學生是主體,宿舍是附體,關聯關系<many-to-one>在主體學生中設置,在學生類中設置宿舍類,由于宿舍類只有一個可以直接用類來設置,在映射學生類(User)中包含宿舍這個類(Room),在映射配置文件(User.hbm.xml)中定義
<many-to-one name="room" column="room_id" cascade="save-update" class="com.wyq.hibernate2.Room"></many-to-one>
哪個是主體類就在哪個配置文件定義關聯關系.
cascade屬性:表示關聯對象的持久化,該屬性也要設置在主體中,作用就是當主控方執行操作時,關聯對象(被動方)是否同步執行同一操作.
cascade的值:all:表示所有情況下都進行級聯操作.
none:所有情況下都不進行級聯操作
save-update:在執行save-update時進行級聯操作.
delete:在執行delete時進行級聯操作.
注意:使用cascade自動持久化時,會先檢查被關聯物件的id屬性,未被持久化的物件之id屬性是由unsaved-value決定,預設是null,如果您使用long這樣的原生型態(primitive type)時,則必須自行指定預設值.
例如:<id name="id" column="ROOM_ID" unsaved-value="0">
<generator class="increment"/>
</id>
如果您不想額外設定unsaved-value資訊,則可以將long改為Long,這可以符合預設的unsaved-value為null的設定 .
二、一對多進行關聯(一間宿舍對應多個學生)---宿舍是主體,學生是附體,關聯關系<one-to-many>在主體宿舍中設置,由于要在宿舍類中設置學生類,一個宿舍包含多個學生,所以在宿舍類中要用Set類來進行設置,用set類(private Set users = new HashSet();)來存儲多個學生類,在映射宿舍類(Room)中要包含<set>這個節點,用來與user相關聯
例如:<set name="users" table="USER">
<key column="ROOM_ID"/>
<one-to-many class="onlyfun.caterpillar.User"/>
</set>
name:表示屬性,table:表示關聯的表名,key:表示通過什么字段進行關聯,<one-to-many>:表示關聯類。這里也可以使用cascade屬性。
三、在表關聯的設計中,不論是一對多還是多對一,都要將關聯字段設置在多的那一方。
例如:user表格和room表格,要將關聯字段room_id設置在user表格中。
四、一對一進行關聯(一個人只有一個房間,一個房間也只有一個人)。
可以通過2中方式進行關聯:
(1)、通過外鍵進行關聯:在多對一的例子中就是通過外鍵進行關聯的.
在user-room的設置中(user.hbm.xml):
<many-to-one name="room"
column="ROOM_ID"
class="onlyfun.caterpillar.Room"
cascade="all"
unique="true"/>
其中unique表示限制一個User有一獨有的 Room,這只是單向的,說明一個user只有一個room.
在room-user的設置中(room.hbm.xml):
<one-to-one name="user"
class="onlyfun.caterpillar.User"
property-ref="room"/>
這樣就完成了雙向的一對一關聯,property-ref告訴hibernate,查詢出user并將其參考至room。
(2)、通過主鍵進行關聯:限制兩個資料表的主鍵使用相同的值,如此一個User與Room就是一對一關係
user.hbm.xml:
<one-to-one name="room"
class="onlyfun.caterpillar.Room"
cascade="all"/>
room.hbm.xml:
<one-to-one name="user"
class="onlyfun.caterpillar.User"
constrained="true"/>
使用constrained="true"告訴Hibernate參考至User的主鍵
五、雙向關聯,就是將一和二結合起來,如果將關聯的維護交給User的話會比較容易,因為每個User都對應至一個Room,在儲存時並用像Room一樣必須對Set中的每個物件作檢查,為了將關聯的維護交給User,我們可以在Room.hbm.xml中的<set>修改,加上inverse="true",表示將關聯的維護「反過來」交給User作
例如:<set name="users" table="users" iinverse="true" cascade="all">
<key column="room_id"/>
<one-to-many class="onlyfun.caterpillar.User"/>
在設立雙向關聯時,關聯由多對一中「多」的哪一方維護,會比由「一」的哪一方維護來的方便,在Hibernate可以藉由inverse來設定,不設定inverse基本上也可以運行,但是效能會較差。
------------------------------------------------------------------------------------------------------------------------
在Hibernate中,集合類的映射可以延遲初始(Lazy Initialization),在多對一或者一對多中,都可以使用延遲初始,例如:一個用戶(user對應user表)有多個email地址(address對應address表),也就是在真正索取該物件的資料時,才向資料庫查詢,就上次例子來說,就是我們在讀取User時,先不取得其中的 addrs屬性中之物件資料,由於只需要讀取User的name屬性,此時我們只要執行一次select即可,真正需要addrs的資料時,才向資料庫要求。在含有集合類的user.hbm.xml中要如下設置:
<set name="addrs" table="ADDRS" lazy="true">
<key column="USER_ID"/>
<element type="string" column="ADDRESS" not-null="true"/>
</set>
--------------------------------------------------------------------------------------------------------------------------
session是hibernate運做的核心,是有SessionFactory所創建,sessionFactory是線程安全的,你可以讓多個線程同時存取SessionFactory,而不會有資源共用的問題,然而session不是設計為線程安全的,所以讓多個線程共用一個session,將發生資料共用而發生混亂的問題.下面是一個標準類.
import java.io.Serializable;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Transaction;
public class HibernateSessionUtil implements Serializable
{
//創建線程局部變量 tLocalsess
public static final ThreadLocal tLocalsess = new ThreadLocal();
//創建線程局部變量 tLocaltx
public static final ThreadLocal tLocaltx = new ThreadLocal();
//取得session
public static Session currentSession(){
//從線程變量tLocalsess中,取得當前session
Session session = (Session) tLocalsess.get();
//判斷session是否為空,如果為空,將創建一個session,并付給線程變量tLocalsess
try{
if (session == null){
session = openSession();
tLocalsess.set(session);
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
return session;
}
//關閉當前session
public static void closeSession(){
//從線程變量tLocalsess中,取得當前session
Session session = (Session) tLocalsess.get();
//設置線程變量tLocalsess為空
tLocalsess.set(null);
try{
//關閉session
if (session != null && session.isOpen()){
session.close();
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
//事物處理
public static void beginTransaction(){
//從線程變量tLocaltx中取得事物管理對象Transaction
Transaction tx = (Transaction) tLocaltx.get();
try{
//如果為空就從session中創建一個tx
if (tx == null){
tx = currentSession().beginTransaction();
tLocaltx.set(tx);
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
//提交事物
public static void commitTransaction(){
//取得事物
Transaction tx = (Transaction) tLocaltx.get();
try{
//如果不為空就提交
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack())
tx.commit();
tLocaltx.set(null);
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
//事物回滾
public static void rollbackTransaction(){
//取得tx事物
Transaction tx = (Transaction) tLocaltx.get();
try{
//將變量清空
tLocaltx.set(null);
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()){
//事物回滾
tx.rollback();
}
}catch (HibernateException e){
throw new InfrastructureException(e);
}
}
//取得session
private static Session openSession() throws HibernateException{
return getSessionFactory().openSession();
}
//取得sessionFactory
private static SessionFactory getSessionFactory() throws HibernateException{
return SingletonSessionFactory.getInstance();
}
}
filter的代碼:
public class HibernateSessionCloser implements Filter{
protected FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig)throws ServletException{
this.filterConfig = filterConfig;
}
public void destroy(){
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
try{
chain.doFilter(request, response);
}
finally{
try{
HibernateSessionUtil.commitTransaction();
}catch (InfrastructureException e){
HibernateSessionUtil.rollbackTransaction();
}finally{
HibernateSessionUtil.closeSession();
}
}
}
}
---------------------------------------------------------------------------------------
(1)、悲觀鎖定(Pessimistic Locking)一如其名稱所示,悲觀的認定每次資料存取時,其它的客戶端也會存取同一筆資料,因此對該筆資料進行鎖定,直到自己操作完成後解除鎖定。
悲觀鎖定通常透過系統或資料庫本身的功能來實現,依賴系統或資料庫本身提供的鎖定機制,Hibernate即是如此,可以利用Query或 Criteria的setLockMode()方法來設定要鎖定的表或列(Row)及其鎖定模式,可設定的鎖定模式有以下的幾個:
LockMode.WRITE:在insert或update時進行鎖定,Hibernate會在save()方法時自動獲得鎖定。
LockMode.UPGRADE:利用SELECT ... FOR UPDATE進行鎖定。
LockMode.UPGRADE_NOWAIT:利用SELECT ... FOR UPDATE NOWAIT進行鎖定,在Oracle環境下使用。
LockMode.READ:在讀取記錄時Hibernate會自動獲得鎖定。
LockMode.NONE:沒有鎖定。
(2)、樂觀鎖定(Optimistic locking)則樂觀的認為資料的存取很少發生同時存取的問題,因而不作資料庫層次上的鎖定,為了維護正確的資料,樂觀鎖定使用應用程式上的邏輯實現版本控制的解決。
在不實行悲觀鎖定策略的情況下,資料不一致的情況一但發生,有幾個解決的方法,一種是先更新為主,一種是後更新的為主,比較複雜的就是檢查發生變動的資料來實現,或是檢查所有屬性來實現樂觀鎖定。
要注意的是,由於樂觀鎖定是使用系統中的程式來控制,而不是使用資料庫中的鎖定機制,因而如果有人特意自行更新版本訊息來越過檢查,則鎖定機制就會無效,例如在上例中自行更改userV2的version屬性,使之與資料庫中的版本號相同的話就不會有錯誤,像這樣版本號被更改,或是由於資料是由外部系統而來,因而版本資訊不受控制時,鎖定機制將會有問題,設計時必須注意。