This is a sign of not properly closing/releasing JDBC resources. You need to acquire and close all JDBC resources in the shortest possible scope, i.e. you need to close them in the reversed order in the finally
block of the try
block of the very same method block as you've acquired them. E.g.
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = database.getConnection();
statement = connection.createStatement();
resultSet = statement.executeQuery(SQL);
// ...
} 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) {}
}
If you don't close them properly as soon as possible, the DB will take it in own hands sooner or later and your application may break sooner or later as you encountered yourself.
To improve connecting performance, make use of a connection pool --you still need to acquire and close them in the same manner as here above though! It's now just the connection pool implementation which under the hoods worries about actually closing the connection or not.