【问题标题】:SQLException Operation not allowed after ResultSet closedResultSet 关闭后不允许 SQLException 操作
【发布时间】:2015-09-29 03:03:27
【问题描述】:

我正在尝试为通过 html 表单中的文本框检索的查询编写 JUnit 测试。文本检索已经过测试并且可以工作,但是我的单元测试失败了。我正在使用 2 个相关类:QueryController 和 QueryControllerTest。我一直在玩弄我在这两个类中关闭的时间和内容,并不断收到错误消息:ResultSet 关闭后不允许操作。

QueryControllerTest.java

 import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.junit.Test;
import static org.junit.Assert.*;

public class QueryControllerTest {

@Test
public void testQuery() {
    ResultSet testRs = null;
    Connection conn = null;
    try {
        Class.forName("com.mysql.jdbc.Driver");

        String connectionUrl = "jdbc:mysql://localhost:3306/test";
        String connectionUser = "root";
        String connectionPassword = "GCImage";
        conn = DriverManager.getConnection(connectionUrl,
                connectionUser, connectionPassword);
        Query testQuery = new Query();
        testQuery
                .setQuery("select * from service_request where FN_contact = 'Greg'");
        testRs = QueryController.executeSelect(conn, testQuery);

        assertEquals("Laughlin", testRs.getString("LN_contact"));
        assertEquals("Hello World", testRs.getString("Notes"));
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        try {
            testRs.close();
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}
}

QueryController.java

import java.util.Map;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class QueryController {
@RequestMapping(value = "/query")
public String processRegistration(@ModelAttribute("query") Query query,
        Map<String, Object> model) {

    String queryString = query.getQuery();

    if (queryString != null && !queryString.isEmpty()) {
        System.out.println("query (from controller): " + queryString);
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String connectionUrl = "jdbc:mysql://localhost:3306/test";
            String connectionUser = "root";
            String connectionPassword = "GCImage";
            conn = DriverManager.getConnection(connectionUrl,
                    connectionUser, connectionPassword);
            if (queryString.toLowerCase().startsWith("select")) {
                ResultSet rs = executeSelect(conn, query);
            } else {
                int rowsUpdated = executeUpdate(conn, query);
                System.out.println(rowsUpdated + " rows updated");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } 

    }

    return "query";
}

public static ResultSet executeSelect(Connection conn, Query query) {
    ResultSet rs = null;
    Statement stmt = null;
    try {
        stmt = conn.createStatement();
        rs = stmt.executeQuery(query.getQuery());
        while (rs.next()) {
            String id = rs.getString("ID");
            String firstName = rs.getString("FN_Contact");
            String lastName = rs.getString("LN_Contact");
            String notes = rs.getString("Notes");
            System.out.println("ID: " + id + ", First Name: " + firstName
                    + ", Last Name: " + lastName + ", Notes: " + notes);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if(rs!=null){
                rs.close();
            }
            if(stmt != null){
                stmt.close();
            }
            if (conn != null)
                conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    return rs;
}
}

【问题讨论】:

  • A ResultSetStatementConnection 的一部分,当它们关闭时,ResultSet 也是如此

标签: java sql unit-testing junit controller


【解决方案1】:

QueryController.executeSelect 正在调用 rs.close(),但是 QueryControllerTest.testQuery 中的 assertEquals 正在调用 testRS 上的方法。由于 executeSelect 返回结果集,首先关闭它没有意义。此外,executeSelect 正在传递连接,所以它也不应该关闭它(如果调用者想要在同一个连接上执行两个不同的选择会发生什么?)。

【讨论】:

  • 我尝试删除 executeSelect() 中的 rs.close() 语句,但得到相同的异常
  • 关闭连接会关闭关联的语句,然后关闭关联的结果集,因此如果您想要返回结果集,您可能无法关闭任何内容,这意味着您必须重构为打开、执行、关闭方法.
  • 哦,哎呀。我错过了你正在传递连接,所以 executeSelect 不应该真的关闭任何东西,只是让连接的关闭关闭一切。
  • 那是因为在 executeSelect 中,您正在遍历 resultSet 中的所有行(while (rs.next())。当您返回 testQuery 时,您已经检索了所有结果集中的行,因此 testRS.getString(...) 没有可从中获取列的行。您也许可以调用 testRS.first() 以返回第一行,但是并非所有数据库和数据库驱动程序都支持。
【解决方案2】:

我认为问题在于您正在创建两个连接。尝试只为您的测试实例化QueryController 类的连接。您将需要提供连接。将其存储在变量中以运行查询后。

Connection con = QueryController.getConnection ();

【讨论】:

  • processRegistration 没有被 testQuery 调用,所以 testQuery 只使用一个连接。我假设在正常使用过程中会使用 processRegistration,但发布者正在为 executeSelect 编写测试,因此必须以与 processRegistration 类似的方式设置它,这意味着在 testQuery 中创建连接。是的,它可以稍微清理一下,例如,URL、用户和密码不必指定两次,但这留给读者作为练习。 :-)
猜你喜欢
  • 2017-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-11
  • 1970-01-01
  • 2018-03-20
  • 1970-01-01
相关资源
最近更新 更多