JDBC Re-read Data In ResultSet - java

I am retrieving a simple ResultSet from my SQL Server database using the Microsoft JDBC Driver (mssqlserver.jar). I think it is the MSSQL2000 Driver which is downloaded from [Microsoft JDBC][1]
I am wanting to call the getter methods more than once to access the values but when you do the following exception is thrown:
java.sql.SQLException: [Microsoft][SQLServer 2000 Driver for JDBC]ResultSet can not re-read row data for column 1.
Question is, I am retrieving data into a ResultSet. From the ResultSet, I am accessing the data in my code before passing the ResultSet on to elsewhere in code to be reused.
Code is similar to as follows:
// build query string
String selectQuery = "SELECT * FROM SomeWhere";
// get the data
Statement statement = sourceConnection.createStatement();
ResultSet rs = statement.executeQuery(selectQuery);
while( rs.next() ) {
// do my own internal processing
doSomethingWithRs(rs);
// now do something with the record set outside - in subclass
afterRowCopied(rs);
}
// ...
private void doSomethingWithRs(ResultSet rs) {
// access data
for( int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
Object o = rs.getObject(i);
// do something with o...
}
}
Edit
I am using Java 1.6 for this.
end Edit
Any thoughts?
All I can think of doing is repackaging the data with the ResultSetMetaData into a custom class.
Not really found too much posts on this issue. Microsoft website not any help at all.

This seems to be by design, according to "ResultSet Can Not Re-Read Row Data"' Error When Reading Data from a JDBC ResultSet Object:
This error occurs with ResultSet objects that contain a BLOB column (for example, text, ntext, or image data types). The driver cannot return a BLOB column out of order because it does not cache all the content of BLOB data types because of size limitations.
For any row in the ResultSet, you can read any column from left to right, and each column should be read only one time. If you try to read columns out of order, or if you re-read a column from the ResultSet, you may receive the error message that the "Symptoms" section describes.
This behavior is by design.
You either need to use a different JDBC driver, or refactor your code to only read those BLOB columns once for any given row. This a good idea anyway, since re-reading BLOBs can be a performance killer.

I would suggest reading the data once and copying to an Object array and passing it to methods instead of passing the resultset. That can be more clean code also.

The long winded nightmare approach (which does work) but I have lazily just implemented most of the getters.
Edit
As I mentioned, I am using JDK 1.6. However, when compiled against the JDK 1.7 a compilation error is encountered:
error: ReadOnlyResultSet is not abstract and does not override abstract method <T>getObject(String,Class<T>) in ResultSet
end Edit
To do this create a class that implements the ResultSet (Let Netbeans add the default method implementation stubs). Then in the constructor, store a reference to the initial ResultSet. In next(), cache the values in an array. Proabably have to do the same for previous() and other set cursor methods. So the class will look like:
public class ReadOnlyResultSet
implements ResultSet {
/**
* The original data source.
*/
private ResultSet source;
/**
* Cached values for the current row.
*/
private Object[] values;
/**
* Creates a new instance of <code>ReadOnlyResultSet</code>.
*/
public ReadOnlyResultSet(ResultSet source) {
this.source = source;
}
#Override
public boolean next() throws SQLException {
// NOTE: values[0] will always be null as JDBC is 1 based arrays
boolean next = source.next();
if( next ) {
values = new Object[source.getMetaData().getColumnCount() + 1];
for(int i = 1; i < source.getMetaData().getColumnCount(); i++ ) {
values[i] = source.getObject(i);
}
} else {
// no current row
values = new Object[] { };
}
return next;
}
// implement all of the not getter/setter methods
#Override
public void close() throws SQLException {
source.close();
}
// implement getters I am interested in
#Override
public String getString(int columnIndex) throws SQLException {
return (String) values[columnIndex];
}
#Override
public String getString(String columnLabel) throws SQLException {
return (String) values[findColumn(columnLabel)];
}
// just too much implementation but hopefully you get the drift
}

Related

Java: MySQL Query and return

I'm trying to access my database, inject some SQL Code and return the value out of it.
First of all, I'm a new to this stuff but I've came up with the following code:
public static ResultSet checkCmdAmount() throws Exception {
try {
// This will load the MySQL driver, each DB has its own driver
Class.forName("com.mysql.jdbc.Driver");
// Setup the connection with the DB
connect = DriverManager.getConnection(""+MyBot.mysqlDbPath+"",""+MyBot.mysqlDbUsername+"",""+MyBot.mysqlDbPassword+"");
PreparedStatement zpst=null;
ResultSet zrs=null;
zpst=connect.prepareStatement("SELECT COUNT(1) FROM eigenebenutzerbefehle");
zrs=zpst.executeQuery();
return zrs;
}catch (Exception e) {
throw e;
} finally {
close();
}
}
In my return, I get the following:
ResultSet: com.mysql.jdbc.JDBC4ResultSet#196da649
But I want actually the Amount of rows in my table.
When I execute the sql code through phpmyadmin I get 3 which is correct.
What is wrong here?
You need to read and get the desired values from the ResultSet. Do it as below:
public static int checkCmdAmount() throws Exception {
// ...
int count = 0;
while (zrs.next()) {
// Get the values from the current row...
count = zrs.getInt(1);
}
return count;
}
A ResultSet object contains all rows returned by executing an SQL query using a PreparedStatment or Statement from a database.
So when you executed
ResultSet zrs=null;
zpst=connect.prepareStatement("SELECT COUNT(1) FROM eigenebenutzerbefehle");
zrs=zpst.executeQuery();
return zrs;
as you said your SQL query will return number of rows, and that information is stored in ResultSet object zrs, but a ResultSet object's job is to store all the rows containing values from all columns specified or all rows in case of using *.
And when you are returning zrs you are returning a ResultSet object, and when you try and print an object what you get is the default value for an object's toString() conversion, which is in most cases objects types fully qualified name + a few extra characters.
And your updated code executes
if(zrs.next()){
return zrs.getInt(1);
}
else{
return -1;
}
here zrs.next() call moves the zrs to valid next record(or row) from where values can be retrieved, it also returns true or false depending upon the presence of record. In your example if you add one more call to zrs.next() then it would be returning false.
zrs.getInt(1) will return the value in the row the zrs pointing to and the value of the first column, it has in that row, which is in your case only column.

Database doesn't like reading values from a loop

I have a java database successfully connected to my java code. Thats all fine as it works and all.
When I store a result from the database into a variable ... it works perfectly.
Now as I have to do this 8 times I used a loop and a array however by using a try catch tool it gives out a error of, Error is: java.lang.NullPointerException
Futher investigation shows that it seems to not like the loop strangely.
public String Title []; //in class but out of any methods
public void gettinginfo ()
{
try
{
int AB = 0; //array base starts from 0
//ID in database starts from 1
for (int i = 1; i<=8; i++)
{
String query = "SELECT * FROM students WHERE ID = " + i;
Rs = St.executeQuery(query);
while (Rs.next())
{
Title[AB] = Rs.getString("StudentName");
AB++;
}
}
}
catch (Exception ex)
{
System.out.println("Error is: " + ex);
}
}
What line is your NullPointerException occurring on? Likely your Title array has not been initialized. If you know how many rows the query will return, you can say:
Title = new String[numRows];
But if you don't, you'll need to either run a SELECT count(*) ... query or use an ArrayList or other resizable list, instead of an array.
Your code is also very poorly structured, which is no small part of why you're having trouble debugging this. I've cleaned up your code below, with comments explaining my changes:
public class YourClass
{
private static final int MAX_ID = 8; // or however you want to set the size
private String[] title; // [] after the type is easier to read, lower case variables
private Connection conn; // I'm assuming the class will be provided a DB connection
// Note the Statement and ResultSet objects are not defined in the class, to
// minimize their scope.
public void queryInfo() // name suggests a query (potentially expensive) will be run
{
title = new String[MAX_ID]; // **initialize title**
// We use a try-with-resources block to ensure the statement is safely closed
// even better would be to use a PreparedStatement here
try(Statement st = conn.statement())
{
// You're executing 8 separate queries here, where one will do
String query = "SELECT * FROM students WHERE ID >= 1 AND ID <= "+MAX_ID;
// Again, we need to close the result set when we're done
try(ResultSet rs = st.executeQuery(query))
{
int i = 0;
while (rs.next())
{
title[i++] = rs.getString("StudentName");
}
} // close our ResultSet
} // close our Statement
}
// provide a separate getter method, rather than making the array public
public String[] getTitles()
{
return title;
}
}
There's still more that could be improved - using an array seems like a poor design, as does calling a method which populates a class variable rather than simply having queryInfo() return a new array. You can also look into using PreparedStatement. Hopefully these suggestions help.
Make sure that Title array and Statement St objects are and not null. These are the only two reasons that I suspect. Give FULL stacktrace if it doesn't work.
Title array is NULL. "new" this array to the size equal to number of rows. If you don't know the rows, fire a count(*) query first, find out the no of rows and then intantiate Title array or use ArrayList<String> instead of String array.
I am assuming that you have not initialized your Title array, you have to set it equal to something or it will just be null which will cause a nullPointerException, but as others have stated there is no way to be sure since your haven't given us a full stack trace or even the line number of the exception. In this case the exception should be handled as such:
try{
//your code here
}catch(Exception ex){
ex.printStackTrace();
}
This code will give you the full stack trace making it much easier to track down the issue.
Also you may want to consider using an ArrayList instead of an array:
List<String> Title = new ArrayList<String>();
Then to add to it:
Title.add(Rs.getString("StudentName"));
If you need it as an array later then:
String[] title = Title.toArray(new String[Title.size()]);
You can read more about ArrayLists here.

Insert data into Database - What is the best way to do it

I have to insert anywhere between 50 - 500 contact's information into the database. I have 4 arraylists that contain image, name, number, bool variable respectively.
Each row in the data is made up of a combination of all the 4 arraylists along with a SNO. Please refer to the image below.
My question is, let's say i have 500 contacts that I retrieve from the User's Contacts list. Is it a good thing that, I have a function that inserts each row at a time into the table and call it 500 times? or is there any other way? A mean idea would be to combine all the arraylists together, pass it to the function and retrieve the data there and repeat the insert statement 500 times.
What is better in terms of performance?
for(int i =0; i < 500; i++)
{
dbObj.insert_row(par1, par2, par3, par4, ...);
}
OR
function insert_row(Combined ArrayLists)
{
for(int i=0; i<500; i++)
{
db.execSql(//Insert Statement);
}
}
Insert data into Database - What is the best way to do it
I suggest you to create own object that will represent your table where properties of object will be equal to columns in table, e.q.
public class Contact {
private String name;
private String number;
private String image;
private boolean conn;
//getters and setters
}
Now your approach sounds like "spaghetti code". There is not need to have four ArrayLists and this design is not efficient and correct.
Now, you will have one ArrayList of Contact object with 500 childs.
What is the best way to insert?
For sure wrap your insertion to one TRANSACTION that speed up your inserts rapidly and what is the main your dealing with database becomes much more safer and then you don't need to care about possibility of losing database integrity.
Example of transaction(one method from my personal example project):
public boolean insertTransaction(int count) throws SQLException {
boolean result = false;
try {
db = openWrite(DataSource.getInstance(mContext));
ContentValues values = new ContentValues();
if (db != null) {
db.beginTransaction();
for (int i = 0; i < count; i++) {
values.put(SQLConstants.KEY_TYPE, "type" + i);
values.put(SQLConstants.KEY_DATE, new Date().toString());
db.insertOrThrow(SQLConstants.TEST_TABLE_NAME, SQLConstants.KEY_TYPE, values);
values.clear();
}
db.setTransactionSuccessful();
result = true;
}
return result;
}
finally {
if (db != null) {
db.endTransaction();
}
close(db);
}
}
If you are going to insert 500 records into database you should use transaction
database.beginTransaction();
try {
// perform inserts
database.setTransactionSuccessful();
finally {
database.endTranasction();
}
As mentioned before create Your own class to represent one row or use ContentValues class
SQlite doesn't provide possibility to insert many rows in one query like in MySQL but there is some way around it You can read here.
If You decide to use method described in this link it is better if You create a function to generate this query and run it just once. Otherwise As others mentioned already you may use transaction to improve a performance of many inserts.
Converting 4 arrays into one object array makes the code better. but you can create these objects without doing it like this.
Prepare the sql statement with bind variables (? or :vars), then execute the statement multiple times in a loop by setting the bind variables for each row.
String sql = "insert into..... values (?,?,?,?)";
// Get connection etc
PreparedStatement stmt = conn.prepareStatement(sql);
for(int i =0; i < 500; i++)
{
stmt.setString(1, name.get(i));
stmt.setNumber(2, number.get(i));
...
stmt.executeUpdate();
}

Total Number of Row Resultset getRow Method

Read the Following Code:
public class selectTable {
public static ResultSet rSet;
public static int total=0;
public static ResultSet onLoad_Opetations(Connection Conn, int rownum,String sql)
{
int rowNum=rownum;
int totalrec=0;
try
{
Conn=ConnectionODBC.getConnection();
Statement stmt = Conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
String sqlStmt = sql;
rSet = stmt.executeQuery(sqlStmt);
total = rSet.getRow();
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
System.out.println("Total Number of Records="+totalrec);
return rSet;
}
}
The folowing code dos't show actual total:
total = rSet.getRow();
my jTable display 4 record in jTable but total = 0; when I evaluate through debug, it shows:
total=(int)0;
rather than total=(int)4
And if I use
rSet=last(); above from the code total = rSet.getRow();
then total shows accurate value = 4 but rSet return nothing. then jTable is empty.
Update me!
BalusC's answer is right! but I have to mention according to the user instance variable such as:
rSet.last();
total = rSet.getRow();
and then which you are missing
rSet.beforeFirst();
the remaining code is same you will get your desire result.
You need to call ResultSet#beforeFirst() to put the cursor back to before the first row before you return the ResultSet object. This way the user will be able to use next() the usual way.
resultSet.last();
rows = resultSet.getRow();
resultSet.beforeFirst();
return resultSet;
However, you have bigger problems with the code given as far. It's leaking DB resources and it is also not a proper OOP approach. Lookup the DAO pattern. Ultimately you'd like to end up as
public List<Operations> list() throws SQLException {
// Declare Connection, Statement, ResultSet, List<Operation>.
try {
// Use Connection, Statement, ResultSet.
while (resultSet.next()) {
// Add new Operation to list.
}
} finally {
// Close ResultSet, Statement, Connection.
}
return list;
}
This way the caller has just to use List#size() to know about the number of records.
The getRow() method retrieves the current row number, not the number of rows. So before starting to iterate over the ResultSet, getRow() returns 0.
To get the actual number of rows returned after executing your query, there is no free method: you are supposed to iterate over it.
Yet, if you really need to retrieve the total number of rows before processing them, you can:
ResultSet.last()
ResultSet.getRow() to get the total number of rows
ResultSet.beforeFirst()
Process the ResultSet normally
As others have answered there is no way to get the count of rows without iterating till the end. You could do that, but you may not want to, note the following points:
For a many RDBMS systems ResultSet is a streaming API, this means
that it does not load (or maybe even fetch) all the rows from the
database server. See this question on SO. By iterating to the
end of the ResultSet you may add significantly to the time taken to
execute in certain cases.
A default ResultSet object is not updatable and has a cursor
that moves forward only. I think this means that unless you
execute
the query with ResultSet.TYPE_SCROLL_INSENSITIVE rSet.beforeFirst() will throw
SQLException. The reason it is this way is because there is cost
with scrollable cursor. According to the documentation, it may throw SQLFeatureNotSupportedException even if you create a scrollable cursor.
Populating and returning a List<Operations> means that you will
also need extra memory. For very large resultsets this will not
work
at all.
So the big question is which RDBMS?. All in all I would suggest not logging the number of records.
One better way would be to use SELECT COUNT statement of SQL.
Just when you need the count of number of rows returned, execute another query returning the exact number of result of that query.
try
{
Conn=ConnectionODBC.getConnection();
Statement stmt = Conn.createStatement();
String sqlStmt = sql;
String sqlrow = SELECT COUNT(*) from (sql) rowquery;
String total = stmt.executeQuery(sqlrow);
int rowcount = total.getInt(1);
}
The getRow() method will always yield 0 after a query:
ResultSet.getRow()
Retrieves the current row number.
Second, you output totalrec but never assign anything to it.
You can't get the number of rows returned in a ResultSet without iterating through it. And why would you return a ResultSet without iterating through it? There'd be no point in executing the query in the first place.
A better solution would be to separate persistence from view. Create a separate Data Access Object that handles all the database queries for you. Let it get the values to be displayed in the JTable, load them into a data structure, and then return it to the UI for display. The UI will have all the information it needs then.
I have solved that problem. The only I do is:
private int num_rows;
And then in your method using the resultset put this code
while (this.rs.next())
{
this.num_rows++;
}
That's all
The best way to get number of rows from resultset is using count function query for database access and then rs.getInt(1) method to get number of rows.
from my code look it:
String query = "SELECT COUNT() FROM table";
ResultSet rs = new DatabaseConnection().selectData(query);
rs.getInt(1);
this will return int value number of rows fetched from database.
Here DatabaseConnection().selectData() is my code for accessing database.
I was also stuck here but then solved...

returning a result set from a java stored procedure through SQL "select * from "

Can I get the result from a java stored procedure (oracle) directly through an SQL select * from statement ?
On the database I would have a java stored procedure / function, which when it called returns a multi-column, multi-row result set.
I would like to access these results directly through a select * from [table] statement.
So the java stored procedure should behave like a table.
In MySQL the following should be possible (but not java stored procedures): SELECT col1 FROM (EXEC proc1)
Is this possible in oracle where proc1 is a java stored procedure?
This answer on a different forum may help you.
Look at the sample in the bottom of the message to see how to select from a collection returned by a Java method ( that may also be a Java Stored Procedure).
Here is a sample on how to do it with Java Stored Procedure
1) Create DB object to define the type of the returned rows:
create type try_obj as object (
field_a number,
field_b varchar2(10)
)
/
create type try_obj_tab as table of try_obj
/
2) Create the Java class on the DB with a static method(GetSampleResult) that returns a Collection
create or replace and compile java source named QueryReturn as
import java.sql.*;
import java.util.*;
import oracle.jdbc.*;
import oracle.jdbc.pool.OracleDataSource;
import oracle.sql.*;
public class QueryReturn implements ORADataFactory,ORAData{
private NUMBER field1;
private CHAR field2;
public QueryReturn(OracleConnection conn,int n,String c) throws SQLException {
field1 = new NUMBER(n);
field2 = new CHAR(c,oracle.sql.CharacterSet.make(conn.getStructAttrCsId()));
}
public QueryReturn(NUMBER n, CHAR c) {
field1 = n;
field2 = c;
}
public QueryReturn(Object[] attributes) {
this(
(NUMBER) attributes[0],
(CHAR) attributes[1]
);
}
public QueryReturn(Datum d) throws SQLException {
this(((STRUCT) d).getOracleAttributes());
}
public ORAData create(Datum d, int sqlType) throws SQLException {
if (d == null)
return null;
else {
return new QueryReturn(d);
}
}
public STRUCT toSTRUCT(Connection conn) throws SQLException {
StructDescriptor sd =
StructDescriptor.createDescriptor("TRY_OBJ", conn);
Object [] attributes = { field1,field2 };
return new STRUCT(sd, conn, attributes);
}
public Datum toDatum(Connection conn) throws SQLException {
return toSTRUCT(conn);
}
public static ARRAY GetSampleResult() throws SQLException, ClassNotFoundException {
// initialize the connection
OracleConnection conn = null;
conn = (OracleConnection) (new oracle.jdbc.OracleDriver()).defaultConnection();
// create the return java array
// There will be two Rows
// 1 abc
// 2 dce
QueryReturn javaArray[] = {
new QueryReturn(conn,1,"abc"),
new QueryReturn(conn,2,"dce")
};
// Map the java class to the Oracle type
Map map = conn.getTypeMap();
map.put("TRY_OBJ", Class.forName("QueryReturn"));
ArrayDescriptor jTryObjArrayDesc = ArrayDescriptor.createDescriptor (
"TRY_OBJ_TAB",
conn
);
// create an Oracle collection on client side to use as parameter
ARRAY oracleCollection = new ARRAY(jTryObjArrayDesc,conn,javaArray);
return oracleCollection;
}
}
3) Create the Wrap to use Java Stored Procedure in a function
create or replace function GetSampleResult
return try_obj_tab
AS LANGUAGE JAVA
NAME 'QueryReturn.GetSampleResult() return oracle.sql.ARRAY';
4) Show the result
SQL> select *
2 from table(GetSampleResult())
3 /
FIELD_A FIELD_B
---------- ----------
1 abc
2 dce
SQL>
Write a SSP (SQL Stored Procedure) to call JSP (Java Stored Procedure), and then use that SSP in your query. Simple eh.
Further, take a look at CallableStatement.
I have never used it in combination with Java calling the stored procedure but my guess is it should be possible to use the 'pipelined' functionality in recent Oracle databases.
See here or Google/Bing for it to learn more.
I'm afraid it is not possible. But if your database supports functionality of selecting data from stored procedure in way you mentioned, you can create a view and select from it.
Anyway Oracle can return resultset from a stored procedure and you can access it from Java. See this link
Tom Kyte says "You cannot return a result set from a java stored procedure to plsql."

Categories

Resources