java jdbc不允许任何操作

时间:2014-08-14 20:53:04

标签: java jdbc

我得到一个No operations allowed after statement closed. - 非常明显且也是自我解释的 至于我的代码是怎么回事。无论如何,我想知道如何以更清洁的方式做到这一点

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);
        }
    }
}

目前我的代码正在抛出com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after statement closed. 关于发生了什么事情的例外很明显,但我想知道如何处理BaseClass中的关闭。

更新

我知道有一些像我这样的相关问题。这些问题的唯一问题是一切都在主类中完成。认为这是一种设计/抽象问题

4 个答案:

答案 0 :(得分:2)

你的设计不好。您应该首先从连接池获取连接,在doSomething()方法的开头创建语句(例如调用超类方法),然后关闭Statements和{{1}当你做了“某事”时。

答案 1 :(得分:1)

在你做出好的设计之前,你必须要了解你想要完成的事情。所以我想为此建立一些目标,然后看看设计如何实现这些目标。为了实现这一目标,我们来看看数据库连接的工作原理。

数据库是一个单独的进程,它可以位于同一台计算机上,也可以位于通过网络访问的另一台计算机上。由于瞬态条件,数据库停机等原因,网络连接可能会过时。即使数据库位于同一台计算机上并且网络不是问题,让应用程序依赖于单独进程的状态仍然是不好的形式恢复方式,这意味着如果数据库出现故障,应用程序无法自行恢复,则必须重新启动它。

连接的一些属性:

  • 他们需要一段时间才能初始化,足够长的时间你不想为每个用户请求创建一个新的。 (如果数据库在同一台机器上,这不会是一个大问题。)

  • 它们数量有限,您不希望一个用户请求占用超过必要的数量,因为这将限制可以同时连接的其他用户的数量。

  • 您的数据库连接对象上有一个提交方法,允许您将操作分组为事务,以便a)您的查询具有一致的数据视图,以及b)操作应用于全部或 - 没有任何方式(如果一个操作失败,你没有半完成的垃圾混乱你必须撤消的数据库)。目前您的连接处于自动提交模式,这意味着每个操作都是单独提交的(并且您不能将操作组合在一起)。

  • 它们是同步的,因此一次只有一个线程可以使用它们。这样多个用户就不会破坏彼此的工作,但如果你只有一个连接,你的应用程序将无法扩展,因为每个用户都排队等候连接。

从这一切我们可以得出设计的一些目标。一个是我们希望能够重新初始化可能变坏的数据库连接,我们希望有多个可用,因此每个人都不等待同一个,我们想要将连接与用户请求相关联,以便我们可以对给定用户进行交易的操作。

很难说出你发布的代码究竟是做什么的,因为它取决于对象的范围,问题未指明。如果这些对象是按用户请求创建的,那么每次都会得到一个新连接,这可以解决过时问题,但可能很慢。为每个表建立不同的连接可能会使速度变慢,不必要地限制应用程序的并发性,也不允许在不同的表上对操作进行分组的事务,因为您没有用于调用commit的公共连接对象。如何关闭联系并不明显;他们被遗弃超时是浪费。

一种常用的替代方法是池化数据库连接(池可以测试数据库连接并替换过时的连接)并将它们交给用户请求,并在请求完成时将它们返回池中( pool可以在一个对象中包装一个连接,在该对象上调用close将其返回到池中。与此同时,用户线程可以对连接执行操作,创建自己的语句并在出路时关闭它们,并提交结果。

Spring has a well-thought out way of handling this situation遵循上述方法(除了功能更多)。有些人不喜欢过于复杂的框架,但我建议至少看一下Spring的例子。这样您就可以了解组织代码的另一种可行方法,并且可以改进自己的设计。

答案 2 :(得分:0)

如果我理解你的问题和目标,你需要在doSomething()中创建多个Statement对象,并且你需要在finally块中清理你的语句和ResultSet,例如 -

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();
    }
}

答案 3 :(得分:0)

我建议的事情很少:

  • 使用连接池设计
  • 要防止语句关闭,您可以使用finally块关闭它们
  • 由于您在另一个查询之后有查询,因此请使用transaction(提交/回滚)来防止事情发生一半"