【问题标题】:Calling PL/SQL procedure with user defined record as its IN parameter using JDBC使用 JDBC 以用户定义的记录作为其 IN 参数调用 PL/SQL 过程
【发布时间】:2015-01-22 15:38:10
【问题描述】:

我正在尝试调用以下 PL/SQL 过程,该过程将用户定义的记录类型作为 IN 参数。

   -- User Defined Record
   TYPE EMP_REC IS RECORD
   (
    id employees.employee_id%type,
    name employees.last_name%type,
    dept_name departments.department_name%type,
    job_title jobs.job_title%type,
    salary employees.salary%type,
    manager_id employees.employee_id%type,
    city locations.city%type,
    phone employees.phone_number%type
   );

这里是用户定义记录的定义:

  -- PURPOSE: Prints all employee information from the employee record 
  -- Example Of: PROCEDURE that takes in a parameter of RECORD type 
  PROCEDURE print_employee_all_details(empl1 emp_rec , emp_rec_string OUT VARCHAR2)

我在查看Oracle JDBC Documentation,它表明 JDBC 不支持 RECORDS 之类的复合类型:

搜索互联网将我带到this link

这是我尝试将用户定义的记录传递给 PL/SQL 过程的代码:

     public String printEmployeeAllDetails()
     {
         Connection conn = null;
         CallableStatement callStmt = null;
         String empDetails = null;

         try 
         {
            // Register the Jdbc Driver
            // Class.forName(JDBC_DRIVER_ORACLE);

            // Create a Database Connection
            conn = DriverManager.getConnection(DB_URL,DB_USER,DB_PWD);

            // Create a query string
            String callProc = "{call HR.EMP_PKG.print_employee_all_details( ? , ?) }";

            // Create a Callable Statement
            callStmt = conn.prepareCall(callProc);


            // Create descriptor for the Oracle Record type "EMP_REC" required
            StructDescriptor recDescriptor = StructDescriptor.createDescriptor("EMP_REC", conn);

            // Stage values for each field in the Oracle record in an array
            Object[] javaEmpRec = new Object[8];


            // Populate those values in the Array
            javaEmpRec[0] = 100;
            javaEmpRec[1] = "Joe Matthew";
            javaEmpRec[2] = "IT";
            javaEmpRec[3] = "Senior Consultant";
            javaEmpRec[4] = 20000;
            javaEmpRec[5] = 101;
            javaEmpRec[6] = "lombard";
            javaEmpRec[7] = "222333444";

            // Cast the java array into the oracle record type
            STRUCT oracleEmpRec = new STRUCT(recDescriptor , conn , javaEmpRec);


            // Bind Values to the IN parameter
            callStmt.setObject(1, oracleEmpRec);

            // Register OUT parameter
            callStmt.registerOutParameter(2, java.sql.Types.VARCHAR);

            // Execute the Callable Statement
            callStmt.execute();

            // Retrieve the value from the OUT parameter
            empDetails = callStmt.getString(2);
            System.out.println("Emp Details: " + empDetails);

         } 
         catch (SQLException se) 
         {
            System.out.println("Exception occured in the database");
            System.out.println("Exception message: "+ se.getMessage());
            System.out.println("Database error code: "+ se.getErrorCode());
            se.printStackTrace();
         }
         finally
         {
            // Clean up
            if(callStmt != null)
            {
                try
                {
                    callStmt.close();
                } 
                catch (SQLException se2) 
                {
                    se2.printStackTrace();
                }
            }

            if(conn != null)
            {
                try
                {
                    conn.close();
                } 
                catch (SQLException se2) 
                {
                    se2.printStackTrace();
                }
            }
         }

         return empDetails;
     }

在运行此代码时,我得到以下异常:

Exception occured in the database
Exception message: invalid name pattern: HR.EMP_REC
java.sql.SQLException: invalid name pattern: HR.EMP_REC
    at oracle.jdbc.oracore.OracleTypeADT.initMetadata(OracleTypeADT.java:554)
    at oracle.jdbc.oracore.OracleTypeADT.init(OracleTypeADT.java:471)
    at oracle.sql.StructDescriptor.initPickler(StructDescriptor.java:324)
    at oracle.sql.StructDescriptor.<init>(StructDescriptor.java:254)
    at oracle.sql.StructDescriptor.createDescriptor(StructDescriptor.java:135)
    at oracle.sql.StructDescriptor.createDescriptor(StructDescriptor.java:103)
Database error code: 17074
    at oracle.sql.StructDescriptor.createDescriptor(StructDescriptor.java:72)
    at com.rolta.HrManager.printEmployeeAllDetails(HrManager.java:1214)
    at com.rolta.HrManager.main(HrManager.java:1334)

我使用ojdbc6.jar 标题JDBC Thin for All Platforms 下的第一个罐子Oracle Database 11g Release 2 (11.2.0.4) JDBC Driverson this page

我想知道是否允许将用户定义的记录(作为 IN 参数)传递给 PL/SQL 过程?有没有人尝试过上述操作?

【问题讨论】:

  • 据我所知,您想找到任何间接方式(因为不支持直接方式)将记录传递给程序,是吗?
  • 是的,这就是尝试

标签: java oracle exception jdbc records


【解决方案1】:

没有办法传递记录。因为它必须是要引用的 SQL 对象,而不是纯 PL/SQL 对象。

创建一个对象就像,

   -- User Defined Record
   CREATE TYPE EMP_REC AS OBJECT
   (
    id  NUMBER,
    name VARCHAR2(100),
    dept_name ...,
    job_title ..,
    salary ..,
    manager_id ..,
    city ..,
    phone ...
   );

不过,这又是一种痛苦。您不能在此处使用 TYPE 属性。因为 TYPE 不能以这种方式具有依赖关系。 请指定确切的数据类型。

【讨论】:

    【解决方案2】:

    是的,允许使用 JDBC 将用户定义的数据类型作为 IN 参数传递。但它不能是RECORD。它必须是架构级别的对象,例如

    CREATE TYPE EMP_REC AS OBJECT
    (
     id employees.employee_id%type,
     name employees.last_name%type,
     dept_name departments.department_name%type,
     job_title jobs.job_title%type,
     salary employees.salary%type,
     manager_id employees.employee_id%type,
     city locations.city%type,
     phone employees.phone_number%type
    );
    

    在您的 PL/SQL 中,您可以将对记录的引用更改为新的对象类型,或者如果您无法更改其余的代码。

    【讨论】:

    【解决方案3】:

    我想对Dmitry's answer 进行补充,这表明您可以通过 JDBC 使用匿名 PL/SQL 块并手动明确地编写您的 RECORD 类型。如果您正在为该单个存储过程寻找解决方案,那么手动编写该块就可以了。但是,如果您正在寻找一个通用解决方案,它可以为具有INOUTIN OUTRECORD 参数的所有 过程生成代码,那么您可能应该编写一个代码生成器,根据以下查询生成存根

    SELECT
      x.TYPE_OWNER, x.TYPE_NAME, x.TYPE_SUBNAME, a.ARGUMENT_NAME ATTR_NAME,
      a.SEQUENCE ATTR_NO, a.TYPE_OWNER ATTR_TYPE_OWNER,
      nvl2(a.TYPE_SUBNAME, a.TYPE_NAME, NULL) package_name,
      COALESCE(a.TYPE_SUBNAME, a.TYPE_NAME, a.DATA_TYPE) ATTR_TYPE_NAME,
      a.DATA_LENGTH LENGTH, a.DATA_PRECISION PRECISION, a.DATA_SCALE SCALE
    FROM SYS.ALL_ARGUMENTS a
    JOIN (
      SELECT
        a.TYPE_OWNER, a.TYPE_NAME, a.TYPE_SUBNAME,
        MIN(a.OWNER) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) OWNER,
        MIN(a.PACKAGE_NAME) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) PACKAGE_NAME,
        MIN(a.SUBPROGRAM_ID) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) SUBPROGRAM_ID,
        MIN(a.SEQUENCE) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) SEQUENCE,
        MIN(next_sibling) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) next_sibling,
        MIN(a.DATA_LEVEL) KEEP (DENSE_RANK FIRST ORDER BY a.OWNER ASC, a.PACKAGE_NAME ASC, a.SUBPROGRAM_ID ASC, a.SEQUENCE ASC) DATA_LEVEL
      FROM (
        SELECT
          lead(a.SEQUENCE, 1, a.SEQUENCE) OVER (
            PARTITION BY a.OWNER, a.PACKAGE_NAME, a.SUBPROGRAM_ID, a.DATA_LEVEL
            ORDER BY a.SEQUENCE ASC
          ) next_sibling,
          a.TYPE_OWNER, a.TYPE_NAME, a.TYPE_SUBNAME, a.OWNER, a.PACKAGE_NAME, 
          a.SUBPROGRAM_ID, a.SEQUENCE, a.DATA_LEVEL, a.DATA_TYPE
        FROM SYS.ALL_ARGUMENTS a
        WHERE a.OWNER IN ('MY_SCHEMA')     -- Possibly replace schema here
        ) a
      WHERE (a.TYPE_OWNER IN ('MY_SCHEMA') -- Possibly replace schema here
      AND a.OWNER         IN ('MY_SCHEMA') -- Possibly replace schema here
      AND a.DATA_TYPE      = 'PL/SQL RECORD')
      GROUP BY a.TYPE_OWNER, a.TYPE_NAME, a.TYPE_SUBNAME
      ) x
    ON ((a.OWNER, a.PACKAGE_NAME, a.SUBPROGRAM_ID) = ((x.OWNER, x.PACKAGE_NAME, x.SUBPROGRAM_ID))
    AND a.SEQUENCE BETWEEN x.SEQUENCE AND next_sibling
    AND a.DATA_LEVEL = (x.DATA_LEVEL + 1))
    ORDER BY x.TYPE_OWNER ASC, x.TYPE_NAME ASC, x.TYPE_SUBNAME ASC, a.SEQUENCE ASC
    

    这将为您提供 MY_SCHEMA 架构中包含的所有包中所有 RECORD 类型的正式定义,您可以从中生成类似于 Dmitry 答案中的存根:

    declare
      rec EMP_REC;
    begin
      rec.id := :ID;
      rec.name:= :NAME;
      -- and so on, rest of fields of record...
      ...
      my_procedure(rec);
    end;
    /
    

    See more details about this technique in this blog post (from which the query was taken).

    【讨论】:

      【解决方案4】:

      如果你可以执行匿名 PL/SQL 块(据我所知,这是可能的),你可以执行以下操作:

      declare
        rec EMP_REC;
      begin
        rec.id := :ID;
        rec.name:= :NAME;
        -- and so on, rest of fields of record...
        ...
        my_procedure(rec);
      end;
      /
      

      在这种情况下,您不需要创建新的数据库对象或更改现有的。您只需要传递参数值来填充记录。

      【讨论】:

        猜你喜欢
        • 2015-01-19
        • 2022-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-25
        • 2016-05-30
        • 1970-01-01
        相关资源
        最近更新 更多