Redrock Postgres 文档
主页 切换暗/亮/自动模式 切换暗/亮/自动模式 切换暗/亮/自动模式 返回首页

发出查询和处理结果

每当要向数据库发出 SQL 语句时,都需要StatementPreparedStatement实例。有了StatementPreparedStatement后,可以使用它发出查询。这将返回一个ResultSet实例,其中包含整个结果(有关如何更改此行为,请参阅此处名为“基于游标获取结果”的部分)。例 5.1 “在 JDBC 中处理简单查询” 说明了此过程。

例 5.1.在 JDBC 中处理简单查询

此示例会使用Statement发出一个简单的查询,并输出每行记录第一列的值。

Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM mytable WHERE columnfoo = 500");
while (rs.next()) {
    System.out.print("Column 1 returned ");
    System.out.println(rs.getString(1));
}
rs.close();
st.close();

此示例发出与之前相同的查询,但在查询中使用PreparedStatement和一个绑定值。

int foovalue = 500;
PreparedStatement st = conn.prepareStatement("SELECT * FROM mytable WHERE columnfoo = ?");
st.setInt(1, foovalue);
ResultSet rs = st.executeQuery();
while (rs.next()) {
    System.out.print("Column 1 returned ");
    System.out.println(rs.getString(1));
}
rs.close();
st.close();

基于游标获取结果

默认情况下,驱动程序会立即收集查询的所有结果。这对于大型数据集来说可能很不方便,因此 JDBC 驱动程序提供了一种基于数据库游标的ResultSet,这种方法一次只读取少量行。

少量行缓存在连接的客户端,当用尽时,下一个行块是通过重新定位游标进行检索的。

基于游标的ResultSets不能在所有情况下使用。有许多限制将使驱动程序默默地退回到一次取回整个ResultSet的结果。

  • 与服务器的连接必须使用 V3 协议。这是服务器版本 7.4 及更高版本默认且仅支持的协议版本。

  • Connection不能处于自动提交模式。后端在事务结束时会关闭游标,因此在自动提交模式下,在从游标获取任何内容之前,后端已经将游标关闭。

  • Statement必须使用ResultSet.TYPE_FORWARD_ONLY类型的ResultSet进行创建。这是默认行为,因此,无需重写代码即可利用这一点,但这也意味着您无法向后滚动,否则会在ResultSet结果集里面跳来跳出。

  • 给定的查询必须是单个语句,而不是用分号串在一起的多个语句。

例 5.2.设置抓取大小以打开和关闭游标

将代码更改为使用游标模式就像将Statement的提取大小设置为适当的大小一样简单。将提取大小设置回 0 将导致缓存所有行(默认行为)。

// make sure autocommit is off
conn.setAutoCommit(false);
Statement st = conn.createStatement();

// Turn use of the cursor on.
st.setFetchSize(50);
ResultSet rs = st.executeQuery("SELECT * FROM mytable");
while (rs.next()) {
    System.out.print("a row was returned.");
}
rs.close();

// Turn the cursor off.
st.setFetchSize(0);
rs = st.executeQuery("SELECT * FROM mytable");
while (rs.next()) {
    System.out.print("many rows were returned.");
}
rs.close();

// Close the statement.
st.close();

使用 Statement 或 PreparedStatement 接口

使用StatementPreparedStatement接口时必须考虑以下几点:

  • 您可以根据需要多次使用单个Statement实例。您可以在打开连接后创建它,并在连接的生存期内使用它。但你必须记住,在任何时间,每个StatementPreparedStatement实例只有一个ResultSet可以存在。

  • 如果需要在处理ResultSet时执行查询,您可以简单地创建和使用另一个Statement

  • 如果使用线程,并且多个线程正在使用数据库,则必须为每个线程使用单独的Statement。 如果您正在考虑使用线程,请参阅在多线程或 Servlet 环境中使用驱动程序,因为它涵盖了一些要点。

  • 当您使用完StatementPreparedStatement后,您应该关闭它。

  • 在 JDBC 中,问号 (?) 是PreparedStatement的位置参数的占位符。 但是,有许多PostgreSQL运算符包含问号。为了防止 SQL 语句中的此类问号被解释为位置参数,可以使用两个问号 ( ?? ) 作为转义序列。 您也可以在Statement中使用此转义序列,但这不是必需的。具体来说,只有在Statement中单个问号 ( ? ) 可以用作运算符。

使用 ResultSet 接口

使用ResultSet接口时必须考虑以下几点:

  • 在读取任何值之前,必须调用next()。如果有结果,则返回 true,但更重要的是,它会准备将要处理的行。

  • 使用完ResultSet后,必须通过调用close()来关闭它。

  • 一旦您使用Statement进行另一个查询并创建ResultSet,当前打开的ResultSet实例会被自动关闭。

  • 使用 PreparedStatement API 时,在执行五次查询后会切换到二进制模式(此默认值由连接属性prepareThreshold设置,请参阅服务端预备语句)。 这可能会导致调用某些方法时出现意外行为。例如,在非字符串数据类型的值上调用getString()方法,即使在逻辑上等效,但在执行超过prepareThreshold设置的次数后,当转换成对象的方法切换到返回类型与返回模式匹配的方法时,格式可能会有所不同。

执行更新

若要更改数据(执行INSERTUPDATEDELETE),请使用executeUpdate()方法。此方法与executeQuery()方法类似,executeQuery()方法用于发出SELECT语句,但它不返回ResultSet,而是返回受INSERTUPDATEDELETE语句影响的行数。

例 5.3 “在 JDBC 中删除行” 说明了用法。

例 5.3.在 JDBC 中删除行

此示例将发出一个简单的DELETE语句并打印出已删除的行数。

int foovalue = 500;
PreparedStatement st = conn.prepareStatement("DELETE FROM mytable WHERE columnfoo = ?");
st.setInt(1, foovalue);
int rowsDeleted = st.executeUpdate();
System.out.println(rowsDeleted + " rows deleted");
st.close();

创建和修改数据库对象

若要创建、修改或删除数据库对象(如表或视图),请使用execute()方法。此方法类似executeQuery()方法,但它不返回结果。

例 5.4 “在 JDBC 中删除表“ 说明了用法。

例 5.4.在 JDBC 中删除表

此示例将删除一个表。

Statement st = conn.createStatement();
st.execute("DROP TABLE mytable");
st.close();

使用 Java 8 的日期和时间类

PostgreSQL JDBC 驱动程序使用 JDBC 4.2 实现了对 Java 8 日期和时间 API(JSR-310)的本机支持。

表 5.1.支持的 Java 8 日期和时间类

PostgreSQL Java SE 8
DATE LocalDate
TIME [ WITHOUT TIME ZONE ] LocalTime
TIMESTAMP [ WITHOUT TIME ZONE ] LocalDateTime
TIMESTAMP WITH TIME ZONE OffsetDateTime

这与 JDBC 4.2 规范中表 B-4 和 B-2 的描述非常一致。

ZonedDateTime , InstantOffsetTime / TIME WITH TIME ZONE不受支持。另请注意,所有OffsetDateTime实例都将采用 UTC(偏移量为 0)。这是因为后端将它们存储为 UTC。

例 5.2.使用 JDBC 读取 Java 8 日期和时间值

Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM mytable WHERE columnfoo = 500");
while (rs.next()) {
    System.out.print("Column 1 returned ");
    LocalDate localDate = rs.getObject(1, LocalDate.class);
    System.out.println(localDate);
}
rs.close();
st.close();

对于其他数据类型,只需将其他类传递给#getObject

Java 数据类型需要与表 7.1 中的 SQL 数据类型匹配。

例 5.3.使用 JDBC 写入 Java 8 日期和时间值

LocalDate localDate = LocalDate.now();
PreparedStatement st = conn.prepareStatement("INSERT INTO mytable (columnfoo) VALUES (?)");
st.setObject(1, localDate);
st.executeUpdate();
st.close();