Upscaling your JDBC app using Oracle object type collection
May 1, 2007 8 Comments
Regarding oracle jdbc bulk array inserts, Greg Rahn wrote this week about the performance gains to be had by batching up calls to the database using the array interface.
As an addendum to his excellent points, please find attached a comparison with using an Oracle collection of an Oracle object type – forgive my Java. Using StructDescriptor, STRUCT, ArrayDescriptor and ARRAY structures is unsightly and unintuitive but they can deliver some further performance gains. If only we could wrap this approach up in some user-friendly layer then I reckon we could kick some of these iterative row-by-row ORM tools into touch.
First up, for the baseline, based On Greg’s example, this is what my batch size performance was like inserting 10000 rows into emp on my system:

And, using an Oracle collection of Oracle object types, uploading the 10000 rows in a single INSERT… TABLE … CAST statement it took 0.219 seconds – Java class here.
Which compared very favourably.
Inline scripts:
create type to_emp as object (EMPNO NUMBER(4) ,ENAME VARCHAR2(10) ,JOB VARCHAR2(9) ,MGR NUMBER(4) ,HIREDATE DATE ,SAL NUMBER(7,2) ,COMM NUMBER(7,2) ,DEPTNO NUMBER(2)); / create type tt_emp as table of to_emp; /
import java.sql.*;
import java.util.*;
import oracle.jdbc.*;
import oracle.jdbc.pool.OracleDataSource;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;
import oracle.sql.ArrayDescriptor;
import oracle.sql.ARRAY;
public class bulkInsert {
public static void main(String[] args) {
try {
OracleDataSource ods = new OracleDataSource();
ods.setURL("jdbc:oracle:oci8:@ora.noldb507");
ods.setUser("scott");
ods.setPassword("tiger");
OracleConnection conn = (OracleConnection) ods.getConnection();
conn.setAutoCommit(false);
short seqnum = 0;
String[] metric = new
String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
metric[OracleConnection.END_TO_END_ACTION_INDEX] = "insertEmp";
metric[OracleConnection.END_TO_END_MODULE_INDEX] = "bulkInsert";
metric[OracleConnection.END_TO_END_CLIENTID_INDEX] = "myClientId";
conn.setEndToEndMetrics(metric, seqnum);
DatabaseMetaData meta = conn.getMetaData();
System.out.println(
"JDBC driver version is " + meta.getDriverVersion());
Statement stmt = conn.createStatement();
stmt.execute("alter session set sql_trace=true");
stmt.execute("truncate table emp");
int numberOfEmployees = Integer.parseInt(args[0]);
STRUCT[] structEmployee = new STRUCT[numberOfEmployees];
oracle.sql.StructDescriptor descEmployee = oracle.sql.StructDescriptor.createDescriptor("SCOTT.TO_EMP",conn);
java.sql.Timestamp now = new java.sql.Timestamp( (new java.util.Date() ).getTime() );
for (int i = 0; i < numberOfEmployees; i++) {
Object[] empValues = {
(i), // EMPNO
("Name" + i), // ENAME
("Job"), // JOB
(i), // MGR
now , //now
(10000 + i), // SAL
(100 + i), // COMM
(10) // DEPTNO
};
structEmployee[i] = new oracle.sql.STRUCT(descEmployee, conn, empValues);
}
oracle.sql.ArrayDescriptor empArrayDescriptor = oracle.sql.ArrayDescriptor.createDescriptor("SCOTT.TT_EMP",conn);
ARRAY empArray = new ARRAY(empArrayDescriptor,conn,structEmployee);
OraclePreparedStatement psEmp = (OraclePreparedStatement) conn.prepareStatement(
"insert /* insEmpBulk */ into emp (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) select * from table (cast (? as tt_emp))");
psEmp.setObject(1,empArray);
long start1 = System.currentTimeMillis();
// Set the batch size for each statment
((OraclePreparedStatement) psEmp).execute();
conn.commit();
psEmp.close();
long elapsedTimeMillis1 = System.currentTimeMillis() - start1;
// Get elapsed time in seconds
float elapsedTimeSec1 = elapsedTimeMillis1 / 1000F;
System.out.println("elapsed seconds: " + elapsedTimeSec1);
conn.close();
} catch (Exception e) {
System.err.println("Got an exception! ");
System.err.println(e.toString());
}
}
}

Recent Comments