Class.forName("oracle.jdbc.driver.OracleDriver");
// 觸發驅動類靜態塊,自動調用 DriverManager.registerDriver()
簡單直接,但 Microsoft JVM (IE Applet 環境) 無法正確初始化,已棄用場景慎用。
// jdbc.properties
jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@host:1521:sid
jdbc.user=scott
jdbc.password=tiger
// 代碼
Properties props = new Properties();
props.load(new FileInputStream("jdbc.properties"));
Class.forName(props.getProperty("jdbc.driver"));
優點:換庫不動代碼,支持多驅動並存。缺點:略顯繁瑣。
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
直接實例化驅動註冊,任何環境 (含 Applet) 均可用。靈活性稍弱,可結合方式 2 動態實例化。
建議:現代項目優先方式 2 + 配置中心;舊 Applet 維護用方式 3。
❌ pstmt.setBinaryStream(1, in, len) 對 BLOB 無效,拋 IO 異常而非 SQL 異常。Sun 文檔長期誤導。
CREATE TABLE tb_file (name VARCHAR2(20), detail LONG RAW);
PreparedStatement pstmt = con.prepareStatement("INSERT INTO tb_file VALUES (?, ?)");
pstmt.setBinaryStream(1, new FileInputStream(file), fileLength);
pstmt.executeUpdate();
// 讀取同理用 getBinaryStream()
缺點:單表僅 1 個 LONG RAW、無法用於 WHERE/ORDER BY、Oracle 12c+ 已不推薦。
// 1. 插入空 BLOB 佔位
con.setAutoCommit(false);
stmt.executeUpdate("INSERT INTO tb_file VALUES ('a.gif', EMPTY_BLOB())");
// 2. 查詢 FOR UPDATE 鎖定行,獲取 BLOB 定位器
ResultSet rs = stmt.executeQuery(
"SELECT detail FROM tb_file WHERE name='a.gif' FOR UPDATE"
);
if (rs.next()) {
oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob(1);
OutputStream out = blob.getBinaryOutputStream();
// 分塊寫入
byte[] buf = new byte[blob.getBufferSize()];
int len;
while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
in.close(); out.close(); con.commit();
}
// 3. 讀取
Blob blob = rs.getBlob("detail");
InputStream in = blob.getBinaryStream(); // 而非 getBinaryStream("detail")
關鍵點:FOR UPDATE、EMPTY_BLOB()、getBinaryOutputStream()、事務控制。
現代最佳實踐:JDBC 4.0+ / Oracle 10g+ 支持標準 setBlob()/getBlob() 流式操作,優先使用標準 API,僅在舊版本回退專有做法。
Oracle 數據庫原生不支持服務端游標滾動。JDBC 驅動實現 TYPE_SCROLL_INSENSITIVE 時,實際是將全量結果集拉入客戶端內存。
TYPE_FORWARD_ONLY(前向只讀,流式處理)OFFSET ... FETCH,舊版用 ROWNUM 子查詢原則:寧可笨拙的分頁查詢,不可隱形的內存災難。