使用JDBC将PL / SQL过程与用户定义的记录一起调用为IN参数

我试图调用以下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) 

我正在查看表示JDBC不支持RECORDS等复合类型的Oracle JDBC文档 :

在此处输入图像描述

搜索互联网带我到这个链接

以下是我尝试将用户定义的记录传递给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:

 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.(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 for Oracle Database 11g Release 2 (11.2.0.4) JDBC Drivers标题下的第一个jar。

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

是的,允许使用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中,您可以将对记录的引用更改为新的对象类型,或者如果无法更改其余代码,则可以编写快速的小型转换器函数将对象类型转换为记录类型。

如果您可以执行匿名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; / 

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

我想补充一下Dmitry的答案 ,它建议您可以通过JDBC使用匿名PL / SQL块并手动和明确地RECORD类型。 如果您正在寻找单个存储过程的解决方案,那么手动编写该块就可以了。 但是,如果您正在寻找为所有具有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 

这将为您提供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; / 

在此博客文章中查看有关此技术的更多详细信息(从中查询) 。

没有办法传递记录。 因为它必须是要引用的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不能以这种方式具有依赖性。 而是指定确切的数据类型。