文档菜单
文档首页
/ / /
Mongoid
/

事务

在本页

  • 使用事务
  • 高级API
  • 低级API

MongoDB服务器4.0版本引入了多文档事务。(更新单个文档中的多个字段在所有MongoDB版本中都是原子性的)。事务需要非独立MongoDB拓扑结构和2.6或更高版本的Ruby驱动程序。高级事务API需要Mongoid版本9.0或更高版本,而低级API需要Mongoid版本6.4或更高版本。

可以通过调用transaction 方法来启动一个事务,该方法可以在 Mongoid 文档类的实例、文档类或 Mongoid 模块上调用

Band.transaction do
Band.create(title: 'Led Zeppelin')
end
band = Band.create(title: 'Deep Purple')
band.transaction do
band.active = false
band.save!
end
Mongoid.transaction do
band.destroy
end

当调用 transaction 方法时,Mongoid 会执行以下操作

  • 在客户端创建一个会话,该会话由 transaction 方法调用的接收者使用;

  • 在会话上启动一个事务;

  • 执行给定的代码块;

  • 如果没有抛出异常,则提交事务;

    • 调用事务内修改的所有对象的 after_commit 回调函数

  • 如果在代码块中抛出异常,则中止事务;

    • 调用事务内修改的所有对象的 after_rollback 回调函数

  • 关闭会话

注意

由于事务与特定客户端相关联,因此只有同一客户端上的操作才会处于事务的作用域内。因此,建议仅在 transaction 方法块内使用使用同一客户端的对象。

class Author
include Mongoid::Document
store_in client: :encrypted_client
end
class User
include Mongoid::Document
store_in client: :encrypted_client
end
class Article
include Mongoid::Document
# This class uses the :default client
end
# Transaction is started on the :encrypted_client
Author.transaction do
# This operation uses the same client, so it is in the transaction
Author.create!
# This operation also uses the same client, so it is in the transaction
User.create!
# This operation uses a different client, so it is NOT in the transaction
Article.create!
end

注意

当在 Mongoid 模块上调用 transaction 方法时,事务将使用 :default 客户端创建。

transaction 方法块内抛出的任何异常都会中止事务。通常情况下,抛出的异常会继续传递,除了 Mongoid::Errors::Rollback。如果您想显式中止事务而不传递异常,则应抛出此错误。

事务API引入了两个新的回调 - after_commitafter_rollback

after_commit 回调在对象被创建、保存或销毁时触发

  • 如果在事务内修改了对象,则在事务提交后触发;

  • 如果在事务外修改了对象,则在对象持久化后触发。

注意

在任何情况下,after_commit 回调只有在所有其他回调成功执行后才触发。因此,如果对象未在事务中修改,则对象可能已持久化,但 after_commit 回调可能未触发(例如,在 after_save 回调中抛出异常)。

after_rollback 回调在事务内创建、保存或销毁的对象触发,如果事务被中止。在没有事务的情况下不会触发 after_rollback

为了开始一个事务,应用程序必须有一个会话.

可以通过在会话上调用 start_transaction 方法来启动一个事务,这可以通过在模型类或实例上调用 with_session 方法来获取

class Person
include Mongoid::Document
end
Person.with_session do |session|
session.start_transaction
end
person = Person.new
person.with_session do |session|
session.start_transaction
end

在启动事务时还可以指定读取关注点、写入关注点和读取优先级

Person.with_session do |session|
session.start_transaction(
read_concern: {level: :majority},
write_concern: {w: 3},
read: {mode: :primary})
end

事务可以是提交或中止。执行这些操作的方法是 commit_transactionabort_transaction,这些方法同样应用于会话实例

Person.with_session do |session|
session.commit_transaction
end
Person.with_session do |session|
session.abort_transaction
end

如果会话以未关闭的事务结束,则事务将中止

事务提交如果在执行过程中失败,可以重试。以下是用Ruby实现重试的代码示例

begin
session.commit_transaction
rescue Mongo::Error => e
if e.label?(Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
retry
else
raise
end
end

请注意,为了在事务中执行操作,操作必须使用与会话启动时相同的客户端。默认情况下,所有操作都在默认客户端上执行

class Person
include Mongoid::Document
end
class Post
include Mongoid::Document
end
Person.with_session do |s|
s.start_transaction
Person.create!
Person.create!
Post.create!
s.commit_transaction
end

要显式使用不同的客户端,请使用with方法

Post.with(client: :other) do
Person.with(client: :other) do
Person.with_session do |s|
s.start_transaction
Person.create!
Person.create!
Post.create!
s.commit_transaction
end
end
end

返回

会话