JDBC 2.0 (JDK 1.2 / J2EE 1.2 引入) 將 API 拆分為核心包 (java.sql) 與標準擴展包 (javax.sql),標誌著 Java 數據訪問邁向企業級成熟期。本文深度解析四大核心新特性:DataSource、連接池、分布式事務 (XA)、RowSet,並揭示其背後的架構設計思想。
Class.forName("oracle.jdbc.driver.OracleDriver"))。// 管理員配置階段 (通常由容器/工具完成)
Context ctx = new InitialContext();
DataSource ds = new OracleDataSource(); // 廠商實現
ds.setURL("jdbc:oracle:thin:@host:1521:sid");
ds.setUser("scott");
ds.setPassword("tiger");
// 綁定到 JNDI
ctx.bind("jdbc/InventoryDB", ds);
// 應用代碼階段 (零硬編碼)
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("jdbc/InventoryDB");
Connection conn = ds.getConnection(); // 或 getConnection(user, pwd)
| 維度 | DriverManager | DataSource |
|---|---|---|
| 可移植性 | 低 (驅動/URL 硬編碼) | 高 (邏輯名稱 jdbc/xxx) |
| 維護性 | 改配置需改代碼 | 改 JNDI 綁定屬性即可,代碼不變 |
| 企業級特性 | 無 | 原生支持連接池、XA 事務 |
| 安全性 | 密碼明文在代碼 | 憑證由容器管理,代碼無敏感信息 |
原理:DataSource.getConnection() 返回的不一定是物理新連接,而是來自池的邏輯連接句柄。conn.close() 不銷毀物理連接,而是歸還池中。
// 應用代碼完全不變!
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("jdbc/EmployeeDB"); // 此 DataSource 內部配置了池
Connection con = ds.getConnection("user", "pwd"); // 從池獲取
try {
// 業務操作...
} finally {
// 關鍵:必須在 finally 中 close,歸還連接給池
if (con != null) con.close();
}
ConnectionPoolDataSource:池管理器調用,生成 PooledConnection (物理連接包裝)。PooledConnection.getConnection():返回邏輯 Connection 句柄給應用。ConnectionEventListener:池監聽句柄關閉事件,回收物理連接。開發者無需直接調用以上接口,僅需確保 finally { conn.close(); } 習慣。
單一業務需同時更新 Oracle 訂單庫、MySQL 庫存庫、MQ 消息隊列,要求全部成功或全部回滾。
XAResource 接口。XAConnection -> XAResource。// 1. 獲取 UserTransaction (JNDI 或 @Resource 注入)
UserTransaction utx = (UserTransaction) ctx.lookup("javax.transaction.UserTransaction");
try {
utx.begin(); // 開啟全局事務
// 2. 從不同 XA DataSource 獲取連接 (自動加入全局事務)
Connection conn1 = ds1.getConnection(); // Oracle
Connection conn2 = ds2.getConnection(); // MySQL
// 3. 業務操作 (不可手動 commit/rollback)
stmt1.executeUpdate("INSERT INTO orders ...");
stmt2.executeUpdate("UPDATE inventory SET qty = qty - 1 ...");
// 4. 提交全局事務 (TM 協調 2PC)
utx.commit();
} catch (Exception e) {
utx.rollback(); // 失敗回滾
}
conn.commit(), conn.rollback(), conn.setAutoCommit(true)。RowSet 繼承 ResultSet,是 JavaBeans 組件,支持:
RowSetListener 監聽游標移動、行變更、行集變更,GUI 綁定極佳。| 類型 | 連接模式 | 適用場景 |
|---|---|---|
CachedRowSet | 斷開 | 通用緩存、序列化傳輸、瘦客戶端 |
JdbcRowSet | 連接 | ResultSet 的 JavaBeans 包裝,支持滾動/更新 |
WebRowSet | 斷開 | XML 序列化 (含 Schema/數據),Web Service 傳輸 |
JoinRowSet | 斷開 | 內存中多 RowSet 類 SQL JOIN |
FilteredRowSet | 斷開 | 內存中行過濾 (Predicate) |
// 1. 創建並配置 (通常由容器注入或工廠創建)
CachedRowSet crs = RowSetProvider.newFactory().createCachedRowSet();
crs.setUrl("jdbc:mysql://localhost/db");
crs.setUsername("user");
crs.setPassword("pwd");
crs.setCommand("SELECT id, name, price FROM products");
// 2. 獲取數據 (內部自動打開/關閉連接)
crs.execute();
// 3. 斷開環境下操作 (可序列化傳遞給前端)
crs.beforeFirst();
while (crs.next()) {
System.out.println(crs.getString("name"));
// 修改數據
if (crs.getInt("id") == 100) {
crs.updateDouble("price", 99.9);
crs.updateRow();
}
}
// 4. 重新連接同步更新 (樂觀鎖)
crs.acceptChanges(); // 內部重新獲取連接執行 UPDATE ... WHERE id=100 AND price=old_price
JDBC 2.0 通過 DataSource 抽象層 統一了「獲取連接」的入口,將連接池、分布式事務、RowSet 等企業級能力封裝在廠商實現中,實現了應用代碼零修改即可獲得高級特性的設計目標。這正是現代連接池 (HikariCP, Druid)、Spring DataSource 抽象、JTA 實現 (Atomikos, Narayana) 的架構藍圖。