不用Hibernate,自己搓ActiveRecord:状态机追踪字段变更,一个save搞定增删改

张开发
2026/4/17 8:30:08 15 分钟阅读

分享文章

不用Hibernate,自己搓ActiveRecord:状态机追踪字段变更,一个save搞定增删改
不用Hibernate自己搓ActiveRecord状态机追踪字段变更一个save()搞定增删改非科班野生程序员深耕政务信息化20年。从VC到PB再到Java自研框架browise也打磨了十几年。最近整理框架代码发现不少有趣的决策写出来和大家聊聊。最后感谢豆包、智谱、OpenCode决策是我做的代码是我搓的文字是他们总结的。问题是什么政务系统里前端编辑一条数据可能是新增、可能是修改、可能是删除。如果每个操作都单独写一套逻辑代码量爆炸。特别是批量操作的场景——一个表单里有的行是新增的有的行是改过的有的行被删了。Hibernate 太重了我需要一个轻量的 ActiveRecord 模式每个 Dao 对象知道自己是什么状态调一个save()就能自动路由到 insert/update/delete。核心设计_t状态机privateint_t0;/* 0不变1新增3修改4删除 *//* 修改前的值 */HashMapString,String_onewHashMapString,String();就这么两个字段驱动整个状态追踪_t0刚查出来的数据没变过_t1前端标记为新增_t3字段被SetItemValue修改过第一次赋值时自动从0跳到3_t4前端标记为删除_o这个 HashMap 存的是修改前的值——哪个字段改了改之前是什么值都记在这里。save() 的自动路由publicvoidsave()throwsutilException{if(this.isInsert())insert();elseif(this.isModefiy())update();elseif(this.isDelete())delete();}三个判断方法publicbooleanisModefiy(){return_t3;}publicbooleanisInsert(){return_t1;}publicbooleanisDelete(){return_t4;}前端传 JSON 过来fromJson()解析的时候自动读取_t值。批量数据里每一行有自己的_t调一次newDataStore.save()遍历所有行每行自动路由到对应操作。insert/update/delete 怎么找 Mapper关键在泛型反射publicvoidinsert()throwsutilException{this.verification();this.check();ParameterizedTypetype(ParameterizedType)this.getClass().getGenericSuperclass();Class?clazz(Class?)type.getActualTypeArguments()[0];DBUtil.SaveDao(clazz,getInsertMethod(),this);}BaseDao 的泛型参数T是 MyBatis 的 Mapper 接口类型。运行时通过反射拿到实际类型直接调DBUtil.SaveDao。子类只要这样写publicclassMyBusinessDaoextendsBaseDaoMyBusinessMapper{// 不用重写 insert/update/delete/save}SetItemValue — 触发状态变更publicvoidSetItemValue(Stringkey,Objectvalue)throws...{if(_t0){_t3;// 自动标记为修改}if(_t2)// 修改保存老值{if(_o.get(key)null){_o.put(key,getItemStringValue(key));}}Fieldfilednull;try{filedgetClass().getDeclaredField(key);}catch(Exceptione){filednull;}if(filed!null){filed.setAccessible(true);Classtypefiled.getType();valueTypeUtil.parseObject(type,value);filed.set(this,value);filed.setAccessible(false);}}这里有两个细节_t0时自动跳到3修改不需要手动设状态_t2时会把旧值存到_o里用于变更审计ver 注解验证保存之前自动校验字段// ver.java — 注解定义Target({ElementType.FIELD,ElementType.PARAMETER})Retention(RetentionPolicy.RUNTIME)publicinterfacever{Stringreg()default;booleanrequire()defaultfalse;}在 Dao 的字段上标注ver(requiretrue)privateStringxm;// 姓名必填ver(reg^\\d{18}$)privateStringsfzh;// 身份证号正则校验verification()方法遍历所有字段publicbooleanverification()throwsutilException{ListFieldfieldListnewArrayListField();ListProperty.listAllFields(this.getClass(),fieldList,ClassType.SELF_CLASS);for(Fieldfield:fieldList){if(field.isAnnotationPresent(ver.class)){ver annfield.getAnnotation(ver.class);StringvalueString.valueOf(field.get(this));if(ann.require()){if(valuenull||null.equals(value)||.equals(value)){thrownewutilException([field.getName()]为空!,-1);}}if(!(valuenull||null.equals(value)||.equals(value))){PatternpPattern.compile(ann.reg());Matchermp.matcher(value);if(!m.matches()){thrownewutilException([field.getName()]不符合要求!,-1);}}}}returntrue;}空值校验和正则校验都支持。校验失败直接抛异常前端收到错误提示。另外还有个check()空方法子类可以重载做业务级校验publicvoidcheck()throwsutilException{// 子类重载}toJson / fromJson — 前后端序列化toJson()把 Dao 转成 JSON 字符串传给前端fromJson()把前端传回的 JSON 解析成 Dao 对象。注意getBlock()方法过滤掉内部字段publicStringgetBlock(){returnsuper.getBlock()insertMethod|deleteMethod|updateMethod|deleteMethod|searchMethod|readOnly|_o|_t|;}_o修改前值和_t状态标记在toJson()时不传给前端但fromJson()解析时能接收前端传回的_t值。search — 查询也封装了publicList?search()throwsutilException{ParameterizedTypetype(ParameterizedType)this.getClass().getGenericSuperclass();Class?clazz(Class?)type.getActualTypeArguments()[0];Class?[]plistthis.getParameterTypes(clazz,this.getSearchMethod());if(plist!nullplist.length0){if(this.getMaxRow()0this.getMinRow()0){RowBoundspagenewRowBounds(this.getMinRow(),this.getMaxRow()-this.getMinRow());listDBUtil.getDao(clazz,getSearchMethod(),this,page);}else{listDBUtil.getDao(clazz,getSearchMethod(),this);}}else{listDBUtil.getDao(clazz,getSearchMethod());}returnlist;}查询条件就是 Dao 自己的字段值分页也内置了。子类可以改searchMethod来指定不同的查询 SQL。小结BaseDao 600行代码实现了一套完整的 ActiveRecord 模式_t状态机追踪每行数据的新增/修改/删除_oHashMap 保存修改前的值save()自动路由到 insert/update/deletever注解做字段级验证泛型反射自动找到对应的 MyBatis MappertoJson/fromJson 处理前后端序列化没有 Hibernate 的 Session 管理没有脏检查没有延迟加载。就是朴素的反射 状态标记。但在政务系统里够用、可控、好排查。ActiveRecord模式大家都在用你们是自己实现的还是用的框架评论区聊聊。标签#Java #ActiveRecord #自研ORM #状态机 #MyBatis #政务信息化 #自研框架

更多文章