事务
Overview
在本指南中,您可以学习;了解如何使用 Laravel MongoDB在MongoDB中执行ACID 事务。 事务允许您运行一系列写入操作,这些操作仅在提交ACID 事务后更新数据。
如果ACID 事务失败,为 Laravel 集成管理MongoDB操作的MongoDB PHP库可确保MongoDB在ACID 事务中所做的所有更改变得可见之前将其丢弃。 事务的这种确保应用或丢弃ACID 事务中的所有更改的属性称为原子性。
MongoDB 以原子方式对单个文档执行写入操作。 如果您需要对多个文档进行写入操作的原子性,或者需要跨多个文档执行操作的数据一致性,请在多文档事务中运行这些操作。
多文档事务符合 ACID ,因为 MongoDB 保证事务操作中的数据保持一致,即使驱动程序遇到意外错误。
要学习;了解有关MongoDB中事务的更多信息,请参阅服务器手册中的事务。
本指南包含以下部分:
要求和限制
要在 MongoDB 中执行事务,必须使用以下 MongoDB 版本和拓扑:
MongoDB 4.0 或更高版本
副本集部署或分片集群
MongoDB Server和 Laravel 集成具有以下限制:
在 MongoDB 4.2及更早版本中,事务中执行的写入操作必须针对现有集合。 在 MongoDB 4.4及更高版本中,当您在事务中执行写入操作时,服务器会根据需要自动创建集合。 要了解有关此限制的更多信息,请参阅 MongoDB Server手册中的 在事务中创建集合和索引 。
MongoDB 不支持嵌套事务。 如果您尝试在另一个事务中启动事务,则扩展会引发
RuntimeException
。 要了解有关此限制的更多信息,请参阅 MongoDB Server手册中的 事务和会话 。Laravel MongoDB不支持数据库测试特征
Illuminate\Foundation\Testing\DatabaseTransactions
和Illuminate\Foundation\Testing\RefreshDatabase
。 作为一种解决方法,您可以使用Illuminate\Foundation\Testing\DatabaseMigrations
特征创建迁移,以便在每次测试后重置数据库。
在回调中运行事务
本部分介绍如何在回调中运行事务。
使用这种运行事务的方法时,回调方法中的所有代码都作为单个事务运行。
在以下示例中,事务由写入操作组成,这些写入操作会将资金从Account
模型表示的银行帐户转移到另一个帐户:
DB::transaction(function () { $transferAmount = 200; $sender = Account::where('number', 223344)->first(); $sender->balance -= $transferAmount; $sender->save(); $receiver = Account::where('number', 776655)->first(); $receiver->balance += $transferAmount; $receiver->save(); });
您可以选择将重试失败ACID 事务的最大次数作为第二个参数传递,如以下代码示例:
DB::transaction(function() { // transaction code }, attempts: 5, );
开始并提交事务
本节介绍如何启动和提交事务。
要使用这种启动和提交事务的方法,请调用DB::beginTransaction()
方法启动事务。 然后,调用DB::commit()
方法结束事务,这会应用在事务中执行的所有更新。
在以下示例中,第一个账户的余额转移到第二个账户,然后删除第一个账户:
DB::beginTransaction(); $oldAccount = Account::where('number', 223344)->first(); $newAccount = Account::where('number', 776655)->first(); $newAccount->balance += $oldAccount->balance; $newAccount->save(); $oldAccount->delete(); DB::commit();
回滚事务
本节介绍如何回滚事务。 回滚会恢复在该事务中执行的所有写入操作。 这意味着数据将恢复到事务之前的状态。
要执行回滚,请在提交事务之前随时调用DB::rollback()
函数。
在以下示例中,事务由写入操作组成,这些写入操作将资金从Account
模型表示的一个帐户转移到多个其他帐户。 如果发送者帐户资金不足,则回滚事务,并且不会更新任何模型:
DB::beginTransaction(); $sender = Account::where('number', 223344)->first(); $receiverA = Account::where('number', 776655)->first(); $receiverB = Account::where('number', 990011)->first(); $amountA = 100; $amountB = 200; $sender->balance -= $amountA; $receiverA->balance += $amountA; $sender->balance -= $amountB; $receiverB->balance += $amountB; if ($sender->balance < 0) { // insufficient balance, roll back the transaction DB::rollback(); } else { DB::commit(); }