在J2SE中,提供了一個稱之爲JDBC-ODBC橋(JDBC-ODBC Bridge[2])的API。通過ODBC,JDBC-ODBC橋驅動程序可以訪問所有支持ODBC的關係型數據庫。與JDBC API不同的是,這個驅動程序並不是由Java代碼而是由機器碼(native code)編寫,並且不是開放源代碼的[3]。
JDBC驅動程序共分四種類型:
[編輯]類型1:JDBC-ODBC橋
這種類型的驅動把所有JDBC的調用傳遞給ODBC,再讓後者調用數據庫本地驅動代碼(也就是數據庫廠商提供的數據庫操作二進制代碼庫,例如Oracle中的oci.dll)。
優點:
- 只要有對應的ODBC驅動(大部分數據庫廠商都會提供),幾乎可以訪問所有的數據庫。
缺點:
- 執行效率比較低,不適合大數據量訪問的應用;
- 由於需要客戶端預裝對應的ODBC驅動,不適合Internet/Intranet應用。
[編輯]類型2:本地API驅動
這種類型的驅動通過客戶端加載數據庫廠商提供的本地代碼庫(C/C++等)來訪問數據庫,而在驅動程序中則包含了Java代碼。
優點:
- 速度快於第一類驅動(但仍比不上第3、第4類驅動)。
缺點
- 由於需要客戶端預裝對應的數據庫廠商代碼庫,仍不適合Internet/Intranet應用。
[編輯]類型3:網絡協議驅動
這種類型的驅動給客戶端提供了一個網絡API,客戶端上的JDBC驅動程序使用套接字(Socket)來調用服務器上的中間件程序,後者在將其請求轉化爲所需的具體API調用。
優點:
- 不需要在客戶端加載數據庫廠商提供的代碼庫,單個驅動程序可以對多個數據庫進行訪問,可擴展性較好。
缺點:
- 在中間件層仍需對最終數據進行配置;
- 由於多出一箇中間件層,速度不如第四類驅動程序。
[編輯]類型4:本地協議驅動
這種類型的驅動使用Socket,直接在客戶端和數據庫間通信。
優點:
- 訪問速度最快;
- 這是最直接、最純粹的Java實現。
缺點:
- 因爲缺乏足夠的文檔和技術支持,幾乎只有數據庫廠商自己才能提供這種類型的JDBC驅動;
- 需要針對不同的數據庫使用不同的驅動程序。
[編輯]API概述
參看J2SE以及java.sql API
JDBC API主要位於JDK中的java.sql包中(之後擴展的內容位於javax.sql包中),主要包括(斜體代表接口,需驅動程序提供者來具體實現):
- DriverManager:負責加載各種不同驅動程序(Driver),並根據不同的請求,向調用者返回相應的數據庫連接(Connection)。
- Driver:驅動程序,會將自身加載到DriverManager中去,並處理相應的請求並返回相應的數據庫連接(Connection)。
- Connection:數據庫連接,負責與進行數據庫間通訊,SQL執行以及事務處理都是在某個特定Connection環境中進行的。可以產生用以執行SQL的Statement。
- Statement:用以執行SQL查詢和更新(針對靜態SQL語句和單次執行)。
- PreparedStatement:用以執行包含動態參數的SQL查詢和更新(在服務器端編譯,允許重複執行以提高效率)。
- CallableStatement:用以調用數據庫中的存儲過程。
- SQLException:代表在數據庫連接的創建和關閉和SQL語句的執行過程中發生了例外情況(即錯誤)。
[編輯]數據類型的映射
SQL類型 | Java類型 |
---|---|
CHAR | java.lang.String |
VARCHAR | java.lang.String |
LONGVARCHAR | java.lang.String |
NUMERIC | java.math.BigDecimal |
DECIMAL | java.math.BigDecimal |
BIT | boolean |
TINYINT | byte |
SMALLINT | short |
INTEGER | int |
BIGINT | long |
REAL | float |
FLOAT | double |
DOUBLE | double |
BINARY | byte[] |
VARBINARY | byte[] |
LONGVARBINARY | byte[] |
DATE | java.sql.Date |
TIME | java.sql.Time |
TIMESTAMP | java.sql.Timestamp |
BLOB | java.sql.Blob |
CLOB | java.sql.Clob |
Array | java.sql.Array |
REF | java.sql.Ref |
Struct | java.sql.Struct |
注:這種類型匹配不是強制性標準,特定的JDBC廠商可能會改變這種類型匹配。例如Oracle中的DATE類型是包含時分秒,而java.sql.Date僅僅支持年月日。
[編輯]例子
利用Class.forName()方法來加載JDBC驅動程序(Driver)至DriverManager:
Class.forName( "com.somejdbcvendor.TheirJdbcDriver" );
然後,從DriverManager中,通過JDBC URL,用戶名,密碼來獲取相應的數據庫連接(Connection):
Connection conn = DriverManager.getConnection( "jdbc:somejdbcvendor:other data needed by some jdbc vendor", // URL "myLogin", // 用戶名 "myPassword" ); // 密碼
try { Class.forName( "com.mysql.jdbc.Driver" ); Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/jmgr?useUnicode=true&characterEncoding=utf-8", // URL "root", // 用戶名 "123456" ); // 密碼 } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }
不同的JDBC驅動程序的URL是不同的,它永遠以“jdbc:”開始,但後面的內容依照驅動程序類型不同而各異。在獲取Connection之後,便可以創建Statement用以執行SQL語句。下面是一個插入(INSERT)的例子:
Statement stmt = conn.createStatement(); stmt.executeUpdate( "INSERT INTO MyTable( name ) VALUES ( 'my name' ) " );
查詢(SELECT)的結果存放於結果集(ResultSet)中,可以按照順序依次訪問:
Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery( "SELECT * FROM MyTable" ); while ( rs.next() ) { int numColumns = rs.getMetaData().getColumnCount(); for ( int i = 1 ; i <= numColumns ; i++ ) { // 與大部分Java API中下標的使用方法不同,字段的下標從1開始 // 當然,還有其他很多的方式(ResultSet.getXXX())獲取數據 System.out.println( "COLUMN " + i + " = " + rs.getObject(i) ); } } rs.close(); stmt.close();
但是,通常,Java程序員們更傾向於使用PreparedStatement。下面的例子使用上例中的conn對象:
PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement( "SELECT i.*, j.* FROM Omega i, Zappa j WHERE i = ? AND j = ?" ); // 使用問號作爲參數的標示 // 進行參數設置 // 與大部分Java API中下標的使用方法不同,字段的下標從1開始,1代表第一個問號 // 當然,還有其他很多針對不同類型的類似的PreparedStatement.setXXX()方法 ps.setString(1, "Poor Yorick"); ps.setInt(2, 8008); // 結果集 rs = ps.executeQuery(); while ( rs.next() ) { int numColumns = rs.getMetaData().getColumnCount(); for ( int i = 1 ; i <= numColumns ; i++ ) { // 與大部分Java API中下標的使用方法不同,字段的下標從1開始 // 當然,還有其他很多的方式(ResultSet.getXXX())獲取數據 System.out.println( "COLUMN " + i + " = " + rs.getObject(i) ); } } catch (SQLException e) { // 異常處理 } finally { // 使用finally進行資源釋放 try { rs.close(); ps.close(); } catch( SQLException e){} // 異常處理:忽略close()時的錯誤 }
http://zh.wikipedia.org/wiki/JDBC