18-RowSet三缓冲 📅 发布时间:2026/8/29 9:21:45 👁 浏览次数: 18-RowSet三缓冲primary、filter、deleteRow管一行RowSet管一组行的生命周期。三个ArrayList——primary存有效行、filter存筛选项、delete存待同步的删除行。remove对INSERT行的特殊处理、collect的脏数据收集、reset的整体重建这篇把222行拆完。文章目录18-RowSet三缓冲primary、filter、delete一、三个缓冲的职责二、add两行代码创建INSERT行三、removeINSERT行的分岔四、collect脏数据收集五、accept与reject提交后的双向清算六、reset查询结果的整体灌入七、getrow这个命名源码browise-data/src/main/java/com/browise/data/RowSet.java222行一、三个缓冲的职责publicclassRowSet{privatefinalListRowprimarynewArrayList();// 当前有效行privatefinalListRowfilternewArrayList();// 筛选结果privatefinalListRowdeletenewArrayList();// 待同步的已删行}缓冲行状态谁写谁读primaryNONE/INSERT/UPDATEadd/remove/查询灌入渲染/编辑filter任意本地筛选客户端过滤渲染筛选视图deleteDELETEremove移入collect提交filter是三缓冲里最轻的——它只是primary某个时刻的视图快照不参与提交协议。真正的提交协议由 primarydelete 两个缓冲构成。二、add两行代码创建INSERT行publicRowadd(MapString,Objectdata){RowrownewRow(data);row.setStatus(RowStatus.INSERT);// 新行天生INSERTprimary.add(row);returnrow;}注意 new Row(data) 构造时状态是NONE第17篇add里立刻改成INSERT——“这一行是用户刚创建的库里没有”。返回row引用让调用方继续填字段RownewRowrowSet.add();newRow.setItemValue(name,张三);// INSERT状态下不记original第17篇三、removeINSERT行的分岔publicvoidremove(Rowrow){intidxprimary.indexOf(row);if(idx0){primary.remove(idx);if(row.getStatus()!RowStatus.INSERT){row.setStatus(RowStatus.DELETE);delete.add(row);// 非INSERT行进delete缓冲}// INSERT行从primary删掉就完了——库里没它无需同步}}这是三缓冲设计最关键的一行判断被删行的状态行为原因INSERT从primary移除不进delete新增未入库删了从未存在提交时无需任何SQLNONE/UPDATE移除标DELETE进delete缓冲库里有这行提交时要发DELETE如果不分岔、INSERT行也进delete——提交时对库里不存在的行发DELETEOracle报0行受影响不报错但日志脏更糟的是和数据库触发器/审计联动时会误判。removeAt(int)是同一逻辑的索引版——两份代码没有互相复用一个indexOf先因为实现都只有8行抽公共方法的成本大于重复。四、collect脏数据收集publicListRowcollect(){ListRowdirtynewArrayList();for(Rowrow:primary){if(row.isDirty())dirty.add(row);// INSERT/UPDATE行}dirty.addAll(delete);// DELETE行returndirty;}三行核心逻辑——primary里的脏行 delete缓冲的全部行。这个列表就是提交报文里rows数组的来源第23篇commonSave按_t分发。注意collect不改变任何状态——纯只读操作。收集完行还是脏的直到accept或reject才清。前端useRowSet的collect(‘auto’|‘all’|‘dirty’)多一个模式参数——后端只有dirty语义提交协议只需要脏数据。all’是前端导出场景把整个primary交出去。五、accept与reject提交后的双向清算第19篇专门讲这里先给对照publicvoidaccept(){// 存活行全部归零for(Rowrow:primary){row.setStatus(RowStatus.NONE);row.getOriginals().clear();}delete.clear();filter.clear();}publicvoidreject(){// ①倒序移除INSERT行for(intiprimary.size()-1;i0;i--){if(primary.get(i).getStatus()RowStatus.INSERT)primary.remove(i);}// ②UPDATE行从originals整体恢复for(Rowrow:primary){if(row.getStatus()RowStatus.UPDATE){row.getMap().clear();row.getMap().putAll(row.getOriginals());row.getOriginals().clear();row.setStatus(RowStatus.NONE);}}delete.clear();filter.clear();}一个值得注意的细节reject恢复UPDATE行用的是getMap().clear()putAll(originals)整体替换而不是逐字段original回填。因为originals只记录了改过的字段——整体替换相当于没改过的字段保留data原值本来就等于original改过的字段回到original。两种写法结果等价clearputAll避免了逐字段比较。还有个隐蔽点——reject后delete里的行丢了delete.clear()直接清空被删的行没有回到primary。这是browise与PB DataWindow的行为差异PB的reject会把delete缓冲的行恢复回primary——第19篇讲修复历史时展开。六、reset查询结果的整体灌入publicvoidreset(ListMapString,Objectrows){primary.clear();filter.clear();delete.clear();if(rows!null){for(MapString,Objectm:rows){primary.add(newRow(m));// 新Row状态NONE}}}每次查询后全量重建——不是增量merge。旧primary里的脏行、delete缓冲全部丢弃。为什么不做增量比如按主键merge保留本地未提交的编辑——场景不需要。查询通常是重新取数用户点了刷新就是想看最新数据本地编辑要么先提交要么先放弃。增量merge带来的主键冲突、状态合并复杂度远超收益。KISS在这个数据结构上贯彻得很彻底。七、getrow这个命名publicRowgetrow(intindex){returnprimary.get(index);}全小写getrow不是getRow——从wiserise的Java代码继承的原名。改名是一行事但前端useRowSet.ts、所有调用点、以及两代项目共用的心智模型都要跟着动。协议稳定的成本意识贯穿到方法命名——不是不能改是不值得改。✅ 亮点222行RowSet拆成三缓冲职责表、remove对INSERT行不进delete缓冲的关键分岔、collect三行逻辑的提交协议角色、reset全量重建不做增量merge的KISS取舍、getrow命名的协议稳定成本。适合实现行集管理的参考。扩展方向第19篇accept/reject修复史、第23篇commonSave、第44篇前端useRowSet。