Java資料庫連接

Java資料庫連接，（Java Database Connectivity，簡稱JDBC）是Java語言中用來規範客戶端程序如何來訪問資料庫的應用程式接口，提供了諸如查詢和更新資料庫中數據的方法。JDBC也是Sun Microsystems的商標。JDBC是面向關係型資料庫的。

在J2SE中，提供了一個稱之為JDBC-ODBC橋（JDBC-ODBC Bridge）的API。通過ODBC，JDBC-ODBC橋驅動程序可以訪問所有支持ODBC的關係型資料庫。與JDBC API不同的是，這個驅動程序並不是由Java代碼而是由機器碼（machine code）編寫，並且不是開放原始碼的。

## 驅動程序類型

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概述

參看Java SE以及 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：

```java
Class.forName( "com.somejdbcvendor.TheirJdbcDriver" );
```

然後，從DriverManager中，通過JDBC URL，使用者名稱，密碼來獲取相應的資料庫連接（Connection）：

```java
Connection conn = DriverManager.getConnection( 
      "jdbc:somejdbcvendor:other data needed by some jdbc vendor", // URL
      "myLogin", // 用户名
      "myPassword" ); // 密碼
```

不同的JDBC驅動程序的URL是不同的，它永遠以「jdbc：」開始，但後面的內容依照驅動程序類型不同而各異。在獲取Connection之後，便可以建立Statement用以執行SQL語句。下面是一個插入（INSERT）的例子：

```java
 Statement stmt = conn.createStatement();
 stmt.executeUpdate( "INSERT INTO MyTable( name ) VALUES ( 'my name' ) " );
```

查詢（SELECT）的結果存放於結果集（ResultSet）中，可以按照順序依次訪問：

```java
 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對象：

```java
 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翻譯成應用程序領域相關的異常（非強制處理異常）並最終回滾資料庫和通知使用者。

一個資料庫事務代碼如下：

```java
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) {}
}
```

## 外部連結

- Sun提供的JDBC驅動程序及其供應商列表
- Sun提供的教程
- Sun Java 2 1.4.2 API
- 一個訂單系統的原形
- DBAccessor: JDBC資料庫訪問封裝類
- 開源的JDBC驅動程序列表
- http://blog.csdn.net/cho3en1/article/details/52695808

## 基本資料

- 日語原文：JDBC
- 軟體版本週期：JDBC 4.3
- 軟體版本週期：2017年9月21日
- 作業系統：跨平台
- 類型：Data access API