2013-09-29 5 views
5

У меня возникла ситуация, когда я хотел бы, чтобы метод работал внутри транзакции, но только если транзакция еще не была запущена. Вот надуманный пример, чтобы отогнать то, что я говорю:Обеспечение транзакции в рамках метода с ActiveRecord

class ConductBusinessLogic 
    def initialize(params) 
    @params = params 
    end 

    def process! 
    ActiveRecord::Base.transaction do 
     ModelA.create_multiple(params[:model_a]) 
     ModelB.create_multiple(params[:model_a]) 
    end 
    end 
end 

class ModelA < ActiveRecord::Base 
    def self.create_multiple(params) 
    # I'd like the below to be more like "ensure_transaction" 
    ActiveRecord::Base.transaction do 
     params.each { |p| create(p) } 
    end 
    end 
end 

class ModelB < ActiveRecord::Base 
    def self.create_multiple(params) 
    # Again, a transaction here is only necessary if one has not already been started 
    ActiveRecord::Base.transaction do 
     params.each { |p| create(p) } 
    end 
    end 
end 

В принципе, я не хочу их, чтобы действовать как вложенные транзакции. Я хочу, чтобы методы .create_multiple запускали только транзакции, если они еще не вызваны в транзакции, например, через ConductBusinessLogic#process!. Если методы модели вызывают сами по себе, они должны начать свою собственную транзакцию, но если они уже вызывается внутри транзакции, то через ConductBusinessLogic#process! они не должны вставлять суб-транзакцию.

Я не знаю, как Rails предоставляет это из коробки. Если я запускаю вышеуказанный код как есть, и откат запускается одним из методов модели, вся транзакция будет продолжаться, потому что суб-транзакция проглатывает исключение ActiveRecord::Rollback. Если я использую опцию requires_new для суб-транзакций, точки сохранения будут использоваться для имитации вложенных транзакций, и только эта суб-транзакция фактически будет отброшена. Поведение, которое я бы хотел, было бы чем-то вроде ActiveRecord::Base.ensure_transaction, так что новая транзакция запускается только в том случае, если еще не существует внешней транзакции, так что любая суб-транзакция может вызвать откат во всей внешней транзакции. Это позволит этим методам быть транзакционными самостоятельно, но отложить их до родительской транзакции, если таковая имеется.

Есть ли встроенный способ достижения такого поведения, а если нет, есть ли камень или патч, который будет работать?

+0

насчет [transaction_open] (HTTP: //api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-transaction_open-3F)? –

ответ

1

Как насчет добавления метода create_multiple_without_transaction к вашим ModelA и ModelB классам? который будет выглядеть примерно так:

class ConductBusinessLogic 
    def initialize(params) 
    @params = params 
    end 

    def process! 
    ActiveRecord::Base.transaction do 
     ModelA.create_multiple_without_transaction(params[:model_a]) 
     ModelB.create_multiple_without_transaction(params[:model_a]) 
    end 
    end 
end 

class ModelA < ActiveRecord::Base 
    def self.create_multiple(params) 
    # I'd like the below to be more like "ensure_transaction" 
    ActiveRecord::Base.transaction do 
     self.create_multiple_without_transaction(params) 
    end 
    end 

    def self.create_multiple_without_transaction(params) 
    params.each { |p| create(p) } 
    end 
end 

, то ваш регулярный create_multiple будет работать, как и раньше, но за исключением случаев, были не нужны транзакции вы бы просто позвонить create_multiple_without_transaction

Смежные вопросы