简体   繁体   English

使用JDBC以用户定义的记录作为其IN参数调用PL / SQL过程

[英]Calling PL/SQL procedure with user defined record as its IN parameter using JDBC

I am trying to call the following PL/SQL procedure that takes a user defined record type as an IN parameter. 我试图调用下面的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
   );

Here is the definition of the user defined record: 这是用户定义记录的定义:

  -- 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)

I was looking at the Oracle JDBC Documentation that indicated JDBC does not support composite types like RECORDS : 我正在查看Oracle JDBC文档 ,该文档表明JDBC不支持RECORDS之类的复合类型:

在此处输入图片说明

Searching the internet took me to this link 搜索互联网将我带到此链接

Here is the code that I tried inorder to pass a user defined record to a PL/SQL procedure: 这是我为了将用户定义的记录传递给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;
     }

On running this code I get the following exception: 运行此代码时,出现以下异常:

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)

I am using the ojdbc6.jar the very first jar under heading JDBC Thin for All Platforms for Oracle Database 11g Release 2 (11.2.0.4) JDBC Drivers on this page . 我现在用的是ojdbc6.jar的第一个罐子标题下的JDBC Thin for All PlatformsOracle Database 11g Release 2 (11.2.0.4) JDBC Drivers 此页面上

I want to know if passing user defined records (as IN parameter) to a PL/SQL procedure is allowed ? 我想知道是否允许将用户定义的记录(作为IN参数)传递给PL / SQL过程? Has anybody tried doing the above? 有没有人尝试做以上?

Yes, it's allowed to pass user-defined datatypes as IN parameters using JDBC. 是的,允许使用JDBC将用户定义的数据类型作为IN参数传递。 But it can't be a RECORD . 但这不能是RECORD It must be a schema level object, eg 它必须是架构级别的对象,例如

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
);

In your PL/SQL, you could change references to your record to your new object type, or you could write a quick little translator function to translate the object type to the record type if you can't change the rest of the code. 在您的PL / SQL中,您可以将对记录的引用更改为新的对象类型,或者,如果您无法更改其余代码,则可以编写一个快速的小型翻译器函数以将对象类型转换为记录类型。

If you can execute anonymous PL/SQL block (as I know, it is possible), you can execute following: 如果您可以执行匿名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;
/

In this case you don't need create new database objects or change existing. 在这种情况下,您不需要创建新的数据库对象或更改现有的对象。 You just need to pass values of parameters to fill a record. 您只需要传递参数值即可填充记录。

There's no way to pass records. 无法传递记录。 Because it has to be SQL object to be referred, not a pure PL/SQL object. 因为它必须是要引用的SQL对象,而不是纯PL/SQL对象。

Creating an object would be like, 创建一个对象就像

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

And it is again a pain though. 然而,这又是一次痛苦。 You cannot use TYPE attribute here. 您不能在此处使用TYPE属性。 Because a TYPE cannot have a dependency that way. 因为TYPE不能那样依赖。 Instead specify the exact datatype. 而是指定确切的数据类型。

I'd like to complement Dmitry's answer which suggests you could use an anonymous PL/SQL block through JDBC and compose your RECORD types manually and explicitly. 我想补充Dmitry的答案 ,它建议您可以通过JDBC使用匿名PL / SQL块,并手动和显式地RECORD类型。 If you're looking for a solution for that single stored procedure, then writing that block manually will do. 如果您正在寻找单个存储过程的解决方案,则可以手动编写该块。 But if you're looking for a general solution that generates code for all procedures that have IN , OUT , or IN OUT RECORD parameters, you should probably write a code generator that generates stubs based on the following query 但是,如果您正在寻找为所有具有INOUTIN OUT RECORD参数的过程生成代码的通用解决方案 ,则可能应该编写一个代码生成器,该代码生成器根据以下查询生成存根

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

This will provide you formal definitions of all the RECORD types in all the packages contained in the MY_SCHEMA schema, from which you can generate stubs that look like the one in Dmitry's answer: 这将为您提供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) . 在此博客文章(从中进行查询)中了解有关此技术的更多详细信息

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用JDBC将SYS_REFCURSOR作为IN参数调用PL / SQL过程 - Calling PL/SQL procedure with SYS_REFCURSOR as IN parameter using JDBC 从Spring JDBC使用PL / SQL记录类型调用Oracle过程 - Calling Oracle procedure with PL/SQL record Type from Spring JDBC 使用JDBC以自定义类型输入参数调用PL / SQL存储过程,所有字段均为空 - Using JDBC to call a PL/SQL stored procedure with custom type input parameter, all fields are null 使用带有布尔 IN 参数的 CallableStatement 在 Java 中调用 Oracle PL/SQL 过程会产生 PLS-00306 oracle 错误: - Calling an Oracle PL/SQL procedure in Java using a CallableStatement with a boolean IN parameter gives an PLS-00306 oracle error: 未为存储过程定义参数@ x…使用MS_SQL JDBC - Parameter @x was not defined for stored procedure… with MS_SQL JDBC 如何使用Spring JDBC存储过程从Java将用户定义的数据类型变量的值传递给MS SQL - How to pass the values of user defined datatype variable to MS SQL from java using spring jdbc store procedure 使用 JDBC 将用户定义的表类型传递给 SQL Server 存储过程 - Passing a user-defined table type to a SQL Server stored procedure using JDBC 从0jdbc6 JDBCthin驱动程序调用带有自定义对象的Oracle PL / SQL过程返回类型 - Calling an Oracle PL/SQL procedure with Custom Object return types from 0jdbc6 JDBCthin drivers 使用JDBC从Java调用Oracle SQL中的存储过程的示例 - Example for calling a stored procedure in Oracle SQL from Java using JDBC 调用存储过程时,如何通过名称而不是在JDBC / JPA中的位置设置参数? - How to set parameter by name instead of its position in JDBC/JPA when calling stored procedure?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM