MySQL before start of exception [duplicate] - java

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.

Related

Trouble with ResultSet using executeUpdate

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.

java.sql.SQLException: no such column and false Period getDays result

I'm a java beginner and struggle with two problems.
1) The SQL Exception: no such column 'Ofen'
This is my Code and I want to get specific data from a SQLite Database called "kleintest.db" with 2 tables "maindata" and "Zahlwertuntertable". maindata contains the 'Ofen' entry as TEXT. The ResultSet rs should generally take all Data from maindata and the ResultSet rs2 should take the weight from Zahlwertuntertable. But running the programm now shows the mentioned error.
public static void readDB() {
try {
Statement stmt = connection.createStatement();
//ResultSet rs = stmt.executeQuery("SELECT * FROM Gewichtsabnahme;");
ResultSet rs = stmt.executeQuery("SELECT * FROM maindata;");
ResultSet rs2 = stmt.executeQuery("SELECT * FROM Zahlwertuntertable;");
while (rs.next()) {
System.out.println("Ofen = " + rs.getString("Ofen"));
System.out.println("Platznummer = " + rs.getInt("Zahlwert"));
System.out.println("Startdatum = " + rs.getString("Startdatum"));
LocalDate heute = LocalDate.now();
String Datum = rs.getString("Startdatum");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
LocalDate Wägetag = LocalDate.parse(Datum, formatter);
Period DiffTag = Period.between(heute, Wägetag);
System.out.format("Tage = " + DiffTag.getDays() + "\n"); //
System.out.println("Gewicht = " + rs2.getInt("Startgewicht"));
}
rs.close();
rs2.close();
connection.close();
} catch (SQLException e) {
System.err.println("Zugriff auf DB nicht möglich.");
e.printStackTrace();
}
}
The table maindata contains the following items:
Laufnummer Ofen Zahlwert Startdatum
But Laufnummer is just a primary key and should not be retrieved.
2) Next Question is the thing with the Period function. This worked well but as a printed result I'll get P 1D or P 1M 2D what looks slightly confusing. I like to print just the simple amount of days like 45 or 45D and added the getDays() to my DiffTag. Now my result is -1 what makes no sense at all. What's wrong here?
Period DiffTag = Period.between(heute, Wägetag);
System.out.format("Tage = " + DiffTag.getDays() + "\n");
Thanks for suggestions and links I may have missed. But everything I looked so far didn't point out my specific questions.
You can only have one result set open at a time for a Statement object so when you execute the second query agains "Zahlwertuntertable" the first one gets closed.
So either add another statement or handle one query at a time.
Also, right now it looks strange that you call rs.next() but never rs2.next()

JDBC ResultSet not iterating all rows

I need a second pair of eyes on some code. I feel like there's something simple I'm missing. I have a table in my MariaDB called jeff_tables, which contains 4 entries. I've created a utility thread that runs the following code inside the run method:
try {
getConnection();
String query = "SELECT * FROM jeff_tables";
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(query);
//rs.last(); //returns 4 rows
//System.out.println("result set size: " + rs.getRow() );
while(rs.next()) {
Integer tenant = rs.getInt("TENANT_ID");
String table = rs.getString("TABLE_NAME");
System.out.println("Inserting k-v pair: " + tenant + " " + table);
tableNames.put(tenant, table);
}
} catch (SQLException e) {
e.printStackTrace();
}
When I uncomment the rs.last() and rs.getRow() lines, it returns 4, which is the correct number of entries it should return. However, what's actually happening is it enters my while loop, prints the correct values for the first row, then throws a null pointer on the put statement. I've also tried running this code outside of a thread, but it's doing the same thing. Any advice would be greatly appreciated. Thanks in advance.
As soon as I posted this, I saw the problem. This was just a simple instance of forgetting to instantiate the HashMap before trying to insert values into it. It's funny how sometimes it takes posting a question to open your eyes.

Java SQL query failing to return valid ResultSet

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().

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.

Categories