This question already has answers here:
How should I use try-with-resources with JDBC?
(5 answers)
Closed 8 years ago.
Yesterday multiple people on Stack recommended using try-with-resources. I am doing this for all my database operations now. Today I wanted to change Statement to PreparedStatement to make the queries more secure. But when I try to use a prepared statement in try-with-resources I keep getting errors like 'identifier expected' or ';' or ')'.
What am I doing wrong? Or isnt this possible? This is my code:
try (Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPASS);
PreparedStatement stmt = conn.prepareStatement("SELECT id FROM users WHERE id = ? LIMIT 1");
stmt.setInt(1, user);
ResultSet rs = stmt.executeQuery()) {
// if no record found
if(!rs.isBeforeFirst()) {
return false;
}
// if record found
else {
return true;
}
} catch (SQLException e) {
// log error but dont do anything, maybe later
String error = "SQLException: " + e.getMessage() + "\nSQLState: " + e.getSQLState() + "\nVendorError: " + e.getErrorCode();
return false;
}
A try-with-resource statement is used to declare (Autoclosable) resources. Connection, PreparedStatement and ResultSet are Autoclosable, so that's fine.
But stmt.setInt(1, user) is NOT a resource, but a simple statement. You cannot have simple statements (that are no resource declarations) within a try-with-resource statement!
Solution: Create multiple try-with-resource statements!
try (Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPASS)) {
executeStatement(conn);
} catch (SQLException e) {
// log error but dont do anything, maybe later
String error = "SQLException: " + e.getMessage() + "\nSQLState: " + e.getSQLState() + "\nVendorError: " + e.getErrorCode();
return false;
}
private void executeStatement(Connection con) throws SQLException {
try (PreparedStatement stmt = conn.prepareStatement("SELECT id FROM users WHERE id=? LIMIT 1")) {
stmt.setInt(1, user);
try (ResultSet rs = stmt.executeQuery()) {
// process result
}
}
}
(Please note that technically it is not required to put the execution of the SQL statement into a separate method as I did. It also works if both, opening the connection and creating the PreparedStatement are within the same try-with-resource statement. I just consider it good practice to separate connection management stuff from the rest of the code).
try this code:
try (Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPASS)) {
PreparedStatement stmt = conn.prepareStatement("SELECT id FROM users WHERE id = ? LIMIT 1");
stmt.setInt(1, user);
ResultSet rs = pstmt.executeQuery())
// if no record found
if(!rs.isBeforeFirst()) {
return false;
}
// if record found
else {
return true;
}
} catch (SQLException e) {
// log error but dont do anything, maybe later
String error = "SQLException: " + e.getMessage() + "\nSQLState: " + e.getSQLState() + "\nVendorError: " + e.getErrorCode();
return false;
}
note that here, resource is your Connection and you have to use it in the try block ()
Move
stmt.setInt(1, user);
ResultSet rs = stmt.executeQuery()
...within the try{ /*HERE*/ }
This is because stmt is the resource being created try (/*HERE*/) {} to be used try{ /*HERE*/ }
Try-with-resources
try (/*Create resources in here such as conn and stmt*/)
{
//Use the resources created above such as stmt
}
The point being that everything created in the resource creation block implements AutoClosable and when the try block is exited, close() is called on them all.
In your code stmt.setInt(1, user); is not an AutoCloseable resource, hence the problem.
Related
I'm not sure the best practice for this, but my overall problem is that I can't figure out why my connection isn't closing.
I'm basically iterating through a list, and then inserting them into a table. Before I insert them into a table, I check and make sure it's not a duplicate. if it is, I update the row instead of inserting it. As of now, I can only get 13 iterations to work before the debug lets me know I had a connection not close.
Since I have 2 connections, I'm having trouble figuring out where I'm suppose to close my connections, and I was trying to use other examples to help. Here is what I got:
Connection con = null;
PreparedStatement stmt = null;
PreparedStatement stmt2 = null;
ResultSet rs = null;
Connection con2 = null;
for (Object itemId: aList.getItemIds()){
try {
con = cpds2.getConnection();
stmt = con.prepareStatement("select [ID] from [DB].[dbo].[Table1] WHERE [ID] = ?");
stmt.setInt(1, aList.getItem(itemId).getBean().getID());
rs = stmt.executeQuery();
//if the row is already there, update the data/
if (rs.isBeforeFirst()){
System.out.println("Duplicate");
stmt2 = con2.prepareStatement("UPDATE [DB].[dbo].[Table1] SET "
+ "[DateSelected]=GETDATE() where [ID] = ?");
stmt2.setInt(1,aList.getItem(itemId).getBean().getID());
stmt2.executeUpdate();
}//end if inserting duplicate
else{
con2 = cpds2.getConnection();
System.out.println("Insertion");
stmt.setInt(1, aList.getItem(itemId).getBean().getID());
//Otherwise, insert them as if they were new
stmt2 = con.prepareStatement("INSERT INTO [DB].[dbo].[Table1] ([ID],[FirstName],"
+ "[LastName],[DateSelected]) VALUES (?,?,?,?)");
stmt2.setInt(1,aList.getItem(itemId).getBean().getID() );
stmt2.setString(2,aList.getItem(itemId).getBean().getFirstName());
stmt2.setString(3,aList.getItem(itemId).getBean().getLastName() );
stmt2.setTimestamp(4, new Timestamp(new Date().getTime()));
stmt2.executeUpdate();
}//End Else
}catch(Exception e){
e.printStackTrace();
}//End Catch
finally{
try { if (rs!=null) rs.close();} catch (Exception e) {}
try { if (stmt2!=null) stmt2.close();} catch (Exception e) {}
try { if (stmt!=null) stmt.close();} catch (Exception e) {}
try { if (con2!=null) con2.close();} catch (Exception e) {}
try {if (con!=null) con.close();} catch (Exception e) {}
}//End Finally
} //end for loop
Notification.show("Save Complete");
This is my pooled connection:
//Pooled connection
cpds2 = new ComboPooledDataSource();
try {
cpds2.setDriverClass("net.sourceforge.jtds.jdbc.Driver");
} catch (PropertyVetoException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} //loads the jdbc driver
cpds2.setJdbcUrl( "jdbc:jtds:sqlserver://SERVERNAME;instance=DB" );
cpds2.setUser("username");
cpds2.setPassword("password");
cpds2.setMaxStatements( 180 );
cpds2.setDebugUnreturnedConnectionStackTraces(true); //To help debug
cpds2.setUnreturnedConnectionTimeout(2); //to help debug
My main questions are, am i closing my connections right? Is my connection pool set up right?
Should I be closing the connection inside the for loop or outside?
Is my problem with c3p0? Or JTDS?
It's great that you are working to be careful to robustly close() your resources, but this is overly complicated.
Unless you are using a pretty old version of Java (something prior to Java 7) you can use try-with-resources, which really simplifies this stuff. Working with two different Connections in one logic unit-of-work invites misunderstandings. Resources should be a close()ed as locally to their use as possible, rather than deferring everything to the end.
Your Exception handling is dangerous. If an Exception occurs that you don't understand, you might want to print its stack trace, but your code should signall the fact that whatever you were doing didn't work. You swallow the Exception, and even notify "Save Complete" despite it.
All this said, your life might be made much easier by a MERGE statement, which I think SQL Server supports.
Here is an (untested, uncompiled) example reorganization:
try ( Connection con = cpds2.getConnection() ) {
for (Object itemId: aList.getItemIds()){
boolean id_is_present = false;
try ( PreparedStatement stmt = con.prepareStatement("select [ID] from [DB].[dbo].[Table1] WHERE [ID] = ?") ) {
stmt.setInt(1, aList.getItem(itemId).getBean().getID());
try ( ResultSet rs = stmt.executeQuery() ) {
id_is_present = rs.next();
}
}
if ( id_is_present ) {
System.out.println("Duplicate");
try ( PreparedStatement stmt = con.prepareStatement("UPDATE [DB].[dbo].[Table1] SET [DateSelected]=GETDATE() where [ID] = ?") ) {
stmt.setInt(1,aList.getItem(itemId).getBean().getID());
stmt.executeUpdate();
}
} else {
System.out.println("Insertion");
try ( PreparedStatement stmt = con.prepareStatement("INSERT INTO [DB].[dbo].[Table1] ([ID],[FirstName], [LastName],[DateSelected]) VALUES (?,?,?,?)") ) {
stmt.setInt(1,aList.getItem(itemId).getBean().getID() );
stmt.setString(2,aList.getItem(itemId).getBean().getFirstName());
stmt.setString(3,aList.getItem(itemId).getBean().getLastName() );
stmt.setTimestamp(4, new Timestamp(new Date().getTime()));
stmt.executeUpdate();
}
}
}
Notification.show("Save Complete");
}
I am trying to select data from a table using prepared statement. But it seems like I am getting syntax error which I cannot solve alone.
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/mydb";
String dbusername = "root";
String dbpassword = ""; // Change it to your Password
// Setup the connection with the DB
connection = DriverManager.getConnection(url, dbusername,
dbpassword);
String query = "SELECT * FROM admin WHERE username = ? AND password = ?";
try {
// connection.setAutoCommit(false);
selectUser = connection.prepareStatement(query);
selectUser.setString(1, username);
selectUser.setString(2, password);
// Execute preparedstatement
ResultSet rs = selectUser.executeQuery(query);
// Output user details and query
System.out.println("Your user name is " + username);
System.out.println("Your password is " + password);
System.out.println("Query: " + query);
boolean more = rs.next();
// if user does not exist set the validity variable to true
if (!more) {
System.out
.println("Sorry, you are not a registered user! Please sign up first!");
user.setValid(false);
}
// if user exists set the validity variable to true
else if (more) {
String name = rs.getString("name");
System.out.println("Welcome " + name);
user.setName(name);
user.setValid(true);
}
} catch (Exception e) {
System.out.println("Prepared Statement Error! " + e);
}
} catch (Exception e) {
System.out.println("Log in failed: An exception has occured! " + e);
} finally {
}
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
System.out.println("Connection closing exception occured! ");
}
connection = null;
}
return user;
}
I get following error.
Prepared Statement Error! com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? AND password = ?' at line 1
But I don't see any error in that code line.
Change
ResultSet rs = selectUser.executeQuery(query);
to
ResultSet rs = selectUser.executeQuery();
when you already prepared the statement in connection.prepareStatement(query); then why to pass the query again in selectUser.executeQuery(query);
what you want to do is use this method
ResultSet rs = selectUser.executeQuery();
You have already loaded your query inside the prepared statement here ,
selectUser = connection.prepareStatement(query);
so execute it by ,
ResultSet rs = selectUser.executeQuery();
Also read ,
How does PreparedStatement.executeQuery work?
I'm trying to get information from a MySQL database. I can connect and do things such as insert data into tables fine, and although I receive a ResultSet, I can't read it. Here's my code:
public ResultSet executeQuery(String query) {
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
rs = stmt.executeQuery(query);
if (stmt.execute(query)) {
rs = stmt.getResultSet();
}
return rs;
}
catch (SQLException ex){
System.err.println("SQLException: " + ex.getMessage());
System.err.println("SQLState: " + ex.getSQLState());
System.err.println("VendorError: " + ex.getErrorCode());
}
finally {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException sqlEx) { } // ignore
stmt = null;
}
}
return null;
}
Trying to read the ResultSet:
ResultSet set = executeQuery("SELECT rank FROM players");
try {
while(set.next()) {
System.out.println(set.getInt("rank") + "");
}
} catch(Exception ex) {
ex.printStackTrace();
} finally {
try {
set.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
I get this error:
java.sql.SQLException: Operation not allowed after ResultSet closed
I've been looking around the internet and on different forums all day. What's wrong with my code?
Your finally block in executeQuery closes your statement before you've iterated over the results.
Well, two things -
1) I do not get why you have both execute() and executeQuery() in your method? I think executeQuery() should work for you.
2) Do not close the statement before iterating through resultset. Make it an instance variable and close it after iterating.
TL;DR Your executeQuery closes the Statement.
From the JavaDoc for Statement.close
Releases this Statement object's database and JDBC resources
immediately instead of waiting for this to happen when it is
automatically closed.
You close() your Statement before you read your ResultSet. Closing a Statement causes all of the ResultSet instances associate with that Statement to also be closed.
You need to pass in a Consumer<ResultSet> into a different method, and process the ResultSet whilst the Statement is open:
public void executeQuery(final String query, final Consumer<ResultSet> consumer) {
try(final Statement stmt = conn.createStatement();
final ResultSet rs = stmt.executeQuery(query)) {
consumer.accept(rs);
}
catch (SQLException ex){
System.err.println("SQLException: " + ex.getMessage());
System.err.println("SQLState: " + ex.getSQLState());
System.err.println("VendorError: " + ex.getErrorCode());
}
}
I have also fixed your code:
I have replaced your try..finally with a try-with-resources
You execute your query twice by calling stmt.executeQuery(query) and then stmt.execute(query). This is not only wasteful, but you lose the reference to the first ResultSet and if Statement.close didn't close all the associated resultsets you would have had a memory leak.
Allright been trying to figure this out the last 2 days.
Statement statement = con.createStatement();
String query = "SELECT * FROM sell";
ResultSet rs = query(query);
while (rs.next()){//<--- I get there operation error here
This is the query method.
public static ResultSet query(String s) throws SQLException {
try {
if (s.toLowerCase().startsWith("select")) {
if(stm == null) {
createConnection();
}
ResultSet rs = stm.executeQuery(s);
return rs;
} else {
if(stm == null) {
createConnection();
}
stm.executeUpdate(s);
}
return null;
} catch (Exception e) {
e.printStackTrace();
con = null;
stm = null;
}
return null;
}
How can I fix this error?
It's hard to be sure just from the code you've posted, but I suspect that the ResultSet is inadvertently getting closed (or stm is getting reused) inside the body of the while loop. This would trigger the exception at the start of the following iteration.
Additionally, you need to make sure there are no other threads in your application that could potentially be using the same DB connection or stm object.
IMHO, you should do everything you need with your ResultSet before you close your connection.
there are few things you need to fix. Opening a connection, running a query to get the rs, closing it, and closing the connection all should be done in the same function scope as far as possible. from your code, you seem to use the "con" variable as a global variable, which could potentially cause a problem. you are not closing the stm object. or the rs object. this code does not run for too long, even if it has no errors. Your code should be like this:
if (stringUtils.isBlank(sql)){
throw new IllegalArgumentsException ("SQL statement is required");
}
Connection con = null;
PreparedStatement ps =null;
Resultset rs = null;
try{
con = getConnection();
ps = con.preparestatement(sql);
rs = ps.executeQuery();
processResults(rs);
close(rs);
close(ps);
close(con);
}catch (Execption e){
log.Exception ("Error in: {}", sql, e);
throw new RuntimeException (e);
}finally{
close(rs);
close(ps);
close(con);
}
use another Statement object in inner loop
Like
Statement st,st1;
st=con.createStatement();
st1=con.createStatement();
//in Inner loop
while(<<your code>>)
{
st1.executeQuery(<<your query>>);
}
I know this is a few years late, but I've found that synchronizing the db methods usually get rid of this problem.
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.