数据库事务
页面摘要: 数据库事务将多个操作组合在一起,使它们作为一个单元成功提交或回滚。实验性的
strapi.db.transaction辅助函数暴露了供包裹服务调用使用的trx、commit 和 rollback 工具。
这是一个实验性功能,未来版本中可能会发生变化。
Strapi 5 提供了一个 API,可将一组操作包裹在事务中,以确保数据的完整性。
事务是作为单一单元一起执行的一组操作。如果任何操作失败,整个事务失败,数据将回滚到其先前状态。如果所有操作都成功,事务被提交,数据将永久保存到数据库。
用法
通过将处理函数传入 strapi.db.transaction 来处理事务:
await strapi.db.transaction(async ({ trx, rollback, commit, onCommit, onRollback }) => {
// It will implicitly use the transaction
const article = await strapi.documents('api::article.article').create({
data: { title: 'My Article', slug: 'my-article' },
});
await strapi.documents('api::log.log').create({
data: { action: 'article_created', targetId: article.documentId },
});
});
事务处理函数执行后,如果所有操作都成功,事务将被提交。如果任何操作抛出异常,事务将回滚,数据恢复到其先前状态。
在 strapi.db.transaction 块内执行的每个 文档服务 API、strapi.db.query 和 strapi.entityService 操作都会隐式使用事务。Strapi 使用 AsyncLocalStorage 来传播事务上下文,因此无需将这些 API 显式传入事务对象。
事务处理函数的属性
处理函数接收一个包含以下属性的对象:
| 属性 | 描述 |
|---|---|
trx | 事务对象。可用于在事务内执行 knex 查询。 |
commit | 用于提交事务的函数。 |
rollback | 用于回滚事务的函数。 |
onCommit | 用于注册在事务提交后执行的回调的函数。 |
onRollback | 用于注册在事务回滚后执行的回调的函数。 |
嵌套事务
事务可以嵌套。当事务嵌套时,内部事务会在外部事务提交或回滚时一并提交或回滚。
await strapi.db.transaction(async () => {
// It will implicitly use the transaction
await strapi.documents('api::article.article').create({
data: { title: 'My Article', slug: 'my-article' },
});
// Nested transactions will implicitly use the outer transaction
await strapi.db.transaction(async () => {
await strapi.documents('api::category.category').create({
data: { name: 'Tech' },
});
});
});
onCommit 与 onRollback
onCommit 和 onRollback 钩子可用于在事务提交或回滚后执行代码。
await strapi.db.transaction(async ({ onCommit, onRollback }) => {
// It will implicitly use the transaction
const user = await strapi.documents('api::user.user').create({
data: { username: 'johndoe', email: 'john@example.com' },
});
onCommit(() => {
// This will be executed after the transaction is committed,
// e.g., send a welcome email
});
onRollback(() => {
// This will be executed after the transaction is rolled back,
// e.g., log the failure
});
});
使用 knex 查询
事务也可以与 knex 查询一起使用,但在这些情况下必须显式调用 .transacting(trx)。
await strapi.db.transaction(async ({ trx, rollback, commit }) => {
await knex('users').where('id', 1).update({ name: 'foo' }).transacting(trx);
});
何时使用事务
当事务的多个操作应一起执行且彼此执行相互依赖时,应使用事务。例如,在创建新用户时,应在数据库中创建该用户,并向用户发送欢迎邮件。如果邮件发送失败,则不应在数据库中创建该用户。
何时不应使用事务
不应将事务用于彼此不依赖的操作,因为这可能导致性能下降。
事务的潜在问题
在事务内执行多个操作可能导致锁定,从而阻止其他进程的事务执行,直到原事务完成。
此外,如果事务未正确提交或回滚,可能会停滞。
例如,如果打开了事务,但你的代码中存在未关闭它的路径,该事务将无限期保持打开状态,并可能导致不稳定,直到服务器重启并强制关闭连接。这些问题可能难以调试,因此在必要情况下使用事务时务必谨慎。