Docs 菜单
Docs 主页
/ / /
Mongoid
/

事务

在此页面上

  • 使用事务
  • 更高级别的 API
  • 较低级别的 API

MongoDB 服务器 4.0版本引入了多文档事务。 (在所有版本的MongoDB中,对单个文档中多个字段的更新都是原子性的)。 事务需要非独立运行的MongoDB拓扑结构和Ruby驾驶员版本2.6或更高版本。 较高级别的ACID 事务API需要 Mongoid 版本9.0或更高版本,而较低级别的API需要 Mongoid 版本6 。 4或更高版本。

可以通过对 Mongoid文档类的实例、Mongoid文档类、或Mongoid模块调用 transaction方法来启动ACID 事务:

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方法调用的接收者使用;

  • 在会话上启动ACID 事务;

  • 执行给定区块;

  • 如果区块中没有引发异常,则提交ACID 事务;

    • 为事务中修改的所有对象调用after_commit回调

  • 如果区块中出现异常,则中止ACID 事务;

    • 为事务中修改的所有对象调用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除外。 如果要显式中止事务而不传递异常,则应引发此错误。

Transaction API引入了两个新的回调 — after_commitafter_rollback

after_commit 为创建、保存或销毁的对象触发回调:

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

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

注意

在任何情况下,只有在所有其他回调成功执行后才会触发after_commit回调。 因此,如果在没有ACID 事务的情况下修改了对象,则可能是对象已持久保存,但未触发after_commit回调(示例,在after_save回调中引发异常)。

after_rollback 如果ACID 事务中止,则会为在ACID 事务中创建、保存或销毁的对象触发回调。 如果没有ACID 事务,则永远不会触发after_rollback

要启动ACID 事务,应用程序必须具有会话。

可以通过在会话上调用start_transaction方法来启动ACID 事务,可以通过在模型类或实例上调用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

还可以在启动事务(transaction)时指定读关注(read concern)、写关注(write concern)和读取偏好(read preference):

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

可以提交或中止ACID 事务。 执行此操作的相应方法是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

后退

会话