news 2026/9/4 14:19:36

17-Row脏标记核心设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
17-Row脏标记核心设计

17-Row._t/_o:脏标记的核心设计

browise数据协议的最小单元是Row——一个Map装数据、一个Map装旧值、一个枚举装状态。177行代码,_t/_o两个协议字段的所有语义都在这里。这篇逐行拆setItemValue的状态机分支和original的"首次记录不覆盖"规则。

文章目录

  • 17-Row._t/_o:脏标记的核心设计
    • 一、三个字段撑起一个数据结构
    • 二、setItemValue:11行的状态机
    • 三、为什么不用"上一次值"实现undo栈
    • 四、三个读方法的语义分层
    • 五、getMap返回引用不是副本
    • 六、toRaw与序列化的边界

源码:browise-data/src/main/java/com/browise/data/Row.java(177行)


一、三个字段撑起一个数据结构

publicclassRow{privateRowStatusstatus=RowStatus.NONE;// _tprivatefinalMap<String,Object>original=newLinkedHashMap<>();// _oprivatefinalMap<String,Object>data=newLinkedHashMap<>();// 业务字段}

RowStatus枚举四个值:

NONE=0// 未修改——查询加载后的默认状态INSERT=1// 新增——add()创建的行UPDATE=3// 修改——NONE行被改字段后自动转DELETE=4// 删除——被remove()移入delete缓冲的行

(跳过2不是疏忽——wiserise时代2曾表示"已存在的新行",browise简化后保留编号间隔兼容旧协议)


二、setItemValue:11行的状态机

publicvoidsetItemValue(Stringkey,Objectvalue){if(status==RowStatus.NONE){// 首次修改该字段时保存原始值if(!original.containsKey(key)&&data.containsKey(key)){original.put(key,data.get(key));}status=RowStatus.UPDATE;// NONE → UPDATE}data.put(key,value);}

四个分支行为:

当前状态original处理status处理
NONE首改字段存旧值→ UPDATE
UPDATE不再记录(保持最早旧值)不变
INSERT不记录(新行没有"旧值"概念)不变
DELETE不记录不变

两个精妙点:

①"首次记录不覆盖"——

Rowrow=newRow(Map.of("name","张三"));row.setItemValue("name","李四");// original={name:张三}, data={name:李四}row.setItemValue("name","王五");// original仍是{张三}!data={王五}

original存的是最初值不是"上一次值"。为什么?——rejectChanges要恢复到查询加载时的样子,不是"用户改了几手后"的样子。_o是回滚锚点,锚点必须钉在最初。

②INSERT行不记original——新add的行,字段值都是用户刚填的,"旧值"毫无意义。rejectChanges对INSERT行的处理是整行丢弃(第18篇)——没有字段级恢复的需求。


三、为什么不用"上一次值"实现undo栈

一个自然的疑问:original只存一份,如果想要"多步撤销"(undo/redo)呢?

刻意不做。编辑场景的回滚语义是"放弃修改恢复原样"——一步到位。多步撤销是编辑器功能(Word/CAD),不是数据集功能。政务表单的用户诉求是"改错了,恢复"——恢复到加载时刻就够了。

如果真要多步,在Row外面包一层历史栈即可——协议保持最小,扩展在外层。这是_t/_o能稳定14年的另一面:不往协议里塞可推导的东西。


四、三个读方法的语义分层

publicObjectgetItemValue(Stringkey)// 当前值——渲染用publicObjectgetOriginal(Stringkey)// 最初值——审计/对比用publicbooleanisDirty()// status != NONE——过滤用

审计模块对getOriginal的重用——browise-audit的AuditEntityLifecycleListener在实体保存前抓快照,字段级diff就是拿当前值 vs getOriginal(key)逐字段比——"psnName从张三改成李四"这种变更记录的两侧数据源就是这两个方法。_o不只服务回滚,还免费服务了审计。


五、getMap返回引用不是副本

publicMap<String,Object>getMap(){returndata;// 直接返回内部Map}

注释明确警告"修改返回值会直接影响行数据"。为什么不defensive copy?

性能——每行每字段都copy一份,几千行编辑时开销可观。约定替代防御——框架内部使用者(RowSet/BaseEntity/审计)遵守"只读getMap"的约定;外部要写值必须走setItemValue(否则脏标记失效)。

这个设计的赌注是:误用导致的bug(改了Map行状态没变)比copy的性能损失更容易被发现——界面上"改了但提交时没带上这个字段"一眼就能看出来。反过来的性能劣化是隐形的。


六、toRaw与序列化的边界

Row还有toRaw()(导出纯数据不含_t/_o)和前后端传输的序列化。传输格式:

{"_t":3,"name":"李四","_o":{"name":"张三"},"age":30}

_t和_o以字段形式混在JSON里——不是包裹结构。前端useRowSet解析时按约定名提取,剩余字段全是业务数据。这个扁平结构的代价:业务字段不能叫"_t"或"_o"(下划线开头的名字被协议保留)——14年没出过冲突,因为业务字段规范要求驼峰命名。


✅ 亮点:177行Row拆成三个字段+11行状态机,重点讲original"首次记录不覆盖"锚定最初值的语义、INSERT不记旧值的原因、getMap返回引用的性能/可发现性取舍、_t/_o扁平混在JSON的命名保留约定。适合实现脏标记数据结构的参考。扩展方向:第18篇RowSet三缓冲、第19篇accept/reject、第44篇前端useRowSet镜像。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 20:05:46

ROS2 Jazzy与Gazebo Harmonic迷宫求解机器人仿真实践

简介&#xff1a;移动机器人导航与路径规划是机器人学的核心领域&#xff0c;仿真环境为算法验证提供了低成本的试验场。在Ubuntu 24.04平台下&#xff0c;ROS2 Jazzy与Gazebo Harmonic构成了新一代LTS组合&#xff0c;支持激光雷达、差速驱动等传感器仿真。通过构建二维栅格迷…

作者头像 李华
网站建设 2026/8/31 1:00:39

告别if-else:用声明式规则语言构建轻量级业务规则引擎

看见 Lemma 这个项目标题时&#xff0c;第一个疑问通常是&#xff1a;业务规则用 if-else 直接写不就行了&#xff0c;为什么还要专门发明一门声明式语言&#xff1f;这个问题的答案&#xff0c;恰好是理解 Lemma、也理解所有声明式业务规则语言的关键。业务规则用命令式代码…

作者头像 李华
网站建设 2026/9/1 2:06:55

京东算法工程师笔试真题复盘:从动态规划到机器学习考点全解析

2018年秋天&#xff0c;我在北京某高校的宣讲会上投了京东的算法工程师岗位&#xff0c;一周后收到了笔试通知。那时候算法岗的竞争已经非常激烈&#xff0c;京东这套题给我的整体印象是&#xff1a;基础扎实、覆盖面广、编程题不偏不怪但需要熟练度。 作为经历过那场笔试的人…

作者头像 李华
网站建设 2026/9/2 10:29:22

蚂蚁工程数据岗笔试全解析:考点分布与备考策略

秋招季聊蚂蚁工程数据岗的笔试&#xff0c;这个话题我其实一直想认真写一篇。原因很简单&#xff0c;工程数据岗这个名字听起来不像后端、算法那么“标准”&#xff0c;导致很多人在准备阶段就容易跑偏——要么当成后端开发去刷八股&#xff0c;要么当成数据分析岗去背AB实验&a…

作者头像 李华
网站建设 2026/9/2 11:07:40

如何在vLLM上跑Gemma4+DFlash?Docker与源码构建双路线完整指南

如何在vLLM上跑Gemma4DFlash&#xff1f;Docker与源码构建双路线完整指南 【免费下载链接】dflash DFlash: Block Diffusion for Flash Speculative Decoding 项目地址: https://gitcode.com/GitHub_Trending/df/dflash DFlash 是一个轻量级块扩散&#xff08;Block Dif…

作者头像 李华
网站建设 2026/9/2 8:10:09

智能车竞赛高效调试:结构化提问与问题解决框架实践

1. 项目概述&#xff1a;从“智能车竞赛”到“有效提问”的认知跃迁“智能车竞赛”这个名字&#xff0c;对于电子、自动化、计算机相关专业的学生和爱好者来说&#xff0c;几乎等同于一个技术试炼场。它绝不仅仅是让一辆小车跑起来那么简单。从最基础的循迹、避障&#xff0c;到…

作者头像 李华