我得到一个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中的关闭。
我知道有一些像我这样的相关问题。这些问题的唯一问题是一切都在主类中完成。认为这是一种设计/抽象问题
答案 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
块关闭它们