必须手动构造xa连接+jndi查找+显式启动队列,否则onmessage()不触发;因oracle aq监听依赖队列启动、dequeue_any权限、al32utf8字符集及xa事务一致性,直连或连接池复用将导致静默失效。

不能靠 Spring Boot 自动配置启动监听,必须手动构造 XA 连接 + JNDI 查找 + 显式启动队列,否则 onMessage() 永远不触发。
为什么 setListener() 后消息进不来?
这不是 Java 代码写错,而是 Oracle AQ 的监听生命周期没被激活。关键点有三个:
- 队列必须已启动:
DBMS_AQADM.START_QUEUE('QUEUE_TEST'),否则receive()或 listener 都会无限阻塞 - Topic 消费必须先调用
connection.setClientID("myclient"),否则createDurableSubscriber()静默失败,连日志都不打 - 用户缺少
DEQUEUE_ANY权限,或未被显式授权具体队列:DBMS_AQADM.GRANT_QUEUE_PRIVILEGE('dequeue', 'QUEUE_TEST', 'aquser')
JNDI 查不到 AQjmsXAConnectionFactory 怎么办?
Oracle AQ 的 JNDI 不是 Spring Boot 里那种内存 Map 式 fake JNDI,它必须指向真实注册源——LDAP、OID 或本地 Wallet 文件。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 禁用所有“直连幻想”:
java.naming.provider.url不能是jdbc:oracle:thin:@...,否则抛javax.naming.NameNotFoundException - 开发阶段可临时用嵌入式工厂:
oracle.jms.AQjmsInitialContextFactory,但仅限单机测试,生产环境必须走 LDAP - 生产配置示例:
java.naming.provider.url=ldap://oid-host:389,且java.naming.security.principal必须是 OID 中注册的服务账号 - 若用 Oracle Wallet,
java.naming.provider.url应为file:/path/to/wallet,并确保ojdbc8.jar和aqapi.jar均在 classpath
AQjmsXAConnectionFactory 和 AQjmsConnectionFactory 怎么选?
核心区别在事务边界:是否要让 JMS 出队和数据库 DML 落在同一个 JTA 事务里。
- 用
AQjmsXAConnectionFactory→ 支持 XA 事务,session.commit()同时提交 SQL 和出队动作,避免“消息已消费但 DB 更新失败”的状态不一致 - 用
AQjmsConnectionFactory→ 非 XA,出队/入队走独立事务,适合纯异步解耦场景,但需自行处理幂等或补偿逻辑 - 两者都要求连接 URL 中带
tns_alias或完整 TNS 描述符,不能只写localhost:1521:ORCL(否则报ORA-12154)
连接池里监听失效却不报错?
因为 DCN/AQ 监听绑定的是物理连接句柄,而连接池(如 HikariCP)会回收连接——连接一回收,监听即失效,且完全静默。
- 不要把 AQ 监听逻辑塞进普通数据源连接池里
- 监听连接必须独占、长生命周期,建议用单独的
DataSource实例或手动管理物理连接 - 若必须复用连接池,需确保连接永不归还(如设
maxLifetime=0),但这违背连接池设计初衷,不推荐
最容易被忽略的是字符集:数据库端必须用 AL32UTF8,否则中文消息可能乱码或解析失败;还有,aqapi.jar 必须与 ojdbc8.jar 版本严格匹配,否则 AQjmsException 报错时连 getErrorNumber() 都拿不到真实 Oracle 错误码。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










