Java數據庫連接
Java數據庫連接,(Java Database Connectivity,簡稱JDBC)是Java語言中用來規範客戶端程序如何來訪問數據庫的應用程序接口,提供了諸如查詢和更新數據庫中數據的方法。JDBC也是Sun Microsystems的商標[1]。JDBC是面向關係型數據庫的。
當前版本 | JDBC 4.3(2017年9月21日 | )
---|---|
操作系統 | 跨平台 |
類型 | Data access API |
網站 | Java SE 7 |
在J2SE中,提供了一個稱之為JDBC-ODBC橋(JDBC-ODBC Bridge[2])的API。通過ODBC,JDBC-ODBC橋驅動程序可以訪問所有支持ODBC的關係型數據庫。與JDBC API不同的是,這個驅動程序並不是由Java代碼而是由機器碼(machine 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概述
編輯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" ); // 密碼
不同的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()时的错误
}
如果數據庫操作失敗,JDBC將拋出一個SQLException。一般來說,此類異常很少能夠恢復,唯一能做的就是盡可能詳細的打印異常日記。推薦的做法是將SQLException翻譯成應用程序領域相關的異常(非強制處理異常)並最終回滾數據庫和通知用戶。
一個數據庫事務代碼如下:
boolean autoCommitDefault = conn.getAutoCommit();
try {
conn.setAutoCommit(false); //关闭自动提交,从而将之后执行的多个SQL语句视为同一个事务(自动提交会使每一个SQL语句执行后提交一次而重新启用新的事务)
/* 在此基于有事務控制的conn執行你的代碼 */
conn.commit(); //最终提交该次事务的修改
} catch (Throwable e) {
try {
conn.rollback(); //回退到上一个事务开始时的状态
} catch (Throwable ignore) {}
throw e;
} finally {
try { conn.setAutoCommit(autoCommitDefault); } catch (Throwable ignore) {}
}