I am running the code, however am getting a "Invalid state, the ResultSet object is closed." error. What is causing the error?
try{
query = "SELECT * FROM BUNDLE_TEMP "
+ "MINUS "
+ "SELECT * FROM BUNDLE";
rs = stmt.executeQuery(query);
while (rs.next()){
String bundle = rs.getString("BUNDLE");
String week = rs.getString("WEEK");
String sched_dt = rs.getString("SCHED_DT").replace(" 00:00:00.0", "");
String dropper_id = rs.getString("DROPPER_ID");
query = "INSERT INTO BUNDLE "
+ "VALUES ('"
+ bundle+"','"
+ week+"','"
+ sched_dt+"','"
+ dropper_id+"')";
stmt.executeUpdate(query);
}
}catch(Exception e){
System.out.println("Error while trying to insert into BUNDLE\n"+query+"\n"+ e);
}
You cannot execute another SQL query on the same Statement you're currently iterating over with a ResultSet. Doing this will close the previously open cursor (your SELECT query resp. ResultSet):
To quote the API docs for Statement:
By default, only one ResultSet object per Statement object can be open
at the same time. Therefore, if the reading of one ResultSet object is
interleaved with the reading of another, each must have been generated
by different Statement objects. All execution methods in the Statement
interface implicitly close a statment's current ResultSet object if an
open one exists.
Create another Statement instance from your Connection, let's call it updateStmt and executeUpdate() on that one.
Also, look into Prepared Statements for your update, it will probably be more performant and secure.
Related
Im implementing a subquery in java which requires 2 parameters to be fed using a prepared statement.
Passing parameter within the inner query is working but then parameter is not passed to the outer one.
During execution i get an error on stacktrace saying :No operations allowed after statement closed. and hence the prepared statement returns wrong results.Question: How best can i pass the parameter successfully to the outer table.
Below is a sample code of my implementation
pst = conn.prepareStatement("SELECT slot.time_slot_id,slot.`date`,slot.count FROM
(SELECT time_slot_id,ue.`date`,COUNT(ue.user_id) AS count FROM user_event ue
RIGHT JOIN `time_slot` t ON ue.time_slot_id = t.id
WHERE ue.status= 1 AND event_id=?
GROUP BY ue.`date`,time_slot_id
) AS slot WHERE slot.count >=? ");
pst.setInt(1,eventId);
//here is the parameter that is passed to the outer table
pst.setInt(2,count);
rs = pst.executeQuery();
here is an update for the entire method am implementing as below;
public static DefaultListModel Fetch(int eventId){
DBconnection.connect();
DefaultListModel fetchedSlots= new DefaultListModel();
try{
//
pst = conn.prepareStatement("SELECT slot.time_slot_id,slot.`date`,slot.count FROM \n" +
"(SELECT time_slot_id,ue.`date`,COUNT(ue.user_id) AS count FROM user_event ue \n" +
"RIGHT JOIN `time_slot` t ON ue.time_slot_id = t.id\n" +
"WHERE ue.status= 1 AND event_id=?\n" +
"GROUP BY ue.`date`,time_slot_id) AS slot\n" +
"WHERE slot.count >? ");
pst.setInt(1,eventId);
//here a function is invocked using eventId to get counts
pst.setInt(2,FetchCounts(eventId));
rs = pst.executeQuery();
while(rs.next()){
fetchedSlots.addElement(new ModelSlot(rs.getInt("time_slot_id"),rs.getString("date")));
}
DBconnection.CloseConnection();
}catch (SQLException ex){
System.out.println(ex.getMessage());
}
return fetchedSlots;
}
I assume that the error happen in case like this :
Open statement
...
Use statement
...
Close statement <---------- You can't use your statement after close it
...
Use same statement again <---------- You are here, your statement is close
To solve your problem you have two choices :
Close the statement in the end of your work, you can check this Closing Database Connections in Java
Open a new statement, for example statement = connection.createprePareStatement();
EDIT
Like i said before, your problem is with connection, when you call this method :
pst.setInt(2, FetchCounts(eventId));
//---------------^^^
In fact this open a new connection, then close it in the end, so when you execute your query it rs = pst.executeQuery();, the connection is closed, and this cause this problem, so don't close the connection in your method FetchCounts(T eventId);.
After trials, i concatenated the second parameter to the prepared statement rather than before and it worked.
AS below;
pst = conn.prepareStatement("SELECT
slot.time_slot_id,slot.`date`,slot.count FROM \n" +
"(SELECT time_slot_id,ue.`date`,COUNT(ue.user_id) AS count FROM user_event ue \n" +
"RIGHT JOIN `time_slot` t ON ue.time_slot_id = t.id\n" +
"WHERE ue.status= 1 AND event_id=?\n" +
"GROUP BY ue.`date`,time_slot_id) AS slot\n" +
"WHERE slot.count >" + FetchCounts(eventId));
pst.setInt(1,eventId);
rs = pst.executeQuery();
AS evident above, the result of the invoked method FetchCounts(eventId) is passed directly to the outer table of the subQuery within the prepared statement.
This is driving me mad because I cannot make any sense of it. I am executing the following code:
nameString = jcbClientList.getItemAt(jcbClientList.getSelectedIndex());
System.out.println(" Name String = " + nameString );
sql = "SELECT * FROM clients WHERE Name = \'" + nameString + "\'";
System.out.println(sql);
try {
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
while( rs.next()) {
clientID = rs.getInt(1);
}
}
catch(SQLException se) {
msg = "Problem getting client ID from DB \n" + se.getLocalizedMessage();
System.out.println(msg);
JOptionPane.showMessageDialog(null, msg);
}
The SQL string be built is correct. I have checked this by taking the System.out.println(sql) output of the string and pasting it into other code and it work perfectly. However, in this context I am getting an exception:
Invalid cursor state - no current row.
Even if I change the sql to be 'SELECT * FROM clients' which should return 20 rows and does elsewhere in the application, it still gives the same error. The database being addressed is an embedded Derby DB.
I seem to recall having run into specific JDBC drivers that did not properly implement the part that says " A ResultSet cursor is initially positioned before the first row ". I got around it by first doing a first() (or beforeFirst()) call and only then start invoking next().
This post showed executing multiple queries in a single JDBC invocation (against a SQL Server database) by separating them with semicolons. When I tried to do the same with Oracle 10G, an error "invalid character" propped up :
class db
{
public static void main(String aa[])throws Exception
{
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:#//192.168.10.29:1521/ttt","username","password");
PreparedStatement stat = conn.prepareStatement("select voila from app where rownum<4; select code from process where rownum<4");
stat.execute();
while (stat.getMoreResults()){
ResultSet rs = stat.getResultSet();
while (rs.next()){
System.out.println(rs.getString(1));
}
}
conn.close();
}
}
What am I doing wrong ?
You are doing nothing wrong (except to assume that all DBMS work the same)
Oracle (and its JDBC driver) simply does not support this.
You need to run each SELECT individually.
Btw: this is one of the reason that some SQL injection attacks don't work with Orace - especially the famous "little bobby tables" cartoon.
It's possible to get multiple result sets back from Oracle into JDBC in a single call. There are a few ways to do it; a good post at Oracle-Base shows how.
The mechanism I use is to make an anonymous block in a callable statement, then bind a SYS_REFCURSOR for each result set as an output parameter.
Here's some code that does just that. It's lazy for error handling, but it gets the idea across:
public void getMultiple() throws Exception {
// get connection
Connection conn = DriverManager.getConnection(TestConfig.JDBC_URL, TestConfig.DB_USERNAME, TestConfig.DB_PASSWORD);
// here's the statement; it uses an anonymous block. In that block,
// we've declared two SYS_REFCURSOR objects which are opened over our
// SELECT statements. Once the statements are opened, we bind the
// SYS_REFCURSOR objects so they can be retrieved from JDBC
String s =
"DECLARE" +
" l_rs1 SYS_REFCURSOR; " +
" l_rs2 SYS_REFCURSOR; " +
"BEGIN "+
" OPEN l_rs1 FOR " +
" SELECT 'Moose' FROM DUAL;" +
" OPEN l_rs2 FOR " +
" SELECT 'Squirrel' FROM DUAL; " +
" ? := l_rs1;" +
" ? := l_rs2;" +
"END;";
// prepare the callable statement, registering
// the output parameter we want
CallableStatement cs = conn.prepareCall(s);
cs.registerOutParameter(1, OracleTypes.CURSOR);
cs.registerOutParameter(2, OracleTypes.CURSOR);
// execute the callable statement
cs.execute();
// retrieve the result sets by getting the bound output objects and
// casting them to Java ResultSet objects
ResultSet rs1 = (ResultSet) cs.getObject(1);
ResultSet rs2 = (ResultSet) cs.getObject(2);
// advance the first result set and print the string it yields
rs1.next();
System.out.printf("Result set 1 has '%s'\n", rs1.getString(1));
// advance the second result set and print the string it yields
rs2.next();
System.out.printf("Result set 2 has '%s'\n", rs2.getString(1));
// close everything up
rs2.close();
rs1.close();
cs.close();
conn.close();
}
I hope that helps you out!
I am trying to execute a BULK INSERT statement on SQL Server 2008 Express.
(It basically takes all fields in a specified file and inserts these fields into appropriate columns in a table.)
Given below is an example of the bulk insert statement--
BULK INSERT SalesHistory FROM 'c:\SalesHistoryText.txt' WITH (FIELDTERMINATOR = ',')
Given below is the Java code I am trying to use (but its not working)...Can someone tell me what I am doing wrong here or point me to a java code sample/tutorial that uses the Bulk Insert statement? --
public void insertdata(String filename)
{
String path = System.getProperty("user.dir");
String createString = "BULK INSERT Assignors FROM " + path + "\\" +filename+ ".txt WITH (FIELDTERMINATOR = ',')";
try
{
// Load the SQLServerDriver class, build the
// connection string, and get a connection
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String connectionUrl = "jdbc:sqlserver://arvind-pc\\sqlexpress;" +
"database=test01;" +
"user=sa;" +
"password=password1983";
Connection con = DriverManager.getConnection(connectionUrl);
System.out.println("Connected.");
// Create and execute an SQL statement that returns some data.
String SQL = "BULK INSERT dbo.Assignor FROM " + path + "\\" +filename+ ".txt WITH (FIELDTERMINATOR = ',')";
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(SQL);
// Iterate through the data in the result set and display it.
while (rs.next())
{
//System.out.println(rs.getString(1) + " " + rs.getString(2));
System.out.println(" Going through data");
}
}
catch(Exception e)
{
System.out.println(e.getMessage());
System.exit(0);
}
}
I'd guess that your SQL string is missing the single quotes around the filename. Try the following:
String SQL = "BULK INSERT dbo.Assignor FROM '" + path + "\\" +filename+ ".txt' WITH (FIELDTERMINATOR = ',')";
EDIT in response to your comment: I wouldn't expect there to be anything in the ResultSet following a bulk insert, in much the same way that I wouldn't expect anything in a ResultSet following an ordinary INSERT statement. These statements just insert the data they are given into a table, they don't return it as well.
If you're not getting any error message, then it looks like your bulk insert is working. If you query the table in SQLCMD or SQL Server Management Studio, do you see the data?
INSERT, UPDATE, DELETE and BULK INSERT statements are not queries, so you shouldn't be using them with the executeQuery() method. executeQuery() is only intended for running SELECT queries. I recommend using the executeUpdate(String) method instead. This method returns an int, which is normally the number of rows inserted/updated/deleted.
When I execute the following code, I get an exception. I think it is because I'm preparing in new statement with he same connection object. How should I rewrite this so that I can create a prepared statement AND get to use rs2? Do I have to create a new connection object even if the connection is to the same DB?
try
{
//Get some stuff
String name = "";
String sql = "SELECT `name` FROM `user` WHERE `id` = " + userId + " LIMIT 1;";
ResultSet rs = statement.executeQuery(sql);
if(rs.next())
{
name = rs.getString("name");
}
String sql2 = "SELECT `id` FROM `profiles` WHERE `id` =" + profId + ";";
ResultSet rs2 = statement.executeQuery(sql2);
String updateSql = "INSERT INTO `blah`............";
PreparedStatement pst = (PreparedStatement)connection.prepareStatement(updateSql);
while(rs2.next())
{
int id = rs2.getInt("id");
int stuff = getStuff(id);
pst.setInt(1, stuff);
pst.addBatch();
}
pst.executeBatch();
}
catch (Exception e)
{
e.printStackTrace();
}
private int getStuff(int id)
{
try
{
String sql = "SELECT ......;";
ResultSet rs = statement.executeQuery(sql);
if(rs.next())
{
return rs.getInt("something");
}
return -1;
}//code continues
The problem is with the way you fetch data in getStuff(). Each time you visit getStuff() you obtain a fresh ResultSet but you don't close it.
This violates the expectation of the Statement class (see here - http://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html):
By default, only one ResultSet object per Statement object can be open at the same time. Therefore, if the reading of one ResultSet object is interleaved with the reading of another, each must have been generated by different Statement objects. All execution methods in the Statement interface implicitly close a statment's current ResultSet object if an open one exists.
What makes things even worse is the rs from the calling code. It is also derived off-of the statement field but it is not closed.
Bottom line: you have several ResultSet pertaining to the same Statement object concurrently opened.
A ResultSet object is automatically
closed when the Statement object that
generated it is closed, re-executed,
or used to retrieve the next result
from a sequence of multiple results.
I guess after while(rs2.next()) you are trying to access something from rs1. But it's already closed since you reexecuted statement to get rs2 from it. Since you didn't close it, I beleive it's used again below.