Send & Retrieve array list to oracle stored procedure - java

I'm trying to push "Arraylist" to oracle stored procedure and after making necessary modification, the object it is returned back.
I have an oracle stored procedure with an inout parameter which is "AS TABLE OF TYPE".
I'm able to make the call using Mybatis by implements TypeHandler and overrides its method public void setParameter(PreparedStatement ps, int i, Object parameter,
JdbcType jdbcType) throws SQLException
But I'm facing issue while retrieving the object.
For retrieving I Override the below method.
public Object getResult(CallableStatement cs, int columnIndex)
throws SQLException {
ARRAY array_to_pass = ((OracleCallableStatement) cs).getARRAY(1);
/* showing content */
Datum[] elements = array_to_pass.getOracleArray();
for (int i = 0; i < elements.length; i++) {
Object[] element = ((STRUCT) elements[i]).getAttributes();
String value = (String) element[0];
System.out.println("array(" + i + ").val=" + value);
}
}
I'm getting the below error
java.lang.IllegalAccessError: tried to access class oracle.jdbc.driver.OracleCallableStatement
Statement generating the error is ARRAY array_to_pass = ((OracleCallableStatement) cs).getARRAY(1);
Any thoughts/help on how to retrieve table of type object from oracle .

I'm able to implement it successfully using Spring, Mybatis & Oracle.
The reference code to implement is as below
1) Create Type in Database
CREATE OR REPLACE TYPE TypeName AS OBJECT(
Field1 varchar(6),
Field2 varchar(6)
)
2) Create Table of Type in Database
CREATE OR REPLACE TYPE TableTypeName AS TABLE OF TypeName;
3) Create transfer object in java with 2 fields
4) Generate necessary arraylist for transfer object
5) Create database call
CALL StoredProcedureName(
#{Parameter_in, javaType=Object, jdbcType=ARRAY, jdbcTypeName=TableTypeName , mode=INOUT, typeHandler=javaHandlername}
)
6) Create handler
public class javaHandlername implements TypeHandler {
// Set data to oracle object
#Override
public void setParameter(PreparedStatement ps, int i, Object parameter,
JdbcType jdbcType) throws SQLException
{
List<TO> TOs = (List<TO>) parameter;
StructDescriptor structDescriptor = StructDescriptor
.createDescriptor("TypeName", ps.getConnection());
STRUCT[] structs = null;
structs = new STRUCT[TOs.size()];
for (int index = 0; index < TOs.size(); index++) {
TO to = TO.get(index);
Object[] params = new Object[2];
params[0] = fSVDisptchSchedDely.getField1();
params[1] = fSVDisptchSchedDely.getField2();
STRUCT struct = new STRUCT(structDescriptor,
ps.getConnection(), params);
structs[index] = struct;
}
ArrayDescriptor desc = ArrayDescriptor.createDescriptor(
"TableTypeName", ps.getConnection());
ARRAY oracleArray = new ARRAY(desc, ps.getConnection(), structs);
ps.setArray(i, oracleArray);
}
// Set the result back to Java object
public Object getResult(CallableStatement cs, int columnIndex)
throws SQLException {
List<TO> TOs= new ArrayList<TO>();
Object[] structArray = (Object[]) cs.getArray(columnIndex).getArray();
oracle.sql.STRUCT mystruct = null;
for (Object structObj : structArray) {
TO to= new TO();
mystruct = (oracle.sql.STRUCT) structObj;
Object[] structAttr = mystruct.getAttributes();
to.setField1((String)structAttr[0]));
to.setField2((String)structAttr[1]));
TOs.add(to);
}
return TOs;
}
}
7) Make the database call
Enjoy... :)

Related

Oracle PLSQL table of Object has a parameter that is filled in Oracle 11g, but not filled in Oracle 12c

A kind of weird problem ...
I have this PL/SQL array :
create or replace MY_TYPE_ARRAY
AS TABLE OF MY_TYPE ;
create or replace TYPE MY_TYPE AS OBJECT
(
PARAM1 VARCHAR2(100),
PARAM2 VARCHAR2(35),
PARAM3 VARCHAR2(35),
PARAM4 NUMBER(20,2),
PARAM5 DATE ,
PARAM6 VARCHAR2(12),
PARAM7 VARCHAR2(10),
PARAM8 VARCHAR2(1),
PARAM9 VARCHAR2(20)
);
this array is filled in a java application through a MyBatis TypeHandler :
public void setParameter(PreparedStatement ps, int i, List<MyObject> parameter, JdbcType jdbcType)
throws SQLException {
Connection connection = ps.getConnection();
Struct[] structs = null;
if (parameter != null && parameter.size() > 0) {
structs = new Struct[parameter.size()];
for (int index = 0; index < parameter.size(); index++) {
MyObject myObject= parameter.get(index);
Object[] params = new Object[9];
params[0] = myObject.getParam1();
params[1] = myObject.getParam2();
params[2] = myObject.getParam3();
params[3] = myObject.getParam4();
params[4] = myObject.getParam5();
params[5] = myObject.getParam6();
params[6] = myObject.getParam7();
params[7] = myObject.getParam8();
params[8] = myObject.getParam9();
structs[index] = connection.createStruct("MY_TYPE", params);
}
);
} else {
parameter = new ArrayList<MyObject>();
structs = new Struct[0];
}
this.parameter = parameter;
Array oracleArray = ((OracleConnection) connection).createOracleArray("MY_TYPE_ARRAY", structs);
ps.setArray(i, oracleArray);
}
Now the issue is that, after the value is passed to the Stored procedure, the field PARAM3 is sometimes filled correctly and sometimes null :
Stored procedure signature :
CREATE PROCEDURE MY_PROC (IN_PARAM_1 IN VARCHAR2,
P_MY_TYPE_LIST IN MY_TYPE_ARRAY
)
I INTEGER := 0;
The portion of code causing error :
FOR MY_OBJ IN P_MY_TYPE_LIST.FIRST .. P_MY_TYPE_LIST.LAST LOOP
V_STTS := 'S';
I := I + 1;
BEGIN
V_TO_IBAN := P_MY_TYPE_LIST(I).PARAM3; //this value is sometimes read correctly, sometimes null though the value is passed correctly from the java
//V_TO_IBAN := P_MY_TYPE_LIST(MY_OBJ).PARAM3; //same behavior
.
.
.
.
The issue is in the last line of code as commented, the code runs fine on test environment (oracle 11g) but wrongly on staging (oracle 12c) . Is the issue related to the DB version?
update : jdbc version used : com.oracle:ojdbc8:12.0.2

Exception: Inconsistent java and sql object types

There are types:
CREATE OR REPLACE TYPE my_type IS OBJECT (
id VARCHAR2(20),
name VARCHAR2(40),
phone NUMBER
);
I'm trying to create objects using these types:
....
Object[] myArray = new Object[3];
Object[] struct = new Object[values.size()];
int arrayIndex = 0;
for (User user : values) {
myArray[0] = user.id().toString();
myArray[1] = user.getName().toString();
myArray[2] = user.getPhone();
struct[arrayIndex++] = con.createStruct("my_type",myArray);
}
On the line with the creation of the structure of con.createStruct, an error occurs:
java.sql.SQLException: Inconsistent java and sql object types, for classes implementing ORAData or OracleData, respective factory classes ORADataFactory and OracleDataFactory should be registered in typeMap.
What can be wrong?
myArray[0] = user.id().toString();
myArray[1] = user.getName().toString();
myArray[2] = user.getPhone();
This should be like:
-- First element of the array.
myArray[0].id = user.id().toString();
myArray[0].name = user.getName().toString();
myArray[0].phone = user.getPhone();
You could employ class StructDescriptor from Oracle JDBC extensions:
StructDescriptor structDescriptor = StructDescriptor.createDescriptor("my_type",conn);
Object[] attributes = {user.id().toString(),
user.getName().toString(),
user.getPhone()};
new STRUCT(structDescriptor, connection, attributes);
https://docs.oracle.com/cd/E18283_01/appdev.112/e13995/oracle/sql/StructDescriptor.html

Read an ARRAY from a STRUCT returned by a stored procedure

In the database are three Oracle custom types (simplified) as follows:
create or replace TYPE T_ENCLOSURE AS OBJECT(
ENCLOSURE_ID NUMBER(32,0),
ENCLOSURE_NAME VARCHAR2(255 BYTE),
ANIMALS T_ARRAY_ANIMALS,
MEMBER FUNCTION CHECK_IF_RED RETURN BOOLEAN
);
create or replace TYPE T_ARRAY_ANIMALS is TABLE OF T_ANIMAL;
create or replace TYPE T_ANIMAL AS OBJECT(
ANIMAL_ID NUMBER(32,0),
NUMBER_OF_HAIRS NUMBER(32,0)
);
and a function, that build the object tree
FUNCTION GET_ENCLOSURE ( f_enclosure_id zoo_schema.ENCLOSURE_TABLE.ENCLOSURE_ID%TYPE ) RETURN T_ENCLOSURE
AS
v_ENC T_ENCLOSURE;
v_idx pls_integer;
BEGIN
v_ENC := T_ENCLOSURE(
f_enclosure_id,
NULL,
T_ARRAY_ANIMALS(T_ANIMAL(NULL,NULL))
);
SELECT ENCLOSURE_NAME
INTO v_ENC.ENCLOSURE_NAME
FROM ENCLOSURE_TABLE WHERE ENCLOSURE_ID = f_ENCLOSURE_ID;
SELECT
CAST(MULTISET(
SELECT ANIMAL_ID, NUMBER_OF_HAIRS
FROM ANIMAL_TABLE
WHERE ENCLOSURE_ID = f_ENCLOSURE_ID
) AS T_ARRAY_ANIMALS
)
INTO v_ENC.ANIMALS
FROM dual;
RETURN v_ENC;
END;
Now I want to call the GET_ENCLOSURE function and work with its result T_ENCLOSURE object in my Java code.
// prepare the call
Connection connection = MyConnectionFactory.getConnection(SOME_CONNECTION_CONFIG);
CallableStatement stmt = connection.prepareCall("{? = call zoo_schema.zoo_utils.GET_ENCLOSURE( ? )}");
stmt.registerOutParameter(1, OracleTypes.STRUCT, "zoo_schema.T_ENCLOSURE");
stmt.setInt(2, 6); // fetch data for ENCLOSURE#6
// execute function
stmt.executeQuery();
// extract the result
Struct resultStruct = (Struct)stmt.getObject(1); // java.sql.Struct
I can access ID and NAME via
Integer id = ((BigInteger)resultStruct.getAttributes()[0]).intValue(); // works for me
String name = (String)resultStruct.getAttributes()[1]); // works for me
However, I cannot seem to get the list of animals
resultStruct.getAttributes()[2].getClass().getCanonicalName(); // oracle.sql.ARRAY
ARRAY arrayAnimals = (ARRAY)jdbcStruct.getAttributes()[2];
arrayAnimals.getArray(); // throws a java.sql.SQLException("Internal Error: Unable to resolve name")
I had a bit of trial and error here including
OracleConnection oracleConnection = connection.unwrap(OracleConnection.class);
STRUCT resultOracleStruct = (STRUCT) stmt.getObject(1); // oracle.sql.STRUCT
oracleConnection.createARRAY("zoo_schema.T_ARRAY_ANIMALS", resultOracleStruct.getAttributes()[2]) // throws an SQLException("Fail to convert to internal representation: oracle.sql.ARRAY#8de7cfc4")
But no luck either.
How can I get the list of animals into a List<TAnimal>?
Create objects that implement java.sql.SQLData. In this scenario, create TEnclosure and TAnimal classes, which both implement SQLData.
Just FYI, in newer Oracle JDBC versions, types such as oracle.sql.ARRAY are deprecated in favor of java.sql types. Although I'm not sure how to write an array (described bellow) using only java.sql API.
When you implement readSQL() you read fields in order. You obtain a java.sql.Array with sqlInput.readArray(). So TEnclosure.readSQL() would look something like this.
#Override
public void readSQL(SQLInput sqlInput, String s) throws SQLException {
id = sqlInput.readBigDecimal();
name = sqlInput.readString();
Array animals = sqlInput.readArray();
// what to do here...
}
Note: readInt() also exists, but Oracle JDBC seems to always provide BigDecimal for NUMBER
You will notice that some APIs such as java.sql.Array have methods that take a type map Map<String, Class<?>> This is a mapping of Oracle type names to their corresponding Java class implementing SQLData (ORAData may work too?).
If you just call Array.getArray(), you will get Struct objects unless the JDBC driver knows about your type mappings via Connection.setTypeMap(typeMap). However, setting typeMap on the connection didn't work for me, so I use getArray(typeMap)
Create your Map<String, Class<?>> typeMap somewhere and add entries for your types:
typeMap.put("T_ENCLOSURE", TEnclosure.class);
typeMap.put("T_ANIMAL", TAnimal.class);
Within a SQLData.readSQL() implementation, call sqlInput.readArray().getArray(typeMap), which returns Object[] where the Object entries or of type TAnimal.
Of course the code to convert to a List<TAnimal> gets tedious, so just use this utility function and adjust it for your needs as far as null vs empty list policy:
/**
* Constructs a list from the given SQL Array
* Note: this needs to be static because it's called from SQLData classes.
*
* #param <T> SQLData implementing class
* #param array Array containing objects of type T
* #param typeClass Class reference used to cast T type
* #return List<T> (empty if array=null)
* #throws SQLException
*/
public static <T> List<T> listFromArray(Array array, Class<T> typeClass) throws SQLException {
if (array == null) {
return Collections.emptyList();
}
// Java does not allow casting Object[] to T[]
final Object[] objectArray = (Object[]) array.getArray(getTypeMap());
List<T> list = new ArrayList<>(objectArray.length);
for (Object o : objectArray) {
list.add(typeClass.cast(o));
}
return list;
}
Writing Arrays
Figuring out how to write an array was frustrating, Oracle APIs require a Connection to create an Array, but you don't have an obvious Connection in the context of writeSQL(SQLOutput sqlOutput). Fortunately, this blog has a trick/hack to get the OracleConnection, which I've used here.
When you create an array with createOracleArray() you specify the list type (T_ARRAY_ANIMALS) for the type name, NOT the singular object type.
Here's a generic function for writing arrays. In your case, listType would be "T_ARRAY_ANIMALS" and you would pass in List<TAnimal>
/**
* Write the list out as an Array
*
* #param sqlOutput SQLOutput to write array to
* #param listType array type name (table of type)
* #param list List of objects to write as an array
* #param <T> Class implementing SQLData that corresponds to the type listType is a list of.
* #throws SQLException
* #throws ClassCastException if SQLOutput is not an OracleSQLOutput
*/
public static <T> void writeArrayFromList(SQLOutput sqlOutput, String listType, #Nullable List<T> list) throws SQLException {
final OracleSQLOutput out = (OracleSQLOutput) sqlOutput;
OracleConnection conn = (OracleConnection) out.getSTRUCT().getJavaSqlConnection();
conn.setTypeMap(getTypeMap()); // not needed?
if (list == null) {
list = Collections.emptyList();
}
final Array array = conn.createOracleArray(listType, list.toArray());
out.writeArray(array);
}
Notes:
At one point I thought setTypeMap was required, but now when I remove that line my code still works, so I'm not sure if it's necessary.
I'm not certain if you should write null or an empty array, but I assumed the empty array is more correct.
Tips on Oracle types
Oracle uppercases everything, so all type names should be uppercase.
You may need to specify SCHEMA.TYPE_NAME if the type isn't in your default schema.
Remember to grant execute on types if the user you are connecting with is not the owner.
If you have execute on the package, but not the type, getArray() will throw an exception when it tries to look for type metadata.
Spring
For developers using Spring, you may want to look at Spring Data JDBC Extensions, which provides SqlArrayValue and SqlReturnArray, which are useful for creating a SimpleJdbcCall for a procedure that takes an array as an argument or returns an array.
Chapter 7.2.1 Setting ARRAY values using SqlArrayValue for an IN parameter explains how to call procedures with array parameters.
As long as a Oracle specific solution is sufficient, the key lies within the DTOs. All of them have to implement ORAData and ORADataFactory
public class TAnimal implements ORAData, ORADataFactory {
Integer animal_id, number_of_hairs;
public TAnimal() { }
// [ Getter and Setter omitted here ]
#Override
public Datum toDatum(Connection connection) throws SQLException {
OracleConnection oracleConnection = connection.unwrap(OracleConnection.class);
StructDescriptor structDescriptor = StructDescriptor.createDescriptor("zoo_schema.T_ANIMAL", oracleConnection);
Object[] attributes = {
this.animal_id,
this.number_of_hairs
};
return new STRUCT(structDescriptor, oracleConnection, attributes);
}
#Override
public TAnimal create(Datum datum, int sqlTypeCode) throws SQLException {
if (datum == null) {
return null;
}
Datum[] attributes = ((STRUCT) datum).getOracleAttributes();
TAnimal result = new TAnimal();
result.animal_id = asInteger(attributes[0]); // see TEnclosure#asInteger(Datum)
result.number_of_hairs = asInteger(attributes[1]); // see TEnclosure#asInteger(Datum)
return result;
}
}
and
public class TEnclosure implements ORAData, ORADataFactory {
Integer enclosureId;
String enclosureName;
List<Animal> animals;
public TEnclosure() {
this.animals = new ArrayList<>();
}
// [ Getter and Setter omitted here ]
#Override
public Datum toDatum(Connection connection) throws SQLException {
OracleConnection oracleConnection = connection.unwrap(OracleConnection.class);
StructDescriptor structDescriptor = StructDescriptor.createDescriptor("zoo_schema.T_ENCLOSURE", oracleConnection);
Object[] attributes = {
this.enclosureId,
this.enclosureName,
null // TODO: solve this; however, retrieving data works without this
};
return new STRUCT(structDescriptor, oracleConnection, attributes);
}
#Override
public TEnclosure create(Datum datum, int sqlTypeCode) throws SQLException {
if (datum == null) {
return null;
}
Datum[] attributes = ((STRUCT) datum).getOracleAttributes();
TEnclosure result = new TEnclosure();
result.enclosureId = asInteger(attributes[0]);
result.enclosureName = asString(attributes[1]);
result.animals = asListOfAnimals(attributes[2]);
return result;
}
// Utility methods
Integer asInteger(Datum datum) throws SQLException {
if (datum == null)
return null;
else
return ((NUMBER) datum).intValue(); // oracle.sql.NUMBER
}
String asString(Datum datum) throws SQLException {
if (datum = null)
return null;
else
return ((CHAR) datum).getString(); // oracle.sql.CHAR
}
List<TAnimal> asListOfAnimals(Datum datum) throws SQLException {
if (datum == null)
return null;
else {
TAnimal factory = new TAnimal();
List<TAnimal> result = new ArrayList<>();
ARRAY array = (ARRAY) datum; // oracle.sql.ARRAY
Datum[] elements = array.getOracleArray();
for (int i = 0; i < elements.length; i++) {
result.add(factory.create(elements[i], 0));
}
return result;
}
}
}
then fetching the data works like so:
TEnclosure factory = new TEnclosure();
Connection connection = null;
OracleConnection oracleConnection = null;
OracleCallableStatement oracleCallableStatement = null;
try {
connection = MyConnectionFactory.getConnection(SOME_CONNECTION_CONFIG);
oracleConnection = connection.unwrap(OracleConnection.class);
oracleCallableStatement = (OracleCallableStatement) oracleConnection.prepareCall("{? = call zoo_schema.zoo_utils.GET_ENCLOSURE( ? )}");
oracleCallableStatement.registerOutParameter(1, OracleTypes.STRUCT, "zoo_schema.T_ENCLOSURE");
oracleCallableStatement.setInt(2, 6); // fetch data for ENCLOSURE#6
// Execute query
oracleCallableStatement.executeQuery();
// Check result
Object oraData = oracleCallableStatement.getORAData(1, factory);
LOGGER.info("oraData is a {}", oraData.getClass().getName()); // acme.zoo.TEnclosure
} finally {
ResourceUtils.closeQuietly(oracleCallableStatement);
ResourceUtils.closeQuietly(oracleConnection);
ResourceUtils.closeQuietly(connection); // probably not necessary...
}
I Just share the logic which worked for me. You can try this to retrieve ARRAY response from PL/SQL to Java.
CallableStatement callstmt = jdbcConnection.prepareCall("{call PROCEDURE_NAME(?, ?)}");
callstmt.setArray(1, array);
callstmt.registerOutParameter(2,Types.ARRAY, <ARRAY_NAME_DECLARED_IN_PL/SQL>);
// Do all execute operations
Array arr = callstmt.getArray(1);
if (arr != null) {
Object[] data = (Object[]) arr.getArray();
for (Object a : data) {
OracleStruct empstruct = (OracleStruct) a;
Object[] objarr = empstruct.getAttributes();
<Your_Pojo_class> r = new <Your_Pojo_class>(objarr[0].toString(), objarr[1].toString());
System.out.println("Response-> : "+ r.toString());
}
}
You could probably cast to java.sql.Array as such:
Object array = ( (Array) resultOracleStruct.getAttributes()[2]) ).getArray();

SQL Procedure call from mybatis

All, I am trying to call a SQL procedure from Java user Mybatis, But i am getting
"java.sql.SQLException: Missing IN or OUT parameter at index:: 1". Googling this doesnt give me the right answers.
Below is my code what i wrote. I am using mybatis first time, Can anyone please guide me.
Mepper.xml :---
<select id="addHardwareList" statementType="CALLABLE" parameterType="java.util.HashMap">
{
call P_HW_SW_CRUD.SAVE_HARDWARE_INVENTORY(
#{pDataIn,javaType=Object,jdbcType=ARRAY,jdbcTypeName=TAB_IDESK_HW_INFO,mode=IN,typeHandler=com.tristar.idesk.mybatis.mapper.HardwareInventoryHandler}
#{pMsgCodeOut,javaType=Long,jdbcType=DECIMAL,mode=OUT}
)
}
</select>
Here is where i setup my MAP --
public void addUpdateInventory(JsonNode jsonNode) {
Map<String, ?> inventoryMap = getMapFromJSONAddUpdate(jsonNode);
HashMap<String,Object> returnMap = new HashMap<String,Object>();
Long pErrodCodeOut = null;
String pErrorMsg = null;
returnMap.put("pDataIn", (List<HardwareInventoryBean>) inventoryMap.get("beanHardwareList"));
returnMap.put("pErrorCodeOut", pErrodCodeOut);
//returnMap.put("pErrodMsgOut", pErrorMsg);
hardwareInventoryMapper.addHardwareList(returnMap);
}
I also implemented a custom type handler to map my java bean attributes to oracleType Object.
public class HardwareInventoryHandler implements TypeHandler{
public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
try{
//new RecLoaderService().getJSONObj("REC_ICAST_CLAIMANT_INFO", "TAB_ICAST_CLAIMANT_INFO", jsonObject, i, ps);
System.out.println(parameter);
List<HardwareInventoryBean> hwInventoryList = (List<HardwareInventoryBean>) parameter;
System.out.println("Parameter Here --"+parameter);
StructDescriptor structDescriptor = StructDescriptor.createDescriptor(InventoryConstants.HW_DB_REC_TYPE_NAME, ps.getConnection());
STRUCT[] structs = null;
structs = new STRUCT[hwInventoryList.size()];
for (int index = 0; index < hwInventoryList.size(); index++) {
HardwareInventoryBean hwInventoryBean = hwInventoryList.get(index);
Object[] params = new Object[12];
params[0] = hwInventoryBean.getHwId();
params[1] = hwInventoryBean.getHwTypeId();
params[2] = hwInventoryBean.getHwModelId();
params[3] = hwInventoryBean.getAssetId();
params[4] = hwInventoryBean.getDateReceived();
params[5] = hwInventoryBean.getHwPrice();
params[6] = hwInventoryBean.getHwStatus();
params[7] = hwInventoryBean.getHwWarranty();
params[8] = hwInventoryBean.getCreatedBy();
params[9] = hwInventoryBean.getCreatedDate();
params[10] = hwInventoryBean.getModifiedBy();
params[11] = hwInventoryBean.getModifiedDate();
System.out.println("---------> " +hwInventoryBean.getHwId());
STRUCT struct = new STRUCT(structDescriptor, ps.getConnection(), params);
structs[index] = struct;
}
ArrayDescriptor desc = ArrayDescriptor.createDescriptor(InventoryConstants.HW_DB_TEB_TYPE_NAME, ps.getConnection());
ARRAY oracleArray = new ARRAY(desc, ps.getConnection(), structs);
ps.setArray(i, oracleArray);
}
catch(Exception e){
e.printStackTrace();
}
}
public JSONObject getResult(ResultSet rs, String columnName)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
public JSONObject getResult(ResultSet rs, int columnIndex)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
public JSONObject getResult(CallableStatement cs, int columnIndex)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
Mapper Interface ----
public void addHardwareList(HashMap<String, Object> returnMap);
My SQL Procedure code is :-
create or replace PACKAGE BODY P_HW_SW_CRUD AS -- body
PROCEDURE save_hardware_inventory (hw IN TAB_IDESK_HW_INFO, nu OUT NUMBER) IS
ERROR_CODE NUMBER(10);
ERROR_DESC VARCHAR2(200);
BEGIN
nu := 10;
FOR i IN 1 .. hw.COUNT
loop
if(hw(i).HW_ID <> 0) then
INSERT INTO IDESK_HW_INVENTORY VALUES (IDESK_HW_INVENTORY_seq.nextval, hw(i).hwTypeId , hw(i).hwModelId,hw(i).assetId
, hw(i).dateReceived , hw(i).hwPrice ,hw(i).hwStatus , hw(i).hwWarrenty , hw(i).createdBy , sysdate,hw(i).modifiedBy, sysdate);
else
update IDESK_HW_INVENTORY set HW_TYPE_ID=hw(i).hwTypeId,HW_MODEL_ID=hw(i).hwModelId,ASSET_ID=hw(i).assetId,DATE_RECEIVED=hw(i).dateReceived,HW_PRICE=hw(i).hwPrice,HW_STATUS=hw(i).hwStatus,HW_WARRENTY=hw(i).hwWarrenty,MODIFIED_BY=hw(i).modifiedBy,MODIFIED_DATE=sysdate where HW_ID=hw(i).HW_ID;
end If;
end loop;
Exception
when others then
ROLLBACK;
ERROR_CODE := SQLCODE;
ERROR_DESC := SQLERRM;
END save_hardware_inventory ;
PROCEDURE save_software_inventory (sw IN TAB_IDESK_SW_INFO ) IS
ERROR_CODE NUMBER(10);
ERROR_DESC VARCHAR2(200);
BEGIN
FOR i IN 1 .. sw.COUNT
loop
if(sw(i).SW_ID <> 0) then
INSERT INTO IDESK_SW_INVENTORY VALUES (IDESK_SW_INVENTORY_seq.nextval, sw(i).SW_VENDOR_ID , sw(i).SW_VERSION_TITLE,sw(i).QUANTITY
, sw(i).PRICE , sw(i).CONTRACT_ACOUNT_NUM ,sw(i).SW_STATUS , sw(i).CREATED_BY ,sysdate, sw(i).MODIFIED_BY , sysdate);
else
update IDESK_SW_INVENTORY set SW_VENDOR_ID=sw(i).SW_VENDOR_ID,SW_VERSION_TITLE=sw(i).SW_VERSION_TITLE,QUANTITY=sw(i).QUANTITY,PRICE=sw(i).PRICE,CONTRACT_ACOUNT_NUM=sw(i).CONTRACT_ACOUNT_NUM,SW_STATUS=sw(i).SW_STATUS,MODIFIED_BY=sw(i).MODIFIED_BY,MODIFIED_DATE=sysdate where SW_ID=sw(i).SW_ID;
end If;
end loop;
Exception
when others then
ROLLBACK;
ERROR_CODE := SQLCODE;
ERROR_DESC := SQLERRM;
END save_software_inventory ;
END P_HW_SW_CRUD ;
Believe you need a CallableStatement (which by the way is a subclass of PreparedStatement) and that you need to use 'registerOutParameter'.
See if this example helps.
I resolve this issue by using the innermost delegate connection
((DelegatingConnection)ps.getConnection()).getInnermostDelegate().
Next error was because of typo error in mapper.xml("," missing in the select id in mapper.xml). See below for the correct syntax.
select id="addHardwareList"
statementType="CALLABLE" parameterType="java.util.HashMap">
{
call P_HW_SW_CRUD.SAVE_HARDWARE_INVENTORY(
#{pDataIn,javaType=Object,
jdbcType=ARRAY,jdbcTypeName=TAB_IDESK_HW_INFO,
mode=IN,typeHandler=com.tristar.idesk.mybatis.mapper.HardwareInventoryHandler}
#{pMsgCodeOut,javaType=Long,jdbcType=DECIMAL,mode=OUT}
)
}

java.lang.ClassCastException: oracle.sql.NUMBER cannot be cast to oracle.sql.CHAR

In the DB have one collection:
CREATE OR REPLACE TYPE TY_Test as object
(
ID NUMBER,
ACCOUNT NUMBER,
PRIORITY CHAR(20),
)
CREATE OR REPLACE TYPE TY_Test_Instance as table of TY_Test
And TY_Test_Instance is passed to one procedure.
Below is Java code:
public static void main(String[] args) throws Exception {
List<Object> list = new ArrayList<Object>();
String[] objs = new String[3];
objs[0] = new String("863");
objs[1] = new String("1");
objs[2] = new String("3");
Connection conn = DBHelper.getInstance().conn;
StructDescriptor sDescriptor = StructDescriptor.createDescriptor("TY_Test", conn);
list.add(new STRUCT(sDescriptor, conn, objs));
Object[] obj_array = list.toArray();
ArrayDescriptor aDescriptor = ArrayDescriptor.createDescriptor("TY_Test_Instance", conn);
ARRAY array = new ARRAY(aDescriptor, conn, obj_array);
CallableStatement cstm = conn.prepareCall("{call PR_TEST(?,?)}");
cstm.setInt(1, 123);
cstm.setArray(2, array);
cstm.registerOutParameter(1, Types.INTEGER);
cstm.execute();
if (cstm.getInt(1) != 0) {
System.out.println("done");
}
}
when execute the script, it pop ups java.lang.ClassCastException: oracle.sql.NUMBER cannot be cast to oracle.sql.CHAR in the code of cstm.setArray(2, array);
I have spent much time but have not got any solution. Please help on it.

Categories