Transactions
数据库事务(Database transactions)允许你的应用程序以"全有或全无"的方式提交一系列数据库变更。考虑这样一个 HTTP 请求:它创建一个新的订单,并通过 afterChange
钩子更新相关商品的库存数量。如果在更新某个商品时发生错误并向用户返回 HTTP 错误,你肯定不希望新的订单被持久化,也不希望其他商品被修改。这种与数据库的交互可以通过事务无缝处理。
默认情况下,只要配置的数据库支持,Payload 会对所有数据变更操作使用事务。所有数据库变更都被包含在 Payload 操作中,任何抛出的错误都会导致所有变更被回滚而不会被提交。当数据库不支持事务时,Payload 会继续按预期运行,只是不使用事务功能。
注意:
MongoDB 需要使用副本集(replicaset)连接才能支持事务功能。
注意:
SQLite 默认禁用事务。你需要传递 transactionOptions: {}
来启用它们。
对 Payload 的初始请求会启动一个新事务,并将其附加到 req.transactionID
上。如果你的 hook
需要与数据库交互,可以通过在参数中传递 req
来选择使用同一个事务。例如:
const afterChange: CollectionAfterChangeHook = async ({ req }) => {
// 由于 req.transactionID 由 Payload 分配并传递,
// my-slug 只会在整个请求成功时才会被持久化
await req.payload.create({
req,
collection: 'my-slug',
data: {
some: 'data',
},
})
}
异步钩子与事务处理
由于 Payload 钩子可以是异步的,并且可以不等待结果就执行,这可能导致在请求回滚时返回错误的成功响应。如果你的钩子中没有 await
结果,那么你不应该传递 req.transactionID
。
const afterChange: CollectionAfterChangeHook = async ({ req }) => {
// 警告:使用相同的 req 进行异步调用但不等待结果,
// 可能会导致失败时返回 OK 响应,而实际数据并未提交
const dangerouslyIgnoreAsync = req.payload.create({
req,
collection: 'my-slug',
data: {
some: 'other data',
},
})
// 如果此调用失败,它不会回滚其他更改,
// 因为没有传递 req(及其 transactionID)
const safelyIgnoredAsync = req.payload.create({
collection: 'my-slug',
data: {
some: 'other data',
},
})
}
直接事务访问
在编写自定义脚本或端点时,你可能希望直接控制事务。这对于在 Payload 的 Local API 之外与数据库交互非常有用。
以下函数可用于管理事务:
payload.db.beginTransaction
- 启动新会话并返回事务 ID,用于其他 Payload Local API 调用payload.db.commitTransaction
- 接收事务标识符,提交所有更改payload.db.rollbackTransaction
- 接收事务标识符,回滚所有更改
Payload 使用 req
对象将事务 ID 传递给数据库适配器。如果不使用 req
对象,可以创建新对象直接将事务 ID 传递给数据库适配器方法和 Local API 调用。
示例:
import payload from 'payload'
import config from './payload.config'
const standalonePayloadScript = async () => {
// 初始化 Payload
await payload.init({ config })
const transactionID = await payload.db.beginTransaction()
try {
// 使用 Local API 进行更新
await payload.update({
collection: 'posts',
data: {
some: 'data',
},
where: {
slug: { equals: 'my-slug' },
},
req: { transactionID },
})
/*
你可以进行额外的数据库更改或运行其他函数
这些操作需要以"全有或全无"的方式提交
*/
// 提交事务
await payload.db.commitTransaction(transactionID)
} catch (error) {
// 回滚事务
await payload.db.rollbackTransaction(transactionID)
}
}
standalonePayloadScript()
禁用事务
如果你想完全禁用事务,可以在数据库适配器配置中将 transactionOptions
设为 false
。所有官方的 Payload 数据库适配器都支持此选项。
除了允许在适配器级别禁用数据库事务外,你还可以通过在 Local API 的直接调用中添加 disableTransaction: true
参数来阻止 Payload 使用事务。例如:
await payload.update({
collection: 'posts',
data: {
some: 'data',
},
where: {
slug: { equals: 'my-slug' },
},
disableTransaction: true,
})