try { threads[i].join();
//等待所有線程運(yùn)行結(jié)束
} catch(InterruptedException e){
// ignore } } System.out.println(“Finally, John's balance is:” + acc.getBalance());}}
注意,上面在Account的deposit和withdraw方法中之所以要把對(duì)amount的運(yùn)算使用一個(gè)臨時(shí)變量首先存儲(chǔ),sleep一段時(shí)間,然后,再賦值給amount,是為了模擬真實(shí)運(yùn)行時(shí)的情況。因?yàn)樵谡鎸?shí)系統(tǒng)中,賬戶信息肯定是存儲(chǔ)在持久媒介中,比如RDBMS中,此處的睡眠的時(shí)間相當(dāng)于比較耗時(shí)的數(shù)據(jù)庫(kù)操作,最后把臨時(shí)變量tmp的值賦值給amount相當(dāng)于把a(bǔ)mount的改動(dòng)寫入數(shù)據(jù)庫(kù)中。運(yùn)行AccountTest,結(jié)果如下(每一次結(jié)果都會(huì)不同): E:javaexerin>java AccountTest Finally, John's balance is:3900.0 E:javaexerin>java AccountTest Finally, John's balance is:4900.0 E:javaexerin>java AccountTest Finally, John's balance is:4700.0
E:javaexerin>java AccountTest
Finally, John's balance is:3900.0
E:javaexerin>java AccountTest
Finally, John's balance is:3900.0
E:javaexerin>java AccountTest
Finally, John's balance is:5200.0
為什么會(huì)出現(xiàn)這樣的問題?這就是多線程中的同步的問題。在我們的程序中,Account中的amount會(huì)同時(shí)被多個(gè)線程所訪問,這就是一個(gè)競(jìng)爭(zhēng)資源,通常稱作競(jìng)態(tài)條件。對(duì)于這樣的多個(gè)線程共享的資源我們必須進(jìn)行同步,以避免一個(gè)線程的改動(dòng)被另一個(gè)線程所覆蓋。在我們這個(gè)程序中,Account中的amount是一個(gè)競(jìng)態(tài)條件,所以所有對(duì)amount的修改訪問都要進(jìn)行同步,我們將deposit()和withdraw()方法進(jìn)行同步,修改為:
public synchronized void deposit(float amt){ float tmp = amount;tmp += amt;try {
Thread.sleep(1);
//模擬其它處理所需要的時(shí)間,比如刷新數(shù)據(jù)庫(kù)等
} catch(InterruptedException e){
// ignore } amount = tmp;}
public synchronized void withdraw(float amt){ float tmp = amount;tmp-= amt;try {
Thread.sleep(1);
//模擬其它處理所需要的時(shí)間,比如刷新數(shù)據(jù)庫(kù)等
} catch(InterruptedException e){ // ignore } amount = tmp;}
此時(shí),再運(yùn)行,我們就能夠得到正確的結(jié)果了。Account中的getBalance()也訪問了amount,為什么不對(duì)getBalance()同步呢?因?yàn)間etBalance()并不會(huì)修改amount的值,所以,同時(shí)多個(gè)線程對(duì)它訪問不會(huì)造成數(shù)據(jù)的混亂。
String StringBuffer 代碼示例
public class Test{
public static void stringReplace(String text){
text=text+”c”;//1 }
public static void bufferReplace(StringBuffer text){ text=text.append(“c”);//2 }
public static void main(String args[]){ String textString=new String(“java”);
StringBuffer textBuffer=new StringBuffer(“java”);stringReplace(textString);bufferReplace(textBuffer);
System.out.println(textString+textBuffer);} }
代碼分析:
這段代碼先產(chǎn)生一個(gè)字符串對(duì)象textString,然后調(diào)用stringReplace方法,在方法里產(chǎn)生一個(gè)text局部變量指向new String(“java”)對(duì)象。這時(shí),執(zhí)行到第一行時(shí),由于字符串對(duì)象不可改變,所以會(huì)新產(chǎn)生一個(gè)字符串對(duì)象”javac”,然后將局部變量text指向他。在做返回的時(shí)候,textString所指向的對(duì)象并沒有改變。所以textString 的值還是”java”。
當(dāng)使用StringBuffer的時(shí)候,調(diào)用bufferReplace方法,在方法里產(chǎn)生了一個(gè)text這樣一個(gè)局部變量,在對(duì)text對(duì)象進(jìn)行操作時(shí),指的是對(duì)text所指向?qū)ο筮M(jìn)行操作。由于StringBuffer不會(huì)產(chǎn)生新的對(duì)象。所以在進(jìn)行到第2行時(shí),它的改變會(huì)影響對(duì)象值的改變,這時(shí)返回之后,結(jié)果是javac
所以最后的結(jié)果是javajavac I/O注意要點(diǎn)
(2008-07-23 12:52:40)轉(zhuǎn)載 標(biāo)簽:
雜談
1.掌握常用的輸入流讀取操作方法
– int read()//讀取一個(gè)字節(jié),返回字節(jié)的ASC碼
–
int read(char cbuf[])//讀取字節(jié)進(jìn)入chuf字符數(shù)組,返回讀取的字節(jié)數(shù) –
int read(char cbuf[], int offset, int length)//讀取字節(jié)進(jìn)入chuf字符數(shù)組,返回讀取的字節(jié)數(shù),offset為數(shù)據(jù)起始位置,length讀入字節(jié)長(zhǎng)度 –
– int available();//獲得讀入流的字節(jié)長(zhǎng)度 –
– close();//關(guān)閉流
對(duì)于讀入流,在建立流的時(shí)候,要求數(shù)據(jù)源必須存在。如建一個(gè)文件流: FileInputStream fin = new FileInputStream(“c:/2.mp3”);這時(shí)候,如果c盤下的2.mp3不存在的話會(huì)拋出一個(gè)FileNotFoundException異常。表示文件未找到。
2.掌握常用的輸出流寫入操作方法
– void write(int b)//寫入一個(gè)字節(jié)進(jìn)輸出流。注意int b是這個(gè)字節(jié)的ASC碼。
– void close()//關(guān)閉流 – void flush()//刷新流。
對(duì)于寫入流,在建立流的時(shí)候,如果數(shù)據(jù)源不存在,系統(tǒng)會(huì)自動(dòng)創(chuàng)建。如建立一個(gè)寫入流。
FileOutputStream fout = new FileOutputStream(“c:/2.mp3”);
這時(shí)候,如果文件c:/2.mp3不存在的話,系統(tǒng)會(huì)自動(dòng)創(chuàng)建一個(gè)2.mp3的文件,而不會(huì)拋出異常。
在操作寫入流時(shí)要注意:寫入流里的數(shù)據(jù)必須經(jīng)過flush刷新或關(guān)閉流之后,數(shù)據(jù)才能寫入目標(biāo)數(shù)據(jù)源。如果不關(guān)閉或刷新流,則可能寫不進(jìn)數(shù)據(jù)源。如建立了一個(gè)文件寫入流,如果在寫入時(shí)不關(guān)閉或刷新流,則文件里的內(nèi)容可能是空的。
類的封裝性
方法的訪問說明符
public protected default private 同類 ok ok ok ok 同包 ok ok ok error 子類 ok ok error error 通用 ok error error error 方法的其他修飾符
final 用于該方法不想被子類覆蓋的情況
static:static修飾的也不能被覆蓋 只是產(chǎn)生了一個(gè)新的static方法
類的繼承性
類的繼承性的特點(diǎn) 子類可以繼承的部分:(1)父類中公開級(jí)的成員;(2)父類中保護(hù)級(jí)的成員;
(3)如果子類和父類在同一個(gè)包里,則子類繼承父類中缺省的包訪問級(jí)的成員; 子類不能繼承的部分:(1)父類中私有級(jí)的成員;
(2)如果不在同一個(gè)包里,則不能繼承缺省級(jí)的成員;(3)同名的成員函數(shù)或成員變量; 繼承中的構(gòu)造函數(shù)
構(gòu)造函數(shù)是比較特殊的一類函數(shù)
在繼承時(shí),構(gòu)造函數(shù)不會(huì)被繼承,也不會(huì)被覆蓋
父類和子類的構(gòu)造函數(shù)依然是獨(dú)立存在,并且分別發(fā)揮著作用
成員的覆蓋
如果子類的成員函數(shù)或成員變量與父類同名,則子類的成員函數(shù)或成員變量將隱蕆父類的同名成員。
如果一定要訪問父類的變量,可以有兩種方法:
(1)在子類內(nèi)部用“super”關(guān)鍵字來訪問父類的同名變量;(2)用多態(tài)性的功能來訪問父類對(duì)象
類方法的覆蓋
方法覆蓋時(shí)應(yīng)遵循的原則:
覆蓋的方法的存取權(quán)限必須高于被覆蓋的方法,如:父類的方法為protected的,子類的方法為public可以;反之,編譯報(bào)錯(cuò)。覆蓋后的方法不能比被覆蓋的方法產(chǎn)生更多的異常
如果用一個(gè)父類的變量指向子類,再調(diào)用同名的函數(shù),會(huì)出現(xiàn)什么情況呢? 調(diào)用的還是子類的函數(shù)。從這點(diǎn)看,成員函數(shù)和成員變量是完全不同的。
類的多態(tài)性 利用動(dòng)態(tài)聯(lián)編,一個(gè)函數(shù)調(diào)用語句可能實(shí)現(xiàn)不同類型的函數(shù),這種現(xiàn)象就是多態(tài)性
當(dāng)你通過父類調(diào)用函數(shù)時(shí),如果變量所指向的是一個(gè)子類對(duì)象,那么所調(diào)的仍然是子類函數(shù),這就是多態(tài)性 抽象函數(shù)和抽象類的概念
抽象函數(shù):僅有定義,沒有具體實(shí)現(xiàn)的函數(shù) 抽象類:含有抽象函數(shù)的類
定義一個(gè)抽象類,需要在類的定義前面加上“abstract”關(guān)鍵字 定義一個(gè)抽象函數(shù),需要在函數(shù)定義的前面加上“abstract”關(guān)鍵字
一個(gè)類如果被定義為抽象類,它就不能實(shí)例化,也就是說,不能有自己的對(duì)象
抽象類的使用
抽象函數(shù)的意義是沒有具體實(shí)現(xiàn)的函數(shù)
它的作用就是被子類的相同函數(shù)覆蓋,或通過多態(tài)性指向子類的相同函數(shù) 通過抽象函數(shù),可以定義一整套完整的函數(shù)功能,再派生出若干子類來實(shí)現(xiàn) 不同的子類可以以不同的形式實(shí)現(xiàn)這些功能,但函數(shù)形式是完全一致的 抽象類必須有子類,不然就沒有意義
抽象類中不僅僅有抽象函數(shù),也可以有普通的成員函數(shù)和成員變量 但如果一個(gè)類中有抽象函數(shù),那么這個(gè)類必須定義為抽象類 如果一個(gè)類繼承了父類的幾個(gè)抽象函數(shù),但沒有全部實(shí)現(xiàn),那么這個(gè)類也必須定義為抽象類
OutOfMemoryError
(一)(2008-07-31 00:46:25)轉(zhuǎn)載 標(biāo)簽:
雜談
OutOfMemoryError
一、內(nèi)存溢出類型
1、java.lang.OutOfMemoryError: PermGen space JVM管理兩種類型的內(nèi)存,堆和非堆。堆是給開發(fā)人員用的上面說的就是,是在JVM啟動(dòng)時(shí)創(chuàng)建;非堆是留給JVM自己用的,用來存放類的信息的。它和堆不同,運(yùn)行期內(nèi)GC不會(huì)釋放空間。如果web app用了大量的第三方j(luò)ar或者應(yīng)用有太多的class文件而恰好MaxPermSize設(shè)置較小,超出了也會(huì)導(dǎo)致這塊內(nèi)存的占用過多造成溢出,或者tomcat熱部署時(shí)侯不會(huì)清理前面加載的環(huán)境,只會(huì)將context更改為新部署的,非堆存的內(nèi)容就會(huì)越來越多。
2、java.lang.OutOfMemoryError: Java heap space 第一種情況是個(gè)補(bǔ)充,主要存在問題就是出現(xiàn)在這個(gè)情況中。其默認(rèn)空間(即-Xms)是物理內(nèi)存的1/64,最大空間(-Xmx)是物理內(nèi)存的1/4。如果內(nèi)存剩余不到40%,JVM就會(huì)增大堆到Xmx設(shè)置的值,內(nèi)存剩余超過70%,JVM就會(huì)減小堆到Xms設(shè)置的值。所以服務(wù)器的Xmx和Xms設(shè)置一般應(yīng)該設(shè)置相同避免每次GC后都要調(diào)整虛擬機(jī)堆的大小。假設(shè)物理內(nèi)存無限大,那么JVM內(nèi)存的最大值跟操作系統(tǒng)有關(guān),一般32位機(jī)是1.5g到3g之間,而64位的就不會(huì)有限制了。
注意:如果Xms超過了Xmx值,或者堆最大值和非堆最大值的總和超過了物理內(nèi)存或者操作系統(tǒng)的最大限制都會(huì)引起服務(wù)器啟動(dòng)不起來。垃圾回收GC的角色
JVM調(diào)用GC的頻度還是很高的,主要兩種情況下進(jìn)行垃圾回收:
當(dāng)應(yīng)用程序線程空閑;另一個(gè)是java內(nèi)存堆不足時(shí),會(huì)不斷調(diào)用GC,若連續(xù)回收都解決不了內(nèi)存堆不足的問題時(shí),就會(huì)報(bào)out of memory錯(cuò)誤。因?yàn)檫@個(gè)異常根據(jù)系統(tǒng)運(yùn)行環(huán)境決定,所以無法預(yù)期它何時(shí)出現(xiàn)。
根據(jù)GC的機(jī)制,程序的運(yùn)行會(huì)引起系統(tǒng)運(yùn)行環(huán)境的變化,增加GC的觸發(fā)機(jī)會(huì)。為了避免這些問題,程序的設(shè)計(jì)和編寫就應(yīng)避免垃圾對(duì)象的內(nèi)存占用和GC的開銷。顯示調(diào)用System.GC()只能建議JVM需要在內(nèi)存中對(duì)垃圾對(duì)象進(jìn)行回收,但不是必須馬上回收,一個(gè)是并不能解決內(nèi)存資源耗空的局面,另外也會(huì)增加GC的消耗。
二、JVM內(nèi)存區(qū)域組成
簡(jiǎn)單的說java中的堆和棧
java把內(nèi)存分兩種:一種是棧內(nèi)存,另一種是堆內(nèi)存
1。在函數(shù)中定義的基本類型變量和對(duì)象的引用變量都在函數(shù)的棧內(nèi)存中分配; 2。堆內(nèi)存用來存放由new創(chuàng)建的對(duì)象和數(shù)組
在函數(shù)(代碼塊)中定義一個(gè)變量時(shí),java就在棧中為這個(gè)變量分配內(nèi)存空間,當(dāng)超過變量的作用域后,java會(huì)自動(dòng)釋放掉為該變量所分配的內(nèi)存空間;在堆中分配的內(nèi)存由java虛擬機(jī)的自動(dòng)垃圾回收器來管理
堆的優(yōu)勢(shì)是可以動(dòng)態(tài)分配內(nèi)存大小,生存期也不必事先告訴編譯器,因?yàn)樗窃谶\(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存的。缺點(diǎn)就是要在運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存,存取速度較慢; 棧的優(yōu)勢(shì)是存取速度比堆要快,缺點(diǎn)是存在棧中的數(shù)據(jù)大小與生存期必須是確定的無靈活性。
java堆分為三個(gè)區(qū):New、Old和Permanent GC有兩個(gè)線程:
新創(chuàng)建的對(duì)象被分配到New區(qū),當(dāng)該區(qū)被填滿時(shí)會(huì)被GC輔助線程移到Old區(qū),當(dāng)Old區(qū)也填滿了會(huì)觸發(fā)GC主線程遍歷堆內(nèi)存里的所有對(duì)象。Old區(qū)的大小等于Xmx減去-Xmn java棧存放
棧調(diào)整:參數(shù)有+UseDefaultStackSize-Xss256K,表示每個(gè)線程可申請(qǐng)256k的棧空間
每個(gè)線程都有他自己的Stack
三、JVM如何設(shè)置虛擬內(nèi)存
提示:在JVM中如果98%的時(shí)間是用于GC且可用的Heap size不足2%的時(shí)候?qū)伋龃水惓P畔ⅰ?/p>
提示:Heap Size最大不要超過可用物理內(nèi)存的80%,一般的要將-Xms和-Xmx選項(xiàng)設(shè)置為相同,而-Xmn為1/4的-Xmx值。
提示:JVM初始分配的內(nèi)存由-Xms指定,默認(rèn)是物理內(nèi)存的1/64;JVM最大分配的內(nèi)存由-Xmx指定,默認(rèn)是物理內(nèi)存的1/4。
默認(rèn)空余堆內(nèi)存小于40%時(shí),JVM就會(huì)增大堆直到-Xmx的最大限制;空余堆內(nèi)存大于70%時(shí),JVM會(huì)減少堆直到-Xms的最小限制。因此服務(wù)器一般設(shè)置-Xms、-Xmx相等以避免在每次GC后調(diào)整堆的大小。
提示:假設(shè)物理內(nèi)存無限大的話,JVM內(nèi)存的最大值跟操作系統(tǒng)有很大的關(guān)系。簡(jiǎn)單的說就32位處理器雖然可控內(nèi)存空間有4GB,但是具體的操作系統(tǒng)會(huì)給一個(gè)限制,這個(gè)限制一般是2GB-3GB(一般來說Windows系統(tǒng)下為1.5G-2G,Linux系統(tǒng)下為2G-3G),而64bit以上的處理器就不會(huì)有限制了
提示:注意:如果Xms超過了Xmx值,或者堆最大值和非堆最大值的總和超過了物理內(nèi)存或者操作系統(tǒng)的最大限制都會(huì)引起服務(wù)器啟動(dòng)不起來。提示:設(shè)置NewSize、MaxNewSize相等,“new”的大小最好不要大于“old”的一半,原因是old區(qū)如果不夠大會(huì)頻繁的觸發(fā)“主” GC,大大降低了性能 JVM使用-XX:PermSize設(shè)置非堆內(nèi)存初始值,默認(rèn)是物理內(nèi)存的1/64; 由XX:MaxPermSize設(shè)置最大非堆內(nèi)存的大小,默認(rèn)是物理內(nèi)存的1/4。解決方法:手動(dòng)設(shè)置Heap size 修改TOMCAT_HOME/bin/catalina.bat 在“echo “Using CATALINA_BASE: $CATALINA_BASE””上面加入以下行: JAVA_OPTS=“-server-Xms800m-Xmx800m-XX:MaxNewSize=256m”
OutOfMemoryError
四、性能檢查工具使用
定位內(nèi)存泄漏:
JProfiler工具主要用于檢查和跟蹤系統(tǒng)(限于Java開發(fā)的)的性能。JProfiler可以通過時(shí)時(shí)的監(jiān)控系統(tǒng)的內(nèi)存使用情況,隨時(shí)監(jiān)視垃圾回收,線程運(yùn)行狀況等手段,從而很好的監(jiān)視JVM運(yùn)行情況及其性能。
1.應(yīng)用服務(wù)器內(nèi)存長(zhǎng)期不合理占用,內(nèi)存經(jīng)常處于高位占用,很難回收到低位; 2.應(yīng)用服務(wù)器極為不穩(wěn)定,幾乎每?jī)商熘匦聠?dòng)一次,有時(shí)甚至每天重新啟動(dòng)一次;
3.應(yīng)用服務(wù)器經(jīng)常做Full GC(Garbage Collection),而且時(shí)間很長(zhǎng),大約需要30-40秒,應(yīng)用服務(wù)器在做Full GC的時(shí)候是不響應(yīng)客戶的交易請(qǐng)求的,非常影響系統(tǒng)性能。
因?yàn)殚_發(fā)環(huán)境和產(chǎn)品環(huán)境會(huì)有不同,導(dǎo)致該問題發(fā)生有時(shí)會(huì)在產(chǎn)品環(huán)境中發(fā)生,通??梢允褂霉ぞ吒櫹到y(tǒng)的內(nèi)存使用情況,在有些個(gè)別情況下或許某個(gè)時(shí)刻確實(shí)是使用了大量?jī)?nèi)存導(dǎo)致out of memory,這時(shí)應(yīng)繼續(xù)跟蹤看接下來是否會(huì)有下降,如果一直居高不下這肯定就因?yàn)槌绦虻脑驅(qū)е聝?nèi)存泄漏。
五、不健壯代碼的特征及解決辦法
1、盡早釋放無用對(duì)象的引用。好的辦法是使用臨時(shí)變量的時(shí)候,讓引用變量在退出活動(dòng)域后,自動(dòng)設(shè)置為null,暗示垃圾收集器來收集該對(duì)象,防止發(fā)生內(nèi)存泄露。
對(duì)于仍然有指針指向的實(shí)例,jvm就不會(huì)回收該資源,因?yàn)槔厥諘?huì)將值為null的對(duì)象作為垃圾,提高GC回收機(jī)制效率;
2、我們的程序里不可避免大量使用字符串處理,避免使用String,應(yīng)大量使用StringBuffer,每一個(gè)String對(duì)象都得獨(dú)立占用內(nèi)存一塊區(qū)域; String str = “aaa”;String str2 = “bbb”;String str3 = str + str2;//假如執(zhí)行此次之后str ,str2以后再不被調(diào)用,那它就會(huì)被放在內(nèi)存中等待Java的gc去回收,程序內(nèi)過多的出現(xiàn)這樣的情況就會(huì)報(bào)上面的那個(gè)錯(cuò)誤,建議在使用字符串時(shí)能使用StringBuffer就不要用String,這樣可以省不少開銷;
3、盡量少用靜態(tài)變量,因?yàn)殪o態(tài)變量是全局的,GC不會(huì)回收的;
4、避免集中創(chuàng)建對(duì)象尤其是大對(duì)象,JVM會(huì)突然需要大量?jī)?nèi)存,這時(shí)必然會(huì)觸發(fā)GC優(yōu)化系統(tǒng)內(nèi)存環(huán)境;顯示的聲明數(shù)組空間,而且申請(qǐng)數(shù)量還極大。這是一個(gè)案例想定供大家警戒
使用jspsmartUpload作文件上傳,運(yùn)行過程中經(jīng)常出現(xiàn)java.outofMemoryError的錯(cuò)誤,檢查之后發(fā)現(xiàn)問題:組件里的代碼
m_totalBytes = m_request.getContentLength();m_binArray = new byte[m_totalBytes];問題原因是totalBytes這個(gè)變量得到的數(shù)極大,導(dǎo)致該數(shù)組分配了很多內(nèi)存空間,而且該數(shù)組不能及時(shí)釋放。解決辦法只能換一種更合適的辦法,至少是不會(huì)引發(fā)outofMemoryError的方式解決。參考:http://bbs.xml.org.cn/blog/more.asp?name=hongrui&id=3747
5、盡量運(yùn)用對(duì)象池技術(shù)以提高系統(tǒng)性能;生命周期長(zhǎng)的對(duì)象擁有生命周期短的對(duì)象時(shí)容易引發(fā)內(nèi)存泄漏,例如大集合對(duì)象擁有大數(shù)據(jù)量的業(yè)務(wù)對(duì)象的時(shí)候,可以考慮分塊進(jìn)行處理,然后解決一塊釋放一塊的策略。
6、不要在經(jīng)常調(diào)用的方法中創(chuàng)建對(duì)象,尤其是忌諱在循環(huán)中創(chuàng)建對(duì)象。可以適當(dāng)?shù)氖褂胔ashtable,vector創(chuàng)建一組對(duì)象容器,然后從容器中去取那些對(duì)象,而不用每次new之后又丟棄
7、一般都是發(fā)生在開啟大型文件或跟數(shù)據(jù)庫(kù)一次拿了太多的數(shù)據(jù),造成Out Of Memory Error的狀況,這時(shí)就大概要計(jì)算一下數(shù)據(jù)量的最大值是多少,并且設(shè)定所需最小及最大的內(nèi)存空間值。
設(shè)計(jì)模式之Singleton(單態(tài))
定義: Singleton模式主要作用是保證在Java應(yīng)用程序中,一個(gè)類Class只有一個(gè)實(shí)例存在。
在很多操作中,比如建立目錄數(shù)據(jù)庫(kù)連接都需要這樣的單線程操作。
還有, singleton能夠被狀態(tài)化;這樣,多個(gè)單態(tài)類在一起就可以作為一個(gè)狀態(tài)倉(cāng)庫(kù)一樣向外提供服務(wù),比如,你要論壇中的帖子計(jì)數(shù)器,每次瀏覽一次需要計(jì)數(shù),單態(tài)類能否保持住這個(gè)計(jì)數(shù),并且能synchronize的安全自動(dòng)加1,如果你要把這個(gè)數(shù)字永久保存到數(shù)據(jù)庫(kù),你可以在不修改單態(tài)接口的情況下方便的做到。
另外方面,Singleton也能夠被無狀態(tài)化。提供工具性質(zhì)的功能,Singleton模式就為我們提供了這樣實(shí)現(xiàn)的可能。使用Singleton的好處還在于可以節(jié)省內(nèi)存,因?yàn)樗拗屏藢?shí)例的個(gè)數(shù),有利于Java垃圾回收(garbage collection)。
我們常常看到工廠模式中類裝入器(class loader)中也用Singleton模式實(shí)現(xiàn)的,因?yàn)楸谎b入的類實(shí)際也屬于資源。如何使用?
一般Singleton模式通常有幾種形式: public class Singleton {
private Singleton(){}
//在自己內(nèi)部定義自己一個(gè)實(shí)例,是不是很奇怪?
//注意這是private 只供內(nèi)部調(diào)用
private static Singleton instance = new Singleton();
}
第二種形式: public class Singleton {
}
使用Singleton.getInstance()可以訪問單態(tài)類。
上面第二中形式是lazy initialization,也就是說第一次調(diào)用時(shí)初始Singleton,以后就不用再生成了。
注意到lazy initialization形式中的synchronized,這個(gè)synchronized很重要,如果沒有synchronized,那么使用getInstance()是有可能得到多個(gè)Singleton實(shí)例。關(guān)于lazy initialization的Singleton有很多涉及double-checked locking(DCL)的討論,有興趣者進(jìn)一步研究。一般認(rèn)為第一種形式要更加安全些。
使用Singleton注意事項(xiàng):
有時(shí)在某些情況下,使用Singleton并不能達(dá)到Singleton的目的,如有多個(gè)Singleton對(duì)象同時(shí)被不同的類裝入器裝載;在EJB這樣的分布式系統(tǒng)中使用也要注意這種情況,因?yàn)镋JB是跨服務(wù)器,跨JVM的。
private static Singleton instance = null;public static synchronized Singleton getInstance(){
//這個(gè)方法比上面有所改進(jìn),不用每次都進(jìn)行生成對(duì)象,只是第一次
//使用時(shí)生成實(shí)例,提高了效率!if(instance==null)
instance=new Singleton();return instance;} //這里提供了一個(gè)供外部訪問本class的靜態(tài)方法,可以直接訪問
public static Singleton getInstance(){
return instance;
} 我們以SUN公司的寵物店源碼(Pet Store 1.3.1)的ServiceLocator為例稍微分析一下:
在Pet Store中ServiceLocator有兩種,一個(gè)是EJB目錄下;一個(gè)是WEB目錄下,我們檢查這兩個(gè)ServiceLocator會(huì)發(fā)現(xiàn)內(nèi)容差不多,都是提供EJB的查詢定位服務(wù),可是為什么要分開呢?仔細(xì)研究對(duì)這兩種ServiceLocator才發(fā)現(xiàn)區(qū)別:在WEB中的ServiceLocator的采取Singleton模式,ServiceLocator屬于資源定位,理所當(dāng)然應(yīng)該使用Singleton模式。但是在EJB中,Singleton模式已經(jīng)失去作用,所以ServiceLocator才分成兩種,一種面向WEB服務(wù)的,一種是面向EJB服務(wù)的。
Singleton模式看起來簡(jiǎn)單,使用方法也很方便,但是真正用好,是非常不容易,需要對(duì)Java的類線程 內(nèi)存等概念有相當(dāng)?shù)牧私狻?/p>
設(shè)計(jì)模式之Factory(2008-08-07 12:59:17)轉(zhuǎn)載 標(biāo)簽:
雜談
設(shè)計(jì)模式之Factory
定義:提供創(chuàng)建對(duì)象的接口.為何使用?
工廠模式是我們最常用的模式了,著名的Jive論壇 ,就大量使用了工廠模式,工廠模式在Java程序系統(tǒng)可以說是隨處可見。
為什么工廠模式是如此常用?因?yàn)楣S模式就相當(dāng)于創(chuàng)建實(shí)例對(duì)象的new,我們經(jīng)常要根據(jù)類Class生成實(shí)例對(duì)象,如A a=new A()工廠模式也是用來創(chuàng)建實(shí)例對(duì)象的,所以以后new時(shí)就要多個(gè)心眼,是否可以考慮實(shí)用工廠模式,雖然這樣做,可能多做一些工作,但會(huì)給你系統(tǒng)帶來更大的可擴(kuò)展性和盡量少的修改量。我們以類Sample為例,如果我們要?jiǎng)?chuàng)建Sample的實(shí)例對(duì)象: Sample sample=new Sample();可是,實(shí)際情況是,通常我們都要在創(chuàng)建sample實(shí)例時(shí)做點(diǎn)初始化的工作,比如賦值查詢數(shù)據(jù)庫(kù)等。
首先,我們想到的是,可以使用Sample的構(gòu)造函數(shù),這樣生成實(shí)例就寫成: Sample sample=new Sample(參數(shù));但是,如果創(chuàng)建sample實(shí)例時(shí)所做的初始化工作不是象賦值這樣簡(jiǎn)單的事,可能是很長(zhǎng)一段代碼,如果也寫入構(gòu)造函數(shù)中,那你的代碼很難看了(就需要Refactor重整)。
為什么說代碼很難看,初學(xué)者可能沒有這種感覺,我們分析如下,初始化工作如果是很長(zhǎng)一段代碼,說明要做的工作很多,將很多工作裝入一個(gè)方法中,相當(dāng)于將很多雞蛋放在一個(gè)籃子里,是很危險(xiǎn)的,這也是有背于Java面向?qū)ο蟮脑瓌t,面向?qū)ο蟮姆庋b(Encapsulation)和分派(Delegation)告訴我們,盡量將長(zhǎng)的代碼分派“切割”成每段,將每段再“封裝”起來(減少段和段之間偶合聯(lián)系性),這樣,就會(huì)將風(fēng)險(xiǎn)分散,以后如果需要修改,只要更改每段,不會(huì)再發(fā)生牽一動(dòng)百的事情。
在本例中,首先,我們需要將創(chuàng)建實(shí)例的工作與使用實(shí)例的工作分開, 也就是說,讓創(chuàng)建實(shí)例所需要的大量初始化工作從Sample的構(gòu)造函數(shù)中分離出去。這時(shí)我們就需要Factory工廠模式來生成對(duì)象了,不能再用上面簡(jiǎn)單new Sample(參數(shù))。還有,如果Sample有個(gè)繼承如MySample, 按照面向接口編程,我們需要將Sample抽象成一個(gè)接口.現(xiàn)在Sample是接口,有兩個(gè)子類MySample 和HisSample.我們要實(shí)例化他們時(shí),如下: Sample mysample=new MySample();Sample hissample=new HisSample();隨著項(xiàng)目的深入,Sample可能還會(huì)“生出很多兒子出來”, 那么我們要對(duì)這些兒子一個(gè)個(gè)實(shí)例化,更糟糕的是,可能還要對(duì)以前的代碼進(jìn)行修改:加入后來生出兒子的實(shí)例.這在傳統(tǒng)程序中是無法避免的.但如果你一開始就有意識(shí)使用了工廠模式,這些麻煩就沒有了.回調(diào)機(jī)制
(2008-08-13 23:55:39)轉(zhuǎn)載 標(biāo)簽:
雜談 什么是回調(diào)
軟件模塊之間總是存在著一定的接口,從調(diào)用方式上,可以把他們分為三類:同步調(diào)用、回調(diào)和異步調(diào)用。同步調(diào)用是一種阻塞式調(diào)用,調(diào)用方要等待對(duì)方執(zhí)行完畢才返回,它是一種單向調(diào)用;回調(diào)是一種雙向調(diào)用模式,也就是說,被調(diào)用方在接口被調(diào)用時(shí)也會(huì)調(diào)用對(duì)方的接口;異步調(diào)用是一種類似消息或事件的機(jī)制,不過它的調(diào)用方向剛好相反,接口的服務(wù)在收到某種訊息或發(fā)生某種事件時(shí),會(huì)主動(dòng)通知客戶方(即調(diào)用客戶方的接口)?;卣{(diào)和異步調(diào)用的關(guān)系非常緊密,通常我們使用回調(diào)來實(shí)現(xiàn)異步消息的注冊(cè),通過異步調(diào)用來實(shí)現(xiàn)消息的通知。同步調(diào)用是三者當(dāng)中最簡(jiǎn)單的,而回調(diào)又常常是異步調(diào)用的基礎(chǔ),因此,下面我們著重討論回調(diào)機(jī)制在不同軟件架構(gòu)中的實(shí)現(xiàn)。
對(duì)于不同類型的語言(如結(jié)構(gòu)化語言和對(duì)象語言)、平臺(tái)(Win32、JDK)或構(gòu)架(CORBA、DCOM、WebService),客戶和服務(wù)的交互除了同步方式以外,都需要具備一定的異步通知機(jī)制,讓服務(wù)方(或接口提供方)在某些情況下能夠主動(dòng)通知客戶,而回調(diào)是實(shí)現(xiàn)異步的一個(gè)最簡(jiǎn)捷的途徑。
對(duì)于一般的結(jié)構(gòu)化語言,可以通過回調(diào)函數(shù)來實(shí)現(xiàn)回調(diào)?;卣{(diào)函數(shù)也是一個(gè)函數(shù)或過程,不過它是一個(gè)由調(diào)用方自己實(shí)現(xiàn),供被調(diào)用方使用的特殊函數(shù)。在面向?qū)ο蟮恼Z言中,回調(diào)則是通過接口或抽象類來實(shí)現(xiàn)的,我們把實(shí)現(xiàn)這種接口的類成為回調(diào)類,回調(diào)類的對(duì)象成為回調(diào)對(duì)象。對(duì)于象C++或Object Pascal這些兼容了過程特性的對(duì)象語言,不僅提供了回調(diào)對(duì)象、回調(diào)方法等特性,也能兼容過程語言的回調(diào)函數(shù)機(jī)制。Windows平臺(tái)的消息機(jī)制也可以看作是回調(diào)的一種應(yīng)用,我們通過系統(tǒng)提供的接口注冊(cè)消息處理函數(shù)(即回調(diào)函數(shù)),從而實(shí)現(xiàn)接收、處理消息的目的。由于Windows平臺(tái)的API是用C語言來構(gòu)建的,我們可以認(rèn)為它也是回調(diào)函數(shù)的一個(gè)特例。
對(duì)于分布式組件代理體系CORBA,異步處理有多種方式,如回調(diào)、事件服務(wù)、通知服務(wù)等。事件服務(wù)和通知服務(wù)是CORBA用來處理異步消息的標(biāo)準(zhǔn)服務(wù),他們主要負(fù)責(zé)消息的處理、派發(fā)、維護(hù)等工作。對(duì)一些簡(jiǎn)單的異步處理過程,我們可以通過回調(diào)機(jī)制來實(shí)現(xiàn)。
反射機(jī)制
(2008-08-14 00:04:19)轉(zhuǎn)載 標(biāo)簽:
雜談
JAVA反射機(jī)制
JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語言的反射機(jī)制。
Java反射機(jī)制主要提供了以下功能: 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類;在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象;在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法;在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法;生成動(dòng)態(tài)代理。1.得到某個(gè)對(duì)象的屬性 public Object getProperty(Object owner, String fieldName)throws Exception { 2 Class ownerClass = owner.getClass();3 4 Field field = ownerClass.getField(fieldName);5 6 Object property = field.get(owner);7 8 return property;9 } Class ownerClass = owner.getClass():得到該對(duì)象的Class。
Field field = ownerClass.getField(fieldName):通過Class得到類聲明的屬性。
Object property = field.get(owner):通過對(duì)象得到該屬性的實(shí)例,如果這個(gè)屬性是非公有的,這里會(huì)報(bào)IllegalAccessException。
2.得到某個(gè)類的靜態(tài)屬性
public Object getStaticProperty(String className, String fieldName)2 throws Exception { 3 Class ownerClass = Class.forName(className);4 5 Field field = ownerClass.getField(fieldName);6 7 Object property = field.get(ownerClass);8 9 return property;10 }
Class ownerClass = Class.forName(className):首先得到這個(gè)類的Class。
Field field = ownerClass.getField(fieldName):和上面一樣,通過Class得到類聲明的屬性。
Object property = field.get(ownerClass):這里和上面有些不同,因?yàn)樵搶傩允庆o態(tài)的,所以直接從類的Class里取。
3.執(zhí)行某對(duì)象的方法
public Object invokeMethod(Object owner, String methodName, Object[] args)throws Exception { 2 3 Class ownerClass = owner.getClass();4 5 Class[] argsClass = new Class[args.length];6 7 for(int i = 0, j = args.length;i < j;i++){ 8 argsClass[i] = args[i].getClass();9 } 10 11 Method method = ownerClass.getMethod(methodName, argsClass);12 13 return method.invoke(owner, args);14 } Class owner_class = owner.getClass():首先還是必須得到這個(gè)對(duì)象的Class。
5~9行:配置參數(shù)的Class數(shù)組,作為尋找Method的條件。
Method method = ownerClass.getMethod(methodName, argsClass):通過Method名和參數(shù)的Class數(shù)組得到要執(zhí)行的Method。
method.invoke(owner, args):執(zhí)行該Method,invoke方法的參數(shù)是執(zhí)行這個(gè)方法的對(duì)象,和參數(shù)數(shù)組。返回值是Object,也既是該方法的返回值。
4.執(zhí)行某個(gè)類的靜態(tài)方法
public Object invokeStaticMethod(String className, String methodName, 2 Object[] args)throws Exception { 3 Class ownerClass = Class.forName(className);4 5 Class[] argsClass = new Class[args.length];6 7 for(int i = 0, j = args.length;i < j;i++){ 8 argsClass[i] = args[i].getClass();9 } 10 11 Method method = ownerClass.getMethod(methodName, argsClass);12 13 return method.invoke(null, args);14 }
基本的原理和實(shí)例3相同,不同點(diǎn)是最后一行,invoke的一個(gè)參數(shù)是null,因?yàn)檫@是靜態(tài)方法,不需要借助實(shí)例運(yùn)行。
5.新建實(shí)例 1 2 public Object newInstance(String className, Object[] args)throws Exception { 3 Class newoneClass = Class.forName(className);4 5 Class[] argsClass = new Class[args.length];6 7 for(int i = 0, j = args.length;i < j;i++){ 8 argsClass[i] = args[i].getClass();9 } 10 11 Constructor cons = newoneClass.getConstructor(argsClass);12 13 return cons.newInstance(args);14 15 }
這里說的方法是執(zhí)行帶參數(shù)的構(gòu)造函數(shù)來新建實(shí)例的方法。如果不需要參數(shù),可以直接使用newoneClass.newInstance()來實(shí)現(xiàn)。
Class newoneClass = Class.forName(className):第一步,得到要構(gòu)造的實(shí)例的Class。
第5~第9行:得到參數(shù)的Class數(shù)組。
Constructor cons = newoneClass.getConstructor(argsClass):得到構(gòu)造子。
cons.newInstance(args):新建實(shí)例。
6.判斷是否為某個(gè)類的實(shí)例 public boolean isInstance(Object obj, Class cls){ 2 return cls.isInstance(obj);3 }
7.得到數(shù)組中的某個(gè)元素 public Object getByArray(Object array, int index){ 2 return Array 在了解Oracle體系結(jié)構(gòu)之前必須掌握以下兩個(gè)基本的概念: 數(shù)據(jù)庫(kù)和實(shí)例。
?/P>
一、數(shù)據(jù)庫(kù) ?/P> 數(shù)據(jù)庫(kù)(database)是一個(gè)數(shù)據(jù)集合.?/P> 無論數(shù)據(jù)庫(kù)是采用關(guān)系結(jié)構(gòu)還是面向?qū)ο蠼Y(jié)構(gòu), oracle數(shù)據(jù)庫(kù)都將 ?/P> 其數(shù)據(jù)存放在數(shù)據(jù)文件中.在其內(nèi)部, 數(shù)據(jù)庫(kù)結(jié)構(gòu)數(shù)據(jù)對(duì)文件的邏輯 ?/P> 映射, 使不同的數(shù)據(jù)分開存儲(chǔ), 這些邏輯劃分稱為表空間.?/P> 表空間和文件介紹: ?/P> 1: 表空間 ?/P> 表空間(tablespace)是數(shù)據(jù)庫(kù)的邏輯劃分, 每個(gè)數(shù)據(jù)庫(kù)至少有一個(gè)表空間,叫做系統(tǒng)表空間(system 表空間).一個(gè)表空間只能屬于一個(gè)數(shù)據(jù)庫(kù)。
?/P> 每個(gè)表空間由同一個(gè)磁盤上的一個(gè)或多個(gè)文件組成, 這些文件稱為數(shù)據(jù)文件.?/P> 表空間的特性: ?/P>(1)控制數(shù)據(jù)庫(kù)數(shù)據(jù)磁盤分配 ?/P>(2)限制用戶在表空間中可以使用的磁盤空間大小 ?/P>(3)表空間具有 online, offline, readonly, readwrite屬性 ?/P> 修改表空間的屬性: ?/P> SQL> alter tablespace 表空間名稱屬性;查詢表空間狀態(tài): SQL> select tablespace_name, status from dba_tablespaces;?/P> 注意: system, undo, temp表空間不能設(shè)為offline屬性.?/P>(4)完成部分?jǐn)?shù)據(jù)庫(kù)的備份與恢復(fù) ?/P>(5)表空間通過數(shù)據(jù)文件來擴(kuò)大, 表空間的大小等于構(gòu)成該表空間的所以數(shù)據(jù)文件的大小只和.?/P> 查詢表空間與數(shù)據(jù)文件對(duì)應(yīng)關(guān)系: ?/P> SQL> select tablespace_name, bytes, file_name from dba_data_files;?/P> 基于表空間的幾個(gè)操作: ?/P>(1)查詢用戶缺省表空間: SQL> select username, default_tablespace from dba_users;(2)查詢表與存儲(chǔ)該表的表空間: SQL> select table_name, tablespace_name from user_tables;(3)修改用戶缺省表空間: SQL> alter user username default tablespace tablespace_name;(4)將數(shù)據(jù)從一個(gè)表空間移動(dòng)到另一個(gè)表空間: SQL> alter table table_name move tablespace tablespace_name;分享 2.數(shù)據(jù)文件 每個(gè)表空間由同一個(gè)磁盤上的一個(gè)或多個(gè)文件組成, 這些文件叫做數(shù)據(jù)文件(datafile),數(shù)據(jù)文件只能屬于一個(gè)表空間.數(shù)據(jù)文件創(chuàng)建后可以改變大小.創(chuàng)建新的表空間需要?jiǎng)?chuàng)建新的數(shù)據(jù)文件.數(shù)據(jù)文件一旦加入到表空間中, 就不能從表空間中移走, 也不能與其他表空間發(fā)生聯(lián)系。
數(shù)據(jù)庫(kù)必須的三類文件是 data file, control file, redolog file.其他文件 prameter file,password file, archived log files并不是數(shù)據(jù)庫(kù)必須的, 他們只是輔助數(shù)據(jù)庫(kù)的。
查看數(shù)據(jù)庫(kù)的物理文件組成:(1)查看數(shù)據(jù)文件: SQL> select * from v$datafile;(2)查看控制文件: SQL> select * from v$controlfile;(3)查看日志文件: SQL> select * from v$logfile;二: 實(shí)例
通俗的講實(shí)例就是操作oracle數(shù)據(jù)庫(kù)的一種手段.數(shù)據(jù)庫(kù)實(shí)例也稱作服務(wù)器, 是用來訪問數(shù)據(jù)庫(kù)文件集的存儲(chǔ)結(jié)構(gòu)及后臺(tái)進(jìn)程的集合.一個(gè)數(shù)據(jù)庫(kù)可以被多個(gè)實(shí)例訪問(稱為真正的應(yīng)用群集選項(xiàng)).決定實(shí)例的大小及組成的各種參數(shù)或者存儲(chǔ)在名稱init.ora的初始化文件中, 或者隱藏
在數(shù)據(jù)庫(kù)內(nèi)部的服務(wù)器參數(shù)文件中.通過spfile引用該文件, spfile存儲(chǔ)在spfile.ora文件中.實(shí)例啟動(dòng)時(shí)讀取初始化文件, 數(shù)據(jù)庫(kù)系統(tǒng)管理員可以修改該文件, 對(duì)初始化文件的修改只有在下次啟動(dòng)時(shí)才有效。
Instance分為兩部分:
1: memory structure(內(nèi)存結(jié)構(gòu))
memory structure分為兩部分:SGA(System Global Area)區(qū)是用于存儲(chǔ)數(shù)據(jù)庫(kù)信息的內(nèi)存區(qū),該信息為數(shù)據(jù)庫(kù)進(jìn)程所共享。它包含Oracle 服務(wù)器的數(shù)據(jù)和控制信息, 它是在Oracle 服務(wù)器所駐留的計(jì)算機(jī)的實(shí)際內(nèi)存中得以分配,如果實(shí)際內(nèi)存不夠再往虛擬內(nèi)存中寫。
包括:.share pool.datafase buffer cache.redo log buffer.other structures PGA(Program Global Area)區(qū)包含單個(gè)服務(wù)器進(jìn)程或單個(gè)后臺(tái)進(jìn)程的數(shù)據(jù)和控制信息,與幾個(gè)進(jìn)程共享的SGA 正相反PGA 是只被一個(gè)進(jìn)程使用的區(qū)域,PGA 在創(chuàng)建進(jìn)程時(shí)分配在終止進(jìn)程時(shí)回收。
2: background process(后臺(tái)進(jìn)程)包括:
.PMON 負(fù)責(zé)在一個(gè)Oracle 進(jìn)程失敗時(shí)清理資源
.SMON 檢查數(shù)據(jù)庫(kù)的一致性如有必要還會(huì)在數(shù)據(jù)庫(kù)打開時(shí)啟動(dòng)數(shù)據(jù)庫(kù)的恢復(fù)
.DBWR 負(fù)責(zé)將更改的數(shù)據(jù)從數(shù)據(jù)庫(kù)緩沖區(qū)高速緩存寫入數(shù)據(jù)文件.LGWR 將重做日志緩沖區(qū)中的更改寫入在線重做日志文件
.CKPT 負(fù)責(zé)在每當(dāng)緩沖區(qū)高速緩存中的更改永久地記錄在數(shù)據(jù)庫(kù)中時(shí),更新控制文件和數(shù)據(jù)文件中的數(shù)據(jù)庫(kù)狀態(tài)信息。
全面解析JDBC
綜述:Java數(shù)據(jù)庫(kù)連接體系結(jié)構(gòu)是用于Java應(yīng)用程序連接數(shù)據(jù)庫(kù)的標(biāo)準(zhǔn)方法。JDBC對(duì)Java程序員而言是API,對(duì)實(shí)現(xiàn)與數(shù)據(jù)庫(kù)連接的服務(wù)提供商而言是接口模型。作為API,JDBC為程序開發(fā)提供標(biāo)準(zhǔn)的接口,并為數(shù)據(jù)庫(kù)廠商及第三方中間件廠商實(shí)現(xiàn)與數(shù)據(jù)庫(kù)的連接提供了標(biāo)準(zhǔn)方法。JDBC使用已有的SQL標(biāo)準(zhǔn)并支持與其它數(shù)據(jù)庫(kù)連接標(biāo)準(zhǔn),如ODBC之間的橋接。JDBC實(shí)現(xiàn)了所有這些面向標(biāo)準(zhǔn)的目標(biāo)并且具有簡(jiǎn)單、嚴(yán)格類型定義且高性能實(shí)現(xiàn)的接口。
如何選擇合適的JDBC產(chǎn)品?
有關(guān)JDBC最新的信息,有興趣的讀者可以查閱JDBC的官方網(wǎng)站--即JavaSoft的主頁,其URL為:http://Java.sun.com/products/jdbc
1.JavaSoft框架
JavaSoft提供三種JDBC產(chǎn)品組件,它們是Java開發(fā)工具包(JDK)的組成部份:JDBC驅(qū)動(dòng)程序管理器、JDBC驅(qū)動(dòng)程序測(cè)試工具包和JDBC-ODBC橋。
JDBC驅(qū)動(dòng)程序管理器是JDBC體系結(jié)構(gòu)的支柱。它實(shí)際上很小,也很簡(jiǎn)單;其主要作用是把Java應(yīng)用程序連接到正確的JDBC驅(qū)動(dòng)程序上,然后即退出。
JDBC驅(qū)動(dòng)程序測(cè)試工具包為使JDBC驅(qū)動(dòng)程序運(yùn)行您的程序提供一定的可信度。只有通過JDBC驅(qū)動(dòng)程序測(cè)試的驅(qū)動(dòng)程序才被認(rèn)為是符合JDBC標(biāo)準(zhǔn)TM的。
JDBC-ODBC橋使ODBC驅(qū)動(dòng)程序可被用作JDBC驅(qū)動(dòng)程序。它的實(shí)現(xiàn)為JDBC的快速發(fā)展提供了一條途徑,其長(zhǎng)遠(yuǎn)目標(biāo)提供一種訪問某些不常見的DBMS(如果對(duì)這些不常見的DBMS未實(shí)現(xiàn)JDBC)的方法。
2.JDBC驅(qū)動(dòng)程序的類型
目前比較常見的JDBC驅(qū)動(dòng)程序可分為以下四個(gè)種類:
(1)JDBC-ODBC橋加ODBC驅(qū)動(dòng)程序
JavaSoft橋產(chǎn)品利用ODBC驅(qū)動(dòng)程序提供JDBC訪問。注意,必須將ODBC二進(jìn)制代碼(許多情況下還包括數(shù)據(jù)庫(kù)客戶機(jī)代碼)加載到使用該驅(qū)動(dòng)程序的每個(gè)客戶機(jī)上。因此,這種類型的驅(qū)動(dòng)程序最適合于企業(yè)網(wǎng)(這種網(wǎng)絡(luò)上客戶機(jī)的安裝不是主要問題),或者是用Java編寫的三層結(jié)構(gòu)的應(yīng)用程序服務(wù)器代碼。
(2)本地API
這種類型的驅(qū)動(dòng)程序把客戶機(jī)API上的JDBC調(diào)用轉(zhuǎn)換為Oracle、Sybase、Informix、DB2或其它DBMS的調(diào)用。注意,象橋驅(qū)動(dòng)程序一樣,這種類型的驅(qū)動(dòng)程序要求將某些二進(jìn)制代碼加載到每臺(tái)客戶機(jī)上。
(3)JDBC網(wǎng)絡(luò)純Java驅(qū)動(dòng)程序
這種驅(qū)動(dòng)程序?qū)DBC轉(zhuǎn)換為與DBMS無關(guān)的網(wǎng)絡(luò)協(xié)議,之后這種協(xié)議又被某個(gè)服務(wù)器轉(zhuǎn)換為一種DBMS協(xié)議。這種網(wǎng)絡(luò)服務(wù)器中間件能夠?qū)⑺募僇ava客戶機(jī)連接到多種不同的數(shù)據(jù)庫(kù)上。所用的具體協(xié)議取決于提供者。通常,這是最為靈活的JDBC驅(qū)動(dòng)程序。有可能所有這種解決方案的提供者都提供適合于Intranet用的產(chǎn)品。為了使這些產(chǎn)品也支持Internet訪問,它們必須處理Web所提出的安全性、通過防火墻的訪問等方面的額外要求。幾家提供者正將JDBC驅(qū)動(dòng)程序加到他們現(xiàn)有的數(shù)據(jù)庫(kù)中間件產(chǎn)品中。
(4)本地協(xié)議純Java驅(qū)動(dòng)程序
這種類型的驅(qū)動(dòng)程序?qū)DBC調(diào)用直接轉(zhuǎn)換為DBMS所使用的網(wǎng)絡(luò)協(xié)議。這將允許從客戶機(jī)機(jī)器上直接調(diào)用DBMS服務(wù)器,是Intranet訪問的一個(gè)很實(shí)用的解決方法。由于許多這樣的協(xié)議都是專用的,因此數(shù)據(jù)庫(kù)提供者自己將是主要來源,有幾家提供者已在著手做這件事了。
據(jù)專家預(yù)計(jì)第(3)、(4)類驅(qū)動(dòng)程序?qū)⒊蔀閺腏DBC訪問數(shù)據(jù)庫(kù)的首方法。第(1)、(2)類驅(qū)動(dòng)程序在直接的純Java驅(qū)動(dòng)程序還沒有上市前會(huì)作為過渡方案來使用。對(duì)第(1)、(2)類驅(qū)動(dòng)程序可能會(huì)有一些變種,這些變種要求有連接器,但通常這些是更加不可取的解決方案。第(3)、(4)類驅(qū)動(dòng)程序提供了Java的所有優(yōu)點(diǎn),包括自動(dòng)安裝(例如,通過使用JDBC驅(qū)動(dòng)程序的appletapplet來下載該驅(qū)動(dòng)程序)。
3.JDBC驅(qū)動(dòng)程序的獲取
目前已有幾十個(gè)(1)類的驅(qū)動(dòng)程序,即可與Javasoft橋聯(lián)合使用的ODBC驅(qū)動(dòng)程序的驅(qū)動(dòng)程序。有大約十多個(gè)屬于種類(2)的驅(qū)動(dòng)程序是以DBMS的本地API為基礎(chǔ)編寫的。只有幾個(gè)屬于種類(3)的驅(qū)動(dòng)程序,其首批提供者是SCO、OpenHorizon、Visigenic和WebLogic。此外,JavaSoft和數(shù)據(jù)庫(kù)連接的領(lǐng)先提供者Intersolv還合作研制了JDBC-ODBC橋和JDBC驅(qū)動(dòng)程序測(cè)試工具包。
如何建立JDBC連接?
Connection 對(duì)象代表與數(shù)據(jù)庫(kù)的連接。連接過程包括所執(zhí)行的 SQL 語句和在該連接上所返回的結(jié)果。一個(gè)應(yīng)用程序可與單個(gè)數(shù)據(jù)庫(kù)有一個(gè)或多個(gè)連接,或者可與許多數(shù)據(jù)庫(kù)有連接。
1.打開連接
與數(shù)據(jù)庫(kù)建立連接的標(biāo)準(zhǔn)方法是調(diào)用DriverManager.getConnection方法。該方法接受含有某個(gè)URL的字符串。DriverManager類(即所謂的JDBC管理層)將嘗試找到可與那個(gè)URL所代表的數(shù)據(jù)庫(kù)進(jìn)行連接的驅(qū)動(dòng)程序。DriverManager類存有已注冊(cè)的Driver類的清單。當(dāng)調(diào)用方法getConnection時(shí),它將檢查清單中的每個(gè)驅(qū)動(dòng)程序,直到找到可與URL中指定的數(shù)據(jù)庫(kù)進(jìn)行連接的驅(qū)動(dòng)程序?yàn)橹埂river的方法connect使用這個(gè)URL來建立實(shí)際的連接。
用戶可繞過JDBC管理層直接調(diào)用Driver方法。這在以下特殊情況下將很有用:當(dāng)兩個(gè)驅(qū)動(dòng)器可同時(shí)連接到數(shù)據(jù)庫(kù)中,而用戶需要明確地選用其中特定的驅(qū)動(dòng)器。但一般情況下,讓DriverManager類處理打開連接這種事將更為簡(jiǎn)單。
下述代碼顯示如何打開一個(gè)與位于URL“jdbc:odbc:wombat”的數(shù)據(jù)庫(kù)的連接。所用的用戶標(biāo)識(shí)符為“freely”,口令為“ec”:
String url = “jdbc:odbc:wombat”;
Connection con = DriverManager.getConnection(url,“freely”,“ec”);
2.一般用法的URL
由于URL常引起混淆,我們將先對(duì)一般URL作簡(jiǎn)單說明,然后再討論JDBCURL。URL(統(tǒng)一資源定位符)提供在Internet上定位資源所需的信息。可將它想象為一個(gè)地址。URL的第一部份指定了訪問信息所用的協(xié)議,后面總是跟著冒號(hào)。常用的協(xié)議有“ftp”(代表“文件傳輸協(xié)議”)和“http”(代表“超文本傳輸協(xié)議”)。如果協(xié)議是“file”,表示資源是在某個(gè)本地文件系統(tǒng)上而非在Internet上(下例用于表示我們所描述的部分;它并非URL的組成部分)。
ftp://Javasoft.com/docs/JDK-1_apidocs.zip
http://Java.sun.com/products/jdk/CurrentRelease
file:/home/haroldw/docs/books/tutorial/summary.html
URL的其余部份(冒號(hào)后面的)給出了數(shù)據(jù)資源所處位置的有關(guān)信息。如果協(xié)議是file,則URL的其余部份是文件的路徑。對(duì)于ftp和http協(xié)議,URL的其余部份標(biāo)識(shí)了主機(jī)并可選地給出某個(gè)更詳盡的地址路徑。例如,以下是JavaSoft主頁的URL。該URL只標(biāo)識(shí)了主機(jī):http://Java.sun.com。從該主頁開始瀏覽,就可以進(jìn)到許多其它的網(wǎng)頁中,其中之一就是JDBC主頁。JDBC主頁的URL更為具體,它具體表示為: http://Java.sun.com/products/jdbc
3.JDBC URL
JDBC URL提供了一種標(biāo)識(shí)數(shù)據(jù)庫(kù)的方法,可以使相應(yīng)的驅(qū)動(dòng)程序能識(shí)別該數(shù)據(jù)庫(kù)并與之建立連接。實(shí)際上,驅(qū)動(dòng)程序編程員將決定用什么JDBC URL來標(biāo)識(shí)特定的驅(qū)動(dòng)程序。用戶不必關(guān)心如何來形成JDBC URL;他們只須使用與所用的驅(qū)動(dòng)程序一起提供的URL即可。JDBC的作用是提供某些約定,驅(qū)動(dòng)程序編程員在構(gòu)造他們的JDBC URL時(shí)應(yīng)該遵循這些約定。
由于JDBC URL要與各種不同的驅(qū)動(dòng)程序一起使用,因此這些約定應(yīng)非常靈活。首先,它們應(yīng)允許不同的驅(qū)動(dòng)程序使用不同的方案來命名數(shù)據(jù)庫(kù)。例如,odbc子協(xié)議允許(但并不是要求)URL含有屬性值。
其次,JDBC URL應(yīng)允許驅(qū)動(dòng)程序編程員將一切所需的信息編入其中。這樣就可以讓要與給定數(shù)據(jù)庫(kù)對(duì)話的applet打開數(shù)據(jù)庫(kù)連接,而無須要求用戶去做任何系統(tǒng)管理工作。
最后,JDBC URL應(yīng)允許某種程度的間接性。也就是說,JDBC URL可指向邏輯主機(jī)或數(shù)據(jù)庫(kù)名,而這種邏輯主機(jī)或數(shù)據(jù)庫(kù)名將由網(wǎng)絡(luò)命名系統(tǒng)動(dòng)態(tài)地轉(zhuǎn)換為實(shí)際的名稱。這可以使系統(tǒng)管理員不必將特定主機(jī)聲明為JDBC名稱的一部份。網(wǎng)絡(luò)命名服務(wù)(例如DNS、NIS和DCE)有多種,而對(duì)于使用哪種命名服務(wù)并無限制。
JDBC URL的標(biāo)準(zhǔn)語法如下所示。它由三部分組成,各部分間用冒號(hào)分隔: jdbc:<子協(xié)遙荊海甲用?疲?br>
JDBC URL的三個(gè)部分可分解如下:
(1)jdbc協(xié)議:JDBC URL中的協(xié)議總是jdbc。
(2)<子協(xié)議>:驅(qū)動(dòng)程序名或數(shù)據(jù)庫(kù)連接機(jī)制(這種機(jī)制可由一個(gè)或多個(gè)驅(qū)動(dòng)程序支持)的名稱。子協(xié)議名的典型示例是“odbc”,該名稱是為用于指定ODBC風(fēng)格的數(shù)據(jù)資源名稱的URL專門保留的。例如,為了通過JDBC-ODBC橋來訪問某個(gè)數(shù)據(jù)庫(kù),可以用如下所示的URL:jdbc:odbc:book。本例中,子協(xié)議為“odbc”,子名稱“book”是本地ODBC數(shù)據(jù)資源。如果要用網(wǎng)絡(luò)命名服務(wù)(這樣JDBC URL中的數(shù)據(jù)庫(kù)名稱不必是實(shí)際名稱),則命名服務(wù)可以作為子協(xié)議。例如,可用如下所示的URL:jdbc:dcenaming:accounts。本例中,該URL指定了本地DCE命名服務(wù)應(yīng)該將數(shù)據(jù)庫(kù)名稱“accounts”解析為更為具體的可用于連接真實(shí)數(shù)據(jù)庫(kù)的名稱。
(3)<子名稱>:種標(biāo)識(shí)數(shù)據(jù)庫(kù)的方法。子名稱可以依不同的子協(xié)議而變化。它還可以有子名稱的子名稱(含有驅(qū)動(dòng)程序編程員所選的任何內(nèi)部語法)。使用子名稱的目的是為定位數(shù)據(jù)庫(kù)提供足夠的信息。前例中,因?yàn)镺DBC將提供其余部份的信息,因此用“book”就已足夠。然而,位于遠(yuǎn)程服務(wù)器上的數(shù)據(jù)庫(kù)需要更多的信息。例如,如果數(shù)據(jù)庫(kù)是通過Internet來訪問的,則在JDBC URL中應(yīng)將網(wǎng)絡(luò)地址作為子名稱的一部份包括進(jìn)去,且必須遵循如下所示的標(biāo)準(zhǔn)URL命名約定://主機(jī)名:端口/子協(xié)議。
假設(shè)“dbnet”是個(gè)用于將某個(gè)主機(jī)連接到Internet上的協(xié)議,則JDBC URL應(yīng)為:jdbc:dbnet://wombat:356/fred。
4.“odbc”子協(xié)議
子協(xié)議odbc是一種特殊情況。它是為用于指定ODBC風(fēng)格的數(shù)據(jù)資源名稱的URL而保留的,并具有下列特性:允許在子名稱(數(shù)據(jù)資源名稱)后面指定任意多個(gè)屬性值。odbc子協(xié)議的完整語法為:
jdbc:odbc:<數(shù)據(jù)資源名稱>[;<屬性名>=<屬性值>],因此,以下都是合法的jdbc:odbc名稱:
jdbc:odbc:qeor7
jdbc:odbc:wombat
jdbc:odbc:wombat;CacheSize=20;ExtensionCase=LOWER
jdbc:odbc:qeora;UID=kgh;PWD=fooey
5.注冊(cè)子協(xié)議
驅(qū)動(dòng)程序編程員可保留某個(gè)名稱以將之用作JDBC URL的子協(xié)議名。當(dāng)DriverManager類將此名稱加到已注冊(cè)的驅(qū)動(dòng)程序清單中時(shí),為之保留該名稱的驅(qū)動(dòng)程序應(yīng)能識(shí)別該名稱并與它所標(biāo)識(shí)的數(shù)據(jù)庫(kù)建立連接。例如,odbc是為JDBC-ODBC橋而保留的。假設(shè)有個(gè)Miracle公司,它可能會(huì)將“miracle”注冊(cè)為連接到其Miracle DBMS上的JDBC驅(qū)動(dòng)程序的子協(xié)議,從而使其他人都無法使用這個(gè)名稱。
JavaSoft目前作為非正式代理負(fù)責(zé)注冊(cè)JDBC子協(xié)議名稱。要注冊(cè)某個(gè)子協(xié)議名稱,請(qǐng)發(fā)送電子郵件到下述地址:jdbc@wombat.eng.sun.com。
6.發(fā)送SQL語句
連接一旦建立,就可用來向它所涉及的數(shù)據(jù)庫(kù)傳送SQL語句。JDBC對(duì)可被發(fā)送的SQL語句類型不加任何限制。這就提供了很大的靈活性,即允許使用特定的數(shù)據(jù)庫(kù)語句或甚至于非SQL語句。然而,它要求用戶自己負(fù)責(zé)確保所涉及的數(shù)據(jù)庫(kù)可以處理所發(fā)送的SQL語句,否則將自食其果。例如,如果某個(gè)應(yīng)用程序試圖向不支持儲(chǔ)存程序的DBMS發(fā)送儲(chǔ)存程序調(diào)用,就會(huì)失敗并將拋出異常。JDBC要求驅(qū)動(dòng)程序應(yīng)至少能提供ANSI SQL-2 Entry Level功能才可算是符合JDBC標(biāo)準(zhǔn)TM的。這意味著用戶至少可信賴這一標(biāo)準(zhǔn)級(jí)別的功能。
JDBC提供了三個(gè)類,用于向數(shù)據(jù)庫(kù)發(fā)送SQL語句。Connection接口中的三個(gè)方法可用于創(chuàng)建這些類的實(shí)例。下面列出這些類及其創(chuàng)建方法:
(1)Statement:由方法createStatement所創(chuàng)建。Statement對(duì)象用于發(fā)送簡(jiǎn)單的SQL語句。
(2)PreparedStatement:由方法prepareStatement所創(chuàng)建。PreparedStatement對(duì)象用于發(fā)送帶有一個(gè)或多個(gè)輸入?yún)?shù)(IN參數(shù))的SQL語句。PreparedStatement擁有一組方法,用于設(shè)置IN參數(shù)的值。執(zhí)行語句時(shí),這些IN參數(shù)將被送到數(shù)據(jù)庫(kù)中。PreparedStatement的實(shí)例擴(kuò)展了Statement,因此它們都包括了Statement的方法。PreparedStatement對(duì)象有可能比Statement對(duì)象的效率更高,因?yàn)樗驯活A(yù)編譯過并存放在那以供將來使用。
(3)CallableStatement:由方法prepareCall所創(chuàng)建。CallableStatement對(duì)象用于執(zhí)行SQL儲(chǔ)存程序─一組可通過名稱來調(diào)用(就象函數(shù)的調(diào)用那樣)的SQL語句。CallableStatement對(duì)象從PreparedStatement中繼承了用于處理IN參數(shù)的方法,而且還增加了用于處理OUT參數(shù)和INOUT參數(shù)的方法。
不過通常來說createStatement方法用于簡(jiǎn)單的SQL語句(不帶參數(shù))、prepareStatement方法用于帶一個(gè)或多個(gè)IN參數(shù)的SQL語句或經(jīng)常被執(zhí)行的簡(jiǎn)單SQL語句,而prepareCall方法用于調(diào)用已儲(chǔ)存過程。
7.事務(wù)
事務(wù)由一個(gè)或多個(gè)這樣的語句組成:這些語句已被執(zhí)行、完成并被提交或還原。當(dāng)調(diào)用方法commit或rollback時(shí),當(dāng)前事務(wù)即告就結(jié)束,另一個(gè)事務(wù)隨即開始。缺省情況下,新連接將處于自動(dòng)提交模式。也就是說,當(dāng)執(zhí)行完語句后,將自動(dòng)對(duì)那個(gè)語句調(diào)用commit方法。這種情況下,由于每個(gè)語句都是被單獨(dú)提交的,因此一個(gè)事務(wù)只由一個(gè)語句組成。如果禁用自動(dòng)提交模式,事務(wù)將要等到commit或rollback方法被顯式調(diào)用時(shí)才結(jié)束,因此它將包括上一次調(diào)用commit或rollback方法以來所有執(zhí)行過的語句。對(duì)于第二種情況,事務(wù)中的所有語句將作為組來提交或還原。
方法commit使SQL語句對(duì)數(shù)據(jù)庫(kù)所做的任何更改成為永久性的,它還將釋放事務(wù)持有的全部鎖。而方法rollback將棄去那些更改。有時(shí)用戶在另一個(gè)更改生效前不想讓此更改生效。這可通過禁用自動(dòng)提交并將兩個(gè)更新組合在一個(gè)事務(wù)中來達(dá)到。如果兩個(gè)更新都是成功,則調(diào)用commit方法,從而使兩個(gè)更新結(jié)果成為永久性的;如果其中之一或兩個(gè)更新都失敗了,則調(diào)用rollback方法,以將值恢復(fù)為進(jìn)行更新之前的值。
大多數(shù)JDBC驅(qū)動(dòng)程序都支持事務(wù)。事實(shí)上,符合JDBC的驅(qū)動(dòng)程序必須支持事務(wù)。DatabaseMetaData給出的信息描述DBMS所提供的事務(wù)支持水平。
8.事務(wù)隔離級(jí)別
如果DBMS支持事務(wù)處理,它必須有某種途徑來管理兩個(gè)事務(wù)同時(shí)對(duì)一個(gè)數(shù)據(jù)庫(kù)進(jìn)行操作時(shí)可能發(fā)生的沖突。用戶可指定事務(wù)隔離級(jí)別,以指明DBMS應(yīng)該花多大精力來解決潛在沖突。例如,當(dāng)事務(wù)更改了某個(gè)值而第二個(gè)事務(wù)卻在該更改被提交或還原前讀取該值時(shí)該怎么辦。
假設(shè)第一個(gè)事務(wù)被還原后,第二個(gè)事務(wù)所讀取的更改值將是無效的,那么是否可允許這種沖突?JDBC用戶可用以下代碼來指示DBMS允許在值被提交前讀取該值(“dirty讀取”),其中con是當(dāng)前連接:
con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);
事務(wù)隔離級(jí)別越高,為避免沖突所花的精力也就越多。Connection接口定義了五級(jí),其中最低級(jí)別指定了根本就不支持事務(wù),而最高級(jí)別則指定當(dāng)事務(wù)在對(duì)某個(gè)數(shù)據(jù)庫(kù)進(jìn)行操作時(shí),任何其它事務(wù)不得對(duì)那個(gè)事務(wù)正在讀取的數(shù)據(jù)進(jìn)行任何更改。通常,隔離級(jí)別越高,應(yīng)用程序執(zhí)行的速度也就越慢(由于用于鎖定的資源耗費(fèi)增加了,而用戶間的并發(fā)操作減少了)。在決定采用什么隔離級(jí)別時(shí),開發(fā)人員必須在性能需求和數(shù)據(jù)一致性需求之間進(jìn)行權(quán)衡。當(dāng)然,實(shí)際所能支持的級(jí)別取決于所涉及的DBMS的功能。
當(dāng)創(chuàng)建Connection對(duì)象時(shí),其事務(wù)隔離級(jí)別取決于驅(qū)動(dòng)程序,但通常是所涉及的數(shù)據(jù)庫(kù)的缺省值。用戶可通過調(diào)用setIsolationLevel方法來更改事務(wù)隔離級(jí)別。新的級(jí)別將在該連接過程的剩余時(shí)間內(nèi)生效。要想只改變一個(gè)事務(wù)的事務(wù)隔離級(jí)別,必須在該事務(wù)開始前進(jìn)行設(shè)置,并在該事務(wù)結(jié)束后進(jìn)行復(fù)位。我們不提倡在事務(wù)的中途對(duì)事務(wù)隔離級(jí)別進(jìn)行更改,因?yàn)檫@將立即觸發(fā)commit方法的調(diào)用,使在此之前所作的任何更改變成永久性的。JDBC驅(qū)動(dòng)管理內(nèi)幕是怎么樣的?
DriverManager 類是 JDBC 的管理層,作用于用戶和驅(qū)動(dòng)程序之間。它跟蹤可用的驅(qū)動(dòng)程序,并在數(shù)據(jù)庫(kù)和相應(yīng)驅(qū)動(dòng)程序之間建立連接。另外,DriverManager類也處理諸如驅(qū)動(dòng)程序登錄時(shí)間限制及登錄和跟蹤消息的顯示等事務(wù)。
對(duì)于簡(jiǎn)單的應(yīng)用程序,一般程序員需要在此類中直接使用的唯一方法是DriverManager.getConnection。正如名稱所示,該方法將建立與數(shù)據(jù)庫(kù)的連接。JDBC允許用戶調(diào)用DriverManager的方法getDriver、getDrivers和registerDriver及Driver的方法connect。但多數(shù)情況下,讓DriverManager類管理建立連接的細(xì)節(jié)為上策。
1.跟蹤可用驅(qū)動(dòng)程序
DriverManager類包含一列Driver類,它們已通過調(diào)用方法DriverManager.registerDriver對(duì)自己進(jìn)行了注冊(cè)。所有Driver類都必須包含有一個(gè)靜態(tài)部分。它創(chuàng)建該類的實(shí)例,然后在加載該實(shí)例時(shí)DriverManager類進(jìn)行注冊(cè)。這樣,用戶正常情況下將不會(huì)直接調(diào)用DriverManager.registerDriver;而是在加載驅(qū)動(dòng)程序時(shí)由驅(qū)動(dòng)程序自動(dòng)調(diào)用。加載Driver類,然后自動(dòng)在DriverManager中注冊(cè)的方式有兩種:
(1)調(diào)用方法Class.forName
這將顯式地加載驅(qū)動(dòng)程序類。由于這與外部設(shè)置無關(guān),因此推薦使用這種加載驅(qū)動(dòng)程序的方法。以下代碼加載類acme.db.Driver:Class.forName(“acme.db.Driver”)。
如果將acme.db.Driver編寫為加載時(shí)創(chuàng)建實(shí)例,并調(diào)用以該實(shí)例為參數(shù)的DriverManager.registerDriver(本該如此),則它在DriverManager的驅(qū)動(dòng)程序列表中,并可用于創(chuàng)建連接。
(2)將驅(qū)動(dòng)程序添加到Java.lang.System的屬性jdbc.drivers中
這是一個(gè)由DriverManager類加載的驅(qū)動(dòng)程序類名的列表,由冒號(hào)分隔:初始化DriverManager類時(shí),它搜索系統(tǒng)屬性jdbc.drivers,如果用戶已輸入了一個(gè)或多個(gè)驅(qū)動(dòng)程序,則DriverManager類將試圖加載它們。以下代碼說明程序員如何在~/.hotJava/properties中輸入三個(gè)驅(qū)動(dòng)程序類(啟動(dòng)時(shí),HotJava將把它加載到系統(tǒng)屬性列表中):
jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.test.ourDriver;
對(duì)DriverManager方法的第一次調(diào)用將自動(dòng)加載這些驅(qū)動(dòng)程序類。注意:加載驅(qū)動(dòng)程序的第二種方法需要持久的預(yù)設(shè)環(huán)境。如果對(duì)這一點(diǎn)不能保證,則調(diào)用方法Class.forName顯式地加載每個(gè)驅(qū)動(dòng)程序就顯得更為安全。這也是引入特定驅(qū)動(dòng)程序的方法,因?yàn)橐坏〥riverManager類被初始化,它將不再檢查jdbc.drivers屬性列表。
在以上兩種情況中,新加載的Driver類都要通過調(diào)用DriverManager.registerDriver類進(jìn)行自我注冊(cè)。如上所述,加載類時(shí)將自動(dòng)執(zhí)行這一過程。
由于安全方面的原因,JDBC管理層將跟蹤哪個(gè)類加載器提供哪個(gè)驅(qū)動(dòng)程序。這樣,當(dāng)DriverManager類打開連接時(shí),它僅使用本地文件系統(tǒng)或與發(fā)出連接請(qǐng)求的代碼相同的類加載器提供的驅(qū)動(dòng)程序。
2.建立連接
加載Driver類并在DriverManager類中注冊(cè)后,它們即可用來與數(shù)據(jù)庫(kù)建立連接。當(dāng)調(diào)用DriverManager.getConnection方法發(fā)出連接請(qǐng)求時(shí),DriverManager將檢查每個(gè)驅(qū)動(dòng)程序,查看它是否可以建立連接。
有時(shí)可能有多個(gè)JDBC驅(qū)動(dòng)程序可以與給定的URL連接。例如,與給定遠(yuǎn)程數(shù)據(jù)庫(kù)連接時(shí),可以使用JDBC-ODBC橋驅(qū)動(dòng)程序、JDBC到通用網(wǎng)絡(luò)協(xié)議驅(qū)動(dòng)程序或數(shù)據(jù)庫(kù)廠商提供的驅(qū)動(dòng)程序。在這種情況下測(cè)試驅(qū)動(dòng)程序的順序至關(guān)重要,因?yàn)镈riverManager將使用它所找到的第一個(gè)可以成功連接到給定URL的驅(qū)動(dòng)程序。
首先DriverManager試圖按注冊(cè)的順序使用每個(gè)驅(qū)動(dòng)程序(jdbc.drivers中列出的驅(qū)動(dòng)程序總是先注冊(cè))。它將跳過代碼不可信任的驅(qū)動(dòng)程序,除非加載它們的源與試圖打開連接的代碼的源相同。它通過輪流在每個(gè)驅(qū)動(dòng)程序上調(diào)用方法Driver.connect,并向它們傳遞用戶開始傳遞給方法DriverManager.getConnection的URL來對(duì)驅(qū)動(dòng)程序進(jìn)行測(cè)試,然后連接第一個(gè)認(rèn)出該URL的驅(qū)動(dòng)程序。這種方法初看起來效率不高,但由于不可能同時(shí)加載數(shù)十個(gè)驅(qū)動(dòng)程序,因此每次連接實(shí)際只需幾個(gè)過程調(diào)用和字符串比較。
以下代碼是通常情況下用驅(qū)動(dòng)程序(例如JDBC-ODBC橋驅(qū)動(dòng)程序)建立連接所需所有步驟的示例:
Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”);//加載驅(qū)動(dòng)程序 String url = “jdbc:odbc:fred”;
DriverManager.getConnection(url,“userID”,“passwd”); 如何利用JDBC發(fā)送SQL語句?
Statement對(duì)象用于將SQL語句發(fā)送到數(shù)據(jù)庫(kù)中。實(shí)際上有三種Statement對(duì)象,它們都作為在給定連接上執(zhí)行SQL語句的包容器:Statement、PreparedStatement(它從Statement繼承而來)和CallableStatement(它從PreparedStatement繼承而來)。它們都專用于發(fā)送特定類型的SQL語句:Statement對(duì)象用于執(zhí)行不帶參數(shù)的簡(jiǎn)單SQL語句;PreparedStatement對(duì)象用于執(zhí)行帶或不帶IN參數(shù)的預(yù)編譯SQL語句;CallableStatement對(duì)象用于執(zhí)行對(duì)數(shù)據(jù)庫(kù)已存儲(chǔ)過程的調(diào)用。
Statement接口提供了執(zhí)行語句和獲取結(jié)果的基本方法;PreparedStatement接口添加了處理IN參數(shù)的方法;而CallableStatement添加了處理OUT參數(shù)的方法。
1.創(chuàng)建Statement對(duì)象
建立了到特定數(shù)據(jù)庫(kù)的連接之后,就可用該連接發(fā)送SQL語句。Statement對(duì)象用Connection的方法createStatement創(chuàng)建,如下列代碼段中所示:
Connection con = DriverManager.getConnection(url,“sunny”,“"); Statement stmt = con.createStatement();
為了執(zhí)行Statement對(duì)象,被發(fā)送到數(shù)據(jù)庫(kù)的SQL語句將被作為參數(shù)提供給Statement的方法:
ResultSet rs = stmt.executeQuery(”SELECT a,b,c FROM Table2“);
2.使用Statement對(duì)象執(zhí)行語句
Statement接口提供了三種執(zhí)行SQL語句的方法:executeQuery、executeUpdate和execute。使用哪一個(gè)方法由SQL語句所產(chǎn)生的內(nèi)容決定。
方法executeQuery用于產(chǎn)生單個(gè)結(jié)果集的語句,例如SELECT語句。方法executeUpdate用于執(zhí)行INSERT、UPDATE或DELETE語句以及SQL DDL(數(shù)據(jù)定義語言)語句,例如CREATE TABLE和DROP TABLE。INSERT、UPDATE或DELETE語句的效果是修改表中零行或多行中的一列或多列。executeUpdate的返回值是一個(gè)整數(shù),指示受影響的行數(shù)(即更新計(jì)數(shù))。對(duì)于CREATE TABLE或DROP TABLE等不操作行的語句,executeUpdate的返回值總為零。
執(zhí)行語句的所有方法都將關(guān)閉所調(diào)用的Statement對(duì)象的當(dāng)前打開結(jié)果集(如果存在)。這意味著在重新執(zhí)行Statement對(duì)象之前,需要完成對(duì)當(dāng)前ResultSet對(duì)象的處理。應(yīng)注意,繼承了Statement接口中所有方法的PreparedStatement接口都有自己的executeQuery、executeUpdate和execute方法。Statement對(duì)象本身不包含SQL語句,因而必須給Statement.execute方法提供SQL語句作為參數(shù)。PreparedStatement對(duì)象并不需要SQL語句作為參數(shù)提供給這些方法,因?yàn)樗鼈円呀?jīng)包含預(yù)編譯SQL語句。
CallableStatement對(duì)象繼承這些方法的PreparedStatement形式。對(duì)于這些方法的PreparedStatement或CallableStatement版本,使用查詢參數(shù)將拋出SQLException。
3.語句完成
當(dāng)連接處于自動(dòng)提交模式時(shí),其中所執(zhí)行的語句在完成時(shí)將自動(dòng)提交或還原。語句在已執(zhí)行且所有結(jié)果返回時(shí),即認(rèn)為已完成。對(duì)于返回一個(gè)結(jié)果集的executeQuery方法,在檢索完ResultSet對(duì)象的所有行時(shí)該語句完成。對(duì)于方法executeUpdate,當(dāng)它執(zhí)行時(shí)語句即完成。但在少數(shù)調(diào)用方法execute的情況中,在檢索所有結(jié)果集或它生成的更新計(jì)數(shù)之后語句才完成。
有些DBMS將已存儲(chǔ)過程中的每條語句視為獨(dú)立的語句;而另外一些則將整個(gè)過程視為一個(gè)復(fù)合語句。在啟用自動(dòng)提交時(shí),這種差別就變得非常重要,因?yàn)樗绊懯裁磿r(shí)候調(diào)用commit方法。在前一種情況中,每條語句單獨(dú)提交;在后一種情況中,所有語句同時(shí)提交。
4.關(guān)閉Statement對(duì)象
Statement對(duì)象將由Java垃圾收集程序自動(dòng)關(guān)閉。而作為一種好的編程風(fēng)格,應(yīng)在不需要Statement對(duì)象時(shí)顯式地關(guān)閉它們。這將立即釋放DBMS資源,有助于避免潛在的內(nèi)存問題。
5.使用方法execute
execute方法應(yīng)該僅在語句能返回多個(gè)ResultSet對(duì)象、多個(gè)更新計(jì)數(shù)或ResultSet對(duì)象與更新計(jì)數(shù)的組合時(shí)使用。當(dāng)執(zhí)行某個(gè)已存儲(chǔ)過程或動(dòng)態(tài)執(zhí)行未知SQL字符串(即應(yīng)用程序程序員在編譯時(shí)未知)時(shí),有可能出現(xiàn)多個(gè)結(jié)果的情況,盡管這種情況很少見。例如,用戶可能執(zhí)行一個(gè)已存儲(chǔ)過程,并且該已存儲(chǔ)過程可執(zhí)行更新,然后執(zhí)行選擇,再進(jìn)行更新,再進(jìn)行選擇,等等。通常使用已存儲(chǔ)過程的人應(yīng)知道它所返回的內(nèi)容。
因?yàn)榉椒╡xecute處理非常規(guī)情況,所以獲取其結(jié)果需要一些特殊處理并不足為怪。例如,假定已知某個(gè)過程返回兩個(gè)結(jié)果集,則在使用方法execute執(zhí)行該過程后,必須調(diào)用方法getResultSet獲得第一個(gè)結(jié)果集,然后調(diào)用適當(dāng)?shù)膅etXXX方法獲取其中的值。要獲得第二個(gè)結(jié)果集,需要先調(diào)用getMoreResults方法,然后再調(diào)用getResultSet方法。如果已知某個(gè)過程返回兩個(gè)更新計(jì)數(shù),則首先調(diào)用方法getUpdateCount,然后調(diào)用getMoreResults,并再次調(diào)用getUpdateCount。
對(duì)于不知道返回內(nèi)容,則情況更為復(fù)雜。如果結(jié)果是ResultSet對(duì)象,則方法execute返回true;如果結(jié)果是Javaint,則返回false。如果返回int,則意味著結(jié)果是更新計(jì)數(shù)或執(zhí)行的語句是DL命令。在調(diào)用方法execute之后要做的第一件事情是調(diào)用getResultSet或getUpdateCount。調(diào)用方法getResultSet可以獲得兩個(gè)或多個(gè)ResultSet對(duì)象中第一個(gè)對(duì)象;或調(diào)用方法getUpdateCount可以獲得兩個(gè)或多個(gè)更新計(jì)數(shù)中第一個(gè)更新計(jì)數(shù)的內(nèi)容。
當(dāng)SQL語句的結(jié)果不是結(jié)果集時(shí),則方法getResultSet將返回null。這可能意味著結(jié)果是一個(gè)更新計(jì)數(shù)或沒有其它結(jié)果。在這種情況下,判斷null真正含義的唯一方法是調(diào)用方法getUpdateCount,它將返回一個(gè)整數(shù)。這個(gè)整數(shù)為調(diào)用語句所影響的行數(shù);如果為-1則表示結(jié)果是結(jié)果集或沒有結(jié)果。如果方法getResultSet已返回null(表示結(jié)果不是ResultSet對(duì)象),則返回值-1表示沒有其它結(jié)果。也就是說,當(dāng)下列條件為真時(shí)表示沒有結(jié)果(或沒有其它結(jié)果):
((stmt.getResultSet()==null)&&(stmt.getUpdateCount()==-1))
如果已經(jīng)調(diào)用方法getResultSet并處理了它返回的ResultSet對(duì)象,則有必要調(diào)用方法getMoreResults以確定是否有其它結(jié)果集或更新計(jì)數(shù)。如果getMoreResults返回true,則需要再次調(diào)用getResultSet來檢索下一個(gè)結(jié)果集。如上所述,如果getResultSet返回null,則需要調(diào)用getUpdateCount來檢查null是表示結(jié)果為更新計(jì)數(shù)還是表示沒有其它結(jié)果。
當(dāng)getMoreResults返回false時(shí),它表示該SQL語句返回一個(gè)更新計(jì)數(shù)或沒有其它結(jié)果。因此需要調(diào)用方法getUpdateCount來檢查它是哪一種情況。在這種情況下,當(dāng)下列條件為真時(shí)表示沒有其它結(jié)果:
通過JDBC連接oracle數(shù)據(jù)庫(kù)的十大技巧
(2008-08-27 22:42:57)轉(zhuǎn)載 標(biāo)簽:
雜談
Java數(shù)據(jù)庫(kù)連接(JDBC)API是一系列能夠讓Java編程人員訪問數(shù)據(jù)庫(kù)的接口,各個(gè)開發(fā)商的接口并不完全相同。在使用多年的Oracle公司的JDBC后,我積累了許多技巧,這些技巧能夠使我們更好地發(fā)揮系統(tǒng)的性能和實(shí)現(xiàn)更多的功能。
1、在客戶端軟件開發(fā)中使用Thin驅(qū)動(dòng)程序
在開發(fā)Java軟件方面,Oracle的數(shù)據(jù)庫(kù)提供了四種類型的驅(qū)動(dòng)程序,二種用于應(yīng)用軟件、applets、servlets等客戶端軟件,另外二種用于數(shù)據(jù)庫(kù)中的Java存儲(chǔ)過程等服務(wù)器端軟件。在客戶機(jī)端軟件的開發(fā)中,我們可以選擇OCI驅(qū)動(dòng)程序或Thin驅(qū)動(dòng)程序。OCI驅(qū)動(dòng)程序利用Java本地化接口(JNI),通過Oracle客戶端軟件與數(shù)據(jù)庫(kù)進(jìn)行通訊。Thin驅(qū)動(dòng)程序是純Java驅(qū)動(dòng)程序,它直接與數(shù)據(jù)庫(kù)進(jìn)行通訊。為了獲得最高的性能,Oracle建議在客戶端軟件的開發(fā)中使用OCI驅(qū)動(dòng)程序,這似乎是正確的。但我建議使用Thin驅(qū)動(dòng)程序,因?yàn)橥ㄟ^多次測(cè)試發(fā)現(xiàn),在通常情況下,Thin驅(qū)動(dòng)程序的性能都超過了OCI驅(qū)動(dòng)程序。
2、關(guān)閉自動(dòng)提交功能,提高系統(tǒng)性能
在第一次建立與數(shù)據(jù)庫(kù)的連接時(shí),在缺省情況下,連接是在自動(dòng)提交模式下的。為了獲得更好的性能,可以通過調(diào)用帶布爾值false參數(shù)的Connection類的setAutoCommit()方法關(guān)閉自動(dòng)提交功能,如下所示:
conn.setAutoCommit(false);
值得注意的是,一旦關(guān)閉了自動(dòng)提交功能,我們就需要通過調(diào)用Connection類的commit()和rollback()方法來人工的方式對(duì)事務(wù)進(jìn)行管理。
3、在動(dòng)態(tài)SQL或有時(shí)間限制的命令中使用Statement對(duì)象
在執(zhí)行SQL命令時(shí),我們有二種選擇:可以使用PreparedStatement對(duì)象,也可以使用Statement對(duì)象。無論多少次地使用同一個(gè)SQL命令,PreparedStatement都只對(duì)它解析和編譯一次。當(dāng)使用Statement對(duì)象時(shí),每次執(zhí)行一個(gè)SQL命令時(shí),都會(huì)對(duì)它進(jìn)行解析和編譯。這可能會(huì)使你認(rèn)為,使用PreparedStatement對(duì)象比使用Statement對(duì)象的速度更快。然而,我進(jìn)行的測(cè)試表明,在客戶端軟件中,情況并非如此。因此,在有時(shí)間限制的SQL操作中,除非成批地處理SQL命令,我們應(yīng)當(dāng)考慮使用Statement對(duì)象。
此外,使用Statement對(duì)象也使得編寫動(dòng)態(tài)SQL命令更加簡(jiǎn)單,因?yàn)槲覀兛梢詫⒆址B接在一起,建立一個(gè)有效的SQL命令。因此,我認(rèn)為,Statement對(duì)象可以使動(dòng)態(tài)SQL命令的創(chuàng)建和執(zhí)行變得更加簡(jiǎn)單。
4、利用helper函數(shù)對(duì)動(dòng)態(tài)SQL命令進(jìn)行格式化
在創(chuàng)建使用Statement對(duì)象執(zhí)行的動(dòng)態(tài)SQL命令時(shí),我們需要處理一些格式化方面的問題。例如,如果我們想創(chuàng)建一個(gè)將名字O'Reilly插入表中的SQL命令,則必須使用二個(gè)相連的“''”號(hào)替換O'Reilly中的“'”號(hào)。完成這些工作的最好的方法是創(chuàng)建一個(gè)完成替換操作的helper方法,然后在連接字符串心服用公式表達(dá)一個(gè)SQL命令時(shí),使用創(chuàng)建的helper方法。與此類似的是,我們可以讓helper方法接受一個(gè)Date型的值,然后讓它輸出基于Oracle的to_date()函數(shù)的字符串表達(dá)式。
5、利用PreparedStatement對(duì)象提高數(shù)據(jù)庫(kù)的總體效率
在使用PreparedStatement對(duì)象執(zhí)行SQL命令時(shí),命令被數(shù)據(jù)庫(kù)進(jìn)行解析和編譯,然后被放到命令緩沖區(qū)。然后,每當(dāng)執(zhí)行同一個(gè)PreparedStatement對(duì)象時(shí),它就會(huì)被再解析一次,但不會(huì)被再次編譯。在緩沖區(qū)中可以發(fā)現(xiàn)預(yù)編譯的命令,并且可以重新使用。在有大量用戶的企業(yè)級(jí)應(yīng)用軟件中,經(jīng)常會(huì)重復(fù)執(zhí)行相同的SQL命令,使用PreparedStatement對(duì)象帶來的編譯次數(shù)的減少能夠提高數(shù)據(jù)庫(kù)的總體性能。如果不是在客戶端創(chuàng)建、預(yù)備、執(zhí)行PreparedStatement任務(wù)需要的時(shí)間長(zhǎng)于Statement任務(wù),我會(huì)建議在除動(dòng)態(tài)SQL命令之外的所有情況下使用PreparedStatement對(duì)象。
6、在成批處理重復(fù)的插入或更新操作中使用PreparedStatement對(duì)象
如果成批地處理插入和更新操作,就能夠顯著地減少它們所需要的時(shí)間。Oracle提供的Statement和 CallableStatement并不真正地支持批處理,只有PreparedStatement對(duì)象才真正地支持批處理。我們可以使用addBatch()和executeBatch()方法選擇標(biāo)準(zhǔn)的JDBC批處理,或者通過利用PreparedStatement對(duì)象的setExecuteBatch()方法和標(biāo)準(zhǔn)的executeUpdate()方法選擇速度更快的Oracle專有的方法。要使用Oracle專有的批處理機(jī)制,可以以如下所示的方式調(diào)用setExecuteBatch():
PreparedStatement pstmt3D null;try {((OraclePreparedStatement)pstmt).setExecuteBatch(30);...pstmt.executeUpdate();}
調(diào)用setExecuteBatch()時(shí)指定的值是一個(gè)上限,當(dāng)達(dá)到該值時(shí),就會(huì)自動(dòng)地引發(fā)SQL命令執(zhí)行,標(biāo)準(zhǔn)的executeUpdate()方法就會(huì)被作為批處理送到數(shù)據(jù)庫(kù)中。我們可以通過調(diào)用PreparedStatement類的sendBatch()方法隨時(shí)傳輸批處理任務(wù)。
7、使用Oracle locator方法插入、更新大對(duì)象(LOB)
Oracle的PreparedStatement類不完全支持BLOB和CLOB等大對(duì)象的處理,尤其是Thin驅(qū)動(dòng)程序不支持利用PreparedStatement對(duì)象的setObject()和setBinaryStream()方法設(shè)置BLOB的值,也不支持利用setCharacterStream()方法設(shè)置CLOB的值。只有l(wèi)ocator本身中的方法才能夠從數(shù)據(jù)庫(kù)中獲取LOB類型的值。可以使用PreparedStatement對(duì)象插入或更新LOB,但需要使用locator才能獲取LOB的值。由于存在這二個(gè)問題,因此,我建議使用locator的方法來插入、更新或獲取LOB的值。
8、使用SQL92語法調(diào)用存儲(chǔ)過程
在調(diào)用存儲(chǔ)過程時(shí),我們可以使用SQL92或Oracle PL/SQL,由于使用Oracle PL/SQL并沒有什么實(shí)際的好處,而且會(huì)給以后維護(hù)你的應(yīng)用程序的開發(fā)人員帶來麻煩,因此,我建議在調(diào)用存儲(chǔ)過程時(shí)使用SQL92。
9、使用Object SQL將對(duì)象模式轉(zhuǎn)移到數(shù)據(jù)庫(kù)中
既然可以將Oracle的數(shù)據(jù)庫(kù)作為一種面向?qū)ο蟮臄?shù)據(jù)庫(kù)來使用,就可以考慮將應(yīng)用程序中的面向?qū)ο竽J睫D(zhuǎn)到數(shù)據(jù)庫(kù)中。目前的方法是創(chuàng)建Java bean作為偽裝的數(shù)據(jù)庫(kù)對(duì)象,將它們的屬性映射到關(guān)系表中,然后在這些bean中添加方法。盡管這樣作在Java中沒有什么問題,但由于操作都是在數(shù)據(jù)庫(kù)之外進(jìn)行的,因此其他訪問數(shù)據(jù)庫(kù)的應(yīng)用軟件無法利用對(duì)象模式。如果利用Oracle的面向?qū)ο蟮募夹g(shù),可以通過創(chuàng)建一個(gè)新的數(shù)據(jù)庫(kù)對(duì)象類型在數(shù)據(jù)庫(kù)中模仿其數(shù)據(jù)和操作,然后使用JPublisher等工具生成自己的Java bean類。如果使用這種方式,不但Java應(yīng)用程序可以使用應(yīng)用軟件的對(duì)象模式,其他需要共享你的應(yīng)用中的數(shù)據(jù)和操作的應(yīng)用軟件也可以使用應(yīng)用軟件中的對(duì)象模式。
10、利用SQL完成數(shù)據(jù)庫(kù)內(nèi)的操作
我要向大家介紹的最重要的經(jīng)驗(yàn)是充分利用SQL的面向集合的方法來解決數(shù)據(jù)庫(kù)處理需求,而不是使用Java等過程化的編程語言。
如果編程人員要在一個(gè)表中查找許多行,結(jié)果中的每個(gè)行都會(huì)查找其他表中的數(shù)據(jù),最后,編程人員創(chuàng)建了獨(dú)立的UPDATE命令來成批地更新第一個(gè)表中的數(shù)據(jù)。與此類似的任務(wù)可以通過在set子句中使用多列子查詢而在一個(gè)UPDATE命令中完成。當(dāng)能夠在單一的SQL命令中完成任務(wù),何必要讓數(shù)據(jù)在網(wǎng)上流來流去的?我建議用戶認(rèn)真學(xué)習(xí)如何最大限度地發(fā)揮SQL的功能
1、這是oracle的規(guī)定,不能對(duì)執(zhí)行觸發(fā)器的表進(jìn)行操作??梢詫?duì)new.xxx進(jìn)行操作啊,對(duì)于oracle行級(jí)觸發(fā)器(for each row),不能對(duì)本表做任何操作,包括讀取
原則:
在before insert觸發(fā)器中,可以實(shí)現(xiàn)對(duì)本表的訪問; 在after insert觸發(fā)器中,不能實(shí)現(xiàn)對(duì)本表的訪問;
在before/after update/delete觸發(fā)器中,都不能實(shí)現(xiàn)對(duì)本表的訪問
其實(shí)原因很簡(jiǎn)單,就是為了防止臟讀
2、寫oracle行級(jí)觸發(fā)器時(shí),不能操作本表,報(bào)”表 *** 發(fā)生了變化,觸發(fā)器/函數(shù)不能讀"的錯(cuò)誤的解決辦法
原因已經(jīng)很明顯了就是行級(jí)的觸發(fā)器代碼中不能操作該表,包括select,是挺郁悶的
當(dāng)然解決方法就是要根據(jù)原因了,正因?yàn)橄薅诵屑?jí)觸發(fā)器的操作,只能選擇表級(jí)的觸發(fā)器了,但是在表級(jí)的觸發(fā)器又不能獲得:new和:old的值,那就只能采取兩種觸發(fā)器并用的方法了,并且還要包或者臨時(shí)表加以輔助.首先在行級(jí)觸發(fā)器中將所需的,:new或者:old的值,寫到包或者臨時(shí)表中
然后在表級(jí)觸發(fā)器中處理包或者臨時(shí)表中已經(jīng)寫入的數(shù)據(jù),操作成功后可以按照需求再刪除臨時(shí)表的數(shù)據(jù).3、ORACLE 觸發(fā)器
ORACLE產(chǎn)生數(shù)據(jù)庫(kù)觸發(fā)器的語法為:
create [or replace] trigger 觸發(fā)器名 觸發(fā)時(shí)間 觸發(fā)事件
on 表名
[for each row]
pl/sql 語句
其中:
觸發(fā)器名:觸發(fā)器對(duì)象的名稱。由于觸發(fā)器是數(shù)據(jù)庫(kù)自動(dòng)執(zhí)行 的,因此該名稱只是一個(gè)名稱,沒有實(shí)質(zhì)的用途。
觸發(fā)時(shí)間:指明觸發(fā)器何時(shí)執(zhí)行,該值可取:
before---表示在數(shù)據(jù)庫(kù)動(dòng)作之前觸發(fā)器執(zhí)行;
after---表示在數(shù)據(jù)庫(kù)動(dòng)作之后出發(fā)器執(zhí)行。
觸發(fā)事件:指明哪些數(shù)據(jù)庫(kù)動(dòng)作會(huì)觸發(fā)此觸發(fā)器:
insert:數(shù)據(jù)庫(kù)插入會(huì)觸發(fā)此觸發(fā)器;
update:數(shù)據(jù)庫(kù)修改會(huì)觸發(fā)此觸發(fā)器;
delete:數(shù)據(jù)庫(kù)刪除會(huì)觸發(fā)此觸發(fā)器。
表 名:數(shù)據(jù)庫(kù)觸發(fā)器所在的表。
for each row:對(duì)表的每一行觸發(fā)器執(zhí)行一次。如果沒有這一
選項(xiàng),則只對(duì)整個(gè)表執(zhí)行一次。
舉例:下面的觸發(fā)器在更新表auths之前觸發(fā),目的是不允許在
周末修改表:
create trigger auth_secure
before insert or update or delete //對(duì)整表更新前觸發(fā)
on auths
begin
if(to_char(sysdate,'DY')='SUN'
RAISE_APPLICATION_ERROR(-20600,'不能在周末修改表auths');
end if;end 分析函數(shù)
(2008-09-04 00:11:55)轉(zhuǎn)載 標(biāo)簽:
雜談 分析函數(shù)
1、ROW_NUMBER(為有序組中的每一行返回一個(gè)唯一的排序值,序號(hào)由order by子句指定,從1開始。)例80:對(duì)所有員工的工資進(jìn)行排名,即使工資相同,其排名也不能相同。select ename,job,deptno,sal, row_number()over(order by sal desc)as sal_rank from emp;例81:對(duì)員工的工資按部門進(jìn)行排名,即使工資相同,排名也不同。select ename,job,deptno,sal, row_number()over(partition by deptno order by sal desc)as sal_rank from emp;
2、RANK(計(jì)算一個(gè)值在一組值中的排位,排位是以1開頭的連續(xù)整數(shù)。具有相等值的行排位相同,序數(shù)隨后跳躍相應(yīng)的數(shù)值,即如果兩行的序數(shù)為1,則沒有序數(shù)2,下一行的序號(hào)為3)例82:如何根據(jù)員工的工資和俑金對(duì)員工在每個(gè)部門中進(jìn)行排位。相同的工資排位相同,并且排位不連續(xù)。
select ename,sal,comm,deptno, rank()over(partition by deptno order by sal desc)as RANK from emp;
3、DENSE_RANK(計(jì)算一個(gè)行在一組有序行中的排位,排位是以1開頭的連續(xù)整數(shù)。具有相等值的行排位相同,并且排位是連續(xù)的。)例83:分別計(jì)算每個(gè)員工的工資在部門中的排位。相等的工資排位相同。select d.dname,e.ename,e.sal, dense_rank()over(partition by e.deptno order by e.sal desc)as denserank from emp e,dept d where e.deptno=d.deptno;