Some time ago I faced the problem with WebLogic feature of wrapping complex Oracle data types. Let's say I have a stored procedure in the database:
create or replace procedure testArrayProc
(aID Number,
aArray out TNumberTable
)
and TNumberTable is a collection type with the following definition:
create or replace type TNUMBERTABLE as table of Number;
Please note, aArray is an out parameter. In my model I have the following code to call the procedure:
The code used to work fine since I used JDBC URL connection type for my Application module. In such case I worked directly with Oracle JDBC drivers and everything was perfect. But when I created JDBC datasource on the WebLogic server and changed the connection type, I got the following exception in the same piece of code:
weblogic.jdbc.wrapper.Array_oracle_sql_ARRAY cannot be cast to oracle.sql.ARRAY
Investigating the problem, I found out that WebLogic wraps complex data type parameters Arrays, Blobs, Clobs, NClobs, etc. with its own classes like weblogic.jdbc.wrapper.Array and for sure it can not be converted to oracle.sql.ARRAY. But my application as well as the code above are supposed to work not only on WebLogic. Other application servers (I've worked with) don't have any wrappers and allow the applications to work directly with drivers.
In WebLogic 10.3.4.0 (it comes as integrated server for JDeveloper 11.1.1.4.0) I found very useful checkbox Wrap Data Types. It is located at the Connection Pool tab of the datasources' settings in Advanced section. And it's possible to turn it off! According to the documentation this option allows features like debugging and connection usage to be done by the server, but turning it off can improve performance and in some cases significantly. Great! After turning the option off, my code started to work again.
Earlier versions of WebLogic don't have this wonderful checkbox, so you cannot switch the wrapping off (or may be you can, but I don't know how). But never mind, you always can modify the code to look like this:
That's all. Thanks.
create or replace procedure testArrayProc
(aID Number,
aArray out TNumberTable
)
and TNumberTable is a collection type with the following definition:
create or replace type TNUMBERTABLE as table of Number;
Please note, aArray is an out parameter. In my model I have the following code to call the procedure:
DBTransactionImpl dbti = (DBTransactionImpl)getDBTransaction(); CallableStatement statement = dbti.createCallableStatement(("BEGIN " + "testArrayProc(?,?);" + "END;"), 0); try { statement.setLong(1, new Long(1)); statement.registerOutParameter(2, Types.ARRAY, "TNUMBERTABLE"); statement.execute(); ARRAY ar = (ARRAY)statement.getArray(2); for (long i : ar.getLongArray()) { //do some work } } catch (SQLException sqlerr) { throw new JboException(sqlerr); } finally { try { if (statement != null) { statement.close(); } } catch (SQLException closeerr) { throw new JboException(closeerr); } }
The code used to work fine since I used JDBC URL connection type for my Application module. In such case I worked directly with Oracle JDBC drivers and everything was perfect. But when I created JDBC datasource on the WebLogic server and changed the connection type, I got the following exception in the same piece of code:
weblogic.jdbc.wrapper.Array_oracle_sql_ARRAY cannot be cast to oracle.sql.ARRAY
Investigating the problem, I found out that WebLogic wraps complex data type parameters Arrays, Blobs, Clobs, NClobs, etc. with its own classes like weblogic.jdbc.wrapper.Array and for sure it can not be converted to oracle.sql.ARRAY. But my application as well as the code above are supposed to work not only on WebLogic. Other application servers (I've worked with) don't have any wrappers and allow the applications to work directly with drivers.
In WebLogic 10.3.4.0 (it comes as integrated server for JDeveloper 11.1.1.4.0) I found very useful checkbox Wrap Data Types. It is located at the Connection Pool tab of the datasources' settings in Advanced section. And it's possible to turn it off! According to the documentation this option allows features like debugging and connection usage to be done by the server, but turning it off can improve performance and in some cases significantly. Great! After turning the option off, my code started to work again.
Earlier versions of WebLogic don't have this wonderful checkbox, so you cannot switch the wrapping off (or may be you can, but I don't know how). But never mind, you always can modify the code to look like this:
DBTransactionImpl dbti = (DBTransactionImpl)getDBTransaction(); CallableStatement statement = dbti.createCallableStatement(("BEGIN " + "testArrayProc(?,?);" + "END;"), 0); try { statement.setLong(1, new Long(1)); statement.registerOutParameter(2, Types.ARRAY, "TNUMBERTABLE"); statement.execute(); ARRAY ar = null; Object someArray = statement.getArray(2); if (someArray instanceof weblogic.jdbc.wrapper.Array) ar = (oracle.sql.ARRAY)(((weblogic.jdbc.wrapper.Array)someArray).unwrap(Class.forName("oracle.sql.ARRAY"))); else ar = (oracle.sql.ARRAY)someArray; for (long i : ar.getLongArray()) { //do some work } } catch (SQLException sqlerr) { throw new JboException(sqlerr); } catch (ClassNotFoundException e) { throw new JboException(e); } finally { try { if (statement != null) { statement.close(); } } catch (SQLException closeerr) { throw new JboException(closeerr); } }
That's all. Thanks.
Nice article. I am having the same problem ( weblogic.jdbc.wrapper.Array_oracle_sql_ARRAY cannot be cast to oracle.sql.ARRAY) in Weblogic 10.3.2 also. There there is no 'Wrap Data Types' checkbox. Any idea how to get away this issue ?
ReplyDeleteHi! I use Weblogic 10.3.2 also and workaround of the issue is in the following code:
ReplyDeleteif (someArray instanceof weblogic.jdbc.wrapper.Array)
ar = (oracle.sql.ARRAY)((weblogic.jdbc.wrapper.Array)someArray).unwrap(Class.forName("oracle.sql.ARRAY")));
else
ar = (oracle.sql.ARRAY)someArray;
hi Eugene Fedorenko
ReplyDeleteI have posted some related questions in OTN forum thread
"weblogic.jdbc.wrapper.* cannot be cast to oracle.sql.Datum"
at https://forums.oracle.com/forums/thread.jspa?threadID=2337937
regards
Jan Vervecken
Hi Eugene,
ReplyDeleteI am having similar issue: weblogic.jdbc.wrapper.Struct_oracle_sql_STRUCT cannot be cast to oracle.sql.STRUCT
I tried the way in your solution:
STRUCT record = null;
Object recordTmp = cs.getSTRUCT(1);
if(recordTmp instanceof Struct){
record =
(oracle.sql.STRUCT)(((weblogic.jdbc.wrapper.Struct)recordTmp).unwrap(Class.forName("oracle.sql.STRUCT")));
}else{
record = (oracle.sql.STRUCT)recordTmp;
}
But I still get error at this point: cs.getSTRUCT(1);
Btw, I am running the app in JDEV IDE (11.1.1.4.0), I haven't deployed the EAR file in the local WLS or created a datasource. Will this has to be deployed in WLS?
Thanks,
Shawn
It looks like this should be used:
ReplyDeleteObject recordTmp = cs.getObject(1);
instead of Object recordTmp = cs.getStruct(1);
Very useful. Thank you very much.
ReplyDeleteHi Eugene,
ReplyDeleteThanks a lot. Its indeed help.
Thanks
Kiran