Skip to content

Instantly share code, notes, and snippets.

@mwnorman
Created March 18, 2010 19:36
Show Gist options
  • Save mwnorman/336772 to your computer and use it in GitHub Desktop.
Save mwnorman/336772 to your computer and use it in GitHub Desktop.
A StoredProcedure in a PL/SQL package uses a PL/SQL record type for arguments:
CREATE OR REPLACE PACKAGE NEST_REC_PACKAGE AS
TYPE TEST_NEST_REC IS RECORD (
VALUE01 VARCHAR2(10),
VALUE02 NUMBER
);
TYPE TEST_REC IS RECORD (
COLUMN01 VARCHAR2(10),
COLUMN02 NUMBER,
NEST01 TEST_NEST_REC
);
PROCEDURE PROCEDURE_RECORD_CALL_NEST(
INPUT IN TEST_REC,
OUTPUT OUT TEST_REC
);
END NEST_REC_PACKAGE;
First, we need some model classes for the PL/SQL record types - very simple:
{for demo purposes, I put everything in the package 'test' but that can be changed easily ...}
package test;
import java.math.BigDecimal;
public class NestedRecord {
public String value01;
public BigDecimal value02;
public NestedRecord() {
}
}
...
public class Record {
public String column01;
public BigDecimal column02;
public NestedRecord nest01;
public Record() {
}
}
A simple test class shows mappings for each type as well as how to build a query to execute the stored procedure.
Some of the key points:
- complex types that are defined in a PL/SQL package (records, collections or any combination thereof)
require 'shadow' types that are defined in the 'global' JDBC namespace outside of any PL/SQL package.
Thus for the record "TEST_REC", we need the shadow type "NEST_REC_PACKAGE_TEST_REC". The
shadow type must have the same 'shape' - same number of fields, in the same order, with the same names.
(Note the simple transform from the PL/SQL world to the JDBC world: package name + "_" + record name).
- in the case of the stored procedure "PROCEDURE_RECORD_CALL_NEST", the record record "TEST_REC" is used
twice, once for the IN parameter "INPUT" and second for the OUT parameter "OUTPUT". However, EclipseLink
needs separate instances of the PLSQLrecord object
{other notes: all Oracle database artifacts - type names, package names, etc. should always be represented in
EclipseLink metadata in UPPERCASE}
...
//javase imports
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
//EclipseLink imports
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.mappings.DirectToFieldMapping;
import org.eclipse.persistence.mappings.structures.ObjectRelationalDataTypeDescriptor;
import org.eclipse.persistence.mappings.structures.StructureMapping;
import org.eclipse.persistence.platform.database.jdbc.JDBCTypes;
import org.eclipse.persistence.platform.database.oracle.Oracle11Platform;
import org.eclipse.persistence.platform.database.oracle.plsql.PLSQLStoredProcedureCall;
import org.eclipse.persistence.platform.database.oracle.plsql.PLSQLrecord;
import org.eclipse.persistence.queries.ValueReadQuery;
import org.eclipse.persistence.sessions.DatabaseLogin;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.DatasourceLogin;
import org.eclipse.persistence.sessions.Project;
public class NestedRecordTest {
static String username;
static String password;
static String url;
static final String DATABASE_USERNAME_KEY = "db.user";
static final String DATABASE_PASSWORD_KEY = "db.pwd";
static final String DATABASE_URL_KEY = "db.url";
static final String DEFAULT_DATABASE_USERNAME = "scott";
static final String DEFAULT_DATABASE_PASSWORD = "tiger";
static final String DEFAULT_DATABASE_DRIVER = "oracle.jdbc.OracleDriver";
static final String DEFAULT_DATABASE_URL = "jdbc:oracle:thin:@localhost:1521:ORCL";
static final String PACKAGE_NAME = "NEST_REC_PACKAGE";
static final String PROCEDURE_NAME = "PROCEDURE_RECORD_CALL_NEST";
static final String NEST_RECORD_TYPE_NAME = "TEST_NEST_REC";
static final String RECORD_TYPE_NAME = "TEST_REC";
static final String CREATE_PACKAGE =
"CREATE OR REPLACE PACKAGE " + PACKAGE_NAME + " AS\n" +
"TYPE " + NEST_RECORD_TYPE_NAME + " IS RECORD (\n" +
"VALUE01 VARCHAR2(10),\n" +
"VALUE02 NUMBER\n" +
");\n" +
"TYPE " + RECORD_TYPE_NAME + " IS RECORD (\n" +
"COLUMN01 VARCHAR2(10),\n" +
"COLUMN02 NUMBER,\n" +
"NEST01 " + NEST_RECORD_TYPE_NAME + " \n" +
");\n" +
"PROCEDURE " + PROCEDURE_NAME + "(\n" +
"INPUT IN " + RECORD_TYPE_NAME + ",\n" +
"OUTPUT OUT " + RECORD_TYPE_NAME + "\n" +
");\n" +
"END " + PACKAGE_NAME + ";";
static final String CREATE_BODY =
"CREATE OR REPLACE PACKAGE BODY " + PACKAGE_NAME + " AS\n" +
"PROCEDURE " + PROCEDURE_NAME + "(\n" +
"INPUT IN " + RECORD_TYPE_NAME + ",\n" +
"OUTPUT OUT " + RECORD_TYPE_NAME + ") AS\n" +
"BEGIN\n" +
"NULL;\n" +
"END " + PROCEDURE_NAME + ";\n" +
"END " + PACKAGE_NAME + ";";
static final String JDBC_SHADOW_TYPE1 =
"NEST_REC_PACKAGE_TEST_NEST_REC";
static final String JDBC_SHADOW_TYPE2 =
"NEST_REC_PACKAGE_TEST_REC";
static final String CREATE_JDBC_SHADOW_TYPE1 =
"CREATE OR REPLACE TYPE " + JDBC_SHADOW_TYPE1 + " AS OBJECT (\n" +
"VALUE01 VARCHAR2(10),\n" +
"VALUE02 NUMBER\n" +
")\n";
static final String CREATE_JDBC_SHADOW_TYPE2 =
"CREATE OR REPLACE TYPE " + JDBC_SHADOW_TYPE2 + " AS OBJECT (\n" +
"COLUMN01 VARCHAR2(10),\n" +
"COLUMN02 NUMBER,\n" +
"NEST01 " + JDBC_SHADOW_TYPE1 + " \n" +
")\n";
static final String DROP_PACKAGE =
"DROP PACKAGE NEST_REC_PACKAGE";
static final String DROP_JDBC_SHADOW_TYPE1 =
"DROP TYPE " + JDBC_SHADOW_TYPE1;
static final String DROP_JDBC_SHADOW_TYPE2 =
"DROP TYPE " + JDBC_SHADOW_TYPE2;
static DatabaseSession ds = null;
public static void main(String...args) {
username = System.getProperty(DATABASE_USERNAME_KEY, DEFAULT_DATABASE_USERNAME);
password = System.getProperty(DATABASE_PASSWORD_KEY, DEFAULT_DATABASE_PASSWORD);
url = System.getProperty(DATABASE_URL_KEY, DEFAULT_DATABASE_URL);
DatasourceLogin login = new DatabaseLogin();
login.setUserName(username);
login.setPassword(password);
((DatabaseLogin)login).setConnectionString(url);
((DatabaseLogin)login).setDriverClassName(DEFAULT_DATABASE_DRIVER);
login.setDatasourcePlatform(new Oracle11Platform());
((DatabaseLogin)login).bindAllParameters();
Project p = new Project(login);
ObjectRelationalDataTypeDescriptor recordDescriptor = new ObjectRelationalDataTypeDescriptor();
recordDescriptor.descriptorIsAggregate();
recordDescriptor.setJavaClass(test.Record.class);
recordDescriptor.setAlias("Record");
recordDescriptor.setStructureName(JDBC_SHADOW_TYPE2);
DirectToFieldMapping column01Mapping = new DirectToFieldMapping();
column01Mapping.setAttributeName("column01");
column01Mapping.setFieldName("COLUMN01");
recordDescriptor.addMapping(column01Mapping);
DirectToFieldMapping column02Mapping = new DirectToFieldMapping();
column02Mapping.setAttributeName("column02");
column02Mapping.setFieldName("COLUMN02");
recordDescriptor.addMapping(column02Mapping);
StructureMapping nest01Mapping = new StructureMapping();
nest01Mapping.setAttributeName("nest01");
nest01Mapping.setFieldName("NEST01");
nest01Mapping.setReferenceClass(test.NestedRecord.class);
recordDescriptor.addMapping(nest01Mapping);
p.addDescriptor(recordDescriptor);
ObjectRelationalDataTypeDescriptor nestedRecordDesc = new ObjectRelationalDataTypeDescriptor();
nestedRecordDesc.descriptorIsAggregate();
nestedRecordDesc.setJavaClass(test.NestedRecord.class);
nestedRecordDesc.setAlias("NestedRecord");
nestedRecordDesc.setStructureName(JDBC_SHADOW_TYPE1);
DirectToFieldMapping value01Mapping = new DirectToFieldMapping();
value01Mapping.setAttributeName("value01");
value01Mapping.setFieldName("VALUE01");
nestedRecordDesc.addMapping(value01Mapping);
DirectToFieldMapping value02Mapping = new DirectToFieldMapping();
value02Mapping.setAttributeName("value02");
value02Mapping.setFieldName("VALUE02");
nestedRecordDesc.addMapping(value02Mapping);
p.addDescriptor(nestedRecordDesc);
ds = p.createDatabaseSession();
ds.setLogLevel(SessionLog.FINE);
ds.login();
try {
ds.executeNonSelectingSQL(CREATE_PACKAGE);
ds.executeNonSelectingSQL(CREATE_BODY);
ds.executeNonSelectingSQL(CREATE_JDBC_SHADOW_TYPE1);
ds.executeNonSelectingSQL(CREATE_JDBC_SHADOW_TYPE2);
}
catch (Exception e) {
// ignore
}
testNestedRecordExecution();
try {
ds.executeNonSelectingSQL(DROP_PACKAGE);
ds.executeNonSelectingSQL(DROP_JDBC_SHADOW_TYPE2);
ds.executeNonSelectingSQL(DROP_JDBC_SHADOW_TYPE1);
}
catch (Exception e) {
// ignore
}
ds.logout();
}
public static void testNestedRecordExecution() {
PLSQLrecord recordNest = new PLSQLrecord();
recordNest.setTypeName(PACKAGE_NAME + "." + NEST_RECORD_TYPE_NAME);
recordNest.setCompatibleType(JDBC_SHADOW_TYPE1);
recordNest.setJavaType(NestedRecord.class);
recordNest.addField("VALUE01", JDBCTypes.VARCHAR_TYPE);
recordNest.addField("VALUE02", JDBCTypes.NUMERIC_TYPE);
// when a PLSQLrecord (or PLSQLcollection) databaseTypes is re-used across
// the arguments, need separate instances - and that includes nested databaseTypes
PLSQLrecord inRecord = new PLSQLrecord();
inRecord.setTypeName(PACKAGE_NAME + "." + RECORD_TYPE_NAME);
inRecord.setCompatibleType(JDBC_SHADOW_TYPE2);
inRecord.setJavaType(Record.class);
inRecord.addField("COLUMN01", JDBCTypes.VARCHAR_TYPE, 10);
inRecord.addField("COLUMN02", JDBCTypes.NUMERIC_TYPE);
inRecord.addField("NEST01", recordNest);
PLSQLrecord outRecord = new PLSQLrecord();
outRecord.setTypeName(PACKAGE_NAME + "." + RECORD_TYPE_NAME);
outRecord.setCompatibleType(JDBC_SHADOW_TYPE2);
outRecord.setJavaType(Record.class);
outRecord.addField("COLUMN01", JDBCTypes.VARCHAR_TYPE, 10);
outRecord.addField("COLUMN02", JDBCTypes.NUMERIC_TYPE);
outRecord.addField("NEST01", recordNest.clone());
PLSQLStoredProcedureCall call = new PLSQLStoredProcedureCall();
call.setProcedureName(PACKAGE_NAME + "." + PROCEDURE_NAME);
call.addNamedArgument("INPUT", inRecord);
call.addNamedOutputArgument("OUTPUT", outRecord);
ValueReadQuery query = new ValueReadQuery();
query.addArgument("INPUT", Record.class);
query.setCall(call);
query.bindAllParameters();
NestedRecord nRec = new NestedRecord();
nRec.value01 = "nestedtest";
nRec.value02 = new BigDecimal("123.456");
Record rec = new Record();
rec.column01 = "test";
rec.column02 = new BigDecimal("789.012");
rec.nest01 = nRec;
List<Object> args = new ArrayList<Object>();
args.add(rec);
ds.executeQuery(query, args);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment