knexjs、typescript 和 mariadb 的交易问题
Transaction issue with knexjs, typescript and mariadb
我想要一个事务,在 typescript 中,以刷新 table 中的一些数据。为此,需要应用以下步骤:
- 截断 table
中的所有记录
- 将 AUTO_INCREMENT 设置为 1
- 在 table
中插入新记录
如果出现问题,我想回滚事务而不更改数据库中的现有记录table。
我尝试了不同的方法,但我想我遗漏了一些东西,我希望有人能发现我做错了什么。
第一次尝试
await knex.transaction(async (trx) => {
await knex(tableName).truncate().transacting(trx);
await knex.raw(`ALTER TABLE ${tableName} AUTO_INCREMENT=1;`).transacting(trx);
await knex(tableName).insert(data).transacting(trx);
await trx.commit();
});
第二次尝试
await knex.transaction(async (trx) => {
try {
await knex(table).truncate().transacting(trx);
await knex.raw(`ALTER TABLE ${table} AUTO_INCREMENT=1;`).transacting(trx);
await knex(table).insert(data).transacting(trx);
await trx.commit();
} catch (e) {
await trx.rollback();
}
});
第三次尝试
const promisify = (fn: any) => new Promise((resolve, reject) => fn(resolve));
const trx: knex.Transaction = <knex.Transaction> await promisify(this.knex.transaction);
try {
await this.knex(this.table).truncate().transacting(trx);
await this.knex.raw(`ALTER TABLE ${this.table} AUTO_INCREMENT=1;`).transacting(trx);
await this.knex(this.table).insert(data).transacting(trx);
await trx.commit();
return Promise.resolve(data);
} catch (e) {
await trx.rollback();
return Promise.reject(e);
}
任何想法都会非常有用。
对于 mysql,所有模式更改 DDL 查询都会隐式提交并且无法回滚。所以你需要改变你的实现以其他方式保证数据库的完整性。
https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html
https://dev.mysql.com/doc/refman/8.0/en/implicit-commit.html
不要那样做。相反:
CREATE TABLE new LIKE real;
populate `new`
RENAME TABLE real TO old, new TO real;
DROP TABLE old;
如果CREATE
或populate
失败,则放弃任务;没有造成伤害。 (嗯,需要DROP TABLE real
。)
RENAME TABLE
非常快,而且是原子的,不太可能失败。
这种方法的优点是,对于其他查询,real
table 可用并且在整个时间内完全填充。
我想要一个事务,在 typescript 中,以刷新 table 中的一些数据。为此,需要应用以下步骤:
- 截断 table 中的所有记录
- 将 AUTO_INCREMENT 设置为 1
- 在 table 中插入新记录
如果出现问题,我想回滚事务而不更改数据库中的现有记录table。
我尝试了不同的方法,但我想我遗漏了一些东西,我希望有人能发现我做错了什么。
第一次尝试
await knex.transaction(async (trx) => {
await knex(tableName).truncate().transacting(trx);
await knex.raw(`ALTER TABLE ${tableName} AUTO_INCREMENT=1;`).transacting(trx);
await knex(tableName).insert(data).transacting(trx);
await trx.commit();
});
第二次尝试
await knex.transaction(async (trx) => {
try {
await knex(table).truncate().transacting(trx);
await knex.raw(`ALTER TABLE ${table} AUTO_INCREMENT=1;`).transacting(trx);
await knex(table).insert(data).transacting(trx);
await trx.commit();
} catch (e) {
await trx.rollback();
}
});
第三次尝试
const promisify = (fn: any) => new Promise((resolve, reject) => fn(resolve));
const trx: knex.Transaction = <knex.Transaction> await promisify(this.knex.transaction);
try {
await this.knex(this.table).truncate().transacting(trx);
await this.knex.raw(`ALTER TABLE ${this.table} AUTO_INCREMENT=1;`).transacting(trx);
await this.knex(this.table).insert(data).transacting(trx);
await trx.commit();
return Promise.resolve(data);
} catch (e) {
await trx.rollback();
return Promise.reject(e);
}
任何想法都会非常有用。
对于 mysql,所有模式更改 DDL 查询都会隐式提交并且无法回滚。所以你需要改变你的实现以其他方式保证数据库的完整性。
https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html https://dev.mysql.com/doc/refman/8.0/en/implicit-commit.html
不要那样做。相反:
CREATE TABLE new LIKE real;
populate `new`
RENAME TABLE real TO old, new TO real;
DROP TABLE old;
如果CREATE
或populate
失败,则放弃任务;没有造成伤害。 (嗯,需要DROP TABLE real
。)
RENAME TABLE
非常快,而且是原子的,不太可能失败。
这种方法的优点是,对于其他查询,real
table 可用并且在整个时间内完全填充。