Let's suppose that you have a web app where you get some records from the user interface and from your DAO, you are trying to pass the records as Oracle ARRAYS to database. Here is a step by step example.
Yes, it's a nightmare as you have to take care of some steps. But, once you understand what to do and what are the issues, it's pretty easy.
Use Case : Let us suppose that we have a java bean Employee and we are trying to send an array of employee records at a time to database.
/*Step 1 : Create a object type in the database*/
/*
CREATE OR REPLACE TYPE "EMP_TYPE" is object(
emp_id Varchar2(500),
emp_name varchar2(500));
*/
/*
Step 2 : Create a type EMP_TYPE_TABLE
CREATE OR REPLACE TYPE "EMP_TYPE_TAB";
*/
/*Step 3 : Create a Java bean which maps the attributes of the above object type in Step 2.*/
import java.io.Serializable;
import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;
public class Employee implements SQLData, Serializable{
static final long serialVersionUID = 4070409649129120458L;
public Employee(){}
// constructor that takes parameters
// getters and setters for emp_id, emp_name
// You have to implement readSQL() and writeSQL() methods, as shown below.
// This is where you are mapping the Employee table's columns to the Employee
//java bean.
public void readSQL(SQLInput stream, String typeName) throws SQLException {
this.emp_id = stream.readString();
this.emp_name = stream.readString();
}
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(emp_id);
stream.writeString(emp_name);
}
}
//EmpDAO class gets a connection to the database and passes the data.
public class EmpDAO{
java.sql.Connection conn;
java.sql.Connection dconn;
/*
Step 1 : Get database connection
This is a very important step. To pass your records of data as Arrays, you need to get a oracle.jdbc.driver.T4CConnection and then use ArrayDescriptor's. So, how do you get a T4CConnection ?
To get T4CConnection from java.sql.Connection, you need to cast like this :
t4cConn = ((DelegatingConnection)conn).getInnermostDelegate();
If you are working on tomcat, you have two options to get a DataSource in your context.xml.
a) By using apache commons-dbcp
OR
b) by directly using javax.sql.DataSource.
Let's see how to get the T4CConnection in both these cases.
*/
public void sendRecordsToDB(){
//Use Case (a) : if you configured apache commons-dbcp
BasicDataSource ds = (BasicDataSource)ctx.lookup(jndiName);
ds.setAccessToUnderlyingConnectionAllowed(true);
conn = ds.getConnection();
dconn = ((DelegatingConnection)conn).getInnermostDelegate();
//Use Case (b) : if you are directly using javax.sql.DataSource
BasicDataSource bds = new BasicDataSource();
bds.setDriverClassName("
bds.setUsername("
bds.setPassword("
bds.setUrl("jdbc:oracle:thin:@
bds.setAccessToUnderlyingConnectionAllowed(true);
conn = bds.getConnection();
dconn = ((DelegatingConnection)conn).getInnermostDelegate();
/*So, using either of the above approaches we got dconn, which is an instance of T4CConnection.*/
/* Now let's build an array list of employees.
*/
final List
14 comments:
Shouldn't
SELECT * FROM TABLE(p_comparray);
be
SELECT * FROM TABLE(p_emparray);
in the procedure insert_employees
Thanks for the correction. You are correct. It should be p_emparray.
Excellent instructions. Thank you! I got this working very quickly thanks to this article.
hi,
i am not able to import oracle.jdbc.driver.T4CConnection into my class even though it was there in the class path.
i am getting error like class is not visible.
i am using ojdbc14-10.2.0.2.jar file.
oracle.jdbc.driver.T4CConnection con = ((DelegatingConnection) getConnection())
.getInnermostDelegate();
help me.
If you are using a Java IDE like Eclipse, check your build path. Looks like the thin driver is not in your IDE's classpath, even though it is in your command line's classpath. This is just a guess, based on what the information you provided.
hi,
i forgot to mention that i am using Eclipse.
jar file is there in the build path.
Tomcat: server.xml entry
Resource name="jdbc/abc" auth="Container" type="javax.sql.DataSource" maxActive="50" maxIdle="20" maxWait="10000"
username="rconstruct_system" password="rconstruct_system" driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@localhost:1521:MFARDB" removeAbandoned="true" logAbandoned="true"
accessToUnderlyingConnectionAllowed=true
Code snippet in java:
Connection con = getConnection();
con = ((DelegatingConnection) con).getInnermostDelegate();
Exception:
java.lang.ClassCastException: org.apache.tomcat.dbcp.dbcp.PoolingDataSource$Pool
GuardConnectionWrapper cannot be cast to org.apache.commons.dbcp.DelegatingConne
ction
thanks in advance.
Are you using BasicDataSource ?
BasicDataSource ds = (BasicDataSource)ctx.lookup(jndiName);
ds.setAccessToUnderlyingConnectionAllowed(true);
conn = ds.getConnection();
dconn = ((DelegatingConnection)conn).getInnermostDelegate();
Can you provide getConnection() method's source code as well.
Thanks,
r-a-v-u
hi ravi,
thanks for your reply,
that is the problem with incompatable jars in common/lib and web-inf/lib.
so i copied naming-dbcp.jar to web-inf/lib then it is working.
now i am getting new exception like 'java.sql.SQLException: Inconsistent java and sql object types'.
code:
String[] regions = to.getRegionsIds();
CentralRegionBudgetDetRow[] array = new CentralRegionBudgetDetRow[regions.length];
for (int i = 0; i < regions.length; i++) {
CentralRegionBudgetDetRow eachRow = new CentralRegionBudgetDetRow();
eachRow.setForeignkey(to.getPrimaryKey());
eachRow.setRegionid(regions[i]);
eachRow.setAmount(to.getBudgetAmount()[i]);
array[i] = eachRow;
eachRow = null;
}
cs = (OracleCallableStatement) con.prepareCall(objQL.getQuery(
"query", "MM4"));
StructDescriptor descriptor = StructDescriptor.createDescriptor(
"TD_CENTRAL_REGIONAL_BUDGET", con);
oracle.sql.STRUCT array_to_pass = new oracle.sql.STRUCT(descriptor,
con, array);
CentralRegionBudgetDetRow.java:
import java.io.Serializable;
import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;
public class CentralRegionBudgetDetRow implements SQLData, Serializable {
private static final long serialVersionUID = 1L;
private String foreignkey;
private String regionid;
private Double amount;
private String sql_type = "TD_CENTRAL_REGIONAL_BUDGET";
public CentralRegionBudgetDetRow() {
}
// constructor that takes parameters
// getters and setters for emp_id, emp_name
// You have to implement readSQL() and writeSQL() methods, as shown below.
// This is where you are mapping the Employee table's columns to the
// Employee
// java bean.
public void readSQL(SQLInput stream, String typeName) throws SQLException {
this.foreignkey = stream.readString();
this.regionid = stream.readString();
this.amount = stream.readDouble();
}
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(getForeignKey());
stream.writeString(getRegionId());
stream.writeDouble(getAmount());
}
public String getForeignKey() {
return foreignkey;
}
public void setForeignkey(String foreignkey) {
this.foreignkey = foreignkey;
}
public String getRegionId() {
return regionid;
}
public void setRegionid(String regionid) {
this.regionid = regionid;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
@Override
public String getSQLTypeName() throws SQLException {
// TODO Auto-generated method stub
return sql_type;
}
}
Database:
CREATE OR REPLACE TYPE "TR_CENTRAL_REGIONAL_BUDGET" AS OBJECT
(
foreignKey VARCHAR2(20),
regionId VARCHAR2(20),
amount NUMBER(18,2)
)
CREATE OR REPLACE TYPE "TD_CENTRAL_REGIONAL_BUDGET" IS TABLE OF TR_CENTRAL_REGIONAL_BUDGET
thanks in advance.
Brahma.
Not sure why you are using a StructDescriptor instead of an ArrayDescriptor. Also, where are you creating the SQL ARRAY and setting it on the CS ?
Look at the example :
// Create a Array Descriptor
ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor( "EMP_TYPE_TAB", dconn );
// Create a SQL ARRAY for the descriptor
ARRAY array_to_pass = new ARRAY( descriptor, dconn, (Object[]) listOfEmployees.toArray());
// Create CS and set the array on it.
ps = (OracleCallableStatement)dconn.prepareCall("begin insert_employees(:1); end;");
ps.setARRAY( 1, array_to_pass );
ps.execute();
conn.commit();
You are giving me bits and pieces of information which is of no help for me. Next time, send me all the code (if you can) and the full stack trace of the exception.
Good Luck buddy,
r-a-v-i
Amiable dispatch and this fill someone in on helped me alot in my college assignement. Thanks you as your information.
Hi,
I am passing an array of records from java to Oracle exacly same way. I am facing a problem when one of the value writing back is null.
public void writeSQL( SQLOutput stream ) throws SQLException {
...
stream.writeDouble( numericValue );
...
}
Here some cases numericValue can be Null and in that instance, its throwing a NullPointer Exception.
Is there a way to Write an SQLNULL using SQLData?
Thanks
SHaj
Ravi,
Is it not possible to write array ibject without using ARRAY class of Oracle JDBC API. Use of this API tight cupled with Oralc eonly. Cant we operate on java.sql.Array interface ?
Thanks,
Sujeeth
I haven't tried, but I think you can do it.
very useful article. well it is working for me but I am not able to get connection from existing basicdatasource bean defined in dispatcher-servlet ,I get the connection but after using getinnermostdeligate my connection becomes null. So I have to explicitly define url,user name ,password etc in order to make connection please kindly help.I am using Spring.
Post a Comment