I'm trying to write a function that updates 2 tables in my database. I'm getting an error which i think is caused by calling next() on a resultset that has no more rows in the set.
I was thinking an if condition on hasNext() would fix this but it's not available to the result set...
The error i'm getting is 'No operations allowed after statement closed.'
private void updateDatabase() throws Exception {
Connection conn = getConnection();
PreparedStatement updateMovieStmt = conn.prepareStatement("INSERT INTO movies VALUES(null,?,null,null,null)", Statement.RETURN_GENERATED_KEYS);
PreparedStatement updateVideoStmt = conn.prepareStatement("INSERT INTO video_files VALUES(null,null,null,?,?)", Statement.RETURN_GENERATED_KEYS);
try {
for (Movie localMovie : getLocalMovies()) {
// fetch a local movie{
boolean newMovie = true;
for (Movie dbMovie : getDatabaseMovies(conn)) {
newMovie = true;
Pattern p = Pattern.compile(localMovie.getTitlePattern(), Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(dbMovie.getTitle());
// if it's already in the database not new movie... but is
// is it a new video rip????????????;
if (m.find()) {
System.out.println("DB movie: " + dbMovie.getTitle() + " matches localpattern of: " + localMovie.getTitlePattern());
newMovie = false;
break;
}
}
if (newMovie == true && localMovie.getTitle() != null) {
updateMovieStmt.setString(1, localMovie.getTitle());
updateMovieStmt.executeUpdate();
// get new movie id and put into new video row
ResultSet rs = updateMovieStmt.getGeneratedKeys();
if (rs.next()) {
updateVideoStmt.setBytes(1, localMovie.getHash());
updateVideoStmt.setInt(2, rs.getInt(1));
updateVideoStmt.executeUpdate();
}
}
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
updateMovieStmt.close();
updateVideoStmt.close();
conn.close();
}
}
If you're not using batches (addBatch()/executeBatch()), nor clearing the parameters (clearParameters()), then you should be creating the statements inside the loop, not outside the loop.
Move the both conn.prepareStatement() lines into the if (newMovie == true && localMovie.getTitle() != null) block, preferably refactored in separate methods. Your current method is doing way too much.
you can have only one open Statement per connection. Once you create a new Statement on the same connection, the previously created one will be closed
This holds true for resultSet that we can have only one resultSet open per statement. With a connection we can have multiple statements open.
Related
I am having a problem with a ResultSet being closed. What confuses me is that it works for a portion of the data and then closes. At first I thought it might be because of connection timeout but that doesn't seem the case.
This portion of the program pertains to comparing an .xlsx workbook to an already present SQL database and for lack of a better term merges/updates it.
First, in my CompareDatabase class I am calling a search function that searches an SQLite database for a specific string every 6 iterations.
int columnCount = 6;
dataPoint = dataPoint.replaceAll("Detail", "");
String[] temp = dataPoint.trim().split("\\s+");
System.out.println(Arrays.toString(temp));
for (String tempDataPoint : temp) {
if ( columnCount == 6) {
System.out.println(search(tempDataPoint, connection));
}
columnCount = 0;
} else {
columnCount++;
}
}
This search function (also in the CompareDatabase class is then supposed to search for the value and return a String (was originally a Boolean but I wanted to see the output).
private String search (String searchValue, Connection connection) throws SQLException {
PreparedStatement pStatement = null;
pStatement = connection.prepareStatement("SELECT * FROM lotdatabase where (Vehicle) = (?)");
pStatement.setString(1, searchValue);
try (ResultSet resultSet = pStatement.executeQuery()){
return resultSet.getString(1);
}finally {
close(pStatement);
}
}
At the end you can see that the PreparedStatement is closed. The ResultSet should also be closed automatically (I read somewhere) but JDBC could possibly be being unreliable.
The Connection however is still open as it will be searching some 200+ strings and opening and closing that many times did not seem like a good idea.
These functions are called by my main class here:
One is commented out since it will error out because of primary key violation.
public static void main(String[] args) {
SQLDatabase sqlDatabase = new SQLDatabase();
//sqlDatabase.convertToSQL("Database1.xlsx");
sqlDatabase.compare("Database2.xlsx");
}
I have a suspicion that I am going about a bunch of this wrong (on the aspect of managing connections an such) and I would appreciate a reference to where I can learn to do it properly.
Also, being that PreparedStatement can only handle one ResultSet I don't see that being my issue since I close it every iteration in the for loop.
If more code or explanation is required please let me know and I will do my best to assist.
Thank you for taking the time to read this.
So after a bit more Googling and sleeping on it here is what worked for me.
The search function in compareDatabase changed to this:
private Boolean search (String searchValue, Connection connection) {
PreparedStatement ps = null;
try {
ps = connection.prepareStatement("SELECT * FROM lotdatabase where " +
"(Vehicle) = (?)");
ps.setString(1, searchValue);
ResultSet resultSet = ps.executeQuery();
//The following if statement checks if the ResultSet is empty.
if (!resultSet.next()){
resultSet.close();
ps.close();
return false;
}else{
resultSet.close();
ps.close();
return true;
}
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
And in the other function within compareDatabase I call the search function like this:
if (search(tempDataPoint, connection)) {
System.out.println("MATCH FOUND: " + tempDataPoint);
}else {
System.out.println("NOT FOUND: " + tempDataPoint);
}
This allows me to check the ResultSet and also be sure that it is closed.
so I am a beginer in JDBC - SQL Programming. I need a little advice which is most probably about SYNTAX.
So, Problem = I'm trying to search a record which has name(string provided in function argument) in the record. Following is my code. Now I've designed this code in such a way that there can be more than 1 records with the same name, so all of that records' data will be printed (by ShowData() Function).
protected static void SearchbyName (String toCompareName)
{
Statement stmt = null;
ResultSet rs = null;
Connection conn = null;
boolean flag = false; //to confirm if record has found atleast once
try
{
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = DriverManager.getConnection(url, username, password);
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT idEmployee FROM employee WHERE name = ' "+toCompareName+" ' ");
if( !(rs.next()) ) //if ResultSet is not empty
{
while(rs.next()) //reading all records with the same name, extracted by Query
{
int foundID = rs.getInt("idEmployee"); //extracting ID of found record
ShowRecord(foundID); //prints record of foundID fromDB
flag = true; //set flag
}
}
if(flag==false) //if no record found
{
JOptionPane.showMessageDialog(null, "ERROR:: No Records Found..", "Not Found", JOptionPane.ERROR_MESSAGE);
}
//close connection
if(rs!=null)
{ rs.close(); }
if(stmt!=null)
{ stmt.close(); }
if(conn!=null)
{ conn.close(); }
}
catch(SQLException e)
{ System.err.println(e); }
catch(Exception e)
{ System.err.println(e); }
}
So here it is. As far as my understanding goes, there is some problem with either RESULTSET rs or the Query I'm executing.
Kindly help. & if you can suggest a better approach for search, sure do please. I'm going to write 4 more functions SearchbyAge, SearchbyQualification, SearchbySpecialization on the same pattern.
Just this is enough
while(rs.next()) //reading all records with the same name, extracted by Query
{
int foundID = rs.getInt("idEmployee"); //extracting ID of found record
ShowRecord(foundID); //prints record of foundID fromDB
flag = true; //set flag
}
You don't have to check the data in resultset this way with a if case
if( !(rs.next()) )
This will move to the next record in the resultset
SOVLED
My error was in query. I was putting spaces in string's syntax which I was comparing.
WRONG = `"(.. WHERE name = " ' +toCompareName+ '" ");
RIGHT = `"(.. WHERE name = "'+toCompareName+'" ");
So thats it. Hope it helps to anyone else. :)
I have to modify a few tables in one function. They must all succeed, or all fail. If one operation fails, I want them all to fail. I have the following:
public void foo() throws Exception {
Connection conn = null;
try {
conn = ...;
conn.setAutoCommit(false);
grok(conn);
conn.commit();
}
catch (Exception ex) {
// do I need to call conn.rollback() here?
}
finally {
if (conn != null) {
conn.close();
conn = null;
}
}
}
private void grok(Connection conn) throws Exception {
PreparedStatement stmt = null;
try {
// modify table "apple"
stmt = conn.prepareStatement(...);
stmt.executeUpdate();
stmt.close();
// modify table "orange"
stmt = conn.prepareStatement(...);
stmt.executeUpdate();
stmt.close();
...
}
finally {
if (stmt != null) {
stmt.close();
}
}
}
I'm wondering if I need to call rollback() in the case that something goes wrong during this process.
Other info: I'm using connection pooling. In the sample above, I'm also making sure to close each PreparedStatement using finally statements as well, just left out for brevity.
Thank you
Yes you need to call rollback if any of your statements fails or you have detected an exception prior to calling commit. This is an old post but the accepted answer is wrong. You can try it for yourself by throwing an exception before commit and observing that your inserts still make it into the database if you do not manually rollback.
JDBC Documentation
https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html#call_rollback
Example Correct Usage from the doc
public void updateCoffeeSales(HashMap<String, Integer> salesForWeek)
throws SQLException {
PreparedStatement updateSales = null;
PreparedStatement updateTotal = null;
String updateString =
"update " + dbName + ".COFFEES " +
"set SALES = ? where COF_NAME = ?";
String updateStatement =
"update " + dbName + ".COFFEES " +
"set TOTAL = TOTAL + ? " +
"where COF_NAME = ?";
try {
con.setAutoCommit(false);
updateSales = con.prepareStatement(updateString);
updateTotal = con.prepareStatement(updateStatement);
for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) {
updateSales.setInt(1, e.getValue().intValue());
updateSales.setString(2, e.getKey());
updateSales.executeUpdate();
updateTotal.setInt(1, e.getValue().intValue());
updateTotal.setString(2, e.getKey());
updateTotal.executeUpdate();
con.commit();
}
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
if (con != null) {
try {
System.err.print("Transaction is being rolled back");
con.rollback();
} catch(SQLException excep) {
JDBCTutorialUtilities.printSQLException(excep);
}
}
} finally {
if (updateSales != null) {
updateSales.close();
}
if (updateTotal != null) {
updateTotal.close();
}
con.setAutoCommit(true);
}
}
You don't need to call rollback(). If the connection closes without completing commit() it will be rolled back.
You don't need to set conn to null either; and since the try block starts after conn is initialized (assuming ... cannot evaluate to null) you don't need the != null in finally either.
If you call "commit" then the transaction will be committed. If you have multiple insert/update statements and one of them fails, committing will cause the inserts/updates that didn't fail to commit to the database. So yes, if you don't want the other statements to commit to the db, you need to call rollback. What you are essentially doing by setting autocommit to false is allowing multiple statements to be committed or rolledback together. Otherwise each individual statement will automatically commit.
I have table VIDEO (VideoID int Primary Key, Address Varchar(100)) and I want to search table to see if there is video with given address. But I am not sure if this code works good, and if this could be better done.
Here is my code:
public boolean checkIfVideoExist(String address) throws SQLException {
int count = 0;
Statement stmt = connection.createStatement();
ResultSet rset = stmt
.executeQuery("SELECT Count(VideoID) from VIDEO WHERE Address='"
+ address + "'");
if (rset.next())
count = rset.getInt(1);
if (count == 0)
return false;
else
return true;
}
Be sure you have an index set on column ADDRESS. Then your query should run fast.
It is better to use a prepared statement to pass the address value to the query. And you should close the result set and the statement.
And
if (count == 0)
return false;
else
return true;
looks a bit strange.
public boolean checkIfVideoExist(String address) throws SQLException {
int count = 0;
PreparedStatement stmt = null;
ResultSet rset = null;
try {
stmt = connection.prepareStatement(
"SELECT Count(VideoID) from VIDEO WHERE Address=?");
stmt.setString(1, address);
rset = stmt.executeQuery();
if (rset.next())
count = rset.getInt(1);
return count > 0;
} finally {
if(rset != null) {
try {
rset.close();
} catch(SQLException e) {
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
} catch(SQLException e) {
e.printStackTrace();
}
}
}
}
The code is fine, except for the way you're embedding strings in your query. If address contains a quote character, the query will become invalid. And this is only a small part of the problem. Doing it like this opens the door to SQL injection attacks, where malicious users could enter an address which completely changes the meaning of the query.
Always use prepared statements to bind parameters:
PreparedStatement stmt = connection.prepareStatement("SELECT Count(VideoID) from VIDEO WHERE Address=?");
stmt.setString(1, address); // proper escaping is done for you by the JDBC driver
ResultSet rset = stmt.executeQuery();
Also, you should use finally blocks to close your result sets and statements.
Your code is vulnerable to SQL Injection, it should use a prepared statement instead of building the SQL query via string concatenation.
Apart from that, it looks OK.
ResultSet rset = stmt.executeQuery("SELECT * from VIDEO WHERE Address='" + address + "'");
return rset.next();
then there is at least one matching record and you are done. No need for aggregate function count() ....
Class.forName("org.sqlite.JDBC");
Connection conn =
DriverManager.getConnection("jdbc:sqlite:userdata.db");
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT * from table WHERE is_query_processed = 0;");
int rowcount = rs.getRow();
System.out.println("Row count = "+rowcount); // output 1
rs.first(); // This statement generates an exception
Why is it so?
The pattern I normally use is as follows:
boolean empty = true;
while( rs.next() ) {
// ResultSet processing here
empty = false;
}
if( empty ) {
// Empty result set
}
Here's a simple method to do it:
public static boolean isResultSetEmpty(ResultSet resultSet) {
return !resultSet.first();
}
Caveats
This moves the cursor to the beginning. But if you just want to test whether it's empty, you probably haven't done anything with it yet anyways.
Alternatively
Use the first() method immediately, before doing any processing.
ResultSet rs = stat.executeQuery("SELECT * from table WHERE is_query_processed = 0;");
if(rs.first()) {
// there's stuff to do
} else {
// rs was empty
}
References
ResultSet (Java Platform SE 6)
You can do this too:
rs.last();
int numberOfRows = rs.getRow();
if(numberOfRows) {
rs.beforeFirst();
while(rs.next()) {
...
}
}
while (results.next())
is used to iterate over a result set.so results.next() will return false if its empty.
Why is execution not entering the
while loop?
If your ResultSet is empty the rs.next() method returns false and the body of the while loop isn't entered regardless to the rownumber (not count) rs.getRow() returns. Colins example works.
Shifting the cursor forth and back to determine the amount of rows is not the normal JDBC practice. The normal JDBC practice is to map the ResultSet to a List of value objects each representing a table row entity and then just use the List methods to determine if there are any rows.
For example:
List<User> users = userDAO.list();
if (users.isEmpty()) {
// It is empty!
if (users.size() == 1) {
// It has only one row!
} else {
// It has more than one row!
}
where the list() method look like as follows:
public List<User> list() throws SQLException {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
List<User> users = new ArrayList<User>();
try {
connection = database.getConnection();
statement = connection.createStatement();
resultSet = statement.executeQuery(SQL_LIST);
while (resultSet.next()) {
User user = new User();
user.setId(resultSet.getLong("id"));
user.setName(resultSet.getString("name"));
// ...
users.add(user);
}
} finally {
if (resultSet != null) try { resultSet.close(); } catch (SQLException logOrIgnore) {}
if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
}
return users;
}
Also see this answer for other JDBC examples.
CLOSE_CURSORS_AT_COMMIT
public static final int CLOSE_CURSORS_AT_COMMIT
The constant indicating that ResultSet objects should be closed when the method Connection.commit is called.
Try with this:
ResultSet MyResult = null;
MyResult = Conexion.createStatement().executeQuery("Your Query Here!!!");
MyResult.last();
int NumResut = MyResult.getRow();MyResult.beforeFirst();
//Follow with your other operations....
This manner you'll be able work normally.
This checks if it's empty or not while not skipping the first record
if (rs.first()) {
do {
// ResultSet is not empty, Iterate over it
} while (rs.next());
} else {
// ResultSet is empty
}
May be you can convert your resultset object into String object and check whether is it empty or not.
`if(resultset.toString().isEmpty()){
// containg null result
}
else{
//This conains the result you want
}`