为什么 Rails 会忽略(伪)嵌套事务中的回滚?
根据文档ActiveRecord::Transactions::ClassMethods,一个非新的嵌套事务将忽略回滚.来自文档:
As per the docs ActiveRecord::Transactions::ClassMethods, a non-new nested transaction will ignore a Rollback. From the docs:
User.transaction do
User.create(username: 'Kotori')
User.transaction do
User.create(username: 'Nemu')
raise ActiveRecord::Rollback
end
end
raise ActiveRecord::Rollback
被忽略,因为它在子事务中(或者更确切地说,它仍然在父事务中而不是它自己的事务中).我不明白为什么两者都会忽略回滚调用?我可以看到,由于子交易"并不是真正的交易,因此它不会回滚Nemu"块,但为什么它不会触发父项的回滚?子事务是否以某种方式隐藏了回滚?
The raise ActiveRecord::Rollback
is ignored because it is in a child transaction (or rather, it is still within the parent transaction and not its own). I do not understand why the Rollback call would be ignored by both? I can see that since the child 'transaction' isn't really a transaction, that it would not roll back the 'Nemu' block, but why does it not trigger a rollback for the parent? Does the child transaction hide the rollback somehow?
换句话说,为什么似乎无法从嵌套的子事务中回滚父事务?
In other words, why is it that there appears to be no way to roll back a parent transaction from within a nested child?
推荐答案
实际上,这正是 嵌套事务 的设计目的.我引用了 oracle 文档:
Actually this is exactly how Nested Transactions was designed for. I quote from oracle docs:
嵌套事务用于为在更大事务范围内执行的操作子集提供事务保证.这样做允许您提交和中止独立于较大事务的操作.
A nested transaction is used to provide a transactional guarantee for a subset of operations performed within the scope of a larger transaction. Doing this allows you to commit and abort the subset of operations independently of the larger transaction.
因此,常规嵌套事务中的子事务对于他或其他子事务或父事务(较大的事务)的行为没有发言权,除了改变相互数据或因异常而失败.
So, a child transaction in a regular nested transaction has no say regarding how him or the other children or parent (larger transaction) could behave, other than changing mutual data or failing for an exception.
但是您可以通过使用 rails docs 通过传递 requires_new: true
But you can grant him (child transaction) a very limited voting chance on his destiny by utilizing the sub-transaction
feature as stated at rails docs by passing requires_new: true
User.transaction do
User.create(username: 'Kotori')
User.transaction(requires_new: true) do
User.create(username: 'Nemu')
raise ActiveRecord::Rollback
end
end
正如文档所说:只创建Kotori".因为强大的Nemu"孩子选择了默默地死去.
Which as the docs say: only creates 'Kotori'. since the powerful 'Nemu' child chose to die silently.
有关嵌套事务规则的更多详细信息(oracle 文档)
More details about Nested transaction rules (oracle docs)
更新:
为了更好地理解为什么 rails nested transactions
以这种方式工作,您需要更多地了解嵌套事务在 DB 级别的工作原理,我引用自 rails api 文档:
To better understand why rails nested transactions
works this way, you need to know a bit more about how nested transactions works in DB level, I quote from rails api docs:
大多数数据库不支持真正的嵌套事务......为了解决这个问题,#transaction 将模拟嵌套事务,通过使用保存点:http://dev.mysql.com/doc/refman/5.0/zh/savepoint.html
Most databases don’t support true nested transactions ...In order to get around this problem, #transaction will emulate the effect of nested transactions, by using savepoints: http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
好的,那么文档描述了上述两种情况下嵌套事务
的行为如下:
Ok, then the docs describes the behavior of a nested transaction
in the two mentioned cases as follows:
如果是嵌套调用,#transaction 的行为如下:
In case of a nested call, #transaction will behave as follows:
块将在不做任何事情的情况下运行.块内发生的所有数据库语句都有效地附加到已打开的数据库事务中.
The block will be run without doing anything. All database statements that happen within the block are effectively appended to the already open database transaction.
但是,如果设置了 :requires_new,则该块将被包装在充当子事务的数据库保存点中.
However, if :requires_new is set, the block will be wrapped in a database savepoint acting as a sub-transaction.
我想象小心,只是想象:
option(1)(没有 requires_new)是否适用于您使用完全支持嵌套事务
的 DBMS 或者您对nested_attributes
option(1) (without requires_new) is there in case you used a DBMS that fully supports nested transactions
or you are happy with the "fake" behavior of nested_attributes
而 option(2) 是支持 savepoint
解决方法(如果您不这样做).
while option(2) is to support the savepoint
workaround if you don't.
相关文章