【问题标题】:array not being filled数组未填充
【发布时间】:2020-09-03 15:54:40
【问题描述】:

当我稍后查看它时,数组中应该至少有 3 个条目,但它只显示一个。我相信这是你有问题的方法,有什么建议吗?

    String[] getKidsNamebyCid(int cid) {
        String[] out = new String[20];
        try {
            String qry = "SELECT KIDSNAME FROM TBLKIDS WHERE CID = ?";//setting query command
            ps = connect.prepareStatement(qry);//preparing statement
            ps.setInt(1, cid);//setting CID
            ps.executeQuery();//running command
            int i = 0;
            while (ps.getResultSet().next()) {
                out[i] = ps.getResultSet().getString("KIDSNAME");
                i++;
            }
        } catch (SQLException se) {
            se.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return out;
    }

【问题讨论】:

  • 你的代码调试了吗?
  • 这里是高中生,不知道该怎么做
  • @Stultuske 我确信它的这种方法和它的数组会导致问题,但我不明白为什么。在while循环中,我放了2个输出,一个输出resultset.getstring,另一个输出out[i]中的字符串。 out[i] 意味着等于结果集字符串,但它只输出 null 而 getString 输出正确的东西
  • 一种模拟最小调试的简单方法:在您的 while 循环中添加打印语句,检查添加了多少元素以及添加了哪些元素
  • 您是在哪里以及如何添加这些打印语句的?

标签: java sql arrays loops jdbc


【解决方案1】:

getResultSet() 调用不是吸气剂。该方法对数据库起作用,您不能只是重复调用它;第一次获得 ResultSet 对象(需要关闭),第二次重置一切。所以不要;您需要调用 getResultSet() 一次

我怎么知道?通过阅读。直接来自getResultSet() 文档:

每个结果只应调用此方法一次。

此外,这段代码也充斥着严重的代码问题,这些问题更普遍地集中在资源上。资源是不只是内存中的一堆位的对象,它们代表(并保持打开)“资源”。对于数据库,它是与数据库引擎的连接。您不能只制造资源,您必须始终将它们放在“保护”块中,以确保资源被清理干净。因此,除非没有其他方法,否则您永远不会希望它们成为一个字段(然后它们作为一个字段内部的东西就变成了一种资源)。

那么,您的 PreparedStatement 是一个字段这一事实?不好。你这样调用 .getResource 的事实,无人看管?不好。

最后,您的异常处理很愚蠢。面对检查异常时的默认行为是将它们添加到您的 throws 子句中。如果你做不到,正确的做法是throw new RuntimeException("uncaught", e);,而不是你所做的。

executeQuery 已经返回一个结果集。一般来说,永远不要打电话给getResultSet*。

最后,数组或多或少已经过时了;你想要收藏。

把它们放在一起:

    // delete your 'ps' field!

    List<String> getKidsNamebyCid(int cid) throws SQLException {
        var out = new ArrayList<String>();
        String qry = "SELECT KIDSNAME FROM TBLKIDS WHERE CID = ?";
        try (PreparedStatement ps = connect.prepareStatement(qry)) {
            ps.setInt(1, cid);

            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) out.add(rs.getString("KIDSNAME"));
            }
        }
        return out;
    }

*) PreparedStatement API 非常不幸。您与 PS 交互的方式与 Statement(您应该很少使用;您不能将用户输入添加到普通的简语句中)完全不同,但是因为原因和历史 PreparedStatement 扩展了 Statement。这意味着一整套方法都在 PreparedStatements 中,你不应该调用。那真不幸。这里有两件事要学习:[1] Java 不会破坏向后兼容性,即使这意味着某些 API 很糟糕,并且 [2] JDBC 并不是真正用于“人类消费”的。我们也不用机器代码对 CPU 进行编程,但机器代码存在并将继续存在。我们使用“机器码”作为胶水;库和语言开发人员用作通用语言的东西。 JDBC 也是如此:它不适合您。使用具有良好 API 的库,例如 JDBI。这可能超出了您高中课程的要求,但是,嘿。没什么可说的,除了:在你的课程和老师中,他们让你使用“真正的”**开发人员不使用的过时工具。

**) “真实”的含义是:正在编写为获得大量资金和/或眼球的应用程序提供动力的代码。

【讨论】:

  • 是的,关于这个项目的 idgaf 兄弟,它只需要工作,即使它是最低限度的。考虑到明天到期,正在寻找快速解决方案
  • "第一次得到 ResultSet 对象" 错误。 --- "第二次全部重置" i> 错误。 --- 答案还有其他问题,但有这么多优点,即使我觉得我应该投反对票,我也不会投反对票。
  • @Travopticat 如果你是 idgaf,我们为什么要 idgaf 关于帮助你?如果您只是想免费解决您的学校作业,那么您来错地方了。见Open letter to students with homework problems
【解决方案2】:

您需要了解PreparedStatement 的实际工作原理。我强烈建议您按照教程来学习如何使用它,然后按照您自己的代码的模式。

但它也都在文档中,所以让我们引用各种相关的部分。

executeQuery() 的 javadoc 说:

在此PreparedStatement 对象中执行SQL 查询并返回查询生成的ResultSet 对象。

此时问题中的代码已经错了,因为它**忽略了executeQuery()调用的返回值。

另外,getResultSet() 的 javadoc 说:

ResultSet 对象的形式检索当前结果。此方法应该每个结果只调用一次

问题中的代码在这一点上更加错误,因为它在循环中重复调用getResultSet()


如果您已经阅读了您正在使用的方法的 javadoc,那么很明显有些地方是错误的。如前所述,通过教程将展示如何正确执行此操作。实际上,任何使用 JDBC 执行查询的示例的网络搜索都会显示这一点。

有关其工作原理的更多背景信息,execute() 的 javadoc 说:

执行此PreparedStatement 对象中的SQL 语句,它可以是任何类型的SQL 语句。 一些准备好的语句返回多个结果; execute 方法处理这些复杂的语句以及由方法executeQueryexecuteUpdate 处理的更简单的语句形式。

execute 方法返回一个布尔值来指示第一个结果的形式。您必须调用方法getResultSetgetUpdateCount 来检索结果;您必须调用 getMoreResults 才能转到任何后续结果。

getMoreResults() 的 javadoc 说:

移动到此Statement 对象的下一个结果,如果它是ResultSet 对象,则返回true,并隐式关闭使用getResultSet 方法获得的任何当前ResultSet 对象.

“返回多个结果”不是指来自单个查询的多行,而是来自多个查询的多个结果。它通常需要执行存储过程或 SQL 代码块才能发生。


这是从执行单个SELECT 语句中正确获取多行的方法:

String qry = "SELECT KIDSNAME FROM TBLKIDS WHERE CID = ?";
try (PreparedStatement ps = connect.prepareStatement(qry)) {
    ps.setInt(1, cid);//setting CID
    try (ResultSet rs = ps.executeQuery()) {
        int i = 0;
        while (rs.next()) {
            out[i] = rs.getString("KIDSNAME");
            i++;
        }
    }
}

如果有问题的 SQL 代码返回了多个结果集,你可以这样做:

try (PreparedStatement ps = connect.prepareStatement(qry)) {
    // call ps.setXxx() methods here
    boolean isResultSet = ps.execute();
    while (isResultSet) {
        try (ResultSet rs = ps.getResultSet()) {
            int i = 0;
            while (rs.next()) {
                // call rs.getXxx() methods here
                i++;
            }
        }
        isResultSet = ps.getMoreResults();
    }
}

最好使用for 循环编写,以将循环逻辑保持在一起:

try (PreparedStatement ps = connect.prepareStatement(qry)) {
    // call ps.setXxx() methods here
    for (boolean isResultSet = ps.execute(); isResultSet; isResultSet = ps.getMoreResults()) {
        try (ResultSet rs = ps.getResultSet()) {
            for (int i = 0; rs.next(); i++) {
                // call rs.getXxx() methods here
            }
        }
    }
}

【讨论】:

  • 我没有看到 OP 的代码有任何问题,它应该可以工作,为什么我这么说,因为我在我的机器上这样做并且它对我有用。
  • @Sujitmohanty30 问题代码错误,因为它调用了executeQuery(),它返回唯一的ResultSet,但代码将其丢弃。然后它在每次循环迭代时调用getResultSet() 两次,即使文档清楚地说明只调用一次每个结果(不是每个查询的行)。 getResultSet()只有在execute()getMoreResults()返回true后才有效,并且这两种方法都没有使用。
  • 关于返回ResultSet同意。但是调用内部循环是没有问题的。见docs.oracle.com/javase/tutorial/jdbc/basics/retrieving.html。我仍然想知道它在我的机器上是如何工作的,没有任何问题,我可以看到 OP 预期的结果。 (仅当数据库明显有 3 条记录时)
  • @Sujitmohanty30 不要将“结果”一词与查询结果集中的行混淆。这不是“结果”在这里的意思。 “结果”是指调用execute() 后得到的第一个结果和调用getMoreResults() 后得到的后续结果。结果本身可通过调用getResultSet()getUpdateCount() 获得,具体取决于execute()/getMoreResults() 方法返回的内容。
  • @Sujitmohanty30 "在循环内调用没问题。看..." ---我看不到,因为那篇文章没有调用getResultSet() at全部,所以没有“内部循环调用”。 --- 它可能在您的机器上工作,因为您使用的是不同的 JDBC 驱动程序。文档没有说明如果你不止一次调用它会发生什么,所以它是 undefined 发生了什么,即不同的驱动程序可能会以不同的方式处理它。
猜你喜欢
  • 2019-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-19
  • 2020-09-01
  • 1970-01-01
相关资源
最近更新 更多