Spring Boot升级后使用Spring JMS DefaultMessageListenerContainer处理Oracle AQ ADT payload时遇JMS-137错误求助
嗨,刚好我之前帮朋友处理过类似的Spring Boot升级+Oracle AQ的问题,给你捋捋怎么解决这个JMS-137错误!
首先得明确:你原来用的XmlMessageListenerContainer是专门为Oracle AQ的XMLTYPE payload做了封装的,升级到Spring Boot 3.x后换成通用的DefaultMessageListenerContainer,就必须手动指定Payload Factory和对应的类型映射——这正是JMS-137错误的核心原因:容器不知道怎么解析你的队列里的XMLTYPE(Oracle把它归为ADT类型) payload。
你之前尝试设置typeMap但没生效,大概率是因为只在单个连接实例上改了配置,而Spring JMS的容器用的是连接池里的新连接,根本没用到你手动改的那个设置。下面给你具体的可落地配置方案:
针对XMLTYPE payload的正确配置步骤
1. 先确保依赖版本匹配
Spring Boot 3.3.2对应Java 17+,所以要用上兼容的Oracle JDBC驱动(比如ojdbc11),别用老版本的ojdbc,不然容易出兼容性问题。
2. 配置带Payload Factory的Oracle AQ连接工厂
你需要把OracleAQjmsXmlPayloadFactory(专门处理XMLTYPE的工厂)绑定到Spring JMS使用的连接工厂上,同时配置类型映射让Oracle知道把SYS.XMLTYPE转成哪个Java类型(比如org.w3c.dom.Document或者String)。
给你个Spring Boot的@Bean配置示例:
@Bean public DefaultMessageListenerContainer appointmentQueueListenerContainer(DataSource dataSource) throws SQLException { // 创建Oracle AQ专用的连接工厂 OracleAQConnectionFactory connectionFactory = new OracleAQConnectionFactory(dataSource); // 关键:给连接工厂设置XMLTYPE对应的Payload Factory OracleAQjmsXmlPayloadFactory xmlPayloadFactory = new OracleAQjmsXmlPayloadFactory(); connectionFactory.setPayloadFactory(xmlPayloadFactory); // 配置类型映射:把SYS.XMLTYPE映射到你要用到的Java类型 try (OracleConnection oracleConn = (OracleConnection) dataSource.getConnection()) { Map<String, Class<?>> typeMap = oracleConn.getTypeMap(); // 这里可以根据你的需求选:用Document或者String都可以 typeMap.put("SYS.XMLTYPE", org.w3c.dom.Document.class); oracleConn.setTypeMap(typeMap); } // 配置DefaultMessageListenerContainer DefaultMessageListenerContainer container = new DefaultMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.setDestinationName("XYZ.APPOINTMENT_QUEUE"); // 绑定你的消息监听器逻辑 container.setMessageListener((MessageListener) message -> { // 这里就可以直接获取XMLTYPE转成的Java对象了 org.w3c.dom.Document xmlDoc = (org.w3c.dom.Document) message.getBody(); // 接下来处理你的业务逻辑就行 }); // 可选:如果需要自定义XML和Java对象的转换,可以设置自己的MessageConverter // container.setMessageConverter(customXmlMessageConverter()); return container; }
3. 额外的排查点
- 如果你不想用
org.w3c.dom.Document,也可以把SYS.XMLTYPE映射到String,这样在监听器里直接拿字符串解析XML就行,更简单。 - 要是你的Java类是用JAXB注解绑定的XML结构,还可以自定义
MessageConverter,把XMLTYPE直接转成你的业务实体类。 - 一定要检查你的队列的payload类型确实是
SYS.XMLTYPE,如果是自定义ADT(不是XMLTYPE),那就要换成OracleAQjmsAdtPayloadFactory,同时把自定义ADT的全限定名映射到对应的Java类。
为什么你之前的配置没生效?
你之前只是手动获取了一个连接并设置typeMap,但Spring JMS的DefaultMessageListenerContainer会从数据源的连接池里获取新的连接,这些新连接根本没有继承你手动设置的typeMap和payload factory配置——必须把这些配置绑定到连接工厂上,才能让容器每次拿连接时都用正确的配置。
备注:内容来源于stack exchange,提问作者raVen

