简体   繁体   中英

java jdbc no operations allowed

I am getting a No operations allowed after statement closed. - very obvious and also self explanatory as to what is going on with my code. In any case I am wondering how I can do this in a cleaner way

public class BaseClass {
    Connection con;
    Statement st;

    protected void establishDBConnection() throws ClassNotFoundException,
      SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        String cString = "....";
        con = DriverManager.getConnection(cString, user, password);
        st = con.createStatement();
    }

    public BaseClass() {
        try {
            createDBConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


public ClassB extends BaseClass {

    public ClassB() {
        super();
    }

    public void doSomething() {
        try {
            String q = "select * from my_table";
            String moreQuery = "update my_table ...."
            String anotherQuery = "do something fancy..."
            rs = st.executeQuery(q);
            while (rs.next()) {
                st.executeUpdate(moreQuery);
                st.executeUpdate(anotherQuery);
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Error in getAllAssociatesOfMra: " + e);
        }
    }
}

Currently my code is throwing a com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after statement closed. The exception is obvious as to what is going on but I was wondering how I can go about handling the close in the BaseClass.

Update

I am aware that there a couple of related questions like mine. The only problem with those questions is that everything is done in the main class. Consider this to be kind of design/abstraction question

Your design is not good. You should be getting the connection, preferably from a connection pool, creating the statements in the beginning of your doSomething() method (for example calling the superclass method), and then closing the Statements and ResultSets when you've done "something".

Before you can make a good design you have to understand what you're trying to accomplish. So I want to establish some goals for this, then look at how the design meets those goals. In order to get the goals for this, let's go over how database connections work.

A database is a separate process, it can be on the same machine or on a different machine where it's accessed over the network. Network connections can go stale due to transient conditions, database downtime, etc. Even if the database is on the same machine and the network is not an issue, it's still bad form to have your application dependent on the state of a separate process with no way to recover, it means that if the database goes down the application can't recover by itself, you have to restart it.

Some properties of connections:

  • They take a while to get initialized, long enough you wouldn't want to create a new one for every user request. (This will not be nearly as big an issue if the database is on the same machine.)

  • There is a limited number of them, you don't want one user request to take more than necessary because that will limit the number of other users who can connect concurrently.

  • There's a commit method on your database connection object that lets you group your operations into transactions, so that a) your queries have a consistent view of the data, and b) the operations get applied in an all-or-nothing manner (if an operation fails you don't have half-done junk cluttering up the database that you have to undo). Currently your connections are in autocommit mode, which means each operation is committed separately (and you can't group operations together).

  • They're synchronized so only one thread at a time can use them. That way multiple users can't corrupt each others' work, but if you have only one connection your application won't scale since every user is waiting in line for the connection.

From all this we can derive some goals for the design. One is that we want to be able to reinitialize database connections that can go bad, we want to have multiple ones available so everybody's not waiting on the same one, and we want to associate a connection with a user request so that we can group the operations for a given user into transactions.

It's hard to tell what your posted code does exactly because it depends on the scope of the objects, which the question leaves unspecified. If these objects are created per-user-request then you will get a new connection every time, which solves the staleness issue, but may be slow. Having a different connection for every table can make the slowness worse, limits the application's concurrency unnecessarily, and also doesn't allow for transactions that group operations on different tables, because you don't have a common connection object to call commit on. How connections get closed is not apparent; it would be wasteful for them to get abandoned to timeout.

A commonly used alternative approach would be to pool the database connections (where the pool can test database connections and replace stale ones) and hand them out to user requests, returning them to the pool when the request is done with them (the pool can wrap a connection in an object where calling close on it returns it to the pool). In the meantime the user thread can execute operations on the connection, create its own statements and close them on the way out, and commit the results.

Spring has a well-thought out way of handling this situation which follows the approach described above (except with a lot more functionality). Some people dislike frameworks for overcomplicating things, but I recommend at least looking at Spring examples. That way you are aware of an alternative viable approach to organizing your code and you can improve your own design.

If I understand your question and objective, you will need to create multiple Statement objects in doSomething() , and you need to clean up your Statements and ResultSet in a finally block with something like -

Statement st = con.createStatement();
String q = "select * from my_table";
String moreQuery = "update my_table ....";
String anotherQuery = "do something fancy...";
ResultSet rs = st.executeQuery(q);

try {
    while (rs.next()) {
        Statement stmt = null;
        try {
            stmt = con.createStatement();
            stmt.executeUpdate(moreQuery);
        } finally {
            if (stmt != null) {
                stmt.close();
            }
        }
        try {
            stmt = con.createStatement();
            stmt.executeUpdate(anotherQuery);
        } finally {
            if (stmt != null) {
                stmt.close();
            }
        }
    }
} finally {
    if (rs != null) {
        rs.close();
    }
    if (st != null) {
        st.close();
    }
}

I suggest few things:

  • Use connection pool design
  • To prevent statement close, you can use finally block to close them
  • Since you have a query after another query, use transaction (commit/rollback) to prevent things "half done"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM