destroy_all 当我们在它前面放置一个 where 子句时,在事务中不起作用

destroy_all not working in transaction when we place a where clause before it

我遇到了意外行为。当我在一个关系上调用 destroy_all 时,它通过 ActiveRecord 事务执行,但是当我在它之前放置一个 where 子句时,出现了一个意外的行为,即每条记录都被单独销毁。

示例:

Actor.find(1).movies.destroy_all 这里 destroy_all 将 运行 在交易中,但是 Actor.find(1).movies.where(id: [1,2,3]).destroy_all 将单独提交每个销毁。

有解释吗?

那是因为你在处理不同的对象:

Actor.find(1).movies.class                    
# Movie::ActiveRecord_Associations_CollectionProxy

Actor.find(1).movies.where(id: [1,2,3]).class 
# Movie::ActiveRecord_AssociationRelation

并且类都以自己的方式定义了自己的delete_all方法:

# File activerecord/lib/active_record/associations/collection_proxy.rb
def destroy_all
  @association.destroy_all.tap { reset_scope }
end

# File activerecord/lib/active_record/relation.rb
def destroy_all
  records.each(&:destroy).tap { reset }
end

因此,当您执行 Actor.find(1).movies.destroy_all 时,将通过在 @association 上调用 destroy_all 来处理操作。

但是通过执行 Actor.find(1).movies.where(id: [1,2,3]).destroy_all 每个对象都有一个迭代,对每个对象调用 destroy

@association.delete_all 被定义为获取 ActiveRecord_Relation 并在单个事务中销毁包含它们的元素:

def destroy_all
  destroy(load_target).tap do
    reset
    loaded!
  end
end

通过试验该方法,您可以获得与第一个示例相同的结果;

Actor.find(1).movies.instance_variable_get("@association").send(:destroy, Movie.all)