I am trying to call the following PL/SQL procedure that takes a user defined record type as an IN
parameter.
-- 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 :

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:
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.
I want to know if passing user defined records (as IN parameter) to a PL/SQL procedure is allowed ? Has anybody tried doing the above?
Yes, it's allowed to pass user-defined datatypes as IN parameters using JDBC. But it can't be a RECORD
. It must be a schema level object, e.g.
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.
If you can execute anonymous PL/SQL block (as I know, it is possible), you can execute following:
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.
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. 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
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:
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).
There's no way to pass records. Because it has to be SQL object to be referred, not a pure PL/SQL
object.
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. Because a TYPE cannot have a dependency that way. Instead specify the exact datatype.
来源:https://stackoverflow.com/questions/27113075/calling-pl-sql-procedure-with-user-defined-record-as-its-in-parameter-using-jdbc