必须用OracleXADataSource,不能复用普通OracleDataSource;因后者返回的Connection不实现XAResource接口,调用xares.start()会抛ClassCastException或静默失败,须显式使用oracle.jdbc.xa.client.OracleXADataSource并调用setURL()、setUser()、setPassword()。

必须用 OracleXADataSource,不能复用普通 OracleDataSource
Java 17 中配置 Oracle XA 事务的第一道坎,就是数据源类型选错。普通 OracleDataSource 返回的 Connection 不实现 XAResource 接口,调用 xares.start(xid, ...) 会直接抛 ClassCastException 或静默失败。必须显式使用 oracle.jdbc.xa.client.OracleXADataSource(注意包名,不是 oracle.ucp.jdbc.PoolXADataSource 或 Spring Boot 自动装配的非XA数据源)。
关键点:
-
setURL()、setUser()、必须逐个调用;不能依赖 JNDI lookup 后再设属性,否则连接池初始化时无法完成 XA handshake - URL 中必须含完整服务名(如
@host:port/service_name),不能用 SID(@host:port:SID)——Oracle XA 在 service_name 模式下才支持分布式事务注册 - 驱动版本建议 ≥ 21c(ojdbc8-21.10+),旧版(如 ojdbc6)对 Java 17 的模块化(
--add-opens)和 TLS 1.3 支持不全,容易在 prepare 阶段卡住
XAException: XAER_RMFAIL 的真实原因往往不是数据库没开XA
这个错误最常被误判为“Oracle 没启用分布式事务”,其实 90% 是客户端配置或权限问题。检查顺序应是:驱动 → 连接参数 → 用户权限 → 数据库实例配置。
逐项确认:
立即学习“Java免费学习笔记(深入)”;
- 执行
SELECT * FROM V$OPTION WHERE PARAMETER = 'Distributed Transaction Processing',结果必须为TRUE;若为FALSE,需在init.ora中设distributed_transactions > 0并重启实例 - Oracle 用户必须有
SELECT ANY DICTIONARY权限(用于查询DBA_PENDING_TRANSACTIONS),仅CREATE SESSION和对象权限不够,prepare 会因视图访问拒绝而失败 - 连接 URL 中不能带
oracle.net.disableOob=true或oracle.jdbc.thinLogon=false等调试参数,它们会干扰 XA handshake 流程
Xid 构造必须严格满足 Oracle 的字节约束
Oracle 对 Xid 的 formatId 和字节数极其敏感,传错直接报 XAER_INVAL,且无明确提示指向哪一项错。
正确写法只有这一种组合:
-
formatId必须为131072(即 0x20000),不是0、-1或其他厂商值 -
globalTransactionId和branchQualifier都是byte[],长度 ≤ 64 字节;推荐用UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)生成,避免中文、空格或不可见字符 - 同一全局事务的所有分支,
globalTransactionId必须完全一致;每个分支的branchQualifier必须不同(例如拼上"svc-order-001"或线程ID哈希)
同一 Oracle 实例配多个 XA 数据源会引发 enq: DX - contention
当应用为同一个 Oracle 数据库实例定义了多个 <datasource>(如 jdbc/OracleDS1、jdbc/OracleDS2),即使它们连的是同一套库,XA 协调器也会把它们视为独立资源管理器(RM),强制走完整 2PC 流程——prepare/commit 各执行 N 次,而非自动合并。
后果很实在:
- 每多一个数据源,就多一个
V$SESSION和V$TRANSACTION条目,高并发下迅速耗尽会话数 - 所有分支的
prepare请求排队争抢 DX enqueue,enq: DX - contention等待飙升,TPS 断崖下跌 - 解决办法只有两个:要么只用一个 XA 数据源(业务层通过不同 schema 或连接属性区分逻辑库),要么在 WildFly/JBoss 中显式配置
same-rm-override-value="true"强制识别为同 RM
XA 的真正复杂性不在代码行数,而在状态边界——prepare 成功后协调器宕机,数据库里就留下 pending transaction,既不能自动 commit 也不能 rollback,必须人工介入 DBA_PENDING_TRANSACTIONS 视图清理。这点在生产环境最容易被忽略。


















