笑着 胖胖蘭原創,轉載請註明。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
http://blog.csdn.net/bluesmile979/archive/2008/10/21/3118083.aspx
前面幾篇文章講的東西的理解消化,加上對應的基礎內容學習,如果沒有基礎的話大概需要3個月左右。心急吃不了熱豆腐。不要着急。如果前面的都學好了。那麼差不多要接觸一個比較重要的東西--數據庫操作了。笑着在這一篇文章裏要談的,是關於數據庫操作的一些注意事項。
首先來看一下Java中最基本的數據庫連結程序。
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
String url="jdbc:oracle:thin:@localhost:1521:dbName";
String user="bluesmile";
String password="xiaozhe";
Connection conn= DriverManager.getConnection(url,user,password);
Statement stmt=conn.createStatement(***,***);
String sql="select * from test";
ResultSet rs=stmt.executeQuery(sql);
while(rs.next()) {
String s = rs.getString(1);
System.out.println("您取到的數據爲:" + s);
}
rs.close();
stmt.close();
conn.close();
當然,如果就用這樣的程序來做數據庫連結操作,那時有很多問題的。這裏我們只是通過這一段程序來看一下數據庫操作的基本流程。具體使用這樣的一段程序有什麼問題,就是笑着這一篇文章要談的。
那麼先來看一下數據庫操作的基本流程。
1. 取得數據庫連接對象Connection
2. 取得SQL語句執行對象
3. 創建SQL語句並執行
4. 從返回結果ResultSet中取得需要的數據
5. 關閉被打開的資源。
以上,是數據庫操作的最最基本的流程,最少最少的,我們也要實現上面流程中的操作。問題,也是就是從如何實現這些基本流程開始的。
1. 關於如何取得數據庫連結對象Connection
a. 關於針對連接對象的參數設定
通常我們採用的方法是DriverManager.getConnection(url,user,password);來取得Connection對象。在這裏很多參數都是採用的默認的。比如oracle數據庫對應有兩個對性能影響比較大的參數defaultRowPreFetch,defaultBatchValue,前一個設定每次從數據庫讀取的數據條數,後一個用來設定SQL執行對象Statement的批處理執行語句條數。這兩參數的默認值笑着記得都是1,這無疑增加了我們跟數據庫打交道的次數。所以明確設定比較好。針對Connection的設定根據數據庫不同會有所不同,具體可以取研究對應的實現了Connection接口的實際針對該數據庫的連接對象,比如Oracle對應的OraclConnection(記不清楚了,好像叫做這個名字)。不同的數據庫提供的JDBC Driver裏面都會有這樣的具體的Connection實現類的。這些具體的實現類會提供參數的設定接口。這裏只能夠提個醒,具體哪些參數,如何設定,需要的時候要心裏有數,自己去研究一下。
b. 關於數據庫連結池
創建Connection對象一般來說開銷是比較大的。每次連接都創建新的對象顯然不是一個很好的做法。幸運的是大多數數據庫應用服務器都提供了數據庫連結池。笑着在這裏要強調的是,我們可能沒有必要去做連結池的具體操作,設定。但是我們必須要明白它的存在,我們的腦海裏面要有連結池,緩衝池這樣的概念,也許大多數情況下,數據庫連結池不要要我們做什麼,甚至不需要我們知道他的存在,但是,實際程序中有很多影響效率的地方,池,也許會幫你解決一些問題。
所謂池的策略,簡單來說,當你請求一個資源,池會看看池中有沒有資源,有的話把這個資源分發給請求,沒有的話,生成新的資源同時把該資源放到池中。當請求歸還的時候,池會判斷池中是否滿了,如果滿了,那麼釋放該資源。其中關鍵的,就是生成新資源,判斷是否釋放資源,以及如何維護池中資源的策略,算法。
c. 關於設定AutoCommit。
Java裏面數據庫的AutoCommit的默認設定是true。什麼意思呢?就是我每執行一個SQL語句就會向數據庫提交一次,數據庫就要實際產生一次動作,這比批量的向數據庫提交SQL語句的效率要差一些。尤其在批量處理insert,update語句的時候。效率差別還是很明顯的。而且,你無法在某一句sql語句失敗的時候rollback,所以,要適當考慮設定autocommit爲false,自己主動在適當的時候commit。因爲Java中默認爲true,可能很多人會忘記考慮這個事情。至少,爲false的時候你的數據在你明確commit之前不會被保存到數據庫中,那麼要不要自動commit至少我們不會忘。不知道Java裏面爲什麼默認值設定爲了true了。
2. 關於SQL語句執行對象
Java中存在Statement,PreparedStatement,CallableStatement三種SQL語句連接對象,一般來說,比較常用的是PreparedStatement,下面簡單說說他們的區別。
來看一下最簡單的SQL語句,select * from table where a=b
大家考慮一下,數據庫查詢的時候,數據庫軟件要如何判斷我們要做什麼呢。
恩,是的,數據庫軟件需要解析我們傳給他的這個SQL語句的字符串,看看select後面是什麼,from後面是什麼,where 後面是什麼。就是說數據庫軟件要解析Key:Value。那麼這個解析過程是要花時間的,越複雜的SQL語句需要花費的時間就越多。
是的,這就是Statement跟PreparedStatement之間的區別,Statement要針對我們傳入的每一條SQL語句進行解析,而PreparedStatement採用?這個符號來匹配變化的值,那麼當inser,update等SQL語句中大部分內容相同的時候,採用?這個符號來匹配少量變化的值,數據庫軟件就只需要對這個SQL語句解析一次,然後等待接受PreparedStatement後續傳入的可變值部分。所以,我們常用的是PreparedStatement。
至於CallableStatement,是調用存儲過程的,需要在數據庫服務期端的存儲過程相配合。因爲使用了服務期端的存儲過程,那麼效率自然就高一些。笑着沒用過,不好說什麼。但是有一點,存儲過程執行本身,因爲它就是在數據庫服務器端運行,數據庫對其也有相當的優化,本身執行速度那肯定是快的。但是Java調用存儲過程,這個調用過程本身也是要花時間的。所以到底快不快是不一定的。
另外,是用SQL執行對象的addBatch,executBatch方法回比直接使用executeQuery的效率好一些。
附:常用用法代示例
a. Statement的
Stmt.addBatch(“insert…..”);
Stmt.addBatch(“update….”);
Stmt.executeBatch();
b. PreparedStatement的
3. 關於創建的SQL語句
SQL語句本身是支持相當多的邏輯判斷,數據篩選的,要適當的使用SQL語句本身的功能。比如Select * From table 然後再通過Java程序篩選需要的數據,那就不如直接使用Select * From table Where a=b讓數據庫來幫忙篩選。
基本原則,我們要減少跟數據庫之間的通信量。這根IO是同樣的道理,程序根數據庫,IO打交道,無論是建立連接,還是數據通信,都是需要花比較多的時間的,所以我們要儘量減少這樣的操作
4. 關於ResultSet
a. 參數設定,ResultSet裏面有fetchSize,FetchDirection這樣的參數,fetchSize參考Connection時候的說明,意思差不多。fecthDirection是設定遊標運行方向的。簡單來說就是數據結構中的鏈表,單向鏈表遊標就只能一直向前,雙向鏈表可以向前也可以向後移動,但是同時自然要增加一些控制用的東西,影響效率。也就是說沒有必要的話就是用一直向前這種就可以了,這也是Java默認的設定。
b. 關於從ResultSet中取值
Java裏提供兩種方法,一種是使用字段位置Index作爲參數,一種是使用字段名稱ColName作爲參數,那麼自然是用Index會快一些,但是笑着測試這個效率影響不是很大,用Index不方便程序閱讀。到底用什麼辦法還是大家自己根據具體情況判斷吧。用Index的話注意把註釋加加好。笑着還是傾向這種方法的。
5. 關閉資源。
本來關於關閉資源沒啥好說的。但是看到網上大量的演示程序給出的代碼稍微有些問題,所以還是提一下。基本原則就是你必須要保證你的資源被關閉,被釋放掉了。本文開始的程序那樣做的話,中間任何語句產生異常的話,這一份資源就不會被釋放掉了,那麼你的CPU佔用率達到100%指日可待。正確用法如下,不多解釋了。
try{
} catch (){
} finally{
if(rs != null){
try(){
rs.close();
} catch() {
}
}
// 以下略
}
6. 再說說基本原則
前面提到了一個基本原則,我們要減少跟數據庫之間的通信量。這根IO是同樣的道理,程序根數據庫,IO打交道,無論是建立連接,還是數據通信,都是需要花比較多的時間的,所以我們要儘量減少這樣的操作。
這裏數據庫也好,IO也好,爲了減少建立連接,數據通信的次數的具體的辦法。都是儘量把有用的,常用的數據保存在內存中,類似池,緩衝的概念,但是什麼事情都有個度。過猶不及,用IO打個比方。一般屬性文件內容比較少,程序中經常要使用,我們可以一次性的讀取到內存中。但是並不一定所有的文件都適合一次性全都讀到內存中。比如文件內容很多的時候。一方面內存開銷太大了,空間方面我們也要考慮。而且過多的沒有用的信息放在內存裏,也是沒有價值的。就是說雖然可以空間換時間,也要我們在掌握了基本原則的基礎之上,根據具體情況來作出不同的判斷。萬事都沒有絕對的。
7.