项目中,我们用 golang 操作 mongo 数据库,再 review 同事代码时看到同事使用 func (order *Order) Update(ctx context.Context)
的写法。当时就觉得这会是一个坑,但没有提出来,以至于使用此函数的代码量越来越多,后期维护成本大大增大。
这个函数不推荐使用,我总结了下,主要有以下问题:
- 命名问题
根据命名的准确性,此方法应该是要对此对象中的所有字段进行更新。即,开发者在看到这个方法名时,就会下意识地认为只要我将此对象的所有属性都设置了,就可以调用这个函数了。这也就是为什么这块功能中这个函数被大量使用以至于后期改动成本变大的原因。
- 添加可更新字段的问题
由于我们在使用 mongo 保存数据时必须要显式地标记更新哪些字段(如下示例),这样就要求了必须写上所有可能被更新的字段才能在使用时尽小地减少一些问题。比如有时候觉得一个很常用的字段,在调用此更新函数时却发现 DB 中存的是空。
condition := bson.M{"_id": id}
setter := bson.M{
"field_1": self.Field_1,
}
if self.Field_2 != "" { // 对于有条件的更新要这样写
setter["field_2"] = self.Field_2
}
updater = bson.M{
"$set": setter,
}
model.model_name.UpdateOne(ctx, condition, updater)
- 并发问题
这是这个方法最严重的问题。
由于在调用此 Update 函数前,一定要先将它从 DB 中查出来,再进行一此业务逻辑,在这期间如果有两个逻辑同时进行处理,势必会造成数据更新错乱的问题。
更新操作的最佳实践
- 使用 Update() 函数的场景
对于一些更新频率不高,且一更新就是全文档更新的操作。由于上述原因,在这个场景下使用此函数是比较合适的。
比如我们写过的商品信息的更新,因为字段复杂,一次提交必须是完整的文档提交过来。而且更新的频率并不高,即便有两个人同时更新一件商品,以后更新者为准则可,不会造成数据紊乱的问题。
- 具体更新具体操作
比如如果是取消订单,就写方法 order.Cancel(ctx)
,只在取消订单的时候用它,只更新它需要更新的字段,在确认收货时用 order.Complete(ctx)
,职责唯一且确定
- 采用先分后合的策略再整合
这个是我接下来要做的。我会把已 refine 的几个方法重新整理一下,一些可以提取到一个函数的方法,则会整合到一块去。比如 order.Accept(ctx)
,order.Complete(ctx)
都只更新了状态和操作历史,我会想办法把它们整合成一个方法 order.UpdateStatus(ctx)
中。
有疑问加站长微信联系(非本文作者)