Used "SimpleJdbcCall" to call stored procedure encountered Error java.lang.ClassCastException :weblogic .jdbc.wrapper.array_oracle_sql_Array - spring

Oracle Types : User Defined Datatypes
create or replace type id_array_input VARRAY(20) OF NUMBER
create or replace type output_data_array VARRAY(20) OF VARCHAR(25)
Procedure
Stored Procedure to be invoked
CREATE OR REPLACE PROCEDURE TESTPROC(id_array in id_array_input,output_data out output_data_array)
as
declare
doc_name varchar2(55);
begin
output_data:=output_data_array();
for i in 1..id_array.count loop
select document_name into doc_name from document_details where doc_id =id_array(i);
output_data.extend;
output_data(i):=doc_name;
end loop;
end;
end TESTPROC;
Spring Part: Request Mapping containing call to procedure with In as an array of integers and out as an array of string
#Autowired
private JdbcTemplate jdbcTemplate;
private SimpleJdbcCall proceCall;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#RequestMapping(value="/procedure",method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
public #ResponseBody Object getProcedure(){
List<Integer> aa_array=new ArrayList<Integer>();
aa_array.add(10);
aa_array.add(20);
aa_array.add(30);
Integer[] int_array=new Integer[aa_array.size()];
int_array=aa_array.toArray(int_array);
proceCall=new SimpleJdbcCall(jdbcTemplate).withProcedureName("TESTPROC")
.declareParameters(new SqlParameter("id_array",OracleTypes.ARRAY,"id_array_input"),
new SqlOutParameter("output_data",Types.ARRAY,"output_data_array" ,new SqlReturnArray()));
Map in = Collections.singletonMap("id_array",new SqlArrayValue(int_array) );
Map<String,Object> st_array=proceCall.execute(String[].class,in);
return st_array;
}

In your example, exception is coming because of new SqlReturnArray(). Refer below code. This will work.
procedureCall=new SimpleJdbcCall(jdbctemplate.getDataSource()).withProcedureName("TESTPROC")
.declareParameters( new SqlParameter("id_array",OracleTypes.ARRAY, "COMPANY_COMMITTEE_ID_ARRAY")
,new SqlOutParameter("output_data",Types.ARRAY, "STRING_ARRAY"));
aa_array=alpha.toArray(aa_array);
Map in =Collections.singletonMap("id_array",new SqlArrayValue(aa_array));
Array hi= procedureCall.executeFunction(Array.class,in);
String[] hh=(String[]) hi.getArray();

You can also make it work by disabling the "Wrap Data Types" option in the WebLogic Administration Console under Services->Data Sources->Connection Pool->Advanced:

Related

How do you map the output of a Spring stored procedure execute?

I am using Spring and stored procedures to retrieve data from a mySQL database. I have the stored procedure and parameters working OK but I'm having problems mapping the result set. At the moment I have some truly ugly code to get the values and I'm sure there has to be a better, cleaner and more elegant way. Can anyone guide me to a better solution?
After the stored procedure class, I have:
List<String> outList = new ArrayList<String>();
Map<String,Object> outMap = execute(parameters_map);
List list = (List) outMap.get("#result-set-1");
for (Object object : list) {
Map map2 = (Map) object;
list.add(map2.get("runname"));
}
return outList;
runname is the column from the database query.
Is there a better way to achieve this?
Example from spring docs using RowMapper:
public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall procReadAllActors;
public void setDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procReadAllActors = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("read_all_actors")
.returningResultSet("actors",
BeanPropertyRowMapper.newInstance(Actor.class));
}
public List getActorsList() {
Map m = procReadAllActors.execute(new HashMap<String, Object>(0));
return (List) m.get("actors");
}
// ... additional methods
}
took a while to interpret the Spring docs but I finally got there.
My solution:
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("DistinctRunNames")
.withoutProcedureColumnMetaDataAccess();
simpleJdbcCall.addDeclaredParameter(new SqlParameter("environment", Types.VARCHAR));
simpleJdbcCall.addDeclaredParameter(new SqlParameter("username", Types.VARCHAR));
simpleJdbcCall.addDeclaredParameter(new SqlParameter("test_suite", Types.VARCHAR));
SqlParameterSource parameters = new MapSqlParameterSource().addValue("environment", environment)
.addValue("username", username).addValue("test_suite", testSuite);
Map map = simpleJdbcCall.returningResultSet("runnames", new ParameterizedRowMapper<RunNameBean>() {
public RunNameBean mapRow(ResultSet rs, int rowNum) throws SQLException {
RunNameBean runNameBean = new RunNameBean();
runNameBean.setName(rs.getString("runname"));
return runNameBean;
}
}).execute(parameters);
return (List) map.get("runnames");
Had problems with expected parameters versus actual, had to break up the simpleJdbcCall object. Maps the results into a list beautifully.
Thank you for answers, helped me to learn about Spring mapping.

No procedure/function/signature

I am relatively new at using stored procedures and I have really run up aganst the wall. I am receiving the following error message using the Spring JdbcTemplate. My dev environment is Xubuntu, jdk 1.8.
The stack trace is:
Exception in thread "main" org.springframework.dao.InvalidDataAccessApiUsageException: Unable to determine the correct call signature - no procedure/function/signature for 'PROCONEINPARAMETER'
at org.springframework.jdbc.core.metadata.GenericCallMetaDataProvider.processProcedureColumns(GenericCallMetaDataProvider.java:347)
at org.springframework.jdbc.core.metadata.GenericCallMetaDataProvider.initializeWithProcedureColumnMetaData(GenericCallMetaDataProvider.java:112)
at org.springframework.jdbc.core.metadata.CallMetaDataProviderFactory$1.processMetaData(CallMetaDataProviderFactory.java:133)
at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:299)
at org.springframework.jdbc.core.metadata.CallMetaDataProviderFactory.createMetaDataProvider(CallMetaDataProviderFactory.java:73)
at org.springframework.jdbc.core.metadata.CallMetaDataContext.initializeMetaData(CallMetaDataContext.java:286)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.compileInternal(AbstractJdbcCall.java:303)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.compile(AbstractJdbcCall.java:288)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.checkCompiled(AbstractJdbcCall.java:348)
at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:375)
at org.springframework.jdbc.core.simple.SimpleJdbcCall.executeFunction(SimpleJdbcCall.java:153)
at test.jdbc.StringDao.executeProcOneINParameter(StringDao.java:21)
at test.jdbc.SimpleJdbcTest.main(SimpleJdbcTest.java:15)
Code:
SimpleJdbc.java
package test.jdbc;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SimpleJdbcTest {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
StringDao dao=(StringDao)ctx.getBean("edao");
String request = new String(" Wow, this works!");
String response = dao.executeProcOneINParameter(request);
if (response != null && !response.equals(new String())) {
System.out.println("stored proc worked: "+ response);
} else {
System.err.println("stored proc did not work.");
}
}
}
StringDao.java
package test.jdbc;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
public class StringDao {
private static final String PROC_NAME = "PROCONEINPARAMETER";
private static final String CAT_NAME = "LISTENER";
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public String executeProcOneINParameter(String callParam){
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withCatalogName(CAT_NAME)
.withProcedureName(PROC_NAME);
return jdbcCall.executeFunction(String.class, callParam);
}
}
Stored Proc:
PROCONEINPARAMETER
CREATE OR REPLACE PROCEDURE procOneINParameter(param1 IN VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE('Hello World IN parameter ' || param1);
END;
Aside from the problems that #Alex posted, and I corrected, the final problem was the following:
static final String PROC_NAME = "PROCONEINPARAMETER";
private static final String CAT_NAME = "LISTENER";
…..
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withCatalogName(CAT_NAME)
.withProcedureName(PROC_NAME);
instead of:
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withSchemaName(CAT_NAME)
.withProcedureName(PROC_NAME);
Obviously, there was no way for anyone to know that I was using catalog and schema name interchangeably.
You are trying to call a procedure, not a function. But you are calling it via the executeFunction() method, and specifying a return type of String.
You need to use execute() instead, still passing the procedure argument, but without the return type (since there isn't one from a procedure):
Map<String,Object> out = jdbcCall.execute(callParam);
Your procedure doesn't have any OUT parameters either so out will be empty.
Had similar problem.
Issue was : Schema Name was in uppercase, so it was taking double quotes.
eg : SchemaName was ABCD, while executing postgres was expecting "ABCD".
Resolution : converted schema name to lowercase.

Insert byte[] into blob field with spring's jdbcTemplate and stored procedure

I'm try to insert byte[] into blob field with stored procedure, and get an Exception :
Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [SELECT ID FROM sp_NEWFILE(?,?,?)]; nested exception is org.firebirdsql.jdbc.field.TypeConversionException: Error converting to object.
Model:
public class fileBody {
private int ID;
private byte[] BODY;
private String FILENAME; //getters an setters}
Insert it to database
public class FileBodyDaoImpl implements FileBodyDao {
public int insertData(final FileBody fileBody) throws IOException {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
LobHandler lobHandler = new DefaultLobHandler();
final InputStream in = new ByteArrayInputStream(fileBody.getBODY());
final int fileSize = in.available();
Map<String, Object> out = jdbcTemplate.queryForMap("SELECT ID FROM sp_NEWFILE(?,?,?)",
new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException,
DataAccessException {
ps.setString(1, fileBody.getFILENAME());
lobCreator.setBlobAsBinaryStream(ps, 2, in, fileSize);
ps.setNull(3, java.sql.Types.INTEGER);
}
});
int last_inserted = Integer.parseInt(String.valueOf(out.get("ID")));
return last_inserted;
}
And my stored procedure
create or alter procedure sp_NEWFILE (
FILENAME varchar(255),
BODY blob sub_type 0 segment size 80,
USEID integer)
returns (
ID integer)
as
begin
if (useid is not null) then ID=USEID;
else ID=GEN_ID(gen_filebody_id,1);
if ((FILENAME is NULL) or (FILENAME='')) then FILENAME='UNDEFINED';
INSERT INTO t_filebody(ID,BODY,FILENAME) VALUES(:ID,:BODY,:FILENAME);
suspend;
end^
and i get an Exception:
Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException:
PreparedStatementCallback;
bad SQL grammar [SELECT ID FROM sp_NEWFILE(?,?,?)]; nested exception is org.firebirdsql.jdbc.field.TypeConversionException: Error converting to object.
Versions:
jaybird-jdk17-2.2.5;
Source: firebird2.5 Version: 2.5.1.26351.ds4-2ubuntu0.1;
The problem is that queryForMap does not support a PreparedStatementCallback (contrary to for example execute), instead your anonymous object is considered a normal parameter for the query to execute, and Jaybird does not support this object type. And if Jaybird had supported it, you would have received an error for missing parameters 2 and 3.
Your code can be greatly simplified by passing the byte array:
Map<String, Object> out = jdbcTemplate.queryForMap("SELECT ID FROM sp_NEWFILE(?,?,?)",
fileBody.getFILENAME(), fileBody.getBODY(), null);
This works as Jaybird considers a BLOB SUB_TYPE 0 as a java.sql.Types.LONGVARBINARY and JDBC 4.2 appendix B declares that byte[] is the default type for that (although you can also use it as a java.sql.Types.BLOB).
As a side note, your stored procedure does not need to be selectable (removing SUSPEND makes it executable), and the procedure could also be replaced by using a TRIGGER to generate the primary key and retrieving the value either by using INSERT .. RETURNING .. or through the JDBC generated keys facility (which in turn is implemented in Jaybird through INSERT .. RETURNING ..).
For those seeking non-Jaybird solution to insert BLOB using spring jdbctemplate, the following syntax worked for me to use stored procedures which is different compared to insert via queries.
Insert via Queries
ByteArrayInputStream inputStream = new ByteArrayInputStream(file.getBytes());
ps.setBlob(1, inputStream);
Insert via Stored Procedure Call
Map<String, Object> inParams = new HashMap<>();
inParams.put("pi_some_id", id);
inParams.put("pi_file_blob", new SqlLobValue(file.getBytes()));
SqlParameterSource sqlParameterSource = new MapSqlParameterSource(inParams);
SqlParameter[] sqlParameters = {
new SqlParameter("pi_some_id", Types.VARCHAR),
new SqlParameter("pi_file_blob", Types.BLOB),
new SqlOutParameter("po_error_flag", Types.VARCHAR),
new SqlOutParameter("po_message", Types.VARCHAR)};
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate).withoutProcedureColumnMetaDataAccess().
withProcedureName(storedProcName).withCatalogName(packageName).
declareParameters(sqlParameters);
Map<String, Object> storedProcResult = simpleJdbcCall.execute(sqlParameterSource);

spring ArrayIndexOutOfBoundsException with SimpleJdbcCall

This is the Oracle procedure I'm trying to call:
PROCEDURE GetCoreReportExtras
( pnAssignment IN NUMBER,
pnUserRole in NUMBER,
psAreaMenu in VARCHAR2,
pnAreaLevel in NUMBER,
curReportList OUT outcur,
psLDO in VARCHAR2 default 'none',
pnAcisNumber in NUMBER default 0);
Here's my Java code:
private class GetStandardReportExtrasSPV2{
int nAreaLevel;
int nAssignment;
int nUserRole;
int nAcisNum = 0;
String strAreaMenu;
String strLDO = null;
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcCall procGetReportExtras;
public GetStandardReportExtrasSPV2(DataSource ds, int nUserRole, String strAreaMenu,
int nAssignment, int nAreaLevel, String strLDO, int nAcisNum) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(ds);
JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procGetReportExtras =
new SimpleJdbcCall(jdbcTemplate)
.withCatalogName("package")
.withProcedureName("proc")
.returningResultSet(REPORT_LIST,
ParameterizedBeanPropertyRowMapper.newInstance(Report.class));
}
public List<Report> getReportsList() {
Map<String, Object> params = new HashMap<String, Object>();
params.put(USER_ASSIGNMENT, nAssignment);
params.put(USER_ROLE, nUserRole);
params.put(AREA_MENU, strAreaMenu);
params.put(AREA_LEVEL, nAreaLevel);
params.put(SEGMENT, strLDO);
params.put(ACIS_NUMBER, nAcisNum);
SqlParameterSource in = new MapSqlParameterSource()
.addValues(params);
Map m = procGetReportExtras.execute(new HashMap<String, Object>(0), in);
return (List) m.get(REPORT_LIST);
}
}
When getReportsList() calls execute(), I get the following Exception:
java.lang.ArrayIndexOutOfBoundsException: 2
org.springframework.jdbc.core.metadata.CallMetaDataContext.matchInParameterValuesWithCallParameters(CallMetaDataContext.java:555)
org.springframework.jdbc.core.simple.AbstractJdbcCall.matchInParameterValuesWithCallParameters(AbstractJdbcCall.java:419)
org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:364)
org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:173)
Any hints as to what I'm doing wrong?
The issue is here
Map m = procGetReportExtras.execute(new HashMap<String, Object>(0), in);
You are calling the overloaded method that takes in a Object .... This method takes
optional array containing the in parameter values to be used in the
call. Parameter values must be provided in the same order as the
parameters are defined for the stored procedure.
In your call, those two parameters happen to be an empty HashMap and a SqlParameterSource.
The call fails in CallMetaDataContext#matchInParameterValuesWithCallParameters(). Source:
public Map<String, ?> matchInParameterValuesWithCallParameters(Object[] parameterValues) {
Map<String, Object> matchedParameters = new HashMap<String, Object>(parameterValues.length);
int i = 0;
for (SqlParameter parameter : this.callParameters) {
if (parameter.isInputValueProvided()) {
String parameterName = parameter.getName();
matchedParameters.put(parameterName, parameterValues[i++]); // fails here
}
}
return matchedParameters;
}
It's expecting that the array you pass has 7 elements (because that's what your stored procedure expects), but it only has 2, the HashMap and the SqlParameterSource. When it tries to access the 3rd one (index 2), it throws an ArrayIndexOutOfBoundsException.
You probably wanted to use the execute() method that accepts an SqlParameterSource
Map m = procGetReportExtras.execute(in);
You are calling the wrong SimpleJDBCCall.execute(Object... args); use SimpleJDBCCall.execute(SqlParameterSource)
procGetReportExtras.execute(in);

How to catch the exception of the execute method while using Spring jdbcTemplate

I use the Spring JDBCTemplate to call a stored procedure ,like this
Map receive10PrmtBill = (Map) getJdbcTemplate().execute(sql, new CallableStatementCallback() {
#Override
public Object doInCallableStatement(CallableStatement cs) throws SQLException,
DataAccessException {
cs.setString(1, Constants.ADD_PROC_TYPE);
Map<String,PrmtBillInfoDatagram> tempMap = new HashMap();
cs.execute();
but the execute method return false and didn't throw any exception ,so I don't know what's wrong with my program , how to catch the Exception? Any help?
This is the proc
create proc sp_xx ( #userid int)
as
begin
select personid, personname from person where personid = #userid
select teamid, teamname from team
end
Try setting "IGNORE_DONE_IN_PROC" property to "true". See at:
http://javabob64.wordpress.com/2011/04/12/sybase-and-the-jdbc-driver/

Resources