| 操作 | ResultSet | PreparedStatement |
| BLOB | getBlob(int/String) | setBlob(int, Blob) |
| CLOB | getClob(int/String) | setClob(int, Clob) |
資料庫間複製 BLOB (零記憶體佔用)
// 1. 讀取來源表 BLOB 引用
String sql = "SELECT blob_col FROM blob_table WHERE id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, 1);
ResultSet rs = ps.executeQuery();
Blob blob = null;
if (rs.next()) blob = rs.getBlob(1); // 僅獲取定位器,不載入實際資料
// 2. 寫入目標表
sql = "INSERT INTO blob_table_2 VALUES (?)";
ps = conn.prepareStatement(sql);
ps.setBlob(1, blob); // 直接傳遞定位器
ps.executeUpdate();
檔案 ↔ BLOB 流式讀寫 (JDBC 2.0 + 廠商擴充)
JDBC 2.0 標準未定義寫入 BLOB 流,需使用廠商擴充 (以 Oracle 為例):
// 檔案 → 資料庫
File file = new File("video.mp4");
Blob blob = ...; // 從 INSERT 空 BLOB 後 SELECT FOR UPDATE 獲取,或 createBlob()
// Oracle 擴充:獲取二進制輸出流
OutputStream out = ((oracle.sql.BLOB) blob).getBinaryOutputStream();
int bufferSize = ((oracle.sql.BLOB) blob).getBufferSize();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file), bufferSize);
byte[] buffer = new byte[bufferSize];
int count;
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
out.close(); in.close();
conn.commit();
// 資料庫 → 檔案
InputStream in = blob.getBinaryStream(); // 標準 JDBC 2.0
// 同樣緩衝讀寫至 FileOutputStream
關鍵點:流式處理避免大物件載入記憶體,getBufferSize() 優化 I/O 效能。
2. 結構化類型 (STRUCT) / SQLData 介面
資料庫端定義 (Oracle)
CREATE OR REPLACE TYPE People AS OBJECT (
NAME VARCHAR2(50),
AGE NUMBER
);
CREATE TABLE SAMPLES (
SA_ID NUMBER,
CURSON People,
SAMPLE BLOB
);
Java 端映射類實作 SQLData
public class People implements SQLData, Serializable {
private String name;
private int age;
private String sql_type; // 對應資料庫型別名稱
public String getSQLTypeName() { return sql_type; }
public void readSQL(SQLInput stream, String typeName) throws SQLException {
this.sql_type = typeName;
this.name = stream.readString();
this.age = stream.readInt();
}
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(name);
stream.writeInt(age);
}
// getter/setter...
}
型別映射註冊與操作
// 註冊型別映射
Map typeMap = conn.getTypeMap();
typeMap.put("PEOPLE", People.class); // 資料庫型別名稱大寫
// 寫入
String sql = "INSERT INTO SAMPLES (SA_ID, CURSON) VALUES (?, ?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, 1);
People p = new People();
p.setName("John");
p.setAge(30);
ps.setObject(2, p); // 自動呼叫 writeSQL
ps.executeUpdate();
// 讀取
sql = "SELECT CURSON FROM SAMPLES WHERE SA_ID = 1";
ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
People p = (People) rs.getObject(2); // 自動呼叫 readSQL
System.out.println(p.getName() + ", " + p.getAge());
}
3. DISTINCT 類型:內建型別別名
-- 資料庫定義
CREATE TYPE BIRTHDATE AS DATE;
-- Java 操作:直接使用對應內建型別方法
ps.setDate(1, birthDate); // PreparedStatement.setDate
Date d = rs.getDate(1); // ResultSet.getDate
// 因本質是 DATE,無需特殊處理
4. 陣列 (ARRAY) 與 REF 物件
API 對照
| 操作 | ResultSet | PreparedStatement |
| ARRAY | getArray(int/String) | setArray(int, Array) |
| REF | getRef(int/String) | setRef(int, Ref) |
ARRAY 特殊能力:轉為 ResultSet
Array arr = rs.getArray(1); // 獲取陣列物件
ResultSet arrRs = arr.getResultSet(); // 作為結果集遍歷
while (arrRs.next()) {
int index = arrRs.getInt(1); // 陣列索引
String value = arrRs.getString(2); // 陣列元素值
System.out.println("[" + index + "] = " + value);
}
// 也可指定範圍:arr.getResultSet(index, count)
REF 物件:指向結構化類型實例的引用
// 查詢包含 People 欄位的表
Ref ref = rs.getRef("CURSON"); // 獲取 REF
// REF 可傳遞給其他查詢,或透過 dereference 獲取實例 (廠商特定)
5. DataLink (JDBC 3.0):外部資料引用
// 寫入 URL 參考
URL docUrl = new URL("https://docs.example.com/spec.pdf");
ps.setURL(1, docUrl);
// 讀取
URL url = rs.getURL(1);
// 應用層決定如何解析 URL (HTTP、FTP、檔案系統等)
適用於:資料庫僅存儲參考,實際內容儲於外部內容管理系統、檔案伺服器、雲端儲存。
廠商相容性與最佳實踐
| 類型 | Oracle | PostgreSQL | MySQL | SQL Server | 建議 |
| BLOB/CLOB | 完整支援 + 擴充流 API | 完整支援 (BYTEA/TEXT) | 支援 (BLOB/TEXT) | 支援 (VARBINARY/VARCHAR(MAX)) | 優先使用標準 API,流操作用廠商擴充 |
| STRUCT | 完整 (Object Type) | 支援 (Composite Type) | 不支援 | 支援 (UDT) | 跨資料庫專案慎用,或封裝適配層 |
| ARRAY | 支援 (VARRAY/Nested Table) | 完整支援 | 不支援 | 支援 (Table-Valued Parameters) | PostgreSQL/Oracle 專案可大膽使用 |
| DataLink | 支援 | 不支援 | 不支援 | FileTable/FILESTREAM | 標準化程度低,建議應用層管理 URL |
總結
JDBC 進階資料類型將關係資料庫能力延伸至複雜物件、大物件、結構化資料、外部引用等領域。核心掌握點:
- BLOB/CLOB 流式處理:大物件零記憶體複製、檔案互轉
- STRUCT/SQLData:物件-關係映射基礎,理解
readSQL/writeSQL 協議
- ARRAY/REF:陣列操作與物件引用,
getResultSet() 是隱藏寶藏
- 廠商擴充:標準 API 覆蓋 80% 需求,關鍵 20% (流控制、效能) 需廠商 API
- 可移植性權衡:核心邏輯用標準 API,廠商特定功能隔離在 DAO 實作層
現代 ORM 框架 (Hibernate、MyBatis、JPA) 已封裝大部分進階類型處理,但理解底層 JDBC 機制對除錯、效能調優、跨框架整合仍具核心價值。
- 上一篇文章: Grails 整合 EJB 3 領域模型:動態 GORM 賦能靜態實體 Bean,企業級 RAD 新範式
- 下一篇文章: Java DB (Derby) 嵌入式/客戶端/網路模式 JDBC 應用完整實戰:從零構建最簡應用到多模式部署
Copyright ©
程式碼之家 All Rights Reserved