在Oracle 11g中执行PL / SQL块并在Java客户端中处理游标

时间:2018-12-12 17:44:40

标签: java oracle

我们正在尝试实施要求我们解决的解决方案,

  1. 定期轮询控制表以查询未处理的交易

  2. 一旦我们从步骤1中提取了交易标识符,我们就必须查询详细信息。这一步是现有功能,只有在第一步中通过将join与控制表结合起来才能进行全面扫描。

在有数量的情况下,现有的解决方案开始减慢处理速度。因此,我们决定从状态表中提取未处理的事务,并进行PK查找以查询详细信息。

我知道这不是理想的解决方案,例如Adapters,CDC或在视图中表达完整的细节。由于产品合同的限制,我们受到限制,该合同禁止我们在源模式或源Oracle实例中的任何地方创建任何类型的对象,使我们无法轮询表,并且此解决方案必须可调用且接近实时(延迟5秒)

DDL:

 CREATE TABLE "CONTROL_TABLE"
(   
"REF_NO" VARCHAR2(16 CHAR), 
"BRANCH" VARCHAR2(3 CHAR),
"INIT_DATE" DATE, 
"STATUS" VARCHAR2(1 CHAR), 
 CONSTRAINT "CONST_CONTROL_TABLE" PRIMARY KEY ("REF_NO")
)

下面是我作为POC尝试过的,执行以下代码块,
1.按如下所示创建一个块以运行SELECT for UPDATE。在下面的块中,我选择使用“跳过锁定”进行更新,并更新游标范围中的记录。

 DECLARE
   CURSOR tCURSOR IS
     SELECT REF_NO FROM CONTROL_TABLE 
        WHERE STATUS = 'U' FOR UPDATE OF STATUS SKIP LOCKED;
 BEGIN
  FOR tCURSORREC IN tCURSOR LOOP
    UPDATE CONTROL_TABLE SET STATUS='W' WHERE STATUS='U';    
  END LOOP; 
  COMMIT;  
 END;  

以上代码块工作正常,我已经通过随机插入新记录并更新来自不同客户端会话的状态进行了测试。我的问题是我希望将游标返回给Java客户端以进行下游处理。参见之前的文章,其中仅SELECT查询将游标bind var返回到Java。任何有用的指针,将不胜感激。 Execute anonymous pl/sql block and get resultset in java

运行该块的Java代码段

    public static void main(String args[]) throws Exception
    {
       final Connection c = DriverManager.getConnection("jdbc:oracle:thin:<<service_name>>:8888:<<schema>>", "user", "passwd");

     String plsql = "declare\r\n" + 
            "  cursor tCursor is\r\n" + 
            "    select ref_no from CONTROL_TABLE \r\n" + 
            "        where status = 'U' for update of REF_NO,STATUS skip locked;\r\n" + 
            "begin\r\n" +        
            "  for tCursorRec in tCursor loop\r\n" + 
            "    update CONTROL_TABLE set status='W' where status ='U';    \r\n" + 
            "  end loop;\r\n" +             
            "  commit;  \r\n" + 
            "? := tCursor" +
            "end;";     

            CallableStatement cs = c.prepareCall(plsql);
            cs.registerOutParameter(1, OracleTypes.CURSOR);
            cs.execute();          

            ResultSet cursorResultSet = (ResultSet) cs.getObject(1);
            while (cursorResultSet.next ())
            {
               System.out.println (cursorResultSet.getString(1));
            } 
                cs.close();
                c.close();

   Exception:Exception in thread "main" java.sql.SQLException: ORA-06550: 
   line 10, column 8:
   PLS-00382: expression is of wrong type
   ORA-06550: line 10, column 1:

1 个答案:

答案 0 :(得分:0)

使用ref游标是有问题的,因为您已经用尽了现有的PL / SQL游标(无论如何,它与ref游标的类型不同,因此会出现错误),并且您无法像以前那样重新查询更新并提交了更改。实际上,无论如何都应该从Java端完成提交,但这是一个单独的问题。

您可以改用集合,例如使用a built-in varray type

    Connection c = ods.getConnection();

    String plsql = "declare\r\n" +
        "  cursor tCursor is\r\n" +
        "    select ref_no from CONTROL_TABLE\r\n" +
        "        where status = 'U' for update of STATUS skip locked;\r\n" +
        "  array SYS.ODCIVARCHAR2LIST := new SYS.ODCIVARCHAR2LIST();\r\n" +
        "begin\r\n" +
        "  for tCursorRec in tCursor loop\r\n" +
        "    update CONTROL_TABLE set status='W' where current of tCURSOR;\r\n" +
        "    array.extend();\r\n" +
        "    array(array.count) := tCURSORREC.REF_NO;\r\n" +
        "  end loop;\r\n" +
        "  commit;\r\n" +
        "  ? := array;\r\n" +
        "end;";

    CallableStatement cs = c.prepareCall(plsql);
    cs.registerOutParameter(1, java.sql.Types.ARRAY, "SYS.ODCIVARCHAR2LIST");
    cs.execute();

    String[] refNos = (String[]) cs.getArray(1).getArray();

    cs.close();
    c.close();

    for (String refNo : refNos)
    {
        // Whatever processing you want to do
        System.out.println(refNo);
    }

生成的PL / SQL块最终显示为:

declare
  cursor tCursor is
    select ref_no from CONTROL_TABLE
        where status = 'U' for update of STATUS skip locked;
  array SYS.ODCIVARCHAR2LIST := new SYS.ODCIVARCHAR2LIST();
begin
  for tCursorRec in tCursor loop
    update CONTROL_TABLE set status='W' where current of tCURSOR;
    array.extend();
    array(array.count) := tCURSORREC.REF_NO;
  end loop;
  commit;
  ? := array;
end;

在游标循环内,扩展了array集合(因此它的末尾有一个空元素),并将该游标行的ref_no添加到了该位置的数组中。 / p>

我还使用where current of将更新更改为一次仅适用于一行,该更新的目标是被for update锁定的行。在您的原始版本中,您对所有带有'U'的行进行了无限制的更新;不仅是您已锁定的对象,而且它们都在第一轮调音器中进行了更新,因此循环的后续迭代没有任何可更新的内容。

在Java端,绑定变量也是一个数组,可以将其强制转换为适当类型的本地数组。

您可能可以避免使用类似以下内容的显式游标:

begin
  update CONTROL_TABLE set status='W' where status = 'U'
  returning REF_NO
  bulk collect into ?;
  commit;
end;
/