【发布时间】:2014-08-18 13:23:31
【问题描述】:
当有一个父记录关联多个子记录时,对父记录使用行锁定是确保一致性的明显方法。但是,我似乎找不到在 ActiveRecord 中执行此操作的干净方法。
例如,假设我们有两个模型:Order 和 OrderProduct。
class Order < ActiveRecord::Base
has_many :order_products
...
end
class OrderProduct < ActiveRecord::Base
belongs_to :order
...
end
更新OrderProduct 会影响Order 的整体状态,因此我们要确保在任何给定时间只有一个事务在更新Order。
如果我们在编辑 OrderProduct 时尝试实现这一点,我能看到的 ruby 中最简洁的方法是:
def edit
product = OrderProduct.find params[:id]
Order.transaction do
product.order.lock!
# Make sure no changes have occurred while we were waiting for the lock
product.reload
# Do stuff...
product.order.some_method
end
end
但是,如果 SQL 查询效率很低,则会产生:
SELECT "order_products".* FROM "order_products" WHERE "order_products"."id" = $1 LIMIT 1 [["id", "2"]]
SELECT "orders".* FROM "orders" WHERE "orders"."id" = 2 LIMIT 1
SELECT "orders".* FROM "orders" WHERE "orders"."id" = $1 LIMIT 1 FOR UPDATE [["id", 2]]
SELECT "order_products".* FROM "order_products" WHERE "order_products"."id" = $1 LIMIT 1 [["id", 2]]
SELECT "orders".* FROM "orders" WHERE "orders"."id" = 2 LIMIT 1
我们可以通过将 更改为以下内容来减少查询次数:
def edit
product = OrderProduct.find params[:id]
Order.transaction do
order = Order.find product.order_id, lock: true
# Make sure no changes have occurred while we were waiting for the lock
product.reload
# Cache the association
product.order = order
# Do stuff...
product.order.some_method
end
end
产生更好的 SQL:
SELECT "order_products".* FROM "order_products" WHERE "order_products"."id" = $1 LIMIT 1 [["id", "2"]]
SELECT "orders".* FROM "orders" WHERE "orders"."id" = 2 LIMIT 1 FOR UPDATE [["id", 2]]
SELECT "order_products".* FROM "order_products" WHERE "order_products"."id" = $1 LIMIT 1 [["id", 2]]
但是代码比较混乱。
有没有更简洁的方法来使用 ActiveRecord? 调用 product.order = order 只是为了缓存关联似乎有点危险。
【问题讨论】:
标签: ruby-on-rails postgresql activerecord locking