java数据库编程——可滚动和可更新的结果集

时间:2022-07-11 12:05:20

【0】README

1) 本文部分文字描述转自 core java volume 2 , 测试源代码均为原创, 旨在理解 java数据库编程——可滚动和可更新的结果集 的基础知识 ;
2)for database connection config, please visit : https://github.com/pacosonTang/core-java-volume/blob/master/coreJavaAdvanced/chapter4/database.properties
3)用户通常希望在结果集上前后滚动。在可滚动结果集中, 可以在其中向前或向后移动, 甚至可以跳过任意位置;


【1】可滚动结果集

1)默认情况,结果集是不可滚动的;

  • 1.1)为了从查询中获得可滚动的结果集, 必须使用下面的方法得到一个不同的Statement 对象:
    Statement stat = conn.createStatement(type, concurrency); (干货——获得滚动的结果集)
  • 1.2)如果要获得预备语句, 请调用下面的方法:
    PreparedStatement stat = conn.preparedStatement(command, type, concurrency);

Attention)下表列出了 type 和 concurrency 的所有可能值:

  • A1)是否希望结果集是滚动的?
  • A2)如果结果集是滚动的, 且数据库在查询生成结果集后发生了变化, 那么是否希望结果集反应出这些变化?
  • A3)是否希望通过编辑结果集就可以更新数据库?
    (干货——以上3个Attention都是干货)

2)看个荔枝:

  • 2.1)如果只想滚动遍历结果集, 而不想编辑它 的数据,那么可以使用以下语句:

    Statement stat = conn.createStatement(ResultSet.Type_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

  • 2.2)现在,通过调用以下方法获得 的所有结果集都将是可滚动的:

    ResultSet rs = stat.executeQuery(query);

  • 2.3) 可滚动的 结果集有一个游标, 用以指示当期位置;

Attention)

  • A1)并不是所有的数据库驱动程序都支持可滚动和可更新的结果集;
  • A2)使用 DatabaseMetadata 接口中的 supportsResultSetType 和 supportsResultSetConcurrency 方法, 我们可以获知在使用特定的驱动程序时, 某个数据库究竟支持哪些结果集类型以及哪些并发模式; (干货——查看某个数据库究竟支持哪些结果集类型以及哪些并发模式)
  • A3)也可以使用 ResultSet 接口中的 getType 和 getConcurrency 方法 查看结果集实际支持的模式;

3)滚动操作: (干货——滚动集的滚动操作)

  • 3.1)向后滚动: rs.previous(); 如果游标位于一个实际的行上,那么该方法将返回true; 如果游标位于第一行之前,那么就返回false;
  • 3.2)将游标向后或向前移动多行: rs.relative(n); n为正数, 向前移动;或负数, 向后移动; n为0, 不移动;
  • 3.3)将游标设置到指定的行号上: rs.absolute(n);
  • 3.4) 调用以下方法将返回当前行的行号: int curRow = rs.getRow();
  • 3.5)结果的第一行是1, 而不是0;如果返回0, 那么当前游标不在任何行上, 它要么位于第一行前,或最后一行之后; (干货——可滚动结果集的第一行的index是1, 而不是0)
  • 3.6)其他操作: first, last, beforeFirst, afterLast 方法 , 与 isFirst , isLast, isBeforeFirst, isAfterLast 方法;

Attention) rs.isAfterLast() 表示当前行游标是在最后一行之后吗。 而 rs.isLast() 表示这是最后一行吗。他们是有差别的。

4)看个荔枝:(只打印奇数行的name)

public static void main(String[] args)
{
try
{
try(Connection conn = getConnection())
{
String sql = "select name from student";
// TYPE_SCROLL_INSENSITIVE == 结果集可以滚动但对数据库变化不敏感;
// CONCUR_READ_ONLY == 且结果集不能用于更新数据库(default);
Statement stat = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stat.executeQuery(sql);

int rowno;
while(true)
{
rowno = rs.getRow();
if(rowno < 1)
{
rs.absolute(1); // 将游标设置到指定的行号上
}
System.out.println("rowno = " + rs.getRow());
// attention for rs.getString not changing row cursor
System.out.println("row[" + rs.getRow() + "] = " + rs.getString(1));
if(!rs.isLast())
{
// 将游标向后或向前移动多行: rs.relative(n); n为正数, 向前移动;或负数, 向后移动; n为0, 不移动;
rs.relative(2);
}
if(rs.isLast())
{
break;
}
}

stat.close();
conn.close();
}
}catch(Exception e)
{
e.printStackTrace();
}
  • 4.3)relative printing results as follows:
    java数据库编程——可滚动和可更新的结果集

【2】可更新的结果集

1)如果希望编辑结果集中的数据,并且将结果集上的数据变更自动反应到数据库中, 那么就必须使用可更新的结果集;可更新的结果集并非必须是可滚动的, 但如果将 数据提供给用户去编辑, 那么通常也会希望结果集时可滚动的;
(干货——可更新的结果集并非必须是可滚动的, 但如果将 数据提供给用户去编辑, 那么通常也会希望结果集时可滚动的;)
(干货——为什么引入可滚动的结果集? 是为了将结果集上的数据变更自动反应到数据库中)
2)如果要获得可更新的结果集,应该使用以下方法创建一条语句:

Statement stat = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATEABLE);
这样, 调用 executeQuery 方法返回的结果集就将是可更新的 结果集了;

Attention)

  • A1)并非所有的查询都会返回可更新的结果集;
  • A2)如果查询涉及多个表的连接操作, 那么它所产生的结果集将是不可更新的;
  • A3)可以调用 ResultSet 接口中的 getConcurrency 方法来确定结果集是否是可更新的;

3)看个荔枝: 迭代遍历所有图书并更新它们的价格:

String query = "select * from book";
ResultSet rs = stat.executeQuery(query);
while(rs.next)
{
if(...)
{
double price = rs.getDouble("price");
rs.updateDouble("price", price + 1000);
rs.updateRow();
// 干货——以上两行是更新数据库的操作
}
}

Attention)

  • A1)在使用第一个参数为列序号的updateXXX 方法时, 请注意这里的列序号指的是该列在结果集中的序号, 而不是数据库的中的列序号;
  • A2)udpateXXX 方法:改变的只是结果集中的行值, 而非数据库 中的值;当更新完字段后,必须调用 updateRow 方法, 这个方法将当前行中的更新信息发送给数据库; (干货——当更新完字段后,必须调用updateRow 方法)
  • A3)cancelRowUpdates方法:取消对当前行的更新;

4)如果想在数据库中添加一行新纪录,按如下步骤进行: (干货——在可更新结果集中添加一行新记录到数据库)

  • step1) 使用 moveToInsertRow 方法: 将游标移动到特定的位置, 我们称之为插入行;
  • step2)调用 udpateXXX 方法 在插入行的位置上创建一个新的行;
  • step3) 调用insertRow 方法:将新建的行发送给 数据库;
  • step4)完成插入后,调用 moveToCurrentRow 方法: 将游标移动会调用  moveToCurrentRow  方法之前的位置;

5)看个实例程序:

rs.moveToInsertRow();
rs.updateString("title", title);
rs.updateString("title1", title1);
rs.updateString("title2", title2);
...
rs.updateDouble("price", price);
rs.insertRow();
rs.moveToCurrentRow();

Attention)

  • A1)你无法控制在结果集或数据库中添加新数据的位置;
  • A2) 对于在插入行中没有指定值的列,将被设置为 null, 而如果这个列有 not null 约束的话, 那么将会抛出异常,而这一行无法插入;
  • A3)你可以使用以下方法删除游标所指的行:
    • A3.1) rs.deleteRow();
    • A3.2)deleteRow 方法: 会立即将该行从结果集和数据库中删除;
    • A3.3)ResultSet 接口中的方法: updateRow, insertRow, deleteRow 方法的执行效果等同于 sql 命令中的update, insert 和 delete 方法;



6)看个荔枝:(将奇数行的name追加_odd, 偶数行的name追加_even)

public static void main(String[] args)
{
try
{
try(Connection conn = getConnection())
{
String sql = "select id, name from student";
// TYPE_SCROLL_SENSITIVE == 结果集可以滚动且对数据库变化不敏感;
// CONCUR_UPDATABLE == 且结果集能够应用于更新数据库;
Statement stat = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
// get 可更新的 结果集
ResultSet rs = stat.executeQuery(sql);

int rowno;
while(true)
{
rowno = rs.getRow();
if(rowno < 1)
{
rs.absolute(1); // 将游标设置到指定的行号上
}

if(rs.getRow() % 2 != 0)
{
rs.updateString("name", rs.getString("name") + "_odd");
}
else
{
rs.updateString("name", rs.getString("name") + "_even");
}
rs.updateRow();
// attention for rs.getString not changing row cursor
System.out.println("row[" + rs.getRow() + "] = " + rs.getString(2));

// 将游标向后或向前移动多行: rs.relative(n); n为正数, 向前移动;或负数, 向后移动; n为0, 不移动;
rs.relative(1);
if(rs.isAfterLast())
{
break;
}
}

stat.close();
conn.close();
}
}catch(Exception e)
{
e.printStackTrace();
}
  • 6.3)relative printing results as follows:
    java数据库编程——可滚动和可更新的结果集