如何排除来自ActiveRecord的交易模型? [英] How do I exclude a model from a transaction in ActiveRecord?

查看:190
本文介绍了如何排除来自ActiveRecord的交易模型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个特殊的情况下,模型不应该成为外部事务的一部分:

I have a special case Model which must not become part of an outer transaction:

Outer.Transaction do
  ...

  Inner.create(:blah)

  ...
end

如何停止内成为交易的一部分,假设内竟然一无所知哪些具体事务是越来越被拉进?

How do I stop Inner becoming part of the transaction, assuming Inner should know nothing about which specific transaction it is getting pulled into?

例如,创建一个内部交易是一个没有去,因为这也将成为外部事务的一部分。

For example, creating an inner transaction is a no go, because that too will become part of the outer transaction.

我想这样做,因为内部模型需要立即,不写等待外部事务提交。

I want to do this because the inner model needs to write immediately and not wait for the outer transaction to commit.

推荐答案

我很好奇,什么都需要这样的结构!

I'm curious as to what would require such a construct!

我想你会为你所描述的努力才能做到没有一点两轮牛车的。例如,你可以在MySQL中,用于内蒙古设置表格的存储类型,以一个不支持事务(如MyISAM数据),同时保持其他类表存储的东西这确实支持交易(议员!)。

I think you're going to struggle to do it without a bit of hackery as you've described. For example, you could, in mysql, set the table storage type for Inner to one that doesn't support transactions (MyIsam eg) whilst keeping other classes' tables storage with something that does support transactions (YUK!).

如果你能,你几乎可以肯定会更好延迟 Inner.create ,直到交易完成后。您可以使用开始与保证,以确保在创建总是会发生的。是这样的:

If you can, you'd almost certainly be better off delaying the Inner.create until after the transaction. You can use begin with ensure to make sure that the create always happens. Something like:

create_inner = false
begin
  Outer.transaction.do
    ...
    create_inner = true # instead of Inner.create(:blah)
    ...
  end
ensure
  if create_inner
    Inner.create(:blah)
  end
end

这将成为,如果你的块的其余部分依赖于创建内蒙古实例更为复杂。你可以在块可能创建实例,并设置 created_inner 假的块结束,这样,如果code运行没有异常,将被创建在交易中,你会不会在保证重新创建。

This would become more complicated if the rest of your block depends on the created Inner instance. You could probably create the instance in the block and set created_inner to false at the end of the block so that, if the code runs without exception it will have been created in the transaction and you won't create again in the ensure.

如果你想这样做在一般情况下,你可以定义在内蒙古 A类方法执行块,但总是创建一个内蒙古对象。你需要一个 after_create 添加到内蒙古太。你会依赖于 Inner.create 呼叫块当交易成功创建它,但如果它被回滚,那么你就需要事后创建它。例如:

If you want to do it in the general case you could define a class method on Inner to execute a block but always create an Inner object. You'd need to add an after_create to Inner too. You would rely on the Inner.create call in the block to create it when the transaction succeeds but if it is rolled back then you'd need to create it afterwards. For example:

class Inner < ActiveRecord::Base

  def self.ensure_created(&block)
    Thread.current[:created_inner] = false        
    begin
      block.call
    rescue => e
      if Thread.current[:created_inner]
        Inner.create(:blah)
      end
      raise e
    end
  end

  def after_create
    # Flag that an instance has been created in this thread so
    # that if we rollback out of a transaction we can create again
    Thread.current[:created_inner] = true
  end

您会再称呼它:

Inner.ensure_created do
  Outer.transaction do
    ...
    Inner.create(:blah)
    ...
  end
end

不过,有很多不足之处,以本办法的,我不知道我会提倡的。这很复杂。如果ActiveRecord的::回滚提高作为例外,它不会工作不会泡出的 Outer.transaction ,但会导致内蒙古实例没有被创建。当两个或多个呼叫嵌套这将无法正常工作。最后我没有测试它彻底! - 慎用

HOWEVER, there's plenty of downsides to this approach and I'm not sure I'd advocate it. It is complicated. It won't work if ActiveRecord::Rollback is raised as that exception won't bubble out of the Outer.transaction but will cause the Inner instance not to be created. It won't work properly when two or more calls are nested. And finally I haven't tested it thoroughly - use with caution!

这篇关于如何排除来自ActiveRecord的交易模型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆