使用first_or_create时遇undefined method '+' for nilClass错误求解
问题分析与解决方案
这个问题既不是语法错误,也不完全是first_or_create的使用方式有误——核心是你误解了first_or_create的执行逻辑,同时没处理新建对象时amount的初始值问题。
为什么原来的代码会出错?
首先要明确first_or_create的运行规则:
- 当数据库中存在匹配
where条件的记录时,直接返回该记录,不会执行后面的块。所以你想在块里实现的“存在时累加amount”逻辑根本不会生效。 - 当不存在匹配记录时,会新建一个
Blob实例,这时候才会执行块里的代码。但新建实例的amount属性默认是nil(如果数据库字段没设默认值),执行s.amount += amount相当于nil + amount,自然会抛出NoMethodError,因为nil没有+方法。
解决方案
根据你的业务场景,推荐两种可行的写法:
方案1:直观处理对象实例(适合需要操作对象的场景)
用find_or_initialize_by先获取或初始化实例,统一处理amount的累加逻辑,最后保存:
blob = Blob.find_or_initialize_by(user_id: user.id, item_id: item.id) # 用to_i把nil转为0,不管是新建还是已存在都能正确累加 blob.amount = blob.amount.to_i + amount blob.save!
这种方式清晰易懂,如果你后续还要对blob做其他属性修改或业务操作,非常适用。
方案2:原子操作避免并发问题(适合高并发场景)
如果你的系统存在并发修改同一条记录的可能,推荐用数据库原子操作来避免竞态条件:
# 用SQL占位符避免注入风险,原子更新数据库中的amount字段 updated_count = Blob.where(user_id: user.id, item_id: item.id).update_all("amount = amount + ?", amount) # 若没有更新任何记录,说明需要新建一条 Blob.create!(user_id: user.id, item_id: item.id, amount: amount) if updated_count == 0
这种方式不需要加载对象,直接操作数据库,能有效防止多个请求同时修改时出现的数值不一致问题。
额外优化:给amount设置默认值
如果能在Blob模型或数据库层面给amount设置默认值0,也可以简化代码:
# 假设amount默认值为0 blob = Blob.where(user_id: user.id, item_id: item.id).first_or_create blob.amount += amount blob.save!
但同样要注意,这种写法在高并发场景下可能存在竞态问题,需要根据实际情况选择。
内容的提问来源于stack exchange,提问作者calyxofheld
相关产品推荐
相关产品推荐

