I am getting ClassCastException after executing stored procedure, written in Oracle, using spring-jdbc.
Here is the code snippet that is giving issue, here data is the List that i get from procedure:
for(Object[] obj :data){
}
Below is the error:
java.lang.ClassCastException: oracle.jdbc.driver.OracleResultSetImpl cannot be cast to [Ljava.lang.Object
I think the issue is after getting the OUT cursor from the procedure How to use it in Java code i.e. how to extract values from it?
Unfortunately, searching the web didn't give me any solutions. What can I try next?
data is not a list (or array) of arrays. It is an implementation of the interface oracle.jdbc.driver.OracleResultSet. So you cannot iterate over it using a for-each loop.
It also implements ResultSet, which defines the method next().
Therefore you can iterate over data using a while-loop:
while (data.next()){
Object obj = data.getObject(col);
// do somthing with obj
}
The method next() moves the cursor to the next row.
col can either be the column index or label.
Here you can find an example on using ResultSet.
Related
I have a Hive table which has column with array data type. I am using JDBC to select rows from the table.
SELECT col1 FROM hive_table WHERE condition = 'condition'
After receiving the resultset, I am using res.getArray() method for the specific array field while looping through resultset.
Array arrayCol = res.getArray(1);
This is throwing a "Method not supported" error. Is it valid to use getArray() method for such queries executed on Hive table?
Unfortunately, no. You can see getArray() method is not implemented in ResultSet class for Hive JDBC. The actual class name is HiveBaseResultSet and the source code is located here.
Depends on what type of values the array holds, a client program need to decode its value by itself. For example, a column of type array<string> is encoded as a single String object like `["VALUE1","VALUE2",...,"VALUEN"]'. And we can use getString() method and freely re-construct any object of type Array<String> or List<String>.
You can loop though the result set and add the column values to arraylist in java. See example below assuming that your table column is of String type.
List<String> list = new ArrayList<String>();
while (res.next()) {
list.add( res.getString(1));
}
I'm using Spring Boot to build REST service which connects to Oracle DB. I'm allso using Spring JDBC.
I have a following code:
jdbcCall.declareParameters(new SqlParameter("P_IN", OracleTypes.VARCHAR));
jdbcCall.declareParameters(new SqlOutParameter("P_OUT", OracleTypes.CURSOR, new ClientMapper()));
MapSqlParameterSource in = new MapSqlParameterSource().addValue("P_IN", "123");
Map out = jdbcCall.execute(in);
Client client = (Client) out.get("P_OUT");
Procedure returns just one result, but when I execute the code I get the following errror:
java.lang.ClassCastException: java.util.ArrayList cannot be cast to com.test.model.Client
In a debug mode I can see that out.get("P_OUT")returns ArrayList with 10 objects, First one is my Client object and others are set to null.
I'm confused, what am I doing wrong and hove to fix it?
Procedure returns one result
How does Spring know that? It needs to be able to cope with cursors that could return any number of rows. To me it makes sense that out.get("P_OUT") returns a List.
In a debug mode I can see that out.get("P_OUT") returns ArrayList with 10 objects
I suspect that when you are seeing the 10 objects you are looking at the internal array used by the ArrayList. Try printing out the size() of the ArrayList. I would expect this to print out 1.
The ArrayList class may keep spare space on the end of its internal array to allow it to add an element quickly. It will also keep track of how many elements in the array are actually used.
So, in short, you need to do the following:
List<Client> clients = (List<Client>) out.get("P_OUT");
Client client = clients.get(0);
You may also want to consider adding some error-handling in the situation that the cursor should, for whatever reason, return no data. The cast to List<Client> may also appear as a warning in your IDE: a #SuppressWarnings("unchecked") annotation should address that.
I have the following types:
create or replace TYPE mytimestamp AS VARRAY(300) OF TIMESTAMP(6);
create or replace TYPE trade_history_dts_array AS VARRAY(300) OF mytimestamp;
The types are used in this way in the stored procedure:
trade_history_dts tradehistorydtsarray ;
FOR i IN 1..20
loop
trade_history_dts(i) := mytimestamp();
LOOP
trade_history_dts(i).extend();
FETCH trade_history_cursor INTO
trade_history_dts(i)(j),
dbms_output.put_line(trade_history_dts(i)(j));
j := j+1;
exit when trade_history_cursor%notfound;
END LOOP;
j:=1;
end loop;
The stored procedure is tested, and I can get the right the result:
information for trade 1
trade_history_dts(1)(1)17-OCT-05 03.49.57.000000 PM
trade_history_dts(1)(2)17-OCT-05 03.49.58.000000 PM
information for trade 2
trade_history_dts(2)(1)27-JUN-05 09.02.59.000000 AM
trade_history_dts(2)(2)27-JUN-05 09.02.59.000000 AM
trade_history_dts(2)(3)27-JUN-05 09.03.01.000000 AM
information for trade 3
trade_history_dts(3)(1)09-FEB-06 09.31.03.000000 AM
trade_history_dts(3)(2)09-FEB-06 09.31.05.000000 AM
....
Now I want to get the this result in java, and transform it to a 2-dimensional array.
I tried the following:
Timestamp[][] trade_history_dts = (Timestamp[][])trade_history_dts_arr.getArray();
but it does not work. Does anybody know how to transform it to a 2-dimensional array in java? Thank you!
The approach that you've adopted will not work, because JDBC on it's own does not have any support for Oracle's collection types. You'll need to work on objects of type contained in the Oracle JDBC driver for accessing the contents of the resultset in the manner that you desire. This would involve casting the ResultSet object into an OracleResultSet so that the inner elements of the collection can be accessed via instances of oracle.sql.ARRAY elements.
You could also read an Object from the ResultSet using the getObject method, and then cast it into the oracle.sql.ARRAY instance which represents the collection. Details on this mechanism, can be found in the Oracle Database JDBC Developer's Guide and Reference.
Additionally, you'll need to parse each element in the result set,and process them in a similar manner, for you are returning a multi-level collection. Details for this are provided in the same guide in a separate section.
My Java code is ,
Query q=session.createSQLQuery(" { call procedureSample(?) }");
List list=q.list();
The procedure procedureSample returns three values, like (int,string,int).
How can I get the results?
Whether I need to create the bean? If so, how can I name the fields in the bean, as the int results in my procedure are functions like count?
How can I retrieve the results from the list list instance?
What you'd get is a List<Object[]>, i.e. a list of rows, each represented as an object array.
How the procedure results are accessed depends on whether the results are returned as one row with 3 columns (in this case you'd access the 3rd element as list.get(0)[2]) or as 3 rows with one column (accessing the 3rd element would then be list.get(2)[0]).
Edit: Note that this assumes you have Java 5+ and cast list to List<Object[]>.
Your list will contain 3 items. You can iterate through the list and get the values.
For example.
Query q=session.createSQLQuery(" { call procedureSample(?) }");
List list=q.list();
Iterator iter=list.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
}
No need of creating bean in this case I guess.
I had an issue in building the resultset using Java.
I am storing a collection object which is organized as row wise taken from a resultset object and putting the collection object (which is stored as vector/array list) in cache and trying to retrieve the same collection object.
Here I need to build back the resultset again using the collection object. Now my doubt is building the resultset in this way possible or not?
The best idea if you are using a collection in place of a cache is to use a CachedRowSet instead of a ResultSet. CachedRowSet is a Subinterface of ResultSet, but the data is already cached. This is far simpler than to write all the data into an ArrayList.
CachedRowSets can also be queried themselves.
CachedRowSet rs;
.......................
.......................
Integer id;
String name;
while (rs.next())
{
if (rs.getInt("id") == 13)
{
id = rs.getInt("id");
name = rs.getString("name"));
}
}
So you just call the CachedRowSet whenever you need the info. It's almost as good as sliced bread. :)
EDIT:
There are no set methods for ResultSet, while there are Update methods. The problem with using the Update method's for the purpose of rebuilding a ResultSet is that it requires selecting a Row to update. Once the ResultSet has freed itself, all rows are set to null. A null reference cannot be called. A List of Lists mimics a ResultSet itself, or more correctly, an array of arrays mimic a ResultSet.
While Vectors are thread safe, there is a huge overhead attached to them. Use the ArrayList instead. As each nested List is created and placed into the outer nest List, insert it in this manner.
nest.add(Collections.unmodifiableList(nested));
After all of the nested Lists are inserted, return the nest List as an umodifiableList as well. This will give you a thread-safe collection without the overhead of the vectors.
Take a look at this page. Try to see if the SimpleResultSet class is fine for your needs.
If you combine its source into a standalone set of classes, it should do the trick.
From what I could get, your code may be like this:
List collection = new ArrayList();
collection.add(" A collection in some order");
List cache = new ArrayList();
cache.add(collection); ...
Now when you retrieve I think you'll get your collection in order, since you have used List.
If this is not what you were expecting, do comment.
I will advise you to use CachedRowSet. Refer http://www.onjava.com/pub/a/onjava/2004/06/23/cachedrowset.html this article to know more about CachedRowSet. Once you create this CachedRowSet, you can disconnect from the database, make some changes to the cached data and letter can even open the DB connection and commit the changes back to the Database.
Another option you should consider is just to refactor your code to accept a Collection instead of a ResultSet.
I'm assuming you pass that ResultSet to a method that iterates over it. You might as well change the method to iterate over an ArrayList...