程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 熱門數據庫JDBC驅動試用心得

熱門數據庫JDBC驅動試用心得

編輯:關於JAVA

一、引言

無論是初級還是中高級技術人員,面對著各式各樣的數據庫平台層出不窮和眾多的操作系統功能不斷升級,難免會眼花缭亂。特別是當系統面臨升級,無論操作平台還是數據庫平台,甚至架構都可能需要更替的時候,如何才能抵住眾說紛纭,把握好你的選擇。幸運的是,利用Java技術可以將這些不同種別的數據庫平台和操作系統無縫地連接起來,真正地做到“集百家之長而為我所用”。

本文將通過一組真實的案例來向讀者介紹如何做到簡單地使用JDBC驅動來實現在不同的操作系統下存取幾款較為熱門的數據庫平台。

特別是對JavaDB這款支持嵌入式模式的純Java數據庫的開發過程進行了詳細分析和展望。希望讀者能做到舉一反三,引入更多的數據庫平台的應用。

二、評測框架

1.操作系統平台和數據庫平台

實例涉及到的操作系統是MS Windows XP + SP2和SUN Solaris 8,數據庫平台有:MS Access 2000(以下簡稱Access),MS SQL Server 2000(以下簡稱SQL Server),My SQL,Oracle和Java DB(J2SE 1.6.0中綁定)。

對於XP平台,可以安裝以上5種數據庫平台。而對於Solaris,只可以安裝My SQL和Java DB兩種。

2.使用平台搭建

(1)安裝支持對應操作系統的JDK(http://java.sun.com/javase/downloads/index.jsp)。注意:如果是Solaris操作系統還必須選擇對應的CPU類型,本案例中選用的是支持SPARC的JDK版本(jdk-6-solaris-sparc.sh)。在XP系統中安裝的JDK Update3版本的JDK(jdk-6u3-windows-i586-p.exe),保證該版本中已經綁定Java DB。

(2)設置JAVA_HOME,PATH和CLASSPATH等環境變量。以便正常編譯和運行Java代碼。

(3)下載My SQL Connector/J驅動,並將其中的mysql-connector-java-5.1.0-bin.jar文件(其中5.1.0為驅動版本號)添加到CLASSPATH變量中。需要說明的是,該驅動文件中包含兩種JDBC驅動,一種是mm.mysql,一種是mysql普通JDBC驅動。兩者都可以使用。

(4)將包含Java DB和Oracle的驅動文件加入到CLASSPATH中。分別為derby.jar和classes12.jar。都可以在相應的產品安裝目錄中找到。

通過上述的配置之後,我們就可以開始在XP系統和Solaris系統中對各類數據庫平台進行使用了。

三、試用准備

1.簡化JDBC函數

為了方便開發人員的使用,作者提煉出以下簡化後的常用JDBC函數:

這些函數基本上已經滿足大部分的使用,初級開發人員按照函數的調用步驟就可以實現通過JDBC驅動與各種數據庫平台進行交互了。

如果用戶對數據庫操作的效率比較關注,那麼還有3組比較重要的,也是常用的JDBC函數,分別是:

(1)事務處理函數:setAutoCommit/commit/rollback

(2)批處理函數:addBatch/execBatch

(3)語句預處理函數:prepareStatement

對於大多數開發人員,只需要知道其用法含義即可,深層次的探索和分析可能需要另外的篇幅來說明,因此作者在此不再贅述。

實際上,上述基本函數的定義,大部分都是對JDK中JDBC函數的封裝,讀者也可以通過JDBC的文檔進行相關的查閱。關鍵代碼參見全文末尾附錄部分。

2.數據庫表定義

作為試用,作者並沒有定義很復雜的數據庫表,以下是測試用數據表結構:

create table user_info(

ui_id varchar(64),

ui_passwd varchar(64),

ui_real_name varchar(64), primary key(ui_id));

3.試用思路

比較簡單,就是通過上述不同類型的JDBC驅動來連接各種數據庫平台,然後向已經初始化的數據表插入10000條記錄,再逐條讀取,並記錄其各個步驟的執行耗費。

四、試用過程

1.平台選擇

作者選擇了MS Windows XP和Solaris兩種平台。其中只有MySQL,JavaDB和Oracle(連接遠程服務器)既可在Windows平台進行了測試,也可在Solaris平台下進行了測試。兩個系統下的測試代碼和框架完全相同。

2.使用JDBC驅動連接數據庫

(1)Access數據庫

通過JDBC驅動連接Access數據庫最常用的是采用建立ODBC數據源(DSN)的方式進行,但是本測試中采用的是通過數據庫連接字符串避開了手工建立DSN的部分。以下是關鍵代碼:

final String connectStr =

“jdbc:odbc:driver={Microsoft Access Driver (*.mdb)};DBQ=./TestDB.mdb”;

……

if( (conn = FoolDB.openDB(connectStr, null, null)) == null)

用過ADO的讀者可能一眼就看出來了,上述的連接字符串中的內容和ADO很相似。事實上,作者就是為了避開建立DNS而嘗試套用了ADO的連接字符串,結果嘗試通過了!

(2)SQL Server數據庫

需要說明的,對於SQL Server的測試沒有采用Microsoft提供的SQL Server專用的JDBC驅動,還是通過借鑒ADO的連接字符串形式,沿用了JDK自帶的JdbcOdbc驅動。

final String connectStr =
“jdbc:odbc:Driver={SQL Server};Server=.;Database=master;UID=sa;PWD=121fs”;
……
if( (conn = FoolDB.openDB(connectStr, null, null)) == null)

注意:用戶名和密碼已經包含到連接字符串中。

SUN公司提供的SQL Server的JDBC驅動的版本應該比較陳舊,所以可能導致在操作數據庫功能支持和效率方面比當前新的JDBC驅動要差一些。Microsoft提供的SQL Server JDBC驅動類名為:“com.microsoft.jdbc.sqlserver.SQLServerDriver”,連接字符串形如:“jdbc:microsoft:sqlserver://hostname:port;DataBaseName=dbname”。

(3)MySQL數據庫

眾所周知,MySQL數據庫既可以在Windows可以在Solaris平台運行,而且執行效率也深得業界的好評。本試例中連接MySQL使用的是MySQL專用驅動(在第一部分已經詳述,在MySQL官方網頁有很多的支持文檔),以下是關鍵代碼:

final String connectStr = “jdbc:mysql://localhost/phome”;
final String userName = “root”; //
final String passwd = ““;
……
if( (conn = FoolDB.openDB(connectStr, userName, passwd)) == null)

在本地連接時,主機名可以使用“localhost”,但如果連接遠程主機時,必須換成該遠程主機的IP,而phome是MySQL數據庫名稱。

(4)Oracle數據庫

Oracle提供了thin和oci兩種類型的驅動,本測試中使用thin類型JDBC驅動。

final String connectStr = “jdbc:oracle:thin:@WBS:1521/oracle088”;
final String userName = “test”;
final String passwd = “test”;

其中“WBS:1521/oracle088”為Oracle服務器的SID。

(5)Java DB

值得一提的,Java DB是由Apache Software Foundation主要參與開發的一個數據庫(DB)項目,SUN在JDK1.6.0中將其進行了綁定,它是一款名副其實的純Java代碼開發的數據庫平台。可以支持Server/Client也可以支持嵌入式運行模式,本實例中主要采用了嵌入式模式(Embedded)進行操作:

final String connectStr = “jdbc:derby:FoolDB”;
……
if( (conn = FoolDB.openDB(connectStr, null, null)) == null)

其中FoolDB是在初始化過程中,使用連接字符串“jdbc:derby:FoolDB;create=true”進行創建的。創建的結果是:在當前目錄中創建一個名為FoolDB的目錄,該目錄又中包含log,seg0和tmp這3個文件夾,而數據庫內容以多文件的方式存放於seg0目錄中。

注意:數據庫一旦創建之後,就無需再次創建了。

3.設置數據庫事務方式

當數據庫連接成功之後,需要設定連接的事務方式(前提是該數據庫平台支持事務處理)為非自動提交,因為JDBC函數默認為自動提交。

提交過於頻繁,就會使數據庫操作的效率較低。特別是對於Oracle這種絕對支持事務處理的數據平台而言,設置是否自動提交對系統的效率將有很大的影響。

4.采用批處理方式插入記錄

同樣一個考慮效率的操作,循環地執行操作語句(Insert,Update)也會增加很多不必要地開銷。試例中采用了批量處理的方式,通過這種方式將要進行的操作先進行匯總,再批量提交執行。這樣就可以獲得較高的執行效率。

//Insert records
for(int i = 0; i < RECORD_COUNT; ++i)
{
//Add each operation to batch
if(FoolDB.addBatch(execStat, “insert into user_info values('guest”
+ Integer.toString(i + 1)
+ “', '666666', '我是中國人')”) == false)
……
FoolDB.execBatch(execStat); //Execute batch operations
FoolDB.commit(conn);
FoolDB.setAutoCommit(conn, true);

這種批處理的方式,可以視同預處理,通過統合批量的操作來減少與數據庫的交互頻率,也減少數據庫訪問IO設備的頻率,從而也可獲得較高的效率。

5.使用行游標讀取記錄字段

記錄插入完畢之後,通過執行查詢語句來獲取數據集。然後再通過簡化函數moveNext來逐行讀取結果集的記錄行:

//Select from table
String sql = "select * from user_info";
if( (queryRS = FoolDB.openQuery(queryStat, sql)) == null)
{

}

while(FoolDB.moveNext(queryRS) == true)
{
//Field ui_id, text(64) type
FoolDB.getFieldByName(queryRS, “ui_id”);
//Field ui_passwd, text(64) type
FoolDB.getFieldByName(queryRS, “ui_passwd”);
//Field ui_real_name, text(64) type
FoolDB.getFieldByName(queryRS, “ui_real_name”);
}

實際上,moveNext的函數名也來源於ADO(在作者看來,JDBC和ADO的應用方式是相同的)。

6.關閉數據庫

這裡主要對以嵌入式模式運行的JavaDB的關閉進行說明,其他數據庫的關閉,直接采用簡化後的關閉函數(closeDB)即可。

在嵌入式模式,應用程序退出時就必須關閉數據庫。但是如果應用程序關閉數據庫失敗,當JVM退出時不會對該未被關閉的連接進行檢查,這樣就占用了數據庫的連接資源,就會影響後續的連接執行效率。所以Apache Derby的建議是采用URL的方式顯示地關閉數據庫。以下是關鍵代碼:

FoolDB.closeDB(conn); //Close connection
if(framework.equals("embedded") == true) //If in embedded mode
{
//Use URL to shutdown Derby
if(FoolDB.openDB("jdbc:derby:;shutdown=true", null, null) == null)
{
//If shutdown failure means that Derby already shutdown
isShutdownOk = true;
}
if (isShutdownOk == false) //Not shutdown normally
{
System.out.println("Derby shutdown NG.");
}
}

注意:關閉和判斷Derby數據庫是否正常關閉的連接字符串中是不包含數據庫名的。

7.記錄各步驟操作的時間戳

以上的操作中,在各個步驟之間添加了時間戳,以此來記錄各個數據庫平台的執行耗費。

五、結果及分析

在對試用結果進行評價之前,讀者需要考慮各款數據庫平台的特點,而不能簡單地從執行時間的長短來進行判斷,以下是筆者根據開發經驗總結出的需要注意的地方:

(1)要保證JDBC驅動和數據庫平台的連接是無縫的。例如Oracle和JavaDB數據庫本身就是由Java開發,其JDBC驅動和數據庫平台的連接可以做到完全無縫,這樣可以充分體現出該款數據庫平台的特性。反之,Access和SQL Server與對應的JdbcOdbc驅動之間的耦合可能就不如與ADO驅動那麼吻合,那麼這些數據平台的很多特性就無法通過這些JDBC驅動得以體現。之前我們也提到,Microsoft專門有提供MS SQL Server的JDBC驅動,數據庫與這種“對口”的JDBC驅動的耦合肯定要超過JdbcOdbc這種通用型的驅動。

(2)數據庫平台要有良好的可移植性。換句話說就是數據庫跨操作系統的性能。在這些方面Oracle,MySQL和JavaDB就要比Access和SQL Server有明顯優勢,它們不僅可以支持Windows平台而且也支持Solaris和Linux平台。而且數據庫平台的可移植很大程度也決定了應用系統的可移植性。

(3)數據庫平台要支持應用的多樣化(需求彈性)。數據庫平台應該不僅可以對應傳統的C/S,B/S模式,而且還可以擴展為三層的,甚至是多層的模式,或者支持嵌入式系統的應用。顯而易見,本身使用Java開發的Oracle和JavaDB在這些方面就具有得天獨厚的優勢。而且JavaDB還支持嵌入式應用,這樣以來數據庫的應用空間就更為廣闊了。

所以基於以上幾點的考慮,我們就可以得出初步的意向:

(1)對於一般的中小型的C/S和B/S架構,MySQL可以說是我們當前首選。它在跨平台和執行效率方面表現得相當的出色。

(2)對於那些對系統構架彈性要求比較高的系統,可以選擇Oracle平台,並結合EJB規范進行搭建系統構架無疑將是很好的選擇。

(3)選用JavaDB進行嵌入式平台開發似乎是不錯的主意。由於現在很多嵌入式設備中都嵌入了Java內核,JavaDB也就可以得以應用了,嵌入式設備與其他系統共享數據的接口就變得及其簡單。

(4)如果只在Windows平台進行開發的中小型系統,用Access或者SQL Server平台也不妨是一種簡單快捷的途徑。

六、結束語

通過前兩部分的說明,相信大家對JDBC的使用應該有相當部分的了解和收獲。即使作為初學者,通過簡化後的JDBC函數和固定的試用方式,就可以實現通過JSP頁面,Java Applet或者是Java Application等程序來訪問各種類型的數據庫平台了。即使是在本文中沒有提到的其他數據庫平台,例如:IBM的DB2,Solaris 10中綁定的PostgreSQL數據庫。

尤其的,對於中高級技術人員而言,也可以通過這幾種熱門的數據庫平台在不同操作系統下的使用案例得到一定的參考。

其中,特別是介紹了對嵌入式數據庫的使用,相信也一定開闊了大家的視野和應用范圍,畢竟嵌入式系統的應用在目前也是如火如荼。在後續的實際開發中筆者會將更多的心得體會和大家一起分享。

七、附錄:

1.主要FoolDB函數參考

//Get a conn to special database.
public static Connection openDB(final String url, final String user, final String passwd)
{
try
{
return (DriverManager.getConnection(url, user, passwd) );
}
catch (SQLException CONNECT_FAILURE)
{

}
}
//Get a statement object that can be used for query (read only)
public static Statement getQueryStat(final Connection conn)
{
try
{
return (conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_READ_ONLY) );
}
catch(SQLException CREATE_QUERY_STATEMENT)
{

}
}
//Get a statement object that can be used for update (can write)
public static Statement getExecStat(final Connection conn)
{
try
{
return (conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE) );
}
catch(SQLException CREATE_EXEC_STATEMENT)
{

}
}
//Execute SQL statement, and get the result set.
public static ResultSet openQuery(final Statement stat, final String sql)
{
try
{
return (stat.executeQuery(sql) );
}
catch(SQLException OPEN_QUERY)
{

}
}
//Get the rows cout of result set.
//The result set type should be ResultSet type is TYPE_SCROLL_SENSITIVE.
public static int getRowsCount(ResultSet rs)
{
try
{
int rowsCount = 0;
//Backup current row no.
int rowNo = rs.getRow();
//Locate last row
rs.last();
//Get the rows count
rowsCount = rs.getRow();
//Return back original row
if(rowNo < 1) //before first row
{
rs.beforeFirst();
}
else //
{
rs.absolute(rowNo);
}
return (rowsCount);
}
catch(SQLException GET_ROWS_COUNT_FAILURE)
{

}
}
//Get the columns count of resut set.
public static int getColsCount(final ResultSet rs)
{
try
{
ResultSetMetaData rsmd = rs.getMetaData();
return (rsmd.getColumnCount() );
}
catch(SQLException GET_COLS_COUNT_FAILURE)
{

}
}
//Get special column name.
//Note: The index of column base 1, but not 0.
public static String getColName(final ResultSet rs, final int colIndex)
{
try
{
ResultSetMetaData rsmd = rs.getMetaData();
return (rsmd.getColumnName(colIndex) );
}
catch(SQLException GET_COL_NAME_FAILURE)
{

}
}
//Move the cursor of result set to next row
public static boolean moveNext(ResultSet rs)
{
try
{
return (rs.next() );
}
catch(SQLException MOVE_NEXT_FAILURE)
{

}
}
//Get the retValue of cell by special row number and column number
//The result set type should be ResultSet type is TYPE_SCROLL_SENSITIVE.
//Note: The index of row and column all base 1, but no 0.
public static Object getValueAt(ResultSet rs, final int rowIndex, final int colIndex)
{
if( (rowIndex < 1) || (colIndex < 1) )
{
return (null);
}
try
{
//Backup current row no.
int rowNo = rs.getRow();
Object retValue = null;
//Locate to special row
rs.absolute(rowIndex);
//Get retValue
retValue = rs.getObject(colIndex);
//Return back origianl row
rs.absolute(rowNo);
return (retValue);
}
catch(SQLException GET_VALUE_FAILURE)
{

}
}
//Get the retValue of cell by special row number and field name
//The result set type should be ResultSet type is TYPE_SCROLL_SENSITIVE.
//Note: The index of row and column all base 1, but no 0.
public static Object getFieldByName(ResultSet rs, final int rowIndex, final String fieldName)
{
if( (rowIndex < 1) || (fieldName.equals("") == true) )
{
return (null);
}
try
{
//Backup current row no.
int rowNo = rs.getRow();
Object retValue = null;
//Locate to special row
rs.absolute(rowNo);
//Get retValue
retValue = rs.getObject(fieldName);
//Return back origianl row no.
rs.absolute(rowNo);
return (retValue);
}
catch(SQLException GET_FIELD_BY_NAME_FAILURE)
{

}
}
//Get the retValue of cell within current row by special field name
//The result set type should be ResultSet type is TYPE_SCROLL_SENSITIVE.
//Note: The index of row and column all base 1, but no 0.
public static Object getFieldByName(final ResultSet rs, final String fieldName)
{
if( (isBOF(rs) == true) || (isEOF(rs) == true) )
{
return (null);
}
try
{
return (rs.getObject(fieldName) );
}
catch(SQLException GET_FIELD_BY_NAME_FAILURE)
{

}
}
//Get the retValue of cell within current row by special column index
//The result set type should be ResultSet type is TYPE_SCROLL_SENSITIVE.
//Note: The index of row and column all base 1, but no 0.
public static Object getFieldByIndex(final ResultSet rs, final int columnIndex)
{
if( (columnIndex < 1) || (isBOF(rs) == true) || (isEOF(rs) == true) )
{
return (null);
}
try
{
return (rs.getObject(columnIndex) );
}
catch(SQLException GET_FIELD_BY_INDEX_FAILURE)
{

}
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved