从Oracle PLSQL调用java时如何将参数传递给java

Posted

技术标签:

【中文标题】从Oracle PLSQL调用java时如何将参数传递给java【英文标题】:How to pass parameters to java when calling java from Oracle PLSQL 【发布时间】:2017-10-10 13:02:10 【问题描述】:

我正在尝试从 plsql 调用 java 函数以返回 csv 字符串中某个索引处的 csv 项。 csv 字符串还可以在引号内包含分隔符(基于this Stack Overflow question)。

代码

set serverout on size 100000

CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "XxpayCsv" AS
import java.io.ByteArrayInputStream;
public class XxpayCsv

    public static String csv(String line, Integer idx)
    
        String otherThanQuote = " [^\"] ";
        String quotedString = String.format(" \" %s* \" ", otherThanQuote);
        String regex = String.format("(?x) "+ // enable comments, ignore white spaces
                ",                         "+ // match a comma
                "(?=                       "+ // start positive look ahead
                "  (?:                     "+ //   start non-capturing group 1
                "    %s*                   "+ //     match 'otherThanQuote' zero or more times
                "    %s                    "+ //     match 'quotedString'
                "  )*                      "+ //   end group 1 and repeat it zero or more times
                "  %s*                     "+ //   match 'otherThanQuote'
                "  $                       "+ // match the end of the string
                ")                         ", // stop positive look ahead
                otherThanQuote, quotedString, otherThanQuote);

        String[] tokens = line.split(regex, -1);
        //for(String t : tokens) 
        //    System.out.println("> "+t);
        //
        return tokens[idx];
    
;
/

CREATE OR REPLACE
FUNCTION xxpay_csv_at(s varchar2, i number) RETURN VARCHAR2 AS
LANGUAGE JAVA NAME 'XxpayCsv.csv (s, i) return java.lang.String';
/

DECLARE
   my_string clob;   -- VARCHAR2(400 CHAR);
BEGIN
   my_string := xxpay_csv_at('a,"b,c",d', 1);
   dbms_output.put_line('The value of the string is: ' || my_string);
END;
/

给我错误

DECLARE
*
ERROR at line 1:
ORA-29531: no method csv in class XxpayCsv
ORA-06512: at "APPS.XXPAY_CSV_AT", line 1
ORA-06512: at line 4

因为我不确定如何将字符串和整数作为参数传递(我不是 Java 程序员)。我做错了什么?

【问题讨论】:

【参考方案1】:

您需要在函数规范中给出数据类型(不是参数名称)以及完整的 java 路径:

CREATE OR REPLACE FUNCTION xxpay_csv_at(s varchar2, i number) RETURN VARCHAR2 AS
LANGUAGE JAVA NAME 'XxpayCsv.csv ( java.lang.String, java.lang.Integer ) return java.lang.String';
/

(注意:java.lang.Integer 而不是 int 以匹配您在 Java 代码中的规范)

但您可能想要的只是在 Oracle 中实现它:

SQL Fiddle

Oracle 11g R2 架构设置

CREATE OR REPLACE FUNCTION xxpay_csv_at(
  s varchar2,
  i number
) RETURN VARCHAR2 DETERMINISTIC
IS
BEGIN
  RETURN REGEXP_SUBSTR(
    s,
    '(^|,)(([^,"]*".*?")*[^,"]*)',
    1,
    i,
    NULL,
    2
  );
END;
/

查询 1

WITH table_name ( csv ) AS (
  SELECT 'foo,bar,c;qual="baz,b""lurb",d;junk="quux,syzygy"' FROM DUAL
)
SELECT csv,
       xxpay_csv_at( csv, 1 ) AS value1,
       xxpay_csv_at( csv, 2 ) AS value2,
       xxpay_csv_at( csv, 3 ) AS value3,
       xxpay_csv_at( csv, 4 ) AS value4
FROM   table_name

Results

|                                               CSV | VALUE1 | VALUE2 |               VALUE3 |               VALUE4 |
|---------------------------------------------------|--------|--------|----------------------|----------------------|
| foo,bar,c;qual="baz,b""lurb",d;junk="quux,syzygy" |    foo |    bar | c;qual="baz,b""lurb" | d;junk="quux,syzygy" |

更新

有什么方法可以从 regexp_substr 中获取匹配值的位置,这样我不仅可以返回某个索引处的 csv 项目,还可以返回原始 csv 字符串中开始和结束的字符位置?

使用REGEXP_INSTR 代替REGEXP_SUBSTR 编写另一个函数。

或者使用下面的函数,分别返回CURRDPOS作为开始和结束。

(注意:SQL 中使用的函数只能返回单个值,因此您要么需要返回具有值、开始和结束属性的对象类型,要么需要为子字符串、开始和结束三个单独的函数。 )

还希望能够添加分隔符和quoted_by 作为参数并支持分隔符,如'、'(逗号后有空格)。

是的,但使用正则表达式并不容易。像这样的东西(部分测试但没有经历所有的边缘情况):

SQL Fiddle

Oracle 11g R2 架构设置

CREATE OR REPLACE FUNCTION xxpay_csv_at(
  s varchar2,
  i number,
  delim VARCHAR2 DEFAULT ','
) RETURN VARCHAR2 DETERMINISTIC
IS
  j    PLS_INTEGER := 1;
  curr PLS_INTEGER := 1;
  dpos PLS_INTEGER;
  qpos PLS_INTEGER;
BEGIN
  WHILE TRUE LOOP
    dpos := INSTR( s, delim, curr );
    qpos := INSTR( s, '"', curr ); -- Start quote
    WHILE qpos BETWEEN curr AND dpos LOOP
      qpos := INSTR( s, '"', qpos + 1 ); -- End quote
      IF qpos = 0 THEN
        RAISE_APPLICATION_ERROR( -20000, 'Invalid String - No matching end-quote' );
      END IF;
      dpos := INSTR( s, delim, qpos + 1 );
      qpos := INSTR( s, '"', qpos + 1 );
    END LOOP;

    IF dpos = 0 THEN
      IF i = j THEN
        RETURN SUBSTR( s, curr );
      ELSE
        RETURN NULL;
      END IF;
    ELSE
      IF i = j THEN
        RETURN SUBSTR( s, curr, dpos - curr );
      ELSE
        j := j + 1;
        curr := dpos + LENGTH( delim );
      END IF;
    END IF;
  END LOOP;
END;
/

查询 2

WITH table_name ( csv ) AS (
  SELECT ', foo,bar, c;qual="baz, b""lurb", d;junk="quux, syzygy", , ' FROM DUAL
)
SELECT csv,
       xxpay_csv_at( csv, 1, ', ' ) AS value1,
       xxpay_csv_at( csv, 2, ', ' ) AS value2,
       xxpay_csv_at( csv, 3, ', ' ) AS value3,
       xxpay_csv_at( csv, 4, ', ' ) AS value4,
       xxpay_csv_at( csv, 5, ', ' ) AS value5,
       xxpay_csv_at( csv, 6, ', ' ) AS value6
FROM   table_name

Results

|                                                         CSV | VALUE1 |  VALUE2 |                VALUE3 |                VALUE4 | VALUE5 | VALUE6 |
|-------------------------------------------------------------|--------|---------|-----------------------|-----------------------|--------|--------|
| , foo,bar, c;qual="baz, b""lurb", d;junk="quux, syzygy", ,  | (null) | foo,bar | c;qual="baz, b""lurb" | d;junk="quux, syzygy" | (null) | (null) |

【讨论】:

太棒了,我讨厌 Java。有没有办法从 regexp_substr 获取匹配值的位置,这样我不仅可以返回某个索引处的 csv 项目,还可以返回原始 csv 字符串中开始和结束的字符位置? 还希望能够添加分隔符和quoted_by 作为参数并支持分隔符,如'、'(逗号后有空格)。 @Superdooperhero 更新【参考方案2】:

原来答案是:

CREATE OR REPLACE
FUNCTION xxpay_csv_at(s varchar2, i number) RETURN VARCHAR2 AS
LANGUAGE JAVA NAME 'XxpayCsv.csv (java.lang.String, int) return java.lang.String';
/

【讨论】:

您可能应该接受@MT0 的回答,而不是单独发布它 @tbone 这是在我回答之前发布的。我最初打算正确使用规范来匹配 java 代码,然后开始进行编辑,试图解决 OP 的 java 代码的一些问题(它不处理 NULLs 或 ArrayIndexOutOfBoundsException),但给了在上面发布了一个没有这些问题的等效 Oracle 函数。 啊,好的,抱歉,只是想在应得的地方给分,看来你们俩几乎同时想通了

以上是关于从Oracle PLSQL调用java时如何将参数传递给java的主要内容,如果未能解决你的问题,请参考以下文章

从 plsql 调用 java 类文件时无法将 int 值分配给 A JAVA.OBJECT

从 Java 调用带有数组输出参数的 Oracle 存储过程

从 Spring JAVA 中的 oracle PLSQL 函数获取结果集时出错

PLSQL 工具怎样调用存储过程咧?

plsql调用oracle存储过程有输入参数和输出参数 sql语句怎么写如题 谢谢了

如何遍历 Oracle PLSQL 中的分隔列表