java - 如何从 Java 调用的管道函数捕获 PL/SQL 错误

标签 java oracle plsql ojdbc

在我们基于 Oracle(11g) 的应用程序中,我们大量使用管道函数。现在事实证明,从此类函数报告错误很困难,如下面的(简化)示例所示。 该过程的存在是为了从 Java 调用,并且它应该接收 PL/SQL 执行期间任何地方发生的错误。

ORACLE部分:

set serveroutput on

create table dummy (id NUMBER);

create or replace package mytest
as
        type t_rec is record (id integer);
        type t_tab is table of t_rec;
        type t_ref_cur IS REF CURSOR RETURN t_rec;

        function foo
        return t_tab pipelined;

        procedure bar( p_ref_cur out t_ref_cur);
end mytest;
/
show errors

create or replace package body mytest
as
        function foo
        return t_tab pipelined
        is
                v_cur SYS_REFCURSOR;
                v_sql varchar2(2000);
                v_rec t_rec;
        begin
                v_sql := 'select wrong_column from DUMMY';
                open v_cur for v_sql;
                loop
                        fetch v_cur into v_rec;
                        exit when v_cur%notfound;

                        pipe row (v_rec);
                end loop;
        exception
                when no_data_needed then
                        null;
                when others then
                        dbms_output.put_line(SQLCODE||' '||sqlerrm );
                        raise no_data_found;
        end foo;

        procedure bar( p_ref_cur out t_ref_cur)
        is
        begin
                open p_ref_cur for select * from table(foo);
        end bar;
end mytest;
/
show errors

-- call procedure bar() from pl/sql
set serveroutput on
declare v_ref_cur mytest.t_ref_cur;
        v_rec mytest.t_rec;
begin
        mytest.bar(v_ref_cur);
        loop
              fetch v_ref_cur into v_rec;
              exit when v_ref_cur%notfound;
              dbms_output.put_line(v_rec.id);
      end loop;
end;
/
show errors

运行上述函数会导致显示异常:

ORA-00904:“WRONG_COLUMN”:无效的列名称

Java部分:

package test1;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import oracle.jdbc.OracleTypes;

public class start {

    public static void main(String[] args) 
    {
        try 
        {
            Connection con = DriverManager.getConnection("jdbc:oracle:thin:@...", "…", "…");
            CallableStatement stmt = con.prepareCall("BEGIN mytest.bar(?); END;");
            stmt.registerOutParameter(1, OracleTypes.CURSOR);
            stmt.executeQuery();
            ResultSet rs = (ResultSet)stmt.getObject(1);
            while (rs.next())
            {
                System.out.println(rs.getInt(1));
            }
            stmt.close();
            con.close();
        } catch (Exception e) 
        {
            e.printStackTrace();
        }
    }
}

异常不会被捕获。结果集为空。

这是预期的行为吗?我们做错了什么吗?是否存在解决方法?

最佳答案

问题是您正在引发 no_data_found,它不会被 bar 中的光标视为异常 - 它对于 select 有效> (实际上是您打开的游标)不返回任何行。当您运行匿名 block 时,您只会看到 dbms_output 调用,而不是引发的异常 - 该 block 成功完成,如果您设置 serveroutput off,您将看不到任何内容。 (编辑:this old AskTom thread talks about this behaviour too)。

目前尚不清楚您为何在捕获异常后尝试更改异常类型。我认为这样做的唯一原因是获得您所拥有的行为,其中任何错误都会给您一个空结果集,而不是您必须处理的异常。如果这不是您想要发生的事情,那么您不应该这样做,这是显而易见的。如果是,那么您需要从 Java 端查找 dbms_output 消息。

但我不明白为什么你会想要这样做而不是重新引发原始异常:

            when others then
                    dbms_output.put_line(SQLCODE||' '||sqlerrm );
                    raise;

...或者由于dbms_output并不总是对客户端可见,只是根本不捕获其他。当您希望(Java)客户端看到异常时,捕获并消除异常似乎有点毫无意义且违反直觉。

Java 端的异常将类似于(来自 e.getMessage()):

ORA-00904: "WRONG_COLUMN": invalid identifier
ORA-06512: at "SCHEMA.MYTEST", line 23

也许您想隐藏 PL/SQL 堆栈跟踪并仅显示遇到的第一个错误的一行,但保留该信息似乎更有用,以便您可以跟踪错误。

关于java - 如何从 Java 调用的管道函数捕获 PL/SQL 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18461608/

相关文章:

java - 将字节显示为无符号

java - 我正在尝试获取一个值,同时使用来自 3 个数组的索引?

java - 打印二叉树中所有大于或等于传入方法的值的方法

sql oracle 当前月份

sql - 在子查询 : ORA-00934: group function is not allowed here 中出现此错误

plsql - 两个值之间的条件选择

sql - 基于子元素值使用 PLSQL 提取 XML

sql - 遇到ORA-00979 : not a GROUP BY expression when using CASE - IN statements in sql

java - 当可以使用 SecureRandom 类时,为什么还要使用 Java 的 Random 类?

SQL - 当第 1 行满足 Case 条件时更改第 3 行的值