Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Traditional DB singleton connection works poorly

I am using singleton database connection inside my java application, here is code of my connection manager class:

public abstract class DatabaseManager {
    //Static instance of connection, only one will ever exist
        private static Connection connection = null;    
        private static String dbName="SNfinal";
        //Returns single instance of connection
        public static Connection getConnection(){       
            //If instance has not been created yet, create it
            if(DatabaseManager.connection == null){
                initConnection();
            }
            return DatabaseManager.connection;
        }   
        //Gets JDBC connection instance
        private static void initConnection(){           
            try{        
                Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
                   String connectionUrl = "jdbc:sqlserver://localhost:1433;" +
                      "databaseName="+dbName+";integratedSecurity=true";

                DatabaseManager.connection =
                             DriverManager.getConnection(connectionUrl);        
            }
            catch (ClassNotFoundException e){       
                System.out.println(e.getMessage());
                System.exit(0);
            }
            catch (SQLException e){         
                System.out.println(e.getMessage());
                System.exit(0);
            }
            catch (Exception e){        
            }       
        }
    public static ResultSet executeQuery(String SQL, String dbName)
    {
        ResultSet rset = null ;
        try {
               Statement st = DatabaseManager.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
               rset = st.executeQuery(SQL);
               //st.close();
        }
        catch (SQLException e) {
            System.out.println(e.getMessage());
            System.exit(0);
        }
        return rset;
     }

    public static void executeUpdate(String SQL, String dbName)
    {
        try {
               Statement st = DatabaseManager.getConnection().createStatement();
               st.executeUpdate(SQL);
               st.close();
        }

        catch (SQLException e) {
            System.out.println(e.getMessage());
            System.exit(0);
        }
     }
}

The problem is my code work perfect at the start but when time past it becomes really slow. What caused that problem and how can i fix that? At starting time my application handles around 20 queries per second, after 1 hour of running it reaches to 10 queries per second and after 3 days of running it reaches to 1 query per 10 seconds!! P.S: My application is a single user application that makes many queries through database. P.S: Here is my JVM parameters in eclipse.ini:

--launcher.XXMaxPermSize
512M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
512m
--launcher.defaultAction
openFile
--launcher.appendVmargs
-vmargs
-Dosgi.requiredJavaVersion=1.6
-Xms500m
-Xmx4G
-XX:MaxHeapSize=4500m

Unfortunately database is remote and I have not any monitoring access to it for finding out what is going on there.

Here is the example of my usage:

String count="select count(*) as counter from TSN";
ResultSet rscount=DatabaseManager.executeQuery(count, "SNfinal");
if(rscount.next()) {
    numberofNodes=rscount.getInt("counter");
}
like image 506
Ali Avatar asked Dec 21 '13 06:12

Ali


2 Answers

What caused that problem and how can i fix that?

The main problem that you have here is in the executeQuery() method. You are not closing the Statement, I suppose that you have commented the line st.close() because you need the ResultSet open for further processing. I can see that your idea is to avoid see duplicate JDBC code in your application, but this is not the right approach.

The rule is: close the ResultSet and after that, close the Statement, otherwise you are not releasing resources correctly and you expose to the kind of problem that you are describing.

Here you can find a good explanation about how to close resources correctly (take in mind that in your case you don´t need to close the connection)

Edit: An example could be

try{
Statement st = DatabaseManager.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
ResultSet rsCount = st.executeQuery(count);         //count="select count(*) as counter from TSN";
if(rsCount.next()) {
    numberofNodes=rscount.getInt("counter");
}
} catch (SQLException e) {
    //log exception
} finally {
    rsCount.close();
    st.close();
}
like image 64
Gabriel Aramburu Avatar answered Sep 21 '22 20:09

Gabriel Aramburu


You should consider using a disconnected resultset like a CachedRowSet http://docs.oracle.com/javase/1.5.0/docs/api/javax/sql/rowset/CachedRowSet.html

public static ResultSet executeQuery(String SQL, String dbName)
{
    CachedRowSetImpl crs = new CachedRowSetImpl();
    ResultSet rset = null ;
    Statement st = null;
    try {
           st = DatabaseManager.getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
           rset = st.executeQuery(SQL);
           crs.populate(rset);
    }
    catch (SQLException e) {
        System.out.println(e.getMessage());
        System.exit(0);
    }finally{
        rset.close();
        st.close();
    }
    return crs;
 }

CachedRowSet implements ResultSet so it should behave like a ResultSet.

http://www.onjava.com/pub/a/onjava/2004/06/23/cachedrowset.html

In addition to these changes, I would recommend you use a pooled datasource to get connections and close them instead of holding on to one open connection.

http://brettwooldridge.github.io/HikariCP/

Or if you arent java7, bonecp or c3po.

EDIT:

To answer your question, this solves your problem because CachedRowSetImpl doesnt stay connected to the database while in use. This allows you to close your Resultset and Statement after you've populated the CachedRowSetImpl.

Hope that answers your question.

like image 28
Sumit Avatar answered Sep 18 '22 20:09

Sumit