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 使用一个或多个 ResultSets。由于您是初学者,因此假设每个Statement 将有一个ResultSet。如果修改了Statement中的数据,则绑定到这个StatementResultSet将被关闭,不能在以后的操作中使用。这就是您遇到问题的原因(如其他答案所述)。

如果您将执行将使用参数的 SQL 语句,请使用 PreparedStatement。否则,您的应用程序将容易受到 SQL 注入攻击(即黑客可能会关闭您的数据库服务器,您和我都知道这是一件坏事)。

您应该在使用资源后关闭它们。这意味着,您应该关闭ResultSets、Statements 和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 关闭后不允许操作

即使 PreparedStatement 仅使用一次,ResultSet 关闭错误后也不允许操作

连接关闭后不允许任何操作

MySQLNonTransientConnectionException:连接关闭后不允许操作。连接