一个比较严重的问题:数据库表字段新增,导致相关对象的_setAttrs方法报错

反馈最近出现一个比较严重的问题,我大概模拟下场景:

    我们有一个客户表customer ,里面有两个字段:customer_name,customer_id,remark,

    sql文件有一个查询:

#namespace("demo.customer")

  #sql ("selectAll")

    select * from `customer` 

    #end

 #end

service有一个方法:

 List<Record> records = Db.find(Db.getSql("demo.customer"));

 Record record = records.get(0);

 Customer customer = new Customer()._setAttrs(records.get(0).getColumns());

  customer.update();

方法是正常响应的,然后执行了sql脚本更新customer表,新增一个remark1字段,然后再调用这个方法,

new Customer()._setAttrs(records.get(0).getColumns()) 这行 就会提示报错:

com.jfinal.plugin.activerecord.ActiveRecordException: The attribute name does not exist: "remark1"

查询报错是 model 的 setAttrs方法报异常。

最近生产就是版本上线的时候执行了sql脚本,客户表新增了两个字段,导致客户相关功能接口异常,服务不可用,影响很大。

JFinal 现在版本是:3.8

这种情况是可以复现的,想问下大家有没有碰到这种情况,有没有好的解决方案,因为对生产影响很多,急需要方案解决,

不然每次更新脚本都要暂停生产服务,影响太大了,谢谢大家


评论区

小风yy

2020-03-24 20:15

JFinal 的model 对象 set方法是否可以没有找到对应的列不报异常,只提示日志警告,put一个空值,这样对整体影响就会小很多,只是数据显示不了

JFinal

2020-03-24 20:19

为什么要查询出来用 Record, 然后 update 的时候再用回 Model ?

这种用法太奇怪了

要么全用 Record , 要么全用 Model

Record 也可以保存:
Db.save("customer", records.get(0));


当前你这种用法之下,如果仅仅是希望不出现异常的话,将
new Customer()._setAttrs(...)
改成
new Customer().put(...) 即可

put 方法不检查你的字段是否存在,就不会报异常。但这种用法你自己要注意是否有安全隐患,为什么要动态动改数据表的结构?

小风yy

2020-03-24 20:26

@JFinal 不是动态改变数据表结构,是版本上线前要执行生产脚本,添加表字段,但是服务是不能发布的,这时候添加表字段就会导致使用_setAttrs方法报错,_setAttrs使用场景是数据库查询的Record把属性和值赋值给新的一个空对象,然后操作新的对象就像set属性操作,类似于对象拷贝

JFinal

2020-03-24 20:30

@小风yy 添加字段就是动态改变数据表结构

你的项目正在运行中,但你为表添加了字段,不抛异常才是错的

小风yy

2020-03-24 20:37

@JFinal 我们这边执行生产脚本和发布生产服务是两部操作,如果添加表字段就要发布服务就影响有点大了,有没有什么方法_setAttrs可以不抛异常,如果setAttrs统一改成put又影响太大了,服务功能都要做回归测试。因为之前用过mybatis ,更改数据库添加字段时,服务都是正常不会有影响的,只有更改表字段名或者删除表字段才有影响

JFinal

2020-03-24 21:22

@小风yy 抛异常的原因是:
1:Db.find(Db.getSql("demo.customer")); 查出来的字段是实时的,也就是说你添加了字段 remark1 以后,上面的代码再执行时,会获取到 remark1 这个字段的值

2:ActiveRecordPlugin 在启动时,会对所有映射的 Model 进行初始化,这个过程会从数据库获取字段名

3:出于安全性考虑,Model._setAttrs(...) 这个方法会检测放入的字段是否存在,而这时使用的字段是前面第二步在启动时初始化决定的

4:由于添加了 remark1 这个字段, Model._setAttrs 检测时发现这个字段在内存中不存在,抛出异常

知道了原理,根据情况解决一下。

其实我在前面的回复中已经给出了几种解决办法,就看你用不用

小风yy

2020-03-25 10:38

@JFinal 谢谢您的讲解,感觉如果直接添加表字段对服务整体影响很大,我还有个疑惑:调用_setAttrs方法时,set方法内部有一个_getModifyFlag().add(attr)方法,如果改用put方法,我看实现是sattrs.putAll方法,没有调用_getModifyFlag()方法,然后在model的update方法中,会判断_getModifyFlag()是否为空,为空则不做处理,所以我想问下put方法会影响update方法使用么,问的比较细,感谢解答

lyh061619

2020-03-26 10:13

@小风yy 波总的解析很全面,根据你的需求及场景直接服Db+Record模式即可做到实时获取,用model/bean模式新添加字段是必须要重启容器加载ActiveRecordPlugin映射的 Model 进行初始化才生效,否则 Model._setAttrs 检测时发现这个字段在内存中不存在,抛出异常。

小风yy

2020-03-26 10:41

@lyh061619 谢谢,我已经理解了,这个没问题了