【问题标题】:Getting output from dbms_output.get_lines using JDBC使用 JDBC 从 dbms_output.get_lines 获取输出
【发布时间】:2018-05-29 13:08:15
【问题描述】:

如何在使用 JDBC 的 Java 应用程序中获取 Oracle 的 dbms_output.get_lines 的输出而不在数据库中创建额外的对象

【问题讨论】:

  • 这可能会有所帮助community.oracle.com/thread/104787
  • @RavinderReddy 不,它没有。那个帖子很古老,那里没有提供解决方案。
  • 您可以使用this(需要新类型)或this(使用内置类型);然后通过 JDBC 调用其中一个?还是有this post from Tom Kyte?日志表可能更合适。完全取决于您需要实现的目标。
  • @AlexPoole 谢谢。我只是通过添加“不在数据库中创建其他对象”来澄清这个问题,因为这些权利很可能不会被授予。
  • 请注意,在最近的 Oracle 版本中,dbms_output.get_lines 被重载,可以返回一个dbmsoutput_linesarray,这应该比旧的dbms_output.chararray 对 JDBC 更友好。

标签: java sql oracle jdbc plsql


【解决方案1】:

I've also blogged about this issue here。这是一个说明如何做到这一点的 sn-p:

try (CallableStatement call = c.prepareCall(
    "declare "
  + "  num integer := 1000;" // Adapt this as needed
  + "begin "

  // You have to enable buffering any server output that you may want to fetch
  + "  dbms_output.enable();"

  // This might as well be a call to third-party stored procedures, etc., whose
  // output you want to capture
  + "  dbms_output.put_line('abc');"
  + "  dbms_output.put_line('hello');"
  + "  dbms_output.put_line('so cool');"

  // This is again your call here to capture the output up until now.
  // The below fetching the PL/SQL TABLE type into a SQL cursor works with Oracle 12c.
  // In an 11g version, you'd need an auxiliary SQL TABLE type
  + "  dbms_output.get_lines(?, num);"

  // Don't forget this or the buffer will overflow eventually
  + "  dbms_output.disable();"
  + "end;"
)) {
    call.registerOutParameter(1, Types.ARRAY, "DBMSOUTPUT_LINESARRAY");
    call.execute();

    Array array = null;
    try {
        array = call.getArray(1);
        System.out.println(Arrays.asList((Object[]) array.getArray()));
    }
    finally {
        if (array != null)
            array.free();
    }
}

上面将打印:

[abc, hello, so cool, null]

请注意,ENABLE / DISABLE 设置是连接范围的设置,因此您也可以通过多个 JDBC 语句执行此操作:

try (Connection c = DriverManager.getConnection(url, properties);
     Statement s = c.createStatement()) {

    try {
        s.executeUpdate("begin dbms_output.enable(); end;");
        s.executeUpdate("begin dbms_output.put_line('abc'); end;");
        s.executeUpdate("begin dbms_output.put_line('hello'); end;");
        s.executeUpdate("begin dbms_output.put_line('so cool'); end;");

        try (CallableStatement call = c.prepareCall(
            "declare "
          + "  num integer := 1000;"
          + "begin "
          + "  dbms_output.get_lines(?, num);"
          + "end;"
        )) {
            call.registerOutParameter(1, Types.ARRAY, "DBMSOUTPUT_LINESARRAY");
            call.execute();

            Array array = null;
            try {
                array = call.getArray(1);
                System.out.println(Arrays.asList((Object[]) array.getArray()));
            }
            finally {
                if (array != null)
                    array.free();
            }
        }
    }
    finally {
        s.executeUpdate("begin dbms_output.disable(); end;");
    }
}

另请注意,这将获取最多 1000 行的固定大小。如果需要更多行,您可能需要循环 PL/SQL 或轮询数据库。

关于调用DBMS_OUTPUT.GET_LINE 的说明

以前,有一个现已删除的答案建议单独调用 DBMS_OUTPUT.GET_LINE,而不是一次返回一行。我已经将该方法与DBMS_OUTPUT.GET_LINES 进行了基准比较,并且差异非常大 - 从 JDBC 调用时速度会慢 30 倍(即使从 PL/SQL 调用过程时并没有太大差异)。

因此,使用DBMS_OUTPUT.GET_LINES 的批量数据传输方法绝对值得。这是基准测试的链接:

https://blog.jooq.org/2017/12/18/the-cost-of-jdbc-server-roundtrips/

【讨论】:

  • 这是调用返回公共 SQL 集合的过程的标准方法吗?在与 Oracle 交互的 Java 应用程序中,这似乎是例行公事。
  • @WilliamRobertson:您不会相信 Oracle 忽略了该功能请求有多长时间!显然,Oracle 12cR2 的 JDBC 驱动程序可以传输 PL/SQL TABLE 类型。我还没有玩过它,所以我从答案中省略了它。同样,它们永远支持 JDBC 的 PL/SQL BOOLEAN 类型(并且您仍然不能在普通 SQL 语句中使用这些类型)。疯了!
  • 但是您不需要 PL/SQL 表类型。 JDBC 肯定可以处理模式级别的 SQL 集合类型吗?
  • 更新后的答案看起来很棒,并确认可以与 Oracle 10、11 和 12 一起使用。
  • 您从哪里获得 Types.ARRAY?我应该导入什么 ARRAY?
猜你喜欢
  • 2020-08-24
  • 2014-07-10
  • 2016-05-23
  • 1970-01-01
  • 2012-06-21
  • 1970-01-01
  • 2017-07-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多