JDBC结果集
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"处于打开状态,以便将来在同一行上进行交易。