I am trying to have a retry logic for getting JDBC connection in case I get SQL Exception with something like :
int counter = 0;
Connection conn = null;
while (null == conn) {
try {
conn = GetConnectionObject;
} catch (SQLException e) {
if (++counter > MAX_RETRY) {
//Log ERROR
break;
}
} finally {
if (null != conn) {
try {
DbUtils.close(conn);
} catch (SQLException e) {
logger.error("Exception while closing the connection object");
}
}
}
}
I cannot test this currently hence need some help.
This will work fine if I get exception and then I can log after retrying. But if we DO NOT get exception, it will come to the finally block and close the connection. Try-Catch-Finally are inside while loop.
So If I close my connection, flow if reach
while( null== conn)
Will my connection object become null after closing ?
Or If there is some other way around to implement retry part ?
No, it won't become null after closing. Use Connection.isClosed() instead of while( null== conn). Also, you should get //Do some task. out of this code since it's goal is to get a JDBC connection.
Here is the tested method for your problem. This method tries 3 times for the connection and when it will get the DB connection it will Print Success message and will run the query and display the result otherwise it will print the error message. Also if the connection is successful then it will close the connection after executing query in finally block.
public void retryDBConnection(){
int counter = 0;
Connection con = null;
while(counter < 3 && con == null){
try{
String str = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
Class.forName(str).newInstance();
con = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;Database=TestDB;", "sa", "sqldb");
System.out.println("DB Connection Successful!");
PreparedStatement prep = con.prepareStatement("select ID, User_Name from tblUser where ID = 9");
ResultSet rs = prep.executeQuery();
if(rs.next()){
System.out.println("User ID = " + rs.getString(1));
//name = rs.getString(2);
}
}
catch(SQLException e){
// System.out.println(e.getSQLState());
if(e.getErrorCode() == 0 || e.getErrorCode() == 4060)
counter++;
System.out.println("Attempt: " + counter +", Could not establish DB Connection!");
System.out.println("Error Code: " + e.getErrorCode());
}
catch(Exception e){
e.printStackTrace();
}finally{
if(con != null){
try {
con.close();
System.out.println("Connection closed...");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
Here is the out put of the method.
Attempt: 1, Could not establish DB Connection! Error Code: 0 Attempt: 2, Could not establish DB Connection! Error Code: 4060 DB
Connection Successful! User ID = 9 Connection closed...
Related
Once try-with-resources statement execution completion after how to check JDBC connection is live or closed ?
Code :
try (Connection conn = ConnectionString.getConnection();
PreparedStatement psmt = conn.prepareStatement(SQL_Queries.GET_PUBLISHER_ID_QUERY);
ResultSet rs = psmt.executeQuery();)
{
psmt.setString(1, appId);
while (rs.next()) {
publisherId = rs.getString(Schema.PUBLISHER_PUBLISHID);
}
//System.out.println(conn != null ? "live" : "close");
}
The idea is that using the try-with-resource block, you are creating an inner scope for your connection. The try-with-resource closes the connection and the conn object scope ends, thus is ready for garbage collection. Read more about scope here.
To check this, run this code
String url, username, pass;
url = "your-connection-url";
username = "db-username";
pass = "db-password";
Connection outerConnection = null;
try (Connection innerConnection = DriverManager.getConnection(url, username, pass);
PreparedStatement psmt = innerConnection.prepareStatement("SELECT 1");
ResultSet rs = psmt.executeQuery();)
{
while (rs.next()) {
System.out.println(rs.getString(1));
}
outerConnection = innerConnection;
// after the try catch, innerConnection won't exist anymore
// (the scope ends, the compiler compains if you access it outside the block!)
} catch (SQLException e)
{
e.printStackTrace();
}
if (outerConnection != null) {
try {
System.out.println("Is closed? " + outerConnection.isClosed());
}
catch (SQLException e) {
e.printStackTrace();
}
}
else {
System.out.println("Is null");
}
In the console you'll get
1
Is closed? true
First of all, the Connection object scope is limited to the try block, it's not visible outside of it.
Secondly, you said it yourself, all resources in a try-with-resources statement, which must all implement AutoCloseable, are closed at the end of the statement. So even if your Connection was visible, it would be closed.
Why are database connections often closed at two positions, once directly after use, and secondly additionally in a finally-block using a check for null in order to prevent them to be closed twice. Doesn't it suffice to use the finally block? The finally-block is to be executed in every case.
Here is an official example of Apache-Tomcat JNDI Datasource HOW-TO. They point out there that a connection MUST be closed under every circumstance. I wonder why it is not sufficient to use the finally-block as the close-commands within the end of the main try {}-block seem to be redundent.
Connection conn = null;
Statement stmt = null; // Or PreparedStatement if needed
ResultSet rs = null;
try
{
conn = ... get connection from connection pool ...
stmt = conn.createStatement("select ...");
rs = stmt.executeQuery();
... iterate through the result set ...
rs.close ();
rs = null;
stmt.close ();
stmt = null;
conn.close (); // Return to connection pool
conn = null; // Make sure we don't close it twice
}
catch (SQLException e)
{
... deal with errors ...
}
finally
{
// Always make sure result sets and statements are closed,
// and the connection is returned to the pool
if (rs != null)
{
try
{
rs.close ();
}
catch (SQLException ignore)
{
}
rs = null;
}
if (stmt != null)
{
try
{
stmt.close ();
}
catch (SQLException ignore)
{
}
stmt = null;
}
if (conn != null)
{
try
{
conn.close ();
}
catch (SQLException ignore)
{
}
conn = null;
}
}
I would like to write much shorter:
Connection conn = null;
Statement stmt = null; // Or PreparedStatement if needed
ResultSet rs = null;
try
{
conn = ... get connection from connection pool ...
stmt = conn.createStatement ("select ...");
rs = stmt.executeQuery();
... iterate through the result set ...
}
catch (SQLException e)
{
// ... deal with errors ...
}
finally
{
// Always make sure result sets and statements are closed,
// and the connection is returned to the pool
try
{
if (rs != null)
rs.close ();
if (stmt != null)
stmt.close ();
if (conn != null)
conn.close ();
}
catch (SQLException ignore)
{
}
}
You have a good question - I don't understand the "official example" either. Finally block is certainly enough.
However, your code has more serious faults, namely if rs.close() would throw an exception, you'd have stmt and conn leaking out and you'd also ignore that exception silently. That is something you should not do. Since Java 7, using try-with-resources construct is the preferred way, but if you cannot go there, at least handle each possible exception (rs, stmt, conn) separately so they don't cause each other to leak.
For example Apache commons DbUtils has closeQuietly() just for this purpose, because it used to be a common scenario. Personally I would go to somewhere like Spring JDBCTemplate that does this sort of stuff behind the scenes.
Edit: try-with-resources is explained by Oracle here. In short, you'd do it something like this:
try (Connection conn = yourCodeToGetConnection();
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
String coffeeName = rs.getString("COF_NAME");
int supplierID = rs.getInt("SUP_ID");
float price = rs.getFloat("PRICE");
int sales = rs.getInt("SALES");
int total = rs.getInt("TOTAL");
System.out.println(coffeeName + ", " + supplierID + ", " +
price + ", " + sales + ", " + total);
}
} catch (SQLException ex) {
// log, report or raise
}
Where the try-statement automatically deals with conn, stmt and rs closing in all cases and in order (the reverse order in which you state them). Possible exceptions you still need to handle yourself.
Thanks for the many comments. So summing up (especially EJP comments to my question [that closing a Connection will close underlying Statements and closing a Statement will it self close the ResultSet]) and as I consider the try-with-resource construct to be a bit difficult to read I suggest writing
Connection conn = null;
Statement stmt = null; // Or PreparedStatement if needed
ResultSet rs = null;
try
{
conn = ... get connection from connection pool ...
stmt = conn.createStatement ("select ...");
rs = stmt.executeQuery();
... iterate through the result set ...
}
catch (SQLException e)
{
// ... deal with errors ...
}
finally
{
// Always make sure result sets and statements are closed,
// and the connection is returned to the pool
try
{
if (conn != null)
conn.close ();
}
catch (SQLException ignore)
{
}
}
and closing only the main Connection while leaving the Statement and the ResultSet always untouched and to be closed by the Connection.
Right?
I will prefer writing a common method for closing the connection, which can be called from a finally block.
Something like this:
Connection conn = null;
Statement stmt = null; // Or PreparedStatement if needed
ResultSet rs = null;
try
{
conn = ... get connection from connection pool ...
stmt = conn.createStatement ("select ...");
rs = stmt.executeQuery();
... iterate through the result set ...
}
catch (SQLException e)
{
// ... deal with errors ...
}
finally
{
CloseTheConnection(conn);
CloseTheStatement(stmt);
}
public void closeTheConnection(Connection conn){
try{
if(conn!=null){
conn.close();
}
}catch(Exception ex){
}
public void closeTheStatement(Statement stmt){
try{
if( stmt != null)
stmt.close();
} catch(Exception ex){
}
}
By creating different methods and calling them from finally will ensure that even if you get any exception from one method the other method will definitely be called. And it will be reusable as well.
I keep getting the SQLITE_BUSY database file is locked error. I have looked on other posts, but none have solved this issue. Can't seem to figure out why this error is occurring. I know that you are only supposed to have one connection at a time, but it appears that I do only have one connection. Any ideas? Does it have something to do with the fact that object Connection connection is initialized as null then reset? Thats the only thing I could think of, however if I take it away, then I am not able to close the connection in the finally block.
public class GetChromeHistory
{
public static void main (String[] args)
{
Connection connection= null;
ResultSet resultSet = null;
Statement statement = null;
try
{
// We think, but are not sure, that the line below registers the sqlite drive with JDBC.
Class.forName("org.sqlite.JDBC");
connection = DriverManager
.getConnection("jdbc:sqlite:db.db");
statement = connection.createStatement();
//problem occurs on line below, database file is locked
resultSet = statement
.executeQuery("SELECT * FROM urls where visit_count > 0");
while(resultSet.next())
{
//eventually save into a set or list
System.out.println ("URL [" + resultSet.getString("url") + "]" +
", visit count [" + resultSet.getString("visit_count") + "]");
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
try
{
resultSet.close();
statement.close();
connection.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
I've got a following problem: I'm trying to insert data (in this case a username) into a table using the following code:
void AddNewUser(String name, Connection conn){
if(ret == null){
ret = new DB_Retriever(conn);
}
if(!ret.UserExists(name, conn)){
try{
Statement stm = conn.createStatement();
stm.executeUpdate(DB_OperationalData.insert_new_user[0][0] + name + DB_OperationalData.insert_new_user[0][1]);
stm.executeUpdate(DB_OperationalData.insert_new_user[1][0] + name + DB_OperationalData.insert_new_user[1][1]);
stm.close();
}
catch(SQLException e){
e.printStackTrace();
}
}
}
By the way: It absolutely doesn't matter what I put in the catch clause, nothing that I put there is executed. Just to make everything clear, here is the content of the DB_OperationalData.insert_new_user String array:
public static final String[][] insert_new_user = {
{"INSERT INTO User (Username, Status) VALUES ('","','IN');"},
{"INSERT INTO Statistics (Player_ID) SELECT ID FROM User WHERE Username='","';"}};
The second statement is supposed to copy the ID of the user that is inserted and put it into Player_ID field of the Statistics table (Table User's ID is an autonumbered field).
The exception I get is:
Error while processing the query: java.sql.SQLException: ResultSet closed
What is interesting, is that it works and the data is added correctly but I simply do not want any exceptions thrown.
That's the console output I get:
This is 'data' Package Testing class
Connection to the database established.
The number of tables existing in the database is: 0
All the queries have been processed successfully
Adding new users:
Error while processing the query: java.sql.SQLException: ResultSet closed
All the lines above the Exception are my own printouts, so I know what has actually happened.
[EDIT]
I have changed the code to use the PreparedStatement instead of ordinary Statement and the current try clause looks as follows:
PreparedStatement pstm = conn.prepareStatement(DB_OperationalData.insert_new_user[0]);
pstm.setString(1, name);
pstm.addBatch();
conn.setAutoCommit(false);
pstm.executeBatch();
conn.setAutoCommit(true);
pstm.close();
And the output is (still regardless of the contents of the catch clause):
This is 'data' Package Testing class
Connection to the database established.
The number of tables existing in the database is: 0
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at org.sqlite.PrepStmt.batch(PrepStmt.java:173)
at org.sqlite.PrepStmt.setString(PrepStmt.java:254)
at data.DB_Writer.AddNewUser(DB_Writer.java:28)
at data.DataHandler.AddNewUser(DataHandler.java:94)
at data.Tester.main(Tester.java:18)
All the queries have been processed successfully
Adding new users:
Error while processing the query: java.sql.SQLException: ResultSet closed
[EDIT 2]
With regards to the original version, when I remove the stm.close(); there is absolutely no difference and I still get the 'ResultSet closed' Exception.
[EDIT 3]
Here is the code of the method that is calling the above:
public void AddNewUser(String username)throws IllegalUsernameException{
if(username.length()==0 || username.length()>20){
throw new IllegalUsernameException();
}
writer.AddNewUser(username, conn);
}
The connection to the database is established by this class:
class DB_Connection {
public static Connection getConnection(){
Connection conn = null;
try{
Class.forName("org.sqlite.JDBC");
}
catch(ClassNotFoundException e){
log("Error while loading the database driver: " + e);
return null;
}
try{
conn = DriverManager.getConnection("jdbc:sqlite:database.db");
}
catch(SQLException e){
log("Unable to connect to the database: " + e);
return null;
}
return conn;
}
public static void log(String msg){
System.out.println(msg);
}
}
The DB_Retriever's method that is checking for the existing username:
boolean UserExists(String name, Connection conn){
String result = "";
try{
Statement stm = conn.createStatement();
ResultSet rs = stm.executeQuery(DB_OperationalData.user_exists[0] + name + DB_OperationalData.user_exists[1]);
result = rs.getString("Username");
}
catch(SQLException e){
System.out.println("Error while processing the query: " + e);
}
if(result.equals(name)){
return true;
}
return false;
}
The only location where Error while processing the query: java.sql.SQLException: ResultSet closed could be printed to the console is in UserExists(..), unless there is another method with a similar catch block. Indeed the ResultSet is not used correctly in UserExists, what may cause the error.
For a more complete description of how to work with JDBC look at this answer or the JDBC documentation. A possible alternative to the existing UserExists is:
boolean userExists(String name, Connection conn) {
PreparedStatement stmt = null;
try{
stmt = conn.prepareStatement("SELECT COUNT(Username) FROM User WHERE Username = ?");
stmt.setString(1, name);
ResultSet rs = stmt.executeQuery();
rs.next(); // set cursor to first row
int count = rs.getInt(1);
rs.close();
return count > 0;
} catch(SQLException e) {
// propagate error
throw new RuntimeException(e);
} finally {
// clean up resources
if (stmt != null) {
try {
stmt.close();
} catch (SQLException ignore) {
log("error on sql clean up", ignore);
}
}
}
}
I m trying to execute this code & it shows successfull execution but i cant get any value n access database file which i had create.
so, please find mistake in my code so that i can get some values in database table.
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
String filename ="D:\\Database for Mini Project\\Database21(example).mdb";
String database = "jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=D:\\Database for Mini Project\\Database21(example).mdb";
try (Connection con = DriverManager.getConnection( database ,"","")) {
Statement s = con.createStatement();
String b=request.getParameter("uname");
String c=request.getParameter("pass");
String query="insert into A.ABC1(uname,pass) values='"+b+"','"+c+"'";
s.executeQuery(query);
s.close();
change s.executeQuery(query); to s.executeUpdate(query);
For data manipulation language use executeQuery(); as mentioned in oracle docs
Try this
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
String filename ="D:\\Database for Mini Project\\Database21(example).mdb";
String database = "jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=D:\\Database for Mini Project\\Database21(example).mdb";
String b=request.getParameter("uname");
String c=request.getParameter("pass");
try {
// connects to the database
Connection con = DriverManager.getConnection( database ,"","")) {
Statement s = con.createStatement();
String query="insert into A.ABC1(uname,pass) values(?,?);
PreparedStatement statement = conn.prepareStatement(query);
s.setString(1, b);
s.setString(2, c);
int row = s.executeUpdate();
if (row > 0) {
message = "Query executed";
}
} catch (SQLException ex) { //catch ur msaccess exception..I use mysql
message = "ERROR: " + ex.getMessage();
ex.printStackTrace();
} finally {
if (con != null) {
// closes the database connection
try {
con.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
I use mysql as my database..so wherever there is catch(SQLException ex) replace it with msaccess exception..