我正在尝试调用以下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 Documentation:
在互联网上搜索我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
on this page的标题JDBC Thin for All Platforms
下使用Oracle Database 11g Release 2 (11.2.0.4) JDBC Drivers
第一个jar。{/ p>
我想知道是否允许将用户定义的记录(作为IN参数)传递给PL / SQL过程?有没有人尝试过上面这个?
答案 0 :(得分: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中,您可以将对记录的引用更改为新的对象类型,或者您可以编写一个快速的小翻译功能,将对象类型转换为记录类型,如果您无法更改其余类型代码。
答案 1 :(得分:1)
如果您可以执行匿名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;
/
在这种情况下,您不需要创建新的数据库对象或更改现有数据库对象。您只需传递参数值以填充记录。
答案 2 :(得分:0)
没有办法传递记录。因为它必须是要引用的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不能以这种方式具有依赖性。 而是指定确切的数据类型。
答案 3 :(得分:0)
我想补充Dmitry's answer,这表明您可以通过JDBC使用匿名PL / SQL块并手动和明确地编写RECORD
类型。如果您正在寻找单个存储过程的解决方案,那么手动编写该块就可以了。但是,如果您正在寻找一个通用解决方案,它会生成具有IN
,OUT
或IN 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
这将为您提供RECORD
架构中包含的所有包中所有MY_SCHEMA
类型的正式定义,您可以从中生成看起来像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)