ResultSet 关闭错误后不允许操作
Posted
技术标签:
【中文标题】ResultSet 关闭错误后不允许操作【英文标题】:Operation not allowed after ResultSet closed error 【发布时间】:2012-10-02 20:51:42 【问题描述】:我有一个代码在处理后将数据插入表中,但我一次又一次收到错误
ResultSet 关闭后不允许操作
这是我的代码。
try
Connection con = null;
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/asteriskcdrdb", "root", "techsoft");
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("Select * from asteriskcdrdb.sp1");
while (rs.next())
AreaCode = rs.getString("AreaCode");
//System.out.println(AreaCode);
String Pulse = rs.getString("Pulse");
Rate = rs.getInt("Rate/pulse");
// System.out.println(Rate);
if (AreaCode.equals(str))
System.out.println("Hii");
try
Connection conn = null;
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/asteriskcdrdb", "root", "techsoft");
Statement stmt = conn.createStatement();
rst = stmt.executeQuery("Select * from cdr where src ='9035020090'");
while (rst.next())
calldate = rst.getString("calldate");
// System.out.println(calldate);
clid = rst.getString("clid");
src = rst.getString("src");
dst = rst.getString("dst");
dcontext = rst.getString("dcontext");
channel = rst.getString("channel");
dstchannel = rst.getString("dstchannel");
lastapp = rst.getString("lastapp");
lastdata = rst.getString("lastdata");
duration = rst.getString("duration");
//System.out.println(duration);
dur = Integer.parseInt(duration);
//System.out.println(dur);
data.add(dur);
billsec = rst.getString("billsec");
disposition = rst.getString("disposition");
amaflags = rst.getString("amaflags");
accountcode = rst.getString("accountcode");
uniqueid = rst.getString("uniqueid");
userfield = rst.getString("userfield");
int newcost = checktime(dur, Rate);
stmt.executeUpdate("insert into cdrcost (
calldate,clid,src,dst,dcontext,channel,
dstchannel,lastapp, lastdata,duration,billsec,
disposition,amaflags,accountcode,uniqueid,
userfield,cdrcost) values ('" + calldate + "','" +
clid + "','" + src + "','" + dst + "','" + dcontext
+ "','" + channel + "','" + dstchannel + "','" +
lastapp + "','" + lastdata + "','" + duration + "','" +
billsec + "','" + disposition + "','" + amaflags
+ "','" + accountcode + "','" + uniqueid + "','" + userfield
+ "','" + newcost + "')");
catch (Exception e)
System.out.println(e);
else if (AreaCode.equals(str2))
System.out.println("Hii2");
catch (Exception e)
System.out.println(e);
public static int checktime(int dur, int Rate)
int cost = 0;
// System.out.println(c);
int min = 60;
int quotient = dur / min;
// System.out.println(quotient);
int reminder = dur % min;
// System.out.println(reminder);
if (reminder > 0)
quotient = quotient + 1;
// System.out.println(quotient);
// System.out.println(cost);
cost = quotient * Rate;
return cost;
【问题讨论】:
【参考方案1】:stmt.executeUpdate("insert into cdrcost (
calldate,clid,src,dst,dcontext,channel,
dstchannel,lastapp, lastdata,duration,billsec,
disposition,amaflags,accountcode,uniqueid,
userfield,cdrcost) values ('" + calldate + "','" +
clid + "','" + src + "','" + dst + "','" + dcontext
+ "','" + channel + "','" + dstchannel + "','" +
lastapp + "','" + lastdata + "','" + duration + "','" +
billsec + "','" + disposition + "','" + amaflags
+ "','" + accountcode + "','" + uniqueid + "','" + userfield
+ "','" + newcost + "')");
在这里,当您在while
循环内执行此update
时,前一个select
查询的当前resultset
将关闭。所以你不能在下一次迭代中做res.next()
。
如果想在断开连接后hold
数据,可以使用Cached Row Set
:
ResultSet res = ....
CachedRowSet rowset = new CachedRowSetImpl();
rowset.populate(res);
CachedRowSet 是一个无连接的 ResultSet。我没有用太多,因为我没有任何需要。但是,我可以在这里分享一些链接来帮助您理解这些概念
http://download.java.net/jdk8/docs/api/javax/sql/rowset/CachedRowSet.html Where to close a JDBC Connection while I want to return the ResultSet或者你最好看看RowSetFactory,它为你提供了方法createCachedRowSet()
,可以为你创建CachedRowSet
实例。
您可以从RowSetProvider 获取RowSetFactory
。然后您可以获取CachedRowSet
并对其进行迭代。
RowSetFactory factory = RowSetProvider.newFactory();
CachedRowSet crs = factory.createCachedRowSet();
crs.populate(res);
while(crs.next())
crs.getString(1); // Works similar to `ResultSet`
【讨论】:
@Rohit Jain .. 谢谢先生您的快速回复..请您指导我如何纠正 @LuiggiMendoza。别着急,我等下次再回复。会给你充分的机会:) :P 您可以提出解决方案以便给出更好的答案。此外,OP 应该为所有操作使用相同的连接,无需为第一个ResultSet
中的每个结果打开一个新的Connection
。
@LuiggiMendoza 那么如何纠正这个错误先生。我真的不知道..请帮我先生
@RohitJain Sir 在哪里放置 CachedRowSet rowset = new CachedRowSetImpl(); rowset.populate(res);在 while (res.next) 循环内部或外部【参考方案2】:
在给你答案之前,你应该了解一些关于数据库访问和 JDBC 的基本知识:
您不得在大型操作中创建许多连接来访问数据库。如果您需要在单个方法中读取、插入、更新或删除数据,则应仅使用 1 个连接。打开连接是一项成本很高的操作。如果您还没有注意到,那是因为您处于单用户(您)环境中。
每个Statement
使用一个或多个 ResultSet
s。由于您是初学者,因此假设每个Statement
将有一个ResultSet
。如果修改了Statement
中的数据,则绑定到这个Statement
的ResultSet
将被关闭,不能在以后的操作中使用。这就是您遇到问题的原因(如其他答案所述)。
如果您将执行将使用参数的 SQL 语句,请使用 PreparedStatement
。否则,您的应用程序将容易受到 SQL 注入攻击(即黑客可能会关闭您的数据库服务器,您和我都知道这是一件坏事)。
您应该在使用资源后关闭它们。这意味着,您应该关闭ResultSet
s、Statement
s 和Connection
(按此顺序)。
根据所有这些注释,您的代码将更改为:
Connection con = null;
Statement st = null;
ResultSet rs = null;
try
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/asteriskcdrdb", "root", "techsoft");
st = con.createStatement();
rs = st.executeQuery("Select * from asteriskcdrdb.sp1");
while (rs.next())
AreaCode = rs.getString("AreaCode");
String Pulse = rs.getString("Pulse");
Rate = rs.getInt("Rate/pulse");
if (AreaCode.equals(str))
Statement stmt = null;
ResultSet rst = null;
PreparedStatement insSt = null;
try
//using the first connection
stmt = con.createStatement();
rst = stmt.executeQuery("Select * from cdr where src ='9035020090'");
while (rst.next())
calldate = rst.getString("calldate");
clid = rst.getString("clid");
src = rst.getString("src");
dst = rst.getString("dst");
dcontext = rst.getString("dcontext");
channel = rst.getString("channel");
dstchannel = rst.getString("dstchannel");
lastapp = rst.getString("lastapp");
lastdata = rst.getString("lastdata");
duration = rst.getString("duration");
dur = Integer.parseInt(duration);
data.add(dur);
billsec = rst.getString("billsec");
disposition = rst.getString("disposition");
amaflags = rst.getString("amaflags");
accountcode = rst.getString("accountcode");
uniqueid = rst.getString("uniqueid");
userfield = rst.getString("userfield");
int newcost = checktime(dur, Rate);
//every ? is a parameter in the query
insSt = con.prepareStatement(
"insert into cdrcost (calldate,clid,src,dst,dcontext,channel, dstchannel, lastapp, lastdata,duration,billsec, disposition,amaflags,accountcode,uniqueid, userfield,cdrcost) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
//setting every parameter
insSt.setObject(1, calldate);
insSt.setObject(2, clid);
insSt.setObject(3, src);
insSt.setObject(4, dst);
insSt.setObject(5, dcontext);
insSt.setObject(6, channel);
insSt.setObject(7, dstchannel);
insSt.setObject(8, lastapp);
insSt.setObject(9, lastdata);
insSt.setObject(10, duration);
insSt.setObject(11, billsec);
insSt.setObject(12, disposition);
insSt.setObject(13, amaflags);
insSt.setObject(14, accountcode);
insSt.setObject(15, uniqueid);
insSt.setObject(16, userfield);
insSt.setObject(17, newcost);
//executing the insert statement
insSt.executeUpdate();
catch (Exception e)
System.out.println(e);
finally
//closing the resources in this transaction
try
//the insSt statement doesn't have a resultset
if (insSt != null)
insSt.close();
//the rst ResultSet is bounded to stmt Statement, it must be closed first
if (rst != null)
rst.close();
if (stmt != null)
stmt.close();
catch (SQLException sqle)
else if (AreaCode.equals(str2))
System.out.println("Hii2");
catch (Exception e)
System.out.println(e);
finally
//closing the resources in this transaction
//similar logic than the used in the last close block code
try
if (rs != null)
rs.close();
if (st != null)
st.close();
//at the last of all the operations, close the connection
if (con != null)
con.close();
catch (SQLException sqle)
附带说明,您是初学者这一事实并不意味着您应该编写代码只是为了让它工作。您应该始终遵循最佳做法。 IMO 最好在这些情况下寻求指导。
【讨论】:
@Ram 如果答案适用于您的情况,请将其标记为答案(单击我的答案旁边的检查)。【参考方案3】:在给定的时间点,一个连接只能被一个语句使用。执行语句的结果集共享相同的连接。因此,如果您想在从结果集中读取每一行时对其执行更新,您应该在创建语句时请求 UPDATEABLE 结果集,然后在浏览结果集时更新当前行中的列。
由于您似乎需要将记录插入到不同的表中,您可以执行以下操作之一:
使用缓存行集
-
将您的结果集放入
CachedRowSet
。
关闭结果集和语句。
遍历缓存的行集,为每个插入创建一个语句并执行它。
使用单独的对象来跟踪需要插入的内容
-
将要插入的记录作为对象构建到列表中。
完成结果集后,关闭它,关闭语句。
为 INSERT 语句准备一条新语句,即 PreparedStatement。
批量插入记录。
最坏情况,使用三个连接,一个用于外部查询,一个用于内部查询,一个用于插入
以下代码(完全不是生产质量,只是上面第三个选项的肮脏实现):
try
// used for outer query.
Connection outerCon = null;
// used for inner query.
Connection innerCon = null;
// used to insert records.
Connection insCon = null;
Class.forName("com.mysql.jdbc.Driver");
outerCon = DriverManager.getConnection("jdbc:mysql://localhost:3306/asteriskcdrdb", "root", "techsoft");
innerCon = DriverManager.getConnection("jdbc:mysql://localhost:3306/asteriskcdrdb", "root", "techsoft");
insCon = DriverManager.getConnection("jdbc:mysql://localhost:3306/asteriskcdrdb", "root", "techsoft");
Statement st = outerCon.createStatement();
ResultSet rs = st.executeQuery("Select * from asteriskcdrdb.sp1");
while (rs.next())
AreaCode = rs.getString("AreaCode");
// System.out.println(AreaCode);
String Pulse = rs.getString("Pulse");
Rate = rs.getInt("Rate/pulse");
// System.out.println(Rate);
if (AreaCode.equals(str))
System.out.println("Hii");
try
Statement stmt = innerCon.createStatement();
ResultSet rst = stmt.executeQuery("Select * from cdr where src ='9035020090'");
while (rst.next())
calldate = rst.getString("calldate");
// System.out.println(calldate);
clid = rst.getString("clid");
src = rst.getString("src");
dst = rst.getString("dst");
dcontext = rst.getString("dcontext");
channel = rst.getString("channel");
dstchannel = rst.getString("dstchannel");
lastapp = rst.getString("lastapp");
lastdata = rst.getString("lastdata");
duration = rst.getString("duration");
// System.out.println(duration);
dur = Integer.parseInt(duration);
// System.out.println(dur);
data.add(dur);
billsec = rst.getString("billsec");
disposition = rst.getString("disposition");
amaflags = rst.getString("amaflags");
accountcode = rst.getString("accountcode");
uniqueid = rst.getString("uniqueid");
userfield = rst.getString("userfield");
int newcost = checktime(dur, Rate);
Statement insStmt = insCon.createStatement();
stmt
.executeUpdate("insert into cdrcost (calldate,clid,src,dst,dcontext,channel,dstchannel,lastapp, lastdata,duration,billsec,disposition,amaflags,accountcode,uniqueid,userfield,cdrcost) values ('"
+ calldate
+ "','"
+ clid
+ "','"
+ src
+ "','"
+ dst
+ "','"
+ dcontext
+ "','"
+ channel
+ "','"
+ dstchannel
+ "','"
+ lastapp
+ "','"
+ lastdata
+ "','"
+ duration
+ "','"
+ billsec
+ "','"
+ disposition
+ "','"
+ amaflags
+ "','"
+ accountcode + "','" + uniqueid + "','" + userfield + "','" + newcost + "')");
insStmt.close();
rst.close();
stmt.close();
catch (Exception e)
System.out.println(e);
else if (AreaCode.equals(str2))
System.out.println("Hii2");
rs.close();
st.close();
catch (Exception e)
System.out.println(e);
【讨论】:
如响应中所建议的,您可以使用这两种方法之一。你在寻找什么具体的东西吗? 先生,我对编程领域很陌生,真的从来没有用过这个......所以请帮我写代码......请先生 请查看有关第三个选项的更新以及对代码的修改以实现该选项。以上是关于ResultSet 关闭错误后不允许操作的主要内容,如果未能解决你的问题,请参考以下文章
ResultSet 关闭后不允许出现 ResultSet、空指针异常和 SQLException 操作
获取 java.sql.SQLException:ResultSet 关闭后不允许操作