You can perform a transaction to run a series of operations that do not change any data until the entire transaction is committed. This usage example uses the Core API to perform a transaction.您可以执行事务以运行一系列操作,这些操作在整个事务提交之前不会更改任何数据。此使用示例使用Core API来执行事务。
Tip
To learn more about the performing transactions in the Node.js driver, see the Transactions guide.要了解更多关于在Node.js驱动程序中执行事务的信息,请参阅事务指南。
The Node.js driver also provides the Convenient Transaction API to perform transactions. Node.js驱动程序还提供了方便事务API来执行事务。To learn more about the Convenient Transaction API, see the Use the Convenient Transaction API usage example.要了解有关便捷事务API的更多信息,请参阅使用便捷事务API用法示例。
Example示例
Consider a situation in which a customer purchases items from your online shop. To record the purchase, your application must update your inventory and the customer's orders. Your application also needs to save the order details.考虑一个客户从你的网上商店购买商品的情况。要记录购买,您的应用程序必须更新您的inventory和客户的订单。您的应用程序还需要保存订单详细信息。
The following table describes the collections that store purchase data and how a purchase changes the data in each collection.下表描述了存储采购数据的集合以及采购如何更改每个集合中的数据。
orders | insert | |
customers | update or upsert | _id from the order document to the order history in the customer document_id附加到客户文档中的订单历史记录中 |
inventory | update |
Sample Data样本数据
The code examples use the following sample data in the 代码示例使用testdb database:testdb数据库中的以下示例数据:
Documents in thecustomerscollection that describe customers and their past orderscustomers集合中描述客户及其过去订单的文档Documents in theinventorycollection that include quantities and descriptions of all itemsinventory集合中的文件,包括所有物品的数量和描述
The following document is in the 以下文件在customers collection:customers集合中:
{ _id: 98765, orders: [] }
The inventory collection contains the following documents:inventory集合包含以下文档:
{ item: "sunblock", item_id: 5432, qty: 85 },
{ item: "beach towel", item_id: 7865, qty: 41 }
You store purchase records in the 您将购买记录存储在orders collection of the testdb database. This collection is empty, as there have been no purchases.testdb数据库的orders集合中。此集合是空的,因为没有购买。
The code examples use the 代码示例使用cart and payment variables to represent a sample list of items purchased and the order payment details. The following code describes the contents of the cart and payment variables:cart(购物车)和payment(付款)变量来表示所购商品的示例列表和订单付款详细信息。以下代码描述了购物车和支付变量的内容:
const cart = [
{ item: 'sunblock', item_id: 5432, qty: 1, price: 5.19 },
{ item: 'beach towel', item_id: 7865, qty: 2, price: 15.99 }
];
const payment = { customer: 98765, total: 37.17 };Implementation实施
The code example in this section demonstrates how to use the Core API to perform a multi-document transaction in a session. In this example, the transaction makes the changes needed when a customer purchases items from your shop.本节中的代码示例演示了如何使用Core API在会话中执行多文档事务。在这个例子中,当客户从你的商店购买商品时,事务会进行所需的更改。
This example code performs a transaction through the following actions:此示例代码通过以下操作执行事务:
Calls the调用startSession()method to create a new sessionstartSession()方法以创建新会话Calls the使用options参数调用startTransaction()method with an options parameter to create a new transactionstartTransaction()方法以创建新事务Performs the following operations within the transaction:在事务中执行以下操作:Inserts a document to the在orderscollection that contains information about the purchase and customerorders集合中插入一个文档,其中包含有关采购和客户的信息Updates the如果有足够的库存来完成采购,则更新inventorycollection if there is sufficient inventory to fulfill the purchaseinventory集合Ends the transaction and throws an exception if there isn't sufficient inventory for any item in the order如果订单中的任何商品没有足够的inventory,则结束事务并抛出异常Adds the ID of the order to the list of past orders for the customer将订单的ID添加到客户的过去订单列表中Returns a message acknowledging that the transaction committed successfully with a copy of the purchase record返回一条消息,确认事务已成功提交,并附上购买记录的副本
Calls the如果所有操作都成功完成,则调用commitTransaction()method to commit the transaction if all operations complete successfullycommitTransaction()方法提交事务Implements a实现一个包含错误处理逻辑的catchblock that contains error-handling logiccatch块Calls the调用abortTransaction()method to end the transactionabortTransaction()方法结束事务Calls the调用endSession()method to end the sessionendSession()方法结束会话
async function placeOrder(client, cart, payment) {
const transactionOptions = {
readConcern: { level: 'snapshot' },
writeConcern: { w: 'majority' },
readPreference: 'primary'
};
// Start the session开始会话
const session = client.startSession();
try {
// Start the transaction in the session, specifying the transaction options在会话中启动事务,指定事务选项
session.startTransaction(transactionOptions);
const ordersCollection = client.db('testdb').collection('orders');
/* Within the session, insert an order that contains information about the customer, items purchased, and the total payment在会话中,插入一个订单,其中包含有关客户、购买的商品和总付款的信息 */
const orderResult = await ordersCollection.insertOne(
{
customer: payment.customer,
items: cart,
total: payment.total,
},
{ session }
);
const inventoryCollection = client.db('testdb').collection('inventory');
for (const item of order) {
/* Update the inventory for the purchased items. End the transaction if the quantity of an item in the inventory is insufficient to complete the purchase.更新采购物品的inventory。如果inventory中某个项目的数量不足以完成采购,则结束事务。 */
const inStock = await inventoryCollection.findOneAndUpdate(
{
item_id: item.item_id,
item_id: { $gte: item.qty }
},
{ $inc: { 'qty': -item.qty }},
{ session }
)
if (inStock === null) {
throw new Error('Insufficient quantity or item ID not found.');
}
}
const customerCollection = client.db('testdb').collection('customers');
// Within the session, add the order details to the "orders" array of the customer document在会话中,将订单详细信息添加到客户文档的`orders`数组中
await customerCollection.updateOne(
{ _id: payment.customer },
{ $push: { orders: orderResult.insertedId }},
{ session }
);
// Commit the transaction to apply all updates performed within it提交事务以应用其中执行的所有更新
await session.commitTransaction();
console.log('Transaction successfully committed.');
} catch (error) {
/* Handle any exceptions thrown during the transaction and end the transaction. Roll back all the updates performed in the transaction.处理事务过程中抛出的任何异常并结束事务。回滚事务中执行的所有更新。 */
if (error instanceof MongoError && error.hasErrorLabel('UnknownTransactionCommitResult')) {
// Add your logic to retry or handle the error添加您的逻辑以重试或处理错误
}
else if (error instanceof MongoError && error.hasErrorLabel('TransientTransactionError')) {
// Add your logic to retry or handle the error添加您的逻辑以重试或处理错误
} else {
console.log('An error occured in the transaction, performing a data rollback:' + error);
}
await session.abortTransaction();
} finally {
// End the session结束会话
await session.endSession();
}
}
Tip
Explicit Resource Management显式资源管理
The Node.js driver natively supports explicit resource management for Node.js驱动程序原生支持MongoClient, ClientSession, ChangeStreams, and cursors. This feature is experimental and subject to change. MongoClient、ClientSession、ChangeStreams和游标的显式资源管理。此功能是实验性的,可能会发生变化。To learn how to use explicit resource management, see the v6.9 Release Notes.要了解如何使用显式资源管理,请参阅v6.9发行说明。
Transaction Results事务结果
This section describes the data changes created by the transaction.本节介绍事务创建的数据更改。
The customers collection contains the customer document with an order _id appended to the orders field:customers集合包含客户文档,订单字段后附加了订单_id:
{
"_id": 98765,
"orders": [
"61dc..."
]
}
The inventory collection contains updated quantities for the items "sunblock" and "beach towel":inventory集合包含"sunblock"(防晒霜)和"beach towel"(沙滩巾)项目的更新数量:
[
{
"_id": ...,
"item": "sunblock",
"item_id": 5432,
"qty": 84
},
{
"_id": ...,
"item": "beach towel",
"item_id": 7865,
"qty": 39
}
]
The orders collection contains the order and payment information:orders集合包含订单和付款信息:
[
{
"_id": "...",
"customer": 98765,
"items": [
{
"item": "sunblock",
"item_id": 5432,
"qty": 1,
"price": 5.19
},
{
"item": "beach towel",
"item_id": 7865,
"qty": 2,
"price": 15.99
}
],
"total": 37.17
}
]API Documentation文档
To learn more about any of the methods or types discussed in this usage example, see the following API Documentation:要了解有关本使用示例中讨论的任何方法或类型的更多信息,请参阅以下API文档: