Yii2 ActiveRecord在AFTER_INSERT_EVENT中保存后跳转view显示旧值如何解决?
问题根因
跳转后显示旧值的问题本质和数据库更新及时性、事务都没有关系,是时间查询条件逻辑错误导致刚写入的数据被过滤:
TimelineTrait的timelineInit方法中,初始化datetime配置项时用的是当前请求初始化模型的时间,所有写入Timeline表的记录用的都是这个时间值TL_attributesOnTime方法的查询SQL中,过滤条件写的是t1.datetime < :datetime,请求处理速度快的时候,跳转后新请求的模型初始化时间和写入时间完全可能为同一秒,刚写入的记录就会被过滤,查不到最新值- 加
sleep(5)后跳转的新请求初始化时间比写入时间晚至少5秒,<条件成立即可查到最新值,手动刷新也是同理,刷新后的请求时间晚于写入时间。
修复方案
- 首先修改查询条件,将
TL_attributesOnTime方法里的<替换为<=,适配同秒写入同秒查询的场景:
$sql = "SELECT t1.* FROM $timelineTable AS t1 LEFT JOIN (SELECT * FROM $timelineTable WHERE `table`=:table AND table_id=:table_id AND datetime<=:datetime) AS t2 ON (t1.table=t2.table and t1.table_id=t2.table_id and t1.datetime<t2.datetime AND t1.attribute=t2.attribute) WHERE t2.id IS NULL AND t1.datetime<=:datetime AND t1.table=:table AND t1.table_id=:table_id ";
- 可选优化:如果不需要严格的历史时间点查询能力,可以把写入时间的精度提高到毫秒,避免同秒冲突。
- 如果开启了Yii的查询缓存,可以在执行历史属性查询时临时关闭缓存,避免读到旧缓存:
$data = \Yii::$app->db->createCommand($sql,$params)->enableCache(false)->queryAll();
当前场景不需要加事务也不需要加锁,写完的记录只要查询条件正确必然能查到,此前事务不生效是因为问题本身和事务原子性无关。
开源建议
完全可以把这个Trait开源到GitHub:
- 这个实现解决了ActiveRecord扩展属性不需要修改原表、属性变更自动留痕可回溯的通用需求,很多需要审计、历史版本追溯的业务场景都可以使用,实用价值很高
- 第一次开源只要补充好README,写清楚依赖的Yii版本、Timeline表的建表语句、使用示例即可,后续可慢慢迭代功能。
内容的提问来源于stack exchange,提问作者Dennis4712
相关产品推荐
相关产品推荐

