Trouble with ResultSet using executeUpdate - java

I am new to programming and have run into a problem while using executeUpdate with the resultSet next() method.
It iterates once only through the result set then the execute update closes the result set. I get error: ResultSet not open. Operation "next" not permitted. Verify that autocommit is off.
I have added the con.setAutoCommit(false) statement but problem still persists.
I need to run the update multiple times with different variable values.
Here is the code I have:
try {
String eidQuery = "SELECT EID FROM EMPLOYEE_DATA WHERE ACTIVE = TRUE ORDER BY EID";
int nextEID;
Statement st = con.createStatement();
con.setAutoCommit(false);
rs = st.executeQuery(eidQuery);
while (rs.next()){
nextEID = rs.getInt(1);
String getDailyTotals = "SELECT DATE, SUM(TOTAL), MAX(OUT_1) FROM PUNCHES WHERE EID = " + nextEID + " AND DATE >= '" + fd + "' "
+ "AND DATE <= '" + td + "' GROUP BY DATE";
ResultSet rs2 = st.executeQuery(getDailyTotals);
while (rs2.next()){
double dailyTotal = rs2.getDouble(2);
if (dailyTotal > 8){
double dailyOT = dailyTotal-8;
String dailyDate = rs2.getDate(1).toString();
Timestamp maxTime = rs2.getTimestamp(3);
String updateOT = "UPDATE PUNCHES SET OT = " + dailyOT + " WHERE EID = " + nextEID + " AND DATE = '" + dailyDate + "' AND OUT_1 = '" + maxTime + "'";
st.executeUpdate(updateOT);
}
}
}
rs = st.executeQuery("SELECT PUNCHES.EID, EMPLOYEE_DATA.FIRST_NAME, EMPLOYEE_DATA.LAST_NAME, SUM(PUNCHES.OT) FROM PUNCHES "
+ "JOIN EMPLOYEE_DATA ON PUNCHES.EID = EMPLOYEE_DATA.EID WHERE PUNCHES.DATE >= '" + fd + "' AND PUNCHES.DATE <= '" + td + "' GROUP BY EMPLOYEE_DATA.FIRST_NAME, EMPLOYEE_DATA.LAST_NAME, PUNCHES.EID");
Reports.setModel(DbUtils.resultSetToTableModel(rs));
} catch (SQLException ex) {
Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
JOptionPane.showMessageDialog(null, ex);
}

You're new to programming and (obviously) Java. Here are a few recommendations that I can offer you:
Do yourself a favor and learn about PreparedStatement. You should not be creating SQL by concatenating Strings.
You are committing the classic newbie sin of mingling database and UI Swing code into a single, hard to debug lump. Better to decompose your app into layers. Start with a data access interface that encapsulates all the database code. Get that tested and give your UI an instance to work with.
Do not interleave an update query inside the loop over a ResultSet. Better to separate the two completely.
Read about MVC. You'll want your Swing View to be separate from the app Controller. Let the Controller interact with the data access interface, get the results, and give the results to the View for display. Keep them decoupled and separate.
Learn JUnit. It'll help you with testing.

From the java.sql.ResultSet javadoc:
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.
After you execute the update, the prior ResultSet is closed. You need to rework your code to account for that.
https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html
The easiest way to rework might be to use two Statements, one for the query and one for the update, but as noted in duffymo's answer there's a fair amount more you could do to improve things.

From API's statement documentation "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"
You need two different Statements if you want to read two different ResultSet in the same nested loops.

Related

UCAExc 3.0.7 unexpected token Errors

I'm working on a project that wants me to integrate a Microsoft Access database into a Java program. I have successfully connected to the database however my SQL statements are not updating the DB. I have defined variables that read user input and use that information as the conditional for the WHERE statement. I have taken parts of the first query out and have gotten positive results but the whole statement refuses to cooperate. What do I need to change about the first query to make it run?
result = statement.executeQuery("SELECT slipNumber FROM Slip WHERE (slipOpen = -1 & slipLength >= " + boatLengthdub + "& slipDepth >= " + boatDepthdub + ")"+ "LIMIT 1" );
statement.executeQuery("INSERT INTO Slip (slipOpen, boatID) VALUES (0," + boatIDdub + ")");
System.out.println("Have a" + result);
You appear to be trying to use the ampersand character (&) where you should be using the SQL keyword AND. You also should be using PreparedStatement objects to perform parameterized queries, e.g.,
String sql =
"SELECT slipNumber FROM Slip " +
"WHERE slipOpen = -1 AND slipLength >= ? AND slipDepth >= ? " +
"LIMIT 1";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setDouble(1, boatLengthdub); // assuming that they are Double values
ps.setDouble(2, boatDepthdub);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
System.out.println(rs.getInt("slipNumber"));
} else {
System.out.println("Not found.");
}
I fully agree. And when you perform an update, I'd suggest to use 'statement.executeUpdate(query)'.

JDBC timeout with ResultSet am I doing it right?

I have a JDBC ResultSet that gives me a TimeOut after only a few thousand rows are processed. I have a few million rows to process, so I'd like to tweak my program to avoid this, just not sure what needs to be tweaked.
Database table is indexed and returns data quickly using selection criteria, so I don't believe it is on the database side. I'm returned 14 columns mixed between address columns and ints. Not a lot of data.
I'm doing a connection.createStatement() and then building the SQL from there. The answer might be I should use a prepared statement.
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
String jobNameFilter = (Cli.getJobName() != null) ? " AND [JobName] = '" + Cli.getJobName() + "'" : "";
String sortOrder = (Cli.isAscending()) ? "ASC" : "DESC";
String orderByClause = Cli.isRandom() ? " ORDER BY [Randomizer] " + sortOrder + ",[RecordID] " + sortOrder : " ORDER BY [RecordID] " + sortOrder;
String startingIdFilter = (Cli.getStartingId() != null) ? " AND [RecordId] > " + Cli.getStartingId() : "";
String driverQuery = "SELECT [RecordID], [Column1] AS [TrackingID], [Address]" + ", [Suite] AS [AptSuiteOther], [City], [Building2Key]"
+ ", [ST] AS [State], [ZIPCode]" + ", [BusinessName], [ContactLastName], [Suite]" + ", [Phone], [EmailAddress]"
+ " FROM [Project].[TestSet] WITH (READUNCOMMITTED)"
+ " INNER JOIN [Project].[State] sttable ON sttable.[ST] = UPPER([Project].[TestSet].[ST]) AND [TerritoryFlag] = 0" + " WHERE [BuildingKey] = 0 " + jobNameFilter
+ startingIdFilter + " AND (([FirstResponse] IS NULL AND ([Building2Key] IS NULL OR [Building2Key] = 0)) OR ([Building2Key] > 0 AND [SecondResponse] IS NULL)) " + orderByClause;
rs = stmt.executeQuery(driverQuery);
} catch (SQLException e1) {
logger.error("SQLException", e1);
}
try {
while (rs.next()) {
int recordId = rs.getInt("RecordID");
// Process data
numberProcessed++;
}
} catch (SQLException sqle) {
logger.error("SQLException", sqle);
}
I'm closing all the ResultSet, Connection and Statement in a finally statement at a different level also.
I'm not sure if I need to set the timeout to something higher, setFetchSize to something greater? Trap timeout and create ResultSet again?
Change logic to only pull one row at a time?
You'd have to profile your app to find out for sure, but I'm guessing that the "// Process data" part is the culprit. You're holding the connection open while process all of the rows.
I'd suggest that you read a batch of rows at a time, close the statement, and then process the batch. Then do a select for the next batch, rinse and repeat.
Selecting one row at a time would introduce a lot of overhead, so I wouldn't suggest doing that.
Also, make sure that you're using a connection pool, so that you don't actually have to build a new Connection each time. The pool will keep it open for you, and recycle it if it goes dead / times out.

Retriving data from different tables in database sending SQL code to be executed, returning data?

I am making a class in Java to connect and otherwise talk to a database, that I have set up elsewhere. I want to use the executeQuery method for a statement, and recieve a ResultSet, from where I will retrieve the information received in the ResultSet.
My issue is with the SQL command, as in; the query I'm sending to the database. Something appears to be wrong, and I get an SQLException at the very point where I send the command, meaning I must have done something wrong.
Maybe I'm writing something wrong? I can't tell, since I've simply tried to follow the example guidance provided for this course the best way I can.
Here is the query I'm trying to send:
"SELECT fr.DepartureLocation, fr.Destination, d.Date, d.Time FROM `FlightRoute` fr,
`Departure` d WHERE d.FlightRouteId = fr.Id AND d.Date > " + dateFrom + " AND
d.Date < " + dateTo + " AND fr.Destination = `" + destination + "`;"
The "dateFrom", "dateTo" and "destination" are all parameters for the method I'm calling, and I'm trying to limit the results I get from this statement, to those within a certain date-span as well as having a specific destination.
I might add that the dates are integers lined up such as this: 20131205
This should make it so that later dates are a higher number.
Is the way that I use the parameters with the SQL code wrong somehow, or did I make generally faulty SQL code here?
Thanks a lot, to anyone who might be able to provide a correct SQL code for me to use, so that I may see what I did wrong (since I'll have to make a few more similarly working SQL statements)!
Thanks in advance! :)
EDIT:
Here is the requested code where I am announcing the query and trying to execute it:
try
{
ArrayList<Departure> departures = new ArrayList<>();
Statement stmt = con.createStatement();
System.out.println("derp0");
String query = "SELECT fr.DepartureLocation, fr.Destination, d.Date, d.Time FROM FlightRoute fr, Departure d WHERE d.FlightRouteId = fr.Id AND d.Date > " + dateFrom + " AND d.Date < " + dateTo + " AND fr.Destination = `" + destination + "`;";
System.out.println("derp0.1");
ResultSet rs = stmt.executeQuery(query);
System.out.println("derp1");
As a short explaination, the souts are to check where the SQLException occured, and I can only say that the exception happens right after "derp0.1".
I suggest the following change:
PreparedStatement ps = connection.prepareStatement(
"SELECT fr.DepartureLocation, fr.Destination, d.Date, d.Time FROM `FlightRoute` fr,"+
"`Departure` d WHERE d.FlightRouteId = fr.Id AND d.Date > ? AND d.Date < ? "+
" AND fr.Destination = ?");
ps.setDate(1, dateFrom);
ps.setDate(2, dateTo);
ps.setString(3, destination); //assuming destination is a String
ResultSet rs = ps.executeQuery();
In JDBC you should always use parametrisation, as above. It might also solve your problem, which could be caused by an invalid date format.

MySQL before start of exception [duplicate]

This question already has answers here:
ResultSet exception - before start of result set
(6 answers)
Closed 5 years ago.
I get an error stating that I got an exception before start of a result set. I'm trying to get a value (score from the MySQL database) and add one to the Java rank based on the player score. This is to create a scoreboard.
So if the player's score is lower than the current score, it gets posted with rank 1. If it's higher, the program checks the score against the next entry in the MySQL database. I haven't yet implemented a feature to change all the current entries rank's to increment by 1.
Bottom Line: I'm creating a scoreboard using MySQL and Java. The Java program creates a score entry based on input, and then sends it off to the MySQL database.
System.out.println("Your score is: "+score*2+" (A lower score is better.)");
try {
// create a java mysql database connection
String myDriver = "com.mysql.jdbc.Driver";
String myUrl = "jdbc:mysql://4.30.110.246:3306/apesbridge2013";
String dbName = "apesbridge2013";
String tbName = period + "period";
Class.forName(myDriver);
Connection conn = DriverManager.getConnection(myUrl, "user", CENSORED);
next = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
ResultSet resultSet = next.executeQuery("SELECT * FROM " + tbName);
int cscore = resultSet.getInt("score");
for(int sscore = score; sscore > cscore;){
resultSet.next();
cscore = resultSet.getInt("score");
rank++;
}
stmt = conn.createStatement();
stmt.executeUpdate("insert into " + dbName + "." + tbName + " " + "values(" + rank + ", '" + name + "', " + score + ")");
stmt.close();
conn.close();
}
catch (Exception e)
{
System.err.println("Got an exception! ");
System.err.println(e.getMessage());
}
}
Put resultSet.next(); right below your executeQuery line.
As stated by #hd1, you need to call ResultSet.next() after the call to executeQuery:
while (resultSet.next()) {
...
Also, better to use PreparedStatement instead of java.sql.Statement and use parameter placeholders to protect against SQL Injection attacks:
There's a problem in your for loop; the exit condition should be when there are no more rows to fetch. Your query doesn't guarantee that the exit condition will ever be met, and you may attempt to fetch past the end of the resultset. (And even when your for loop does happen to be entered, and when if the for loop does happen to be exited, the rank value derived by that loop is non-deterministic, it's dependent on the order that rows are returned by the database.
I also don't see any call to resultSet.close() or next.close().
There's so many problems here, it's hard to know where to begin.
But firstly, it would be much more efficient to have the database return the rank to you, with a query:
"SELECT COUNT(1) AS rank FROM " + tbName + " WHERE score < " + score
rather than pulling back all the rows back, and comparing each score. That's just painful, and a whole lot of code that is just noise. That would allow you to focus on the code that DOES need to be there.
Once you get that working, you need to ensure that your statement is not vulnerable to SQL injection, and prepared statements with bind variables is really the way to go there.
And you really do need to ensure that calls are made to the close() methods on the resultset, prepared statements, and the connection. We typically want these in a finally block. Either use nested try/catch blocks, where the variables are immediately initialized, like this:
try {
Connection conn = DriverManager.getConnection(...
try {
stmt = conn.CreateStatement();
String query = "SELECT COUNT(1) AS `rank` FROM " + tbName + " WHERE `score` < " + score ;
try {
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
rank = rs.getInt("rank");
}
} finally {
if (rs!=null) { rs.close() };
}
} finally {
if (stmt!=null) { stmt.close() };
}
} finally {
if (conn!=null) { conn.close() };
}
Or one big try/catch block can also be workable:
} finally {
if (resultSet!=null) { resultSet.close() };
if (next!=null) { next.close() };
if (conn!=null) { conn.close() };
)
The point is, the close methods really do need to be called.

Invalid state, the ResultSet object is closed

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.

Categories