【问题标题】:Preferred way to query a database multiple times?多次查询数据库的首选方式?
【发布时间】:2012-10-09 10:24:38
【问题描述】:

在 Java 中使用 JDBC 时,普遍接受的查询数据库的方法是获取一个连接,从该连接创建一个语句,然后从该语句执行一个查询。

// load driver
Connection con = DriverManager.getConnection(..);
Statement stmt = con.createStatement();
ResultSet result = stmt.executeQuery("SELECT..");
// ...

但是,我不确定如何处理对同一数据库的第二次查询。

  1. 能否在同一个Statement 对象上安全地执行另一个查询,或者必须从Connection 对象创建另一个语句才能执行另一个查询?

  2. 如果同一个Statement 对象可用于多个查询,Statement 类的用途是什么(因为存在Connection.executeQuery() 方法会更有意义)?

【问题讨论】:

  • 对于第一部分,您自己尝试过吗?您是否得到任何异常或任何不需要的输出?
  • 网络上有大量示例。您可以在那里观看,自己尝试一下,如果您遇到异常或问题,请改进您的问题,以便我们帮助您更准确。
  • @RohitJain 我没有,但我几乎可以肯定,可以从同一个 Statement 选项执行多个查询,这让我想知道这样做是否有任何缺点或安全问题(问题1),因此,Statement 类的重点是什么(问题 2)。

标签: java mysql database jdbc


【解决方案1】:

是的,您可以重复使用 Statement 对象,但 executeQuery 返回的 ResultSet 对象会关闭已打开的结果集。

解释见javadoc

默认情况下,每个 Statement 对象只能打开一个 ResultSet 对象 同时。因此,如果读取一个 ResultSet 对象是 与另一个阅读交错,每个必须已经生成 通过不同的 Statement 对象。 Statement中的所有执行方法 接口隐式关闭语句的当前 ResultSet 对象,如果 打开一个存在。

于是发生了以下情况:

// load driver
Connection con = DriverManager.getConnection(..);
Statement stmt = con.createStatement();
ResultSet result = stmt.executeQuery("select ..");
// do something with result ... or not
ResultSet result2 = stmt.executeQuery("select ...");
// result is now closed, you cannot read from it anymore
// do something with result2
stmt.close(); // will close the resultset bound to it

例如,您可以在 jTDS 项目中找到 Statement 的开源实现。 在Statement.executeQuery() method 中,您可以看到对initialize() 的调用closes all the resultsets 已经打开

protected void initialize() throws SQLException {
    updateCount = -1;
    resultQueue.clear();
    genKeyResultSet = null;
    tds.clearResponseQueue();
    // FIXME Should old exceptions found now be thrown instead of lost?
    messages.exceptions = null;
    messages.clearWarnings();
    closeAllResultSets();
}

【讨论】:

  • 如果所有执行方法都隐式关闭一个语句的当前ResultSet(如果打开),是否需要在执行另一个查询之前显式关闭一个ResultSet?
  • @Vulcan 你是对的,正如你注意到的,正如 javadoc 中所说,Statement 上的execute*() 方法似乎关闭了任何从它派生的打开的ResultSet,所以实际上,在发出新的执行之前调用close() 并不是强制性的,但最好关闭已打开的资源。
  • 以我对 jTDS 开源实现的编辑为例。
  • 谢谢。将此答案标记为已接受,因为我觉得它最准确地回答了原始问题,尽管我也很欣赏其他人的答案,因为每个人都教会了我一些新东西。
【解决方案2】:

这就是为什么我们在面向对象编程中有类的概念。一个类定义了使它的实例具有状态和行为的组成成员。这里的语句处理与 sql 语句相关的所有内容。还有更多功能可以执行,例如批量查询等。

【讨论】:

    【解决方案3】:

    以编程方式,您可以对多个查询重复使用相同的连接和相同的语句,并在最后关闭语句和连接。

    但是,这不是一个好习惯。应用程序性能对数据库的访问方式非常敏感。理想情况下,每个连接都应该打开尽可能少的时间。然后,必须汇集连接。这样一来,您会将每个查询包含在{open connection, create a prepared statement, run query, close statement, close connection} 的块中。这也是大多数 SQL 模板的实现方式。如果并发允许,您可以使用线程池同时触发多个此类查询。

    【讨论】:

    • 听起来很合理,但在尽可能短的时间内保持连接打开的原因是什么?是保持一个连接打开以每分钟处理 10 个查询更好,还是平均每 6 秒打开一个新连接?
    • @Vulcan 它不一样。您打开连接的时间最少,然后将其返回到池中。池将为您保持连接打开。因为每次您确定它总是返回到池中时,您都会“关闭”连接。如果您自己处理,那么您错过关闭并遇到数据库会话或内存泄漏问题的风险就会增加。
    【解决方案4】:

    通常,一个查询一个语句。可能没有必要这样做,但是在编写真正的应用程序时,您不想一次又一次地重复这些相同的步骤。这违反了 DRY 原则,而且随着应用程序的增长,它也会变得更加复杂。

    最好编写对象来处理这种低级(重复)的东西,并通过提供查询来提供不同的方法来访问数据库。

    【讨论】:

      【解决方案5】:

      我不会担心创建新语句。但是,打开数据库连接可能会占用大量资源,并且打开和关闭连接确实会影响性能。

      以某种自我管理的方式断开连接通常是非常糟糕的。

      您应该考虑使用connection pooling。您通常会发出关闭突击队,但您只是将该连接返回给池。当您请求新连接时,它将重新使用您之前返回的连接。

      您可能希望对一个连接使用不同的语句。语句是一个实现和一个接口。根据您的需要,您有时需要使用CallableStatment。某些逻辑在需要时可能会被重用。

      【讨论】:

      • 谢谢,我以前从未遇到过连接池,这似乎是一个非常有用的概念,我将在我的服务器应用程序中使用。
      • @Vulcan 如果服务器应用程序是 Web 应用程序,那么应用程序服务器可以进行连接池。配置应在您的应用程序服务器中描述。否则,您可以使用 commons.apache.org/dbcp 或您选择的其他工具集。
      • 抱歉,我不相信我对那个声明很清楚;它只是一个托管服务器套接字的 Java 应用程序,供客户端套接字连接。
      【解决方案6】:

      如果您在线程环境中使用 Connection 和 Statement,我还有一件事要补充。 我的经验表明 stmt.executeQuery(..) 可以保存在并行环境中使用,但结果是每个查询都被序列化并因此按顺序处理,不会产生任何加速。 所以最好为每个线程使用一个新的连接(而不是语句)。

      对于标准的顺序环境,我的经验表明重用语句完全没有问题,结果集不需要手动关闭。

      【讨论】:

      • 我发现这非常有帮助,因为我在线程环境中使用 Connection 和 Statement。结合 Udo Held 的回答建议,我觉得我已经了解了多线程数据库连接所需的知识。
      猜你喜欢
      • 2012-12-21
      • 1970-01-01
      • 1970-01-01
      • 2018-07-17
      • 1970-01-01
      • 2012-03-08
      • 2021-07-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多