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