为何启动Knex事务需await knex.transaction()?
为什么Knex启动事务必须用
await knex.transaction()? 首先得明确:Knex的transaction()方法是异步的——它会立刻向数据库发送BEGIN请求开启事务,但这个请求是异步执行的。如果你直接写const trx = knex.transaction(),拿到的根本不是事务实例,而是一个Promise对象,这时候你调用trx('myTable1').insert(data1)肯定会报错,因为Promise没有这些方法。
你设想的写法为啥行不通?
你期望的这段代码:
const trx = knex.transaction() trx('myTable1').insert(data1) trx('myTable2').insert(data2) await trx.commit
这里的trx是个Promise,不是能操作事务的对象,连调用表操作的资格都没有。而且Knex的设计逻辑是:必须等事务实例完全初始化(也就是数据库确认BEGIN执行成功)后,才能在这个事务上下文里执行后续SQL,不然你的插入操作会脱离事务,变成独立执行的语句,根本达不到原子性的目的。
正确的两种写法
1. 异步/await 风格(也就是你疑惑的需要await的写法)
const trx = await knex.transaction() try { await trx('myTable1').insert(data1) await trx('myTable2').insert(data2) await trx.commit() } catch (err) { await trx.rollback() throw err }
await knex.transaction()会等数据库返回有效的事务实例,确保事务已经成功开启。之后所有通过trx执行的操作都会绑定到这个事务里。每一步加await是为了保证前一个SQL执行完成再走下一个,避免并发问题(当然Knex也会按顺序排队,但显式await更稳妥)。
2. 回调风格(不用显式await,但内部还是异步处理)
knex.transaction(async (trx) => { await trx('myTable1').insert(data1) await trx('myTable2').insert(data2) }) .then(() => console.log('事务提交成功')) .catch((err) => console.log('事务回滚', err))
这种写法下,Knex会自动帮你处理事务的开启、提交和回滚——回调函数顺利执行完就自动commit,一旦抛出异常就自动rollback,不用手动调用这两个方法。
关于底层SQL生成
只要你正确初始化了事务实例,Knex最终生成的SQL就是你想要的样子:
BEGIN INSERT INTO "myTable1" VALUES(...) INSERT INTO "myTable2" VALUES(...) COMMIT
(顺便提一句,你写的VALES是笔误,正确是VALUES)
核心原因总结
knex.transaction()返回的是Promise,必须用await才能拿到真正能操作事务的对象。- 事务开启是异步操作,必须等数据库确认事务开启成功,后续SQL才能绑定到这个事务上下文里,否则操作会脱离事务,失去原子性。
内容的提问来源于stack exchange,提问作者friartuck
相关产品推荐
相关产品推荐

