JDBC结果集

时间:2020-01-09 10:36:47  来源:igfitidea点击:

Java JDBC ResultSet接口表示数据库查询的结果。有关查询的文本显示了如何将查询结果作为java.sql.ResultSet返回。然后,对这个" ResultSet"进行迭代以检查结果。本教程说明了如何使用ResultSet接口。

结果集包含记录

JDBCResultSet包含记录。每个记录包含一组列。每个记录包含相同数量的列,尽管并非所有列都可以具有值。列可以具有" null"值。

创建一个结果集

我们可以通过执行Statement或者PreparedStatement来创建ResultSet,如下所示:

Statement statement = connection.createStatement();

ResultSet result = statement.executeQuery("select * from people");

或者像这样:

String sql = "select * from people";
PreparedStatement statement = connection.prepareStatement(sql);

ResultSet result = statement.executeQuery();

结果集类型,并发性和可保留性

创建ResultSet时,可以设置三个属性。这些是:

  • Type
  • Concurrency
  • Holdability

在创建Statement或者PreparedStatement时已经设置了这些,如下所示:

Statement statement = connection.createStatement(
    ResultSet.TYPE_FORWARD_ONLY,
    ResultSet.CONCUR_READ_ONLY,
    ResultSet.CLOSE_CURSORS_OVER_COMMIT
   );

PreparedStatement statement = connection.prepareStatement(sql,
    ResultSet.TYPE_FORWARD_ONLY,
    ResultSet.CONCUR_READ_ONLY,
    ResultSet.CLOSE_CURSORS_OVER_COMMIT
   );

这些属性的确切含义将在本文后面进行解释。但是现在我们现在其中指定它们。

迭代ResultSet

要迭代ResultSet,请使用其next()方法。如果ResultSet具有下一条记录,则next()方法返回true,然后将ResultSet移至指向下一条记录。如果没有更多记录,next()将返回false,我们将无法再执行。一旦next()方法返回false,就不应再调用它。这样做可能会导致异常。

这是一个使用next()方法迭代ResultSet的例子:

while(result.next()) {
    // ... get column values from this record
}

如我们所见,next()方法实际上是在访问第一条记录之前调用的。这意味着,ResultSet开始指向第一条记录之前。一旦调用了" next()",它将指向第一条记录。

类似地,当调用next()并返回false时,ResultSet实际上指向最后一条记录。

我们不能获取ResultSet中的行数,除非我们对其进行完全迭代并计算行数。但是,如果ResultSet是仅向前的,则不能在其后向后移动。即使我们可以向后移动,这也将是一种缓慢的计算ResultSet中的行的方法。我们最好对代码进行结构化,以便我们无需提前知道记录数。

访问列值

迭代ResultSet时,我们想访问每个记录的列值。我们可以通过调用许多" getXXX()"方法中的一个或者多个方法来实现。我们将列名传递给许多getXXX()方法以获得值。例如:

while(result.next()) {

    result.getString    ("name");
    result.getInt       ("age");
    result.getBigDecimal("coefficient");

    // etc.
}

我们可以调用很多" getXXX()"方法,这些方法会将列的值作为某种数据类型返回,例如String,int,long,double,BigDecimal等。它们都使用列的名称来获取其列值作为参数。以下是这些getXXX()方法的快速示例列表:

result.getArray("columnName");
result.getAsciiStream("columnName");
result.getBigDecimal("columnName");
result.getBinaryStream("columnName");
result.getBlob("columnName");
result.getBoolean("columnName");
result.getBlob("columnName");
result.getBoolean("columnName");
result.getByte("columnName");
result.getBytes("columnName");
result.getCharacterStream("columnName");
result.getClob("columnName");
result.getDate("columnName");
result.getDouble("columnName");
result.getFloat("columnName");
result.getInt("columnName");
result.getLong("columnName");
result.getNCharacterStream("columnName");
result.getObject("columnName");
result.getRef("columnName");
result.getRowId("columnName");
result.getShort("columnName");
result.getSQLXML("columnName");
result.getString("columnName");
result.getTime("columnName");
result.getTimestamp("columnName");
result.getUnicodeStream("columnName");
result.getURL("columnName");

getXXX()方法也有采用列索引而不是列名的版本。例如:

while(result.next()) {

    result.getString    (1);
    result.getInt       (2);
    result.getBigDecimal(3);

    // etc.
}

列的索引通常取决于SQL语句中列的索引。例如,SQL语句

select name, age, coefficient from person

有三列。列名首先列出,因此将在ResultSet中具有索引1. 列的年龄将具有索引2,列的系数将具有索引3.

有时我们可能不提前知道某个列的索引。例如,如果我们使用SQL查询类型中的select select *,我们将不知道列的顺序。

如果我们不知道某个列的索引,则可以使用ResultSet.findColumn(String columnName)方法找到该列的索引,如下所示:

int nameIndex   = result.findColumn("name");
int ageIndex    = result.findColumn("age");
int coeffIndex  = result.findColumn("coefficient");

while(result.next()) {
    String     name        = result.getString     (nameIndex);
    int        age         = result.getInt        (ageIndex);
    BigDecimal coefficient = result.getBigDecimal (coeffIndex);
}

结果集类型

一个"结果集"可以是某种类型。类型确定"结果集"的某些特征和函数。

并非所有数据库和JDBC驱动程序都支持所有类型。我们将必须检查数据库和JDBC驱动程序,以查看其是否支持要使用的类型。根据是否支持给定类型,DatabaseMetaData.supportsResultSetType(int type)方法返回true或者false。 " DatabaseMetaData"类将在以后的文章中介绍。

在撰写本文时,有三种ResultSet类型:

  • ResultSet.TYPE_FORWARD_ONLY
  • ResultSet.TYPE_SCROLL_INSENSITIVE
  • ResultSet.TYPE_SCROLL_SENSITIVE

默认类型是" TYPE_FORWARD_ONLY"

TYPE_FORWARD_ONLY表示结果集只能向前导航。也就是说,我们只能从第1行移至第2行,然后移至第3行,依此类推。我们不能在ResultSet中向后移动。

TYPE_SCROLL_INSENSITIVE意味着可以前后导航(滚动)ResultSet。我们还可以跳到相对于当前位置的位置,或者跳到绝对位置。打开" ResultSet"时," ResultSet"对基础数据源中的更改不敏感。也就是说,如果" ResultSet"中的记录被另一个线程或者进程在数据库中更改,则该记录将不会反映在已经打开的" ResulsSet"中。

TYPE_SCROLL_SENSITIVE表示可以前后导航(滚动)ResultSet。我们还可以跳到相对于当前位置的位置,或者跳到绝对位置。打开" ResultSet"时," ResultSet"对基础数据源中的更改敏感。也就是说,如果"结果集"中的记录被另一个线程或者进程在数据库中更改,则该记录将反映在已经打开的这种类型的" ResulsSet"中。

导航方法

" ResultSet"界面包含以下导航方法。请记住,并非所有方法都适用于所有ResultSet类型。哪种方法有效取决于数据库,JDBC驱动程序和ResultSet类型。

方法描述
absolute()移动ResultSet以指向绝对位置。该位置是行号,作为参数传递给absolute()方法。
afterLast()将结果集移动到结果集最后一行之后。
beforeFirst()将“ ResultSet”移动到指向“ ResultSet”中第一行之前。
first()将“ ResultSet”移动到指向“ ResultSet”中的第一行。
last()将“ ResultSet”移动到指向“ ResultSet”中的最后一行。
next()将“ ResultSet”移动到指向“ ResultSet”中的下一行。
previous()将“ ResultSet”移动到指向“ ResultSet”中的前一行。
relative()移动“ ResultSet”以指向相对于其当前位置的位置。
相对位置作为参数传递给相对方法,可以是正数也可以是负数。

" ResultSet"界面还包含一组方法,可用于查询" ResultSet"的当前位置。这些是:

方法描述
getRow()返回当前行的行号-“结果集”当前指向的行。
getType()返回ResultSet类型。
isAfterLast()如果ResultSet指向最后一行之后,则返回true。否则为假。
isBeforeFirst()如果ResultSet指向第一行,则返回true。否则为假。
isFirst()如果ResultSet指向第一行,则返回true。否则为假。

最后,ResultSet接口还包含一种方法,如果ResultSet对更改敏感,则使用数据库更改来更新行。

方法描述
refreshRow()使用数据库中的最新值刷新该行的列值。

ResultSet并发

" ResultSet"并发性决定" ResultSet"是可以更新还是只能读取。

一些数据库和JDBC驱动程序支持更新" ResultSet",但并非所有数据库和JDBC驱动程序都可以更新。根据是否支持给定的并发模式,DatabaseMetaData.supportsResultSetConcurrency(int concurrency)方法返回true或者false。 " DatabaseMetaData"类将在以后的文章中介绍。

ResultSet可以具有两个并发级别之一:

  • ResultSet.CONCUR_READ_ONLY
  • ResultSet.CONCUR_UPDATABLE

CONCUR_READ_ONLY表示只能读取ResultSet

CONCUR_UPDATABLE意味着ResultSet可以被读取和更新。

更新结果集

如果"结果集"是可更新的,则可以更新"结果集"中每一行的列。我们可以使用许多updateXXX()方法来实现。例如:

result.updateString     ("name"       , "Alex");
    result.updateInt        ("age"        , 55);
    result.updateBigDecimal ("coefficient", new BigDecimal("0.1323");
    result.updateRow();

我们也可以使用列索引而不是列名来更新列。这是一个例子:

result.updateString     (1, "Alex");
    result.updateInt        (2, 55);
    result.updateBigDecimal (3, new BigDecimal("0.1323");
    result.updateRow();

注意" updateRow()"调用。只有在调用" updateRow()"时,数据库才会用该行的值进行更新。如果我们不调用此方法,则" ResultSet"中更新的值永远不会发送到数据库。如果我们在事务内调用updateRow(),则直到事务提交后,数据才真正提交给数据库。

在结果集中插入行

如果ResultSet是可更新的,则也可以其中插入行。我们可以这样做:

  • 调用ResultSet.moveToInsertRow()
  • 更新行列值
  • 调用ResultSet.insertRow()

这是一个例子:

result.moveToInsertRow();
result.updateString     (1, "Alex");
result.updateInt        (2, 55);
result.updateBigDecimal (3, new BigDecimal("0.1323");
result.insertRow();

result.beforeFirst();

在调用moveToInsertRow()之后指向的行是一个特殊的行,即缓冲区,我们可以使用它来建立行,直到在该行上设置了所有列值为止。

一旦准备好将行插入到" ResultSet"中,请调用" insertRow()"方法。

插入行后,ResultSet仍指向插入行。但是,我们无法确定一旦插入行,如果尝试访问它将会发生什么。因此,在插入新行后,应将" ResultSet"移动到有效位置。如果我们需要插入另一行,则显式调用moveToInsertRow(),以将其告知" ResultSet"。

结果集可保存性

"结果集"的可保持性确定当调用基础"连接"的" commit()"方法时是否关闭"结果集"。

并非所有数据库和JDBC驱动程序都支持所有可保留性模式。根据是否支持给定的可保存性模式," DatabaseMetaData.supportsResultSetHoldability(int holdability)"将返回true或者false。 " DatabaseMetaData"类将在以后的文章中介绍。

有两种类型的可保存性:

  • ResultSet.CLOSE_CURSORS_OVER_COMMIT
  • ResultSet.HOLD_CURSORS_OVER_COMMIT

" CLOSE_CURSORS_OVER_COMMIT"的可保持性意味着,在创建" ResultSet"的连接上调用" connection.commit()"方法时,所有" ResultSet"实例都将关闭。

" HOLD_CURSORS_OVER_COMMIT"的可保持性意味着,在创建" ResultSet"的连接上调用" connection.commit()"方法时," ResultSet"保持打开状态。

如果使用" ResultSet"更新数据库中的值,则" HOLD_CURSORS_OVER_COMMIT"的可保持性可能会很有用。因此,我们可以打开一个" ResultSet",更新其中的行,调用" connection.commit()",并且仍然保持相同的" ResultSet"处于打开状态,以便将来在同一行上进行交易。