【问题标题】:Returning a ResultSet to another function [duplicate]将结果集返回到另一个函数[重复]
【发布时间】:2020-11-17 01:00:47
【问题描述】:

我正在创建一个程序来管理 MySQL 服务器。我有一个可以正常工作的 UI 和代码,可以将新条目添加到数据库中。下图是我正在使用的 proto GUI,对这个问题很重要的部分是表格框,它将在工作时显示数据库的条目。

我用于读取数据库内容的代码可以正常工作。该程序的结构使我为界面、场景控制器和 SQL 命令提供了单独的类。我正在处理的问题是我可以从数据库中提取所需的数据,但是通过场景控制器类将其写入数据库根本无法正常工作。

下面包含相关的代码。

SQL 函数:

public ResultSet readDataBase() throws Exception{
    try {
        // Establish connection to server
        Class.forName("com.mysql.cj.jdbc.Driver");
        connect=DriverManager.getConnection("jdbc:mysql://localhost/library?"+"user=tempUser&password=12345");
        statement=connect.createStatement();

        // Execute query and write the results
        resultSet=statement.executeQuery("select * from library.books");
        return resultSet;

        // writeResultSet(resultSet);

    } catch (Exception e){
        throw e;
    } finally {
        close();
    }
}

场景控制器代码:

public void fillTable() throws SQLException{
    ResultSet resultSet=null;
    try {
        resultSet=commands.readDataBase();

        while(resultSet.next()) {
            String title = resultSet.getString("title");
            String author = resultSet.getString("author");
            String genre = resultSet.getString("genre");
            String format = resultSet.getString("format");
            String isbn = resultSet.getString("isbn");

            System.out.println("Title: " + title);
            System.out.println("Author: " + author);
            System.out.println("Genre: " + genre);
            System.out.println("Format: " + format);
            System.out.println("ISBN: " + isbn);
            System.out.println();
        }
    }catch (Exception e){
        e.printStackTrace();
    }
}

场景控制器块中的代码的目的只是暂时测试和读取结果集。稍后将添加用于将结果中的数据写入其各自表的写入代码。选择此代码是因为我最初将它放在 SQL 函数类中并且它在那里工作,所以我知道代码很好并且完成了它的工作。

然而,每当我运行代码时,我都会得到这个错误结果。

java.sql.SQLException: Operation not allowed after ResultSet closed
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63)
at com.mysql.cj.jdbc.result.ResultSetImpl.checkClosed(ResultSetImpl.java:445)
at com.mysql.cj.jdbc.result.ResultSetImpl.next(ResultSetImpl.java:1726)
at library.test.windows.interfaceSceneController.fillTable(interfaceSceneController.java:108)
at library.test.windows.winInterface.main(winInterface.java:33)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)

我之前已经对此进行了一些阅读,但我能找到的唯一真正相关的信息来源是七年前的 post。该问题的答案提到了使用 JavaBeans 作为将信息放在中间位置的地方,但随后在他们的示例代码中使用了一个名为“Biler”的类。除了他的帖子和我的 IDE(如果相关的话,IntelliJ)根本不认识它,我在任何地方都找不到对 Biler 的任何引用。我一直在用 JavaBeans 做一些实验,但我不确定它是否能解决我的问题。

总之,我的问题是这样的。我需要做什么才能正确地将 ResultSet 从访问 SQL 服务器的函数传递到包含用于将其写入接口中的表的代码的类?我知道这一定是可能的,但我似乎无法弄清楚。

【问题讨论】:

  • Biler 类不是通用类,而是特定于该问题的 pojo/JavaBean(顺便说一句,它与您的问题重复)。该答案的作者使用它是因为问题的 OP 是从名为 Biler 的表中选择的。重点是创建一个类并用每一行的数据填充它,然后传递这些对象的列表而不是结果集。

标签: java jdbc javabeans resultset


【解决方案1】:

您的数据库访问代码应与您的用户界面代码完全分开。 UI 不应处理活动的ResultSet

您需要从结果集中复制数据,或使用实用程序为您完成此操作。

CachedRowSet

RowSet 可能是解决方案。这个接口扩展了ResultSet。有关说明,请参阅 Oracle 的 Java Tutorials

您可以使用CachedRowSet 接口将结果集数据的副本保存在内存中,与数据库分离。 Oracle 提供了一个实现,其他供应商(例如您的JDBC driver)也提供了一个实现。

CachedRowSet 实现已与数据库断开连接。相反,ResultSet 维护数据库连接(问题的根源)。引用 Javadoc:

CachedRowSet 对象是数据行的容器,将其行缓存在内存中,这样就可以在不始终连接到其数据源的情况下进行操作。此外,它是一个 JavaBeans™ 组件,可滚动、可更新和可序列化。

该接口由更多接口扩展。

POJO

普通的旧 Java 对象是另一种选择,将 ResultSet 中每一行的每个字段复制到 Java 对象的属性中。

您可以在实例化记录时简单地循环结果集。或者,您可以使用各种框架中的任何一个来提供帮助。

记录

使用 Java 16 中的新 records 功能(现在预览 in Java 15)时,为此类 POJO 定义一个类要简单得多。构造函数、getter、toStringequals & hashCode 都是编译器合成的。您只需声明属性即可。

一条记录可以声明为一个独立的类,也可以是一个嵌套的类,甚至可以在方法中本地声明。

【讨论】:

  • 这看起来很有希望!我的印象是通过使用 ResultSet 将访问代码与接口代码分开。我的错误是没有完全理解 ResultSet 是如何工作的。我认为它类似于任何标准变量,如 Int、String 或 Array,它更永久且不依赖于仍然处于活动状态的连接。缓存的行集似乎非常适合我的问题,我会在回到计算机后立即对其进行测试。
  • 好吧,让 cachedRowSet 正常工作所需的时间比我想象的要长得多(URL 只是拒绝工作,老实说我不知道​​为什么),但我终于让它工作了。 cachedRowSet 做了我需要的一切!非常感谢您的帮助!
  • 我建议不要使用CachedRowSet,而是填充pojos列表。 Java 中包含的javax.sql.RowSet 参考实现有很多错误(或者至少,每次我接触它时,我都会遇到不正确的行为和对 JDBC 要求的错误解释的问题)。
  • @MarkRotteveel 在做出这样的批评时,我建议链接到困扰您的具体实现。具有特定问题的特定实现不会使行集的概念无效。
  • 我提一下具体实现:Java自带的参考实现,大部分人都会用。
【解决方案2】:

好吧,我假设您正在关闭您的 close() 方法中的连接。所以这就是它关闭的原因。 finally 在方法体之后但在结果传递给调用函数之前执行。

try {

    return resultSet;

} finally {
    close();
}

恕我直言,无论如何传回结果集都是一种不好的做法,因为结果集并不是真正的类型安全。如果您更改查询,您将在不注意的情况下破坏 fillTable() 函数。而且,当然,您需要找到一种方法来在 some 点关闭结果集和底层数据库连接。这可能具有挑战性。

【讨论】:

  • 这是我发现的困难方法。我真的低估了 ResultSets 的工作原理,只是假设它们像一个典型的变量一样工作,比如一个数组,你可以在函数和类之间毫无问题地传递它们。我的假设是关闭连接就可以做到这一点,关闭连接但给我留下一个表,我可以使用我的接口安全地缓存在内存中。那是一个强有力的学习曲线。 @fhossfel
猜你喜欢
  • 2016-09-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-31
  • 1970-01-01
  • 1970-01-01
  • 2016-04-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多