У меня возникла ситуация, когда я хотел бы, чтобы метод работал внутри транзакции, но только если транзакция еще не была запущена. Вот надуманный пример, чтобы отогнать то, что я говорю:Обеспечение транзакции в рамках метода с 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
, так что новая транзакция запускается только в том случае, если еще не существует внешней транзакции, так что любая суб-транзакция может вызвать откат во всей внешней транзакции. Это позволит этим методам быть транзакционными самостоятельно, но отложить их до родительской транзакции, если таковая имеется.
Есть ли встроенный способ достижения такого поведения, а если нет, есть ли камень или патч, который будет работать?
насчет [transaction_open] (HTTP: //api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-transaction_open-3F)? –