oraclejdbcadvanced-queuing

Is it possible to dequeue from ANYDATA Synchronous Capture queue using JDBC?


What I'm trying to do is subscribe to all changes in Oracle table using Synchronous Capture, from Java process.

ANYDATA queue is created with

BEGIN
  DBMS_STREAMS_ADM.SET_UP_QUEUE(
    queue_table  => 'hcb_qtab_any',
    queue_name   => 'hcb_queue_any',
    queue_user   => 'gguser');
END;

And then I'm using code from Dequeue using Java for Oracle 11g queue as an example. I'm trying to do

message = queue.dequeue(deq_option, XMLType.getORADataFactory());

But all I get is oracle.AQ.AQOracleSQLException: Error creating descriptor: Invalid arguments. I've debugged it a bit, it turns out, for ANYDATA TypeDescriptor.getTypeDescriptor() will return OpaqueDescriptor which isn't considered as StructDescriptor.isValidObject().

I did another take using thin JDBC AQ:

AQDequeueOptions deqopt = new AQDequeueOptions();
deqopt.setConsumerName("subscriber1");
AQMessage msg = conn.dequeue("hcb_queue_any", deqopt, "SYS.ANYDATA");
OPAQUE opq = (OPAQUE)msg.getANYDATAPayload().accessDatum();

And I'm facing a new unique problem here. This OPAQUE has getDescriptor().getTypeName() == "XMLTYPE", so I would very much like to turn it to XML. But there's a problem: Only thin JDBC driver supports AQ, while only ACI driver supports turning OPAQUE to XML. I'm getting Only LOB or String Storage is supported in Thin XMLType error when I'm trying to do new XMLType(opq)

How do I get Synchronous Capture XML from AQ using JDBC?


Solution

  • You could write a stored procedure and dequeue the message in PL/SQL and convert it to XML text and return it as a CLOB. Then you can call the stored procedure from Java with JDBC. I have used a similar workaround before when the plain AQ API lacked a feature.

    Example:

    create or replace procedure dequeue_lcr(
        p_queue_name   varchar2,
        p_consumer     varchar2,
        p_wait_seconds number,
        p_lcr          out clob) as
      deq_lcr     anydata;
      deq_xml     xmltype;
      msgid       raw(16); 
      deqopt      dbms_aq.dequeue_options_t; 
      mprop       dbms_aq.message_properties_t;
      no_messages exception; 
      pragma exception_init (no_messages, -25228);
    begin
      deqopt.consumer_name := p_consumer;
      deqopt.wait := p_wait_seconds;
      deqopt.navigation := dbms_aq.first_message;
      deqopt.dequeue_mode  := dbms_aq.remove;
      begin
        dbms_aq.dequeue( 
          queue_name         =>  p_queue_name,
          dequeue_options    =>  deqopt,
          message_properties =>  mprop,
          payload            =>  deq_lcr,
          msgid              =>  msgid);
         deq_xml := dbms_streams.convert_lcr_to_xml(deq_lcr);
         p_lcr := deq_xml.getclobval();    
         commit;
      exception
        when no_messages then
          p_lcr := null;
      end;
    end;
    

    This works when I call it from PL/SQL with the proper queue and consumer:

    declare
      v_clob clob;
    begin
      dequeue_lcr('aqtest.hcb_queue_any', 'LOCAL_AGENT', 5, v_clob);
      if (v_clob is not null) then
        dbms_output.put_line('Data: ' || v_clob);
      else
        dbms_output.put_line('No messages');  
      end if;
    end;
    

    Just make the call from Java with a CallableStatement with a clob as output parameter and you should be good to go!