bigywater.tech
Java · 319

RowSet:可断开连接的数据集

RowSet 是 JDBC 里「可以断开连接的结果集」。离线修改后怎么写回?别人同时改了同一行会怎样?这篇用真实的 PostgreSQL 跑 CachedRowSet、WebRowSet、FilteredRowSet、JoinRowSet 和分页。

1 conflicts
离线期间别人改了同一行时 acceptChanges 抛出的 SyncProviderException 信息
3934 字符
WebRowSet.writeXml 对 3 行结果输出的 XML 大小
autoCommit
acceptChanges 在 autocommit=true 连接上会先写入再抛异常

CachedRowSet

实测 · 本机执行输出
1 RowSetProvider.newFactory() 的实现:com.sun.rowset.RowSetFactoryImpl
  execute(conn) 之后行数 3;内容 [1:apple:10, 2:pear:20, 3:plum:30]
2 原连接已 close(isClosed=true),CachedRowSet 仍可浏览与修改:
  离线修改后:[2:pear:99, 4:cherry:40, 3:plum:30](此时数据库还没有变化)
  数据库里现在是:1:10,2:20,3:30
3 autocommit=false 时 acceptChanges(conn) 并 commit 之后数据库里:2:99,3:30,4:40
  acceptChanges 时服务器收到的写语句(pg.log): ['SELECT id, name, qty FROM item WHERE id = $1 ', 'DELETE FROM item WHERE id = $1 ', 'SELECT id, name, qty FROM item WHERE id = $1 ', 'UPDATE item SET qty = $1 WHERE id = $2 ', 'SELECT id, name, qty FROM item ', "SELECT        result.TABLE_CAT,        result.TABLE_SCHEM,        result.TABLE_NAME,        result.COLUMN_NAME,        result.KEY_SEQ,        result.PK_NAME FROM      (SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM,   ct.relname AS TABLE_NAME, a.attname AS COLUMN_NAME,   (information_schema._pg_expandarray(i.indkey)).n AS KEY_SEQ, ci.relname AS PK_NAME,   information_schema._pg_expandarray(i.indkey) AS KEYS, a.attnum AS A_ATTNUM FROM pg_catalog.pg_class ct   JOIN pg_catalog.pg_attribute a ON (ct.oid = a.attrelid)   JOIN pg_catalog.pg_namespace n ON (ct.relnamespace = n.oid)   JOIN pg_catalog.pg_index i ON ( a.attrelid = i.indrelid)   JOIN pg_catalog.pg_class ci ON (ci.oid = i.indexrelid) WHERE true  AND ct.relname = 'item' AND i.indisprimary  ) result where  result.A_ATTNUM = (result.KEYS).x  ORDER BY result.table_name, result.pk_name, result.key_seq", 'INSERT INTO item (id, name, qty) VALUES ($1, $2, $3)']

autocommit 的陷阱

实测 · 本机执行输出
3b autocommit=true 的连接上 acceptChanges 抛 SyncProviderException(null): Cannot commit when autoCommit is enabled.
  抛异常之后数据库里实际是:2:99,3:30(修改与删除已经生效并自动提交了)
  再调一次 acceptChanges:SyncProviderException(null): 2 conflicts while synchronizing
org.postgresql.util.PSQLException: Cannot commit when autoCommit is enabled.
	at org.postgresql.jdbc.PgConnection.commit(PgConnection.java:998)
	at java.sql.rowset/com.sun.rowset.internal.CachedRowSetWriter.commit(CachedRowSetWriter.java:1410)
	at java.sql.rowset/com.sun.rowset.CachedRowSetImpl.acceptChanges(CachedRowSetImpl.java:911)
	at java.sql.rowset/com.sun.rowset.CachedRowSetImpl.acceptChanges(CachedRowSetImpl.java:960)
	at Main.main(Main.java:32)

乐观冲突检测

实测 · 本机执行输出
4 acceptChanges 抛 SyncProviderException:SyncProviderException(null): 1 conflicts while synchronizing
  冲突类型 0(UPDATE_ROW_CONFLICT=0);我的值 qty=99,数据库当前值 qty=555
  冲突行数 1
  数据库里 id=2 的 qty:555

WebRowSet 与序列化

实测 · 本机执行输出
5 WebRowSet.writeXml:3934 字符,109 行;前 12 行:
    <?xml version="1.0"?>
    <webRowSet xmlns="http://java.sun.com/xml/ns/jdbc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/jdbc http://java.sun.com/xml/ns/jdbc/webrowset.xsd">
      <properties>
        <command>select id, name, qty from item order by id</command>
        <concurrency>1008</concurrency>
        <datasource><null/></datasource>
        <escape-processing>true</escape-processing>
        <fetch-direction>1000</fetch-direction>
        <fetch-size>0</fetch-size>
        <isolation-level>2</isolation-level>
        <key-columns>
  readXml 读回:行数 3,内容 [1:apple:10, 2:pear:20, 3:plum:30]
6 Java 序列化 CachedRowSet:5440 字节;反序列化后行数 3,内容 [1:apple:10, 2:pear:20, 3:plum:30]

其它 RowSet

实测 · 本机执行输出
7 FilteredRowSet(qty >= 20):[2:pear:20, 3:plum:30]
  去掉过滤器:[1:apple:10, 2:pear:20, 3:plum:30]
8 JoinRowSet(两个离线结果集按 id 内连接):[2:pear:bob, 1:apple:ann]
9 分页(setPageSize(5),表共 18 行):每页行数 [5, 5, 5, 3],每页第一行 id [1, 12, 17, 22]
10 JdbcRowSet(保持连接,当作 JavaBean 用 setUrl/setCommand/execute):行数 3,最后一行 plum

分步演示

RowSet
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12(java.sql.rowset 模块,com.sun.rowset 实现);自己起的 PostgreSQL 16.15(端口 9501),用户自己的数据库没有被使用;pgjdbc 42.7.4
    • 没有验证:其它数据库、大结果集下 CachedRowSet 的内存、SyncProvider 的自定义与 SyncResolver 的冲突解决(没有调用 setResolvedValue)、WebRowSet 的 diffgram(没有写出修改后的 XML)、RowSet 在 Jakarta / 现代框架中的实际使用、并发修改下的线程安全
    京ICP备2026067502号-1京公网安备11010502063487号