18

Note: we reuse single connection.

************************************************
public Connection connection() {        
    try {
        if ((connection == null) || (connection.isClosed()))
        {
            if (connection!=null)
                log.severe("Connection was closed !");
            connection = DriverManager.getConnection(jdbcURL, username, password);
        }
    } catch (SQLException e) {
        log.severe("can't connect: " + e.getMessage());
    }
    return connection;        
}
**************************************************

public IngisObject[] select(String query, String idColumnName, String[] columns) {
    Connection con = connection();

    Vector<IngisObject> objects = new Vector<IngisObject>();
    try {
        Statement stmt = con.createStatement();

        String sql = query;
        ResultSet rs =stmt.executeQuery(sql);//oracle increases cursors count here
        while(rs.next()) {
            IngisObject o = new IngisObject("New Result");
            o.setIdColumnName(idColumnName);            
            o.setDatabase(this);
            for(String column: columns)
                o.attrs().put(column, rs.getObject(column));
            objects.add(o);
        }

        rs.close();// oracle don't decrease cursor count here, while it's expected
        stmt.close();
    } 
    catch (SQLException ex) {
        System.out.println(query);
        ex.printStackTrace();
    }
General Grievance
  • 4,555
  • 31
  • 31
  • 45
Vladimir
  • 4,782
  • 7
  • 35
  • 56
  • 1
    Are you refering to "ORA-01000: maximum open cursors exceeded"? If so, see http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:685336504294 for an explanation. – Oliver Michels Apr 01 '10 at 14:32
  • @Oliver Michels that looks helpful. Could you add it as an answer? – Vladimir Apr 02 '10 at 18:46
  • It tried to write a short summary. Best Regards. – Oliver Michels Apr 06 '10 at 07:42
  • An antivirus is Running on Database Server? If antivirus is necessary on your database server (which is not a good practice - as many have pointed out), atleast have an exclusion in the scanning of database files and database server binaries. – ABDAOUI Wahbi Dec 27 '16 at 09:06

4 Answers4

28

The init.ora parameter open_cursors defines the maximum of opened cursors a session can have at once. It has a default value of 50. If the application exceeds this number the error "ORA-01000: maximum open cursors exceeded" is raised.

Therefore it's mandatory to close the JDBC resources when they are not needed any longer, in particular java.sql.ResultSet and java.sql.Statement. If they are not closed, the application has a resource leak.

In case of reusing the Connection object, you must be aware of the fact that the opened oracle cursors are kept open and in use as long the connection exists and the transaction has not ended. When the application commits, the opened cursors are released.

Therefore as an application designer you need to know a rough estimation of the needed open cursors for your most complex transaction.

The difficulty lies in the inability of oracle's internal parameter views (v$open_cursor, v$sesstat, et. al.) to show the difference between opened cursors, which are reusable and opened cursors, which are still blocked (not reusable!) by an unclosed ResulSet or Statement. If you close all Statement and ResultSet objects in your finally block, your application is perfectly fine.

Adjusting the init.ora parameter works like this (our application needs 800 cursors at a maximum)

ALTER SYSTEM SET open_cursors = 800 SCOPE=BOTH;
Oliver Michels
  • 2,837
  • 1
  • 19
  • 14
  • 3
    I think it should be emphasized that one has to close ResultSet **AND** Statement. – Hajo Thelen Oct 16 '12 at 09:48
  • Hy, I have a DBconnection with open_cursor=150, autocommit=false. I close all my PreparedStatement and ResultSet but i have this problem. It might be because of triggers that start after some delete and insert, but inserting commit afetr that may be dangerous, because where committed all transaction maded at that point, and if then in othe rparts of the programs something fails, i can't make a real rollback of the whole transaction. any suggestion? – Andrea_86 Jan 20 '15 at 09:33
  • @Andrea_86: committing in triggers is your direct path to hell ;-) Try to increase your open_cursors setting. – Oliver Michels Feb 13 '15 at 11:56
  • Very helpful. But 8000 is a better size for any sort of enterprise app than 800 – pojo-guy Mar 05 '19 at 17:56
  • SESSION_CACHED_CURSORS: https://twitter.com/dbms_xtender/status/1413665573741993986?s=20 – Sayan Malakshinov Jul 10 '21 at 01:11
  • + doc: https://docs.oracle.com/cd/B19306_01/server.102/b14237/initparams189.htm#REFRN10195 – Sayan Malakshinov Jul 10 '21 at 01:12
7

Normally you would put the close statements for your ResultSet and Statement into a finally block to ensure that they are called even if an exception occurs (could be the issue you are having here). In your current code, if a SQLException occurs then the two close( ) method calls will never occur and cursors would be left open.

Also what query are you using in Oracle to see the count of open cursors?

Edit:
That code should be closing the cursor. If it isn't then you should be able to see a 1 to 1 correlation of calling your method and the cursor count going up by 1. Be sure there isn't some unexpected process that is causing the cursor count to go up.

If you have the privileges, you can run this query against the database to see the open cursor count by sid to see if maybe it is some other process that is increasing the cursors and not yours specifically. It will pull back any with more than 10 cursors open, you can raise this to filter out the noise or narrow it specifically by username or osuser:

select oc.sid,
       count(*) numCur,
       s.username username,
       s.osuser osuser,
       oc.sql_text,
       s.program
  from v$open_cursor oc,
       v$session s
 where s.sid = oc.sid
group by oc.sid, 
         oc.sql_text, 
         s.username, 
         s.osuser, 
         s.program
having count(*) > 10
order by oc.sid;

Another query that may be helpful, in case multiple sid's are using the same query string so the above does not reveal the offender well:

 select oc.sql_text, count(*) 
   from v$open_cursor oc 
   group by oc.sql_text 
   having count(*) > 10 
   order by count(*) desc;
rogerdpack
  • 62,887
  • 36
  • 269
  • 388
Doug Porter
  • 7,721
  • 4
  • 40
  • 55
  • I know that it's better to close them in finally. But excpetion is not an issue. It doesn't occur in my testing. – Vladimir Apr 01 '10 at 13:22
  • @Vladimir: I added additional info to my answer. Your code should be working if no exception is being thrown. Look at the sid level and make sure it is your process alone that is affecting the cursor count. – Doug Porter Apr 01 '10 at 16:06
  • this query helped me figure out the problem (in the end, I was accidentally "leaking" preparedstatements in java, oops) – rogerdpack Nov 25 '13 at 23:15
7

The correct way to do it is to close every resource in a finally block in its own try/catch block. I usually use a static utility class like this:

public class DatabaseUtils
{
    public static void close(Connection connection)
    {
        try
        {
            if (connection != null)
            {
                connection.close();
            }
        }
        catch (SQLException e)
        {
            // log exception here.
        }
    }

    // similar methods for ResultSet and Statement
}

So I'd write your code like this:

public IngisObject[] select(String query, String idColumnName, String[] columns) {

Vector<IngisObject> objects = new Vector<IngisObject>();

Connection con = null;
Statement stmt = null;
ResultSet rs = null;

try 
{
    connection = connection();
    stmt = con.createStatement();

    // This is a SQL injection attack waiting to happen; I'd recommend PreparedStatemen
    String sql = query;
    rs =stmt.executeQuery(sql);//oracle increases cursors count here
    while(rs.next()) 
    {
       IngisObject o = new IngisObject("New Result");
       o.setIdColumnName(idColumnName);            
       o.setDatabase(this);
       for(String column: columns) o.attrs().put(column, rs.getObject(column));
       objects.add(o);
    }

} 
catch (SQLException ex) 
{
    System.out.println(query);
    ex.printStackTrace();
}
finally
{
    DatabaseUtils.close(rs);
    DatabaseUtils.close(stmt);
    DatabaseUtils.close(con);
}
duffymo
  • 305,152
  • 44
  • 369
  • 561
  • how to execute public IngisObject[] select(String query, String idColumnName, String[] columns) from psvm main method of Java Code. Can you include that too in your code snippet – deepakl.2000 Jul 09 '21 at 17:53
  • Easy - I'll leave it as an exercise for you. You can convert a List to an array - call the method I wrote and return an array. – duffymo Jul 10 '21 at 00:04
6

I just had the same problem and found that - if you do not close the connection (because you will maybe reuse it later on) - you at least have to do a connection.rollback() or connection.commit() to free the open cursors togehther with closing the ResultSet and Statements.

FrVaBe
  • 47,963
  • 16
  • 124
  • 157
  • I would contradict with your opinion, it is always better to close your ResultSet instead of depending on Garbage Collector to do it for you. – Aman J Nov 05 '12 at 06:27
  • @Amandeep Jiddewar Of corse you should close the ResultSet. But as the question is about open cursors after closing the ResultSet I just pointed out that you also have to do a connection rollback/commit to free the cursor. So nothing to contradict :-) – FrVaBe Nov 05 '12 at 08:13
  • In my case, I had to close the connection immediately.. commit / rollback did not help much. – RAS Nov 01 '18 at 13:38