SQL Procedure call from mybatis - java

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}
)
}

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

how to get oracle array of object and use them in my code?

I am trying to get array of objects from oracle procedure that I want to use in my code.
I am getting the below error
java.math.BigDecimal cannot be cast to oracle.sql.STRUCT
on this line java.sql.Struct myStruct = java.sql.Struct)resultSet.getObject(1);
this is my code
CustResponse resp = new CustResponse();
sqlString = "begin " + procName + "(?,?,?,?,?);end;";
callstmt = conn.prepareCall(sqlString);
callstmt.setString(1, Request.getBANK_CLINT());
callstmt.setString(2, Request.getCLIENT_NBR());
callstmt.registerOutParameter(3, Types.ARRAY,"BANK_RECORD_OBJ_ARRAY");
callstmt.registerOutParameter(4, Types.VARCHAR);
callstmt.registerOutParameter(5, Types.VARCHAR);
callstmt.execute();
resp.setCode(callstmt.getString(4));
resp.setDescription(callstmt.getString(5));
ARRAY objArray = (ARRAY) ((weblogic.jdbc.wrapper.Array) (callstmt).getObject(3)).unwrap(ARRAY.class);
resultSet = objArray.getResultSet();
ResponseCustBodyType[] responseCustBodyType = new ResponseCustBodyType[resultSet.getFetchSize()];
while (resultSet.next())
{
i = i+1;
java.sql.Struct myStruct = (java.sql.Struct)resultSet.getObject(1);
Object[] bankInfoStruct = myStruct.getAttributes();
responseCustBodyType[i].setLOAN_REF(bankInfoStruct[1].toString());
responseCustBodyType[i].setLOAN_TYPE(bankInfoStruct[2].toString());
responseCustBodyType[i].setLOAN_AMOUNT(new BigDecimal(bankInfoStruct[3].toString()));
responseCustBodyType[i].setLAON_CY(bankInfoStruct[4].toString());
try {
responseCustBodyType[i].setAPP_DATE(formatter1.parse(bankInfoStruct[5].toString()));
}
catch (ParseException e)
{
text = e.toString();
}
responseCustBodyType[i].setSTATUS(bankInfoStruct[6].toString());
responseCustBodyType[i].setREJ_REASON(bankInfoStruct[7].toString());
}
resp.setResponseCustBodyType(responseCustBodyType);
conn.close();
return resp;
From the error you posted it looks like resultSet.getObject(1) returns a java.math.BigDecimal object, not a java.sql.Struct.

Send & Retrieve array list to oracle stored procedure

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... :)

Bugged by de'bug

An unexpected token error occurs near the Class Database, yet everything in the syntax looks fine. I guess I made the error in the way I called the value from the class?
import de.bezier.data.sql.*;
PostgreSQL pgsql;
Database
void setup()
{
size( 100, 100 );
String user = "user";
String pass = "pass";
String database = "db";
pgsql = new PostgreSQL( this, "127.0.0.1", database, user, pass );
println ("ok");
}
void draw()
{
val1.update();
}
Token error here
Class Database
{
Float val;
database (Float col) {
val = col;
}
void update( )
{
//sets up database
pgsql = new PostgreSQL( this, "127.0.0.1", database, user, pass );
if ( pgsql.connect() )
{
pgsql.query( "SELECT col FROM table ORDER BY col DESC LIMIT 1; " );
return( pgsql.getFloat("col") );
}
else
{
return float (col = 0);
}
}
}
Some text here....
This line
pgsql = new PostgreSQL( this, "127.0.0.1", database, user, pass )
is invoking a constructor. From the documentation you need to extend processing.core.PApplet. "this" refers to the current instance of "this" class.

Pass and return custom array object in ibatis and oracle in java

I've looked around for a good example of this, but I haven't run into one yet. I want to pass a custom string array from java to oracle and back, using the IBATIS framework. Does anyone have a good link to an example? I'm calling stored procs from IBATIS.
Thanks
You've got to start with a custom instance of TypeHandler. We'd prefer to implement the simpler TypeHandlerCallback, but in this scenario we need access to the underlying Connection.
public class ArrayTypeHandler implements TypeHandler {
public void setParameter(PreparedStatement ps, int i, Object param, String jdbcType)
throws SQLException {
if (param == null) {
ps.setNull(i, Types.ARRAY);
} else {
Connection conn = ps.getConnection();
Array loc = conn.createArrayOf("myArrayType", (Object[]) param);
ps.setArray(i, loc);
}
}
public Object getResult(CallableStatement statement, int i)
throws SQLException {
return statement.getArray(i).getArray();
}
...
}
Then, to wire it up in the iBATIS config:
<?xml version="1.0"?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="arrayTest">
<parameterMap id="storedprocParams" class="map">
<parameter property="result" mode="OUT" jdbcType="ARRAY" typeHandler="ArrayTypeHandler"/>
<parameter property="argument" mode="IN" jdbcType="ARRAY" typeHandler="ArrayTypeHandler"/>
</parameterMap>
<procedure id="storedproc" parameterMap="arrayTest.storedprocParams">
{? = call My_Array_Function( ? )}
</procedure>
</sqlMap>
Hope this helps!
bsanders gave me a good starting point - here's what I had to do to make it work within the RAD environment (websphere 6.2).
public Object getResult(CallableStatement statement, int i) throws SQLException {
return statement.getArray(i).getArray(); //getting null pointer exception here
}
public void setParameter(PreparedStatement ps, int i, Object param, String jdbcType) throws SQLException {
if (param == null) {
ps.setNull(i, Types.ARRAY);
} else {
String[] a = (String[]) param;
//ARRAY aOracle = ARRAY.toARRAY(a, (OracleConnection)ps.getConnection());
//com.ibm.ws.rsadapter.jdbc.WSJdbcConnection
w = (com.ibm.ws.rsadapter.jdbc.WSJdbcConnection)ps.getConnection());
//com.ibm.ws.rsadapter.jdbc.WSJdbcObject x;
Connection nativeConnection = Connection)WSJdbcUtil.getNativeConnection((WSJdbcConnection)ps.getConnection());
ArrayDescriptor descriptor = ArrayDescriptor.createDescriptor("F2_LIST", nativeConnection);
ARRAY dataArray = new ARRAY(descriptor, nativeConnection, a);
ps.setArray(i, dataArray);
}
}
Notice the nativeConnection I had to get, the descriptor I had to make, and so on. However, while I can pass things into the database as an array of Strings, I haven't been able to figure out why I'm not getting anything back. My OUT parameter (the getResult(CallableStatement statment, int i) is throwing a null pointer exception, even though I'm setting the out parameter in the plsql in the database.
--stored procedure to take a | delimited ids
PROCEDURE array_test (argument IN f2_list, result OUT f2_list)
AS
l_procname_v VARCHAR2 (50) := 'array_test';
l_param_list VARCHAR2 (2000)
:= l_procname_v || ' param_values: p_string: ';
p_status_n NUMBER;
p_message_v VARCHAR2 (2000);
ret_list f2_list := new f2_list();
l_count_v varchar2(200);
BEGIN
l_count_v := argument.COUNT;
for x in 1..argument.count
LOOP
pkg_az_common_util.az_debug (package_nm,
l_procname_v,
pkg_az_data_type_def.debug_num,
argument(x)
);
end loop;
pkg_az_common_util.az_debug (package_nm,
l_procname_v,
pkg_az_data_type_def.debug_num,
l_count_v
);
ret_list.extend();
ret_list(1) := 'W';
ret_list.extend();
ret_list(2) := 'X';
ret_list.extend();
ret_list(3) := 'Y';
ret_list.extend();
ret_list(4) := 'Z';
result := ret_list;
EXCEPTION
WHEN OTHERS
THEN
p_status_n := pkg_az_common_util.get_error_code;
p_message_v :=
TO_CHAR (p_status_n)
|| '|'
|| 'Oracle Internal Exception('
|| l_procname_v
|| ')'
|| '|'
|| TO_CHAR (SQLCODE)
|| '|'
|| SQLERRM
|| l_param_list;
standard_pkg.log_error (package_nm,
l_procname_v,
SQLCODE,
p_message_v
);
IF p_status_n = 1
THEN
RAISE;
END IF;
END array_test;
Here is how I'm accessing it:
Map queryParamsTest = new HashMap();
String[] testArray = {"A", "B", "C"};
queryParamsTest.put("argument", testArray);
DaoUtils.executeQuery(super.getSqlMapClientTemplate(),
"arrayTest", queryParamsTest, queryParamsTest
.toString()); //just executes query
String[] resultArray = (String[])queryParamsTest.get("result");
for(int x = 0; x< resultArray.length; x++)
{
System.out.println("Result: " + resultArray[x]);
}
<parameterMap id="storedprocParams" class="map">
<parameter property="argument" mode="IN" jdbcType="ARRAY" typeHandler="ArrayTypeHandler"/>
<parameter property="result" mode="OUT" jdbcType="ARRAY" typeHandler="ArrayTypeHandler"/>
</parameterMap>
<procedure id="arrayTest" parameterMap="storedprocParams">
{call pkg_az_basic_dev.array_test(?, ? )}
</procedure>
Any ideas?
Well, guys in company found out the solution: you need to have implemented getResult method(s) in your typeHandler and provided additional attribute jdbcTypeName=ORACLE_REAL_ARRAY_TYPE in your mapper
Try using statement.getObject(i) and then casting to an array.

Categories