ZengChang

          統(tǒng)計

          留言簿(1)

          積分與排名

          友情鏈接

          閱讀排行榜

          評論排行榜

          優(yōu)化JDBC編程

          優(yōu)化 JDBC 編程
          這是我根據(jù) MS SQL SERVER 2000 JDBC DRIVER HELP ,并參考其它資料整理而成。 ms 的這個幫助文件實在有失大家風范,示例代碼很 ..... 有興趣者可以去下載 http://download.microsoft.com/download/SQLSVR2000/jdbc/2000/NT45XP/EN-US/setup.exe 。由于本人水平有限,文中不當之處請大家批評指正。
          1.
          盡量減少對數(shù)據(jù)庫元數(shù)據(jù)方法的使用
          ????
          同樣是產(chǎn)生一個 ResultSet 對象, DatabaseMetaData 對象的方法比其它 JDBC 方法相對要慢,因此平繁使用該方法會降低系統(tǒng)的性能。 ?? 在程序中應當對產(chǎn)生的結(jié)果集信息進行高速緩存,比如將 getTypeInfo() 返回的結(jié)果集存入 Vector Hashtable 中,這樣可大大提高程序的效率。
          2.
          應避免的方法調(diào)用模式
          ????
          在方法調(diào)用時應當盡量避免傳入 null 做為參數(shù),雖然有時能執(zhí)行成功,但這對 DB Server 負擔很重。其實在很多情況下所需的參數(shù)是已知的。比如:
          ????//
          這里略去了捕獲違例代碼(下同)。
          ????DatabaseMetaData md=...;
          ????ResultSet rs=md.getTables(null,null,"authors",null);//
          取得 MS SQL SERVER pubs 數(shù)據(jù)庫中 authors 表的信息 .
          ????
          應當寫成:
          ???????ResultSet rs=md.getTables("northwind","dbo","authors",new String[]{"TABLE"});
          ????
          這樣使程序更有效可靠。
          3.
          使用啞查詢語句來取得表的相關(guān)特征信息
          ????
          一個啞查詢語句( Dummy Query ,譯為啞查詢不知是否恰當,愿與大家探討)不會產(chǎn)生有記錄的結(jié)果集,比如: select * from tableName where 1=0 ,因為條件永不成立, DB Server 不會執(zhí)行這條語句。因此,在不需產(chǎn)生記錄行的情況下,啞查詢能極大地提高程序的執(zhí)行效率。比如我們要了解一個表的有關(guān)列信息時,上面的語句比 select * from tableName 這個語句要高效得多,后者數(shù)據(jù)庫服務器要檢索所有的行并返回一個記錄集,而前者不需要。針對這一問題, JDBC 可以有以下兩種方法:
          ????case 1:
          使用 getColumns() 方法
          ????????//getColumns()
          DatabaseMetaData 的一個方法,其有關(guān)信息請查閱 JDK1.3 文檔
          ????????ResultSet rs=md.getColumns("pubs","dbo","authors",...);//
          返回一個有記錄的結(jié)果集
          ????????while(rs.next())//
          通過滾動結(jié)果集取得列名
          ????????????System.out.println(rs.getString(4));
          ????case 2:
          使用 getMetaData() 方法
          ????????Statement stmt=conn.createStatement();
          ????????//
          數(shù)據(jù)庫服務器永遠不會執(zhí)行這條查詢語句
          ????????ResultSet rs=stmt.executeQuery("select * from authors where 1=0");
          ????????ResultSetMetaData rsmd=rs.getMetaData();
          ????????int colCount=rsmd.getColumnCount();//
          取得列數(shù)
          ????????for(int col=1;col<=colCount;col++)
          ????????????System.out.println(rsmd.getColumnName(col));
          ????????//!
          這里列的順序是 select 后列出現(xiàn)的順序,并不一定與表中列順序?qū)?/span>
          ????
          通過以上的分析,第二種方法應是我們的選擇。
          4.
          關(guān)于存儲過程的調(diào)用
          ????
          由于所有的 JDBC 驅(qū)動總是將 SQL 語句作為字符串發(fā)送到數(shù)據(jù)庫服務器,數(shù)據(jù)庫服務器經(jīng)過語法分析、參數(shù)類型驗證,然后將參數(shù)轉(zhuǎn)換成正確的數(shù)據(jù)類型再去執(zhí)行。比如有這么一個存儲過程:
          ??????CallableStatement cstmt=conn.prepareCall("{call getCustomerName(123)}");
          ??????//
          獲得指定 id 的客戶的名字,輸入?yún)?shù), id 是個正整數(shù)
          ??????ResultSet rs=cstmt.executeQuery();
          ??????
          在這里我們認為 123 是一個正整數(shù),但實際 "call getCustomerName(123)" 作為字符串整個被發(fā)送到數(shù)據(jù)庫服務器端,數(shù)據(jù)庫服務器經(jīng)過分析,離析出 "123" 將其轉(zhuǎn)換為整數(shù)型值再做為參數(shù)送給存儲過程執(zhí)行。很明顯,這樣效率極低,因為我們把已知的東西仍要服務器去判斷,這無疑額外加重了服務器的負擔。做為優(yōu)化也是我們常見的存儲過程的調(diào)用方法應是:
          ??????CallableStatement cstmt=conn.prepareCall("call getCustomerName(?)");
          ??????cstmt.setLong(1,123);//
          將值和類型信息編碼后發(fā)送
          ??????ResultSet rs=cstmt.executeQuery();
          ??????//do something
          5.
          正確使用 Statement PreparedStatement 對象及其 execute 方法
          ????Statement
          對象是為僅執(zhí)行一次的查詢語句優(yōu)化而設計的, PreparedStatement 對象是為兩次或更多次執(zhí)行同一查詢語句而設計的。 PreparedStatement 對象第一次執(zhí)行一個準備好的查詢要花一定的代價,然而它帶來的好處是為以后的查詢加快了速度;因為 SQL 語句已經(jīng)進行編譯并放入高速緩存,你可以一直重復使用;想要改變查詢條件獲得不同的結(jié)果集只需用 setXXX 方法改變主機變量( ? )的值就行了。
          ????
          由于 PreparedStatement CallableStatement 都是 Statement 的子類,所以它們都有 execute(String sql),executeQuery(String sql),executeUpdate(String sql),executeBatch() 方法。
          ??????execute(String sql)
          方法返回一個 boolean 值,它執(zhí)行任意復雜的 sql 語句,可以產(chǎn)生多個結(jié)果集。如果有結(jié)果產(chǎn)生返回 true ,如果沒有結(jié)果集產(chǎn)生或僅是一個更新記數(shù)則返回 false 。它產(chǎn)生的結(jié)果集可以通過 getResultSet() getMoreResults() 獲得,更新記數(shù)可通過 getUpdateCount() 獲得。顯然 execute(String sql) 方法的使用要復雜一些,因此如果只是簡單的查詢或更新操作請使用 executeQuery(String sql) executeUpdate(String sql) 方法。 executeUpdate(String sql) 能執(zhí)行 INSERT UPDATE DELETE 語句,及 DDL DML 命令(此時返回值為 0 )。
          ??????
          如果需要進行更多的更新操作,只需將這些更新命令打包后一起提交給數(shù)據(jù)庫,數(shù)據(jù)庫一次處理所有的請求,這比逐條提交要高效得多。例如:
          ??????//
          保存當前提交模式
          ??????boolean commitState=conn.getAutoCommit();
          ??????//
          關(guān)閉自動提交模式
          ??????conn.setAutoCommit(false);
          ??????Statement stmt = conn.createStatement();
          ??????//
          逐條加入
          ??????stmt.addBatch("INSERT INTO employees VALUES (1000, 'Joe Jones')");
          ??????stmt.addBatch("INSERT INTO departments VALUES (260, 'Shoe')");
          ??????stmt.addBatch("INSERT INTO emp_dept VALUES (1000, 260)");
          ??????//
          一次提交
          ??????int[] updateCounts = stmt.executeBatch();
          ??????conn.commit();//
          使更新生效
          ??????conn.setAutoCommit(commitState);//
          恢復原來的提交模式
          ??????PreparedStatement
          CallalbeStatement 對象的使用基本與 Statement 一樣,請參閱 JDBC2.1API
          6.
          正確的使用游標
          ????JDBC2.1
          核心 API 提供三種結(jié)果集類型: forward-only, scroll-insensitive scroll-sensitive
          ????forward-only
          :僅向前型。如果你僅需要前向順序滾動結(jié)果集中的所有行,僅向前游標能提供極高的性能;然而它不能從第一行上直接滾動到最后一行,也不能從最后一行滾到第一行。
          ????scroll-insensitive
          :滾動不敏感型。對于要求較高處理級別的應用來說,滾動不敏感型結(jié)果集是一個理想的選擇,它支持向前和向后的記錄集滾動。滾動不敏感型結(jié)果集的第一次請求是從數(shù)據(jù)庫服務端取得所有滿足條件的行,然后將它存儲在客戶端,也就是說是一個包含數(shù)據(jù)的客戶端靜態(tài)視圖;雖然以后的操作比較快,但數(shù)據(jù)庫服務器處理第一次的請求非常慢,尤其是當返回的數(shù)據(jù)量比較大時。因此,如果返回的只是一行記錄我們就不應使用這種游標,使用僅向前就滿足要求了;相反,如果返回的記錄非常多,也不推薦使用這種游標,因為這些數(shù)據(jù)都存放在內(nèi)存里,大量的數(shù)據(jù)將很快使內(nèi)存耗盡。有些滾動不敏感游標的實現(xiàn)是將數(shù)據(jù)緩存到數(shù)據(jù)庫服務器的一個臨時表中,以免占用過多的內(nèi)存資源。
          ????scroll-sensitive
          :滾動敏感型,有時也叫鍵集驅(qū)動游標。它是在你的數(shù)據(jù)庫上對滿足條件的記錄行做了一個標識,好像行的主鍵,當你滾動結(jié)果集的時候,只有有標識的數(shù)據(jù)才會返回。由于每次的請求都要產(chǎn)生一次網(wǎng)絡連接,因此速度是很慢的。
          7.
          只返回需要的行或列
          ????
          聽了上周六范生對 Oracle 核心的剖析,我算是搞清楚了對表的查詢或更新,數(shù)據(jù)庫低層操作其實是對磁盤文件的 read or write ,而 I/O 操作數(shù)據(jù)量越大耗時越多,軟盤的讀寫速度大家是有目共睹的。為了避免不必要的數(shù)據(jù)傳輸,請小心使用 select * from ... 這樣的語句,如果只需要一列就沒必要返回所有的列,特別是當你不需要的列中含有大數(shù)據(jù)類型(如 BINARY BLOB CLOB )或者說數(shù)據(jù)量較大時,會影響系統(tǒng)性能。
          8.
          使用連接池
          ????
          連接池對數(shù)據(jù)庫訪問性能的提高是非常顯著地,因為創(chuàng)建和銷毀一個連接的代價都非常昂貴。連接池實現(xiàn)了數(shù)據(jù)庫連接的共享,一個連接對象可以被多個用戶多次重復使用。由容器管理的連接池就像是一個租賃公司,誰要使用就租給他一個,用完后還給我,下次要用接著出租,這樣就免去了每次請求都要造個新的,而用完后又把它扔了。
          ????
          關(guān)于連接池技術(shù)較為復雜,不過你也完全可以寫自己的連接池對象,如果你看了《 JAVA2 高級編程》。

          [
          參考資料 ]
          ??1.MS SQL SERVER 2000 JDBC DRIVER HELP
          ??2.
          JAVA2 高級編程》
          ??3.
          JAVA2 核心技術(shù) II
          ??4.
          J2EE 構(gòu)建企業(yè)系統(tǒng)專家級解決方案》
          ??5.
          JSP 高級編程》
          ??6.SUN JDBC2.1 API

          posted on 2006-06-01 09:09 ZengChang 閱讀(239) 評論(0)  編輯  收藏 所屬分類: 學習筆記

          主站蜘蛛池模板: 陵水| 凤翔县| 抚顺市| 大竹县| 惠水县| 建宁县| 德清县| 大足县| 梁山县| 花莲市| 句容市| 叙永县| 宜良县| 祁连县| 伊宁县| 道真| 玉屏| 洛川县| 静乐县| 安图县| 社旗县| 新密市| 巴林左旗| 绍兴市| 波密县| 盐边县| 深泽县| 白水县| 灌南县| 普兰县| 永泰县| 嘉峪关市| 中牟县| 宾川县| 信阳市| 金塔县| 石城县| 五大连池市| 兰坪| 肥西县| 阳山县|