无法通过 JMS 客户端将大型文本消息排队到 Oracle AQ - ORA-00942:table 或视图不存在

Can't enqueue large text messages onto Oracle AQ via JMS client - ORA-00942: table or view does not exist

我正在通过 JMS 客户端将 JSON 消息排队到 JVM 上的 Oracle AQ 上。这对于小文本消息工作正常,但对于较大的消息则失败。我认为这与 Oracle 使用 VARCHAR 处理较小的消息并切换到 CLOB 处理大于 4000 个字符的消息有关。

AQ数据库脚本为

BEGIN
    DBMS_AQADM.CREATE_QUEUE_TABLE (
        queue_table => 'MY.AQT_MY_INBOX',
        queue_payload_type => 'SYS.AQ$_JMS_TEXT_MESSAGE',
        comment => 'QueueTable for MY Inbox Messages',
        multiple_consumers => FALSE,
        sort_list => 'priority,enq_time'
    );

    DBMS_AQADM.CREATE_QUEUE (
        queue_name => 'MY.AQ_MY_INBOX',
        comment => 'Queue for MY Inbox Messages',
        queue_table => 'MY.AQT_MY_INBOX',
        queue_type => SYS.DBMS_AQADM.NORMAL_QUEUE,
        max_retries => 2880,
        retry_delay => 30
    );

    DBMS_AQADM.GRANT_QUEUE_PRIVILEGE(
        privilege => 'ENQUEUE',
        queue_name => 'MY.AQ_MY_INBOX',
        grantee => 'MY_USER'
    );

    DBMS_AQADM.START_QUEUE (
        queue_name => 'MY.AQ_MY_INBOX'
    );
END;
/

oracle 依赖项通过 Maven 配置为

<dependency>
    <groupId>com.oracle.jdbc</groupId>
    <artifactId>ojdbc8</artifactId>
    <version>18.3.0.0</version>
</dependency>
<dependency>
    <groupId>com.oracle</groupId>
    <artifactId>aqapi_g</artifactId>
    <version>11.2.0.4</version>
</dependency>

异常堆栈跟踪是

Caused by: oracle.jms.AQjmsException: ORA-00942: table or view does not exist
    at oracle.jms.AQjmsUtil.writeClob(AQjmsUtil.java:640)
    at oracle.jms.AQjmsTextMessage.writeLob(AQjmsTextMessage.java:294)
    at oracle.jms.AQjmsProducer.jdbcEnqueue(AQjmsProducer.java:1054)
    at oracle.jms.AQjmsProducer.send(AQjmsProducer.java:747)
    at oracle.jms.AQjmsProducer.send(AQjmsProducer.java:517)
    at org.springframework.jms.core.JmsTemplate.doSend(JmsTemplate.java:634)
    at org.springframework.jms.core.JmsTemplate.doSend(JmsTemplate.java:608)
    at org.springframework.jms.core.JmsTemplate.lambda$send(JmsTemplate.java:586)
    at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:504)
    ... 20 common frames omitted
Caused by: java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist
    at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:494)
    at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:446)
    at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1052)
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:537)
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:255)
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:610)
    at oracle.jdbc.driver.T4CCallableStatement.doOall8(T4CCallableStatement.java:249)
    at oracle.jdbc.driver.T4CCallableStatement.doOall8(T4CCallableStatement.java:82)
    at oracle.jdbc.driver.T4CCallableStatement.executeForRows(T4CCallableStatement.java:924)
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1136)
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3640)
    at oracle.jdbc.driver.T4CCallableStatement.executeInternal(T4CCallableStatement.java:1318)
    at oracle.jdbc.driver.OraclePreparedStatement.executeLargeUpdate(OraclePreparedStatement.java:3730)
    at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3710)
    at oracle.jdbc.driver.OracleCallableStatement.executeUpdate(OracleCallableStatement.java:4265)
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1061)
    at oracle.jms.AQjmsUtil.writeClob(AQjmsUtil.java:605)
    ... 28 common frames omitted
Caused by: oracle.jdbc.OracleDatabaseException: ORA-00942: table or view does not exist
    at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:498)
    ... 44 common frames omitted

我能够在 AQjmsUtil.writeClob 中放置一个断点,并且我可以看到针对以下

抛出了异常
clobStmt = (OracleCallableStatement)db_conn.prepareCall("UPDATE " + queueTable + " tab set tab.user_data.text_lob = ? where tab.msgid = ?");
clobStmt.setString(1, textData);
clobStmt.setBytes(2, msgid);
int count = clobStmt.executeUpdate();

CLOB 逻辑执行 UPDATE,而对于使用 VARCHAR 的较小字符串,我认为它只执行 INSERT。违规的 UPDATE 声明是

UPDATE MY.AQT_MY_INBOX tab set tab.user_data.text_lob = ? where tab.msgid = ?

已通过执行以下 GRANT 脚本解决此问题

GRANT SELECT, UPDATE ON MY.AQT_MY_INBOX TO MY_USER;

此解决方案的奇怪之处在于,我不需要为此 table 提供 INSERT 授权(只有 ENQUEUE 用于覆盖队列的授权)。我觉得这是 oracle 实现中的一个错误,INSERTUPDATE grant 都应该由 ENQUEUE grant 添加。那个或 JMS API 应该为 CLOB 做一个 INSERT 而不是 UPDATE (类似于 VARCHAR 的实现方式)