在 Java 分散式事務開發中,開發者常會發現一個有趣的現象:JDBC 規範中的 XAConnection 並不繼承 Connection 介面,而在 JMS 規範中,XAConnection 則明確繼承了 Connection。同樣地,XADataSource 與 DataSource 之間也沒有繼承關係。這個設計差異背後有什麼深意?
以 OC4J 的 data-sources.xml 為例,我們可以配置一個支援全域事務的受管資料源:
<managed-data-source name="MIFSystemLogXADataSource"
connection-pool-name="MIFSysLogXAConnectionFactory"
jndi-name="jdbc/mifXAStatusLog" tx-level='global'/>
<connection-pool name="MIFSysLogXAConnectionFactory">
<connection-factory
factory-class="oracle.jdbc.xa.client.OracleXADataSource"
user="gavin" password="gavin"
url="jdbc:oracle:thin:@luga:1521:lugadb"/>
</connection-pool>
但在 Java 程式碼中,我們依然使用標準的 JDBC API:
DataSource dataSource = (DataSource) context.lookup("jdbc/mifXAStatusLog");
Connection connection = dataSource.getConnection();
查詢得到的 DataSource 實作類別是 oracle.sql.oc4j.ManagedDataSource,它實作了 DataSource 介面,與 XADataSource 無關;取得的 Connection 也與 XAConnection 無關。然而,這個連線物件卻能表現出 XA 連線的行為。這說明容器返回的很可能是一個動態代理,它實作 Connection 介面,內部委派給真正的 XAConnection 完成分散式事務功能。
這樣做的核心目的是配置與程式碼解耦。透過修改設定檔(例如將 tx-level 從 'global' 改為 'local'),即可在 XA 與非 XA 模式之間切換,而完全不需要修改應用程式碼。如果 XAConnection 繼承 Connection,雖然多態上看似更直觀,但會強制應用程式依賴 XA 相關介面,降低了部署彈性。
JDBC 專家組選擇不讓 XADataSource 繼承 DataSource、XAConnection 繼承 Connection,主要基於以下考量:
這種設計體現了「配置勝過程式碼」的思想,也是 Java EE 容器管理資源、實現透明事務管理的基石。