更新记录时,MySQL 中的 Sequelize .forEach() 与 SQLite 的行为不一致

问题描述 投票:0回答:1

我在使用 MySQL 时遇到了 Sequelize 的奇怪行为。该脚本创建一个包含一列的表,然后将一条记录播种到该表中。然后,它使用

.forEach()
for (const ... of ...)
迭代
.findAll()
的结果并更新每条记录(在本例中,仅更新一条记录)。

(node.js 20.12.2 + 续集 6.37.3)

const Sequelize = require('sequelize');

let db;
if (0) {
  // SQLite case:
  db = new Sequelize({
    dialect: 'sqlite',
    storage: ':memory:',
  });
} else {
  // MySQL case:
  db = new Sequelize({
    dialect: 'mysql',
    hostname: '127.0.0.1',
    username: 'root',
    password: process.env.DB_PASSWORD,
    database: 'test',
  });
}

(async () => {
  const Foo = db.define('Foo', {
    bar: Sequelize.DataTypes.STRING,
  });

  // Creating table for database:
  await Foo.sync({
    force: true
  });

  // Seed data.
  await Foo.create({
    bar: 'abc',
  });

  // forEach case:
  let items = await Foo.findAll();
  console.log(items[0].bar);

  items.forEach(async item => {
    item.bar = 'forEach';
    await item.save();
  });
  items = await Foo.findAll();
  console.log(items[0].bar);

  // for-of case:
  items = await Foo.findAll();
  console.log(items[0].bar);

  for (const item of items) {
    item.bar = 'for-of';
    await item.save();
  };
  items = await Foo.findAll();
  console.log(items[0].bar);
})().finally(() => {
  process.exit();
});

我设置了4个

console.log()
语句来观察数据变化。在 SQLite 中,行为符合预期:

Executing (default): DROP TABLE IF EXISTS `Foos`;
Executing (default): CREATE TABLE IF NOT EXISTS `Foos` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `bar` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);
Executing (default): PRAGMA INDEX_LIST(`Foos`)
Executing (default): INSERT INTO `Foos` (`id`,`bar`,`createdAt`,`updatedAt`) VALUES (NULL,$1,$2,$3);
Executing (default): SELECT `id`, `bar`, `createdAt`, `updatedAt` FROM `Foos` AS `Foo`;
abc
Executing (default): SELECT `id`, `bar`, `createdAt`, `updatedAt` FROM `Foos` AS `Foo`;
Executing (default): UPDATE `Foos` SET `bar`=$1,`updatedAt`=$2 WHERE `id` = $3
forEach
Executing (default): SELECT `id`, `bar`, `createdAt`, `updatedAt` FROM `Foos` AS `Foo`;
forEach
Executing (default): UPDATE `Foos` SET `bar`=$1,`updatedAt`=$2 WHERE `id` = $3
Executing (default): SELECT `id`, `bar`, `createdAt`, `updatedAt` FROM `Foos` AS `Foo`;
for-of

但是,在MySQL中,

forEach
更新似乎不起作用:

Executing (default): DROP TABLE IF EXISTS `Foos`;
Executing (default): CREATE TABLE IF NOT EXISTS `Foos` (`id` INTEGER NOT NULL auto_increment , `bar` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM `Foos`
Executing (default): INSERT INTO `Foos` (`id`,`bar`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?,?);
Executing (default): SELECT `id`, `bar`, `createdAt`, `updatedAt` FROM `Foos` AS `Foo`;
abc
Executing (default): SELECT `id`, `bar`, `createdAt`, `updatedAt` FROM `Foos` AS `Foo`;
Executing (default): UPDATE `Foos` SET `bar`=?,`updatedAt`=? WHERE `id` = ?
abc
Executing (default): SELECT `id`, `bar`, `createdAt`, `updatedAt` FROM `Foos` AS `Foo`;
forEach
Executing (default): UPDATE `Foos` SET `bar`=?,`updatedAt`=? WHERE `id` = ?
Executing (default): SELECT `id`, `bar`, `createdAt`, `updatedAt` FROM `Foos` AS `Foo`;
for-of

在 SQLite 中,

.forEach()
for-of
循环都可以正确更新记录。但是,在 MySQL 中,
.forEach()
循环似乎不应用更新,而
for-of
循环则应用更新。

mysql node.js sequelize.js
1个回答
0
投票

脚本的问题与 forEach 循环内的异步行为有关。在 JavaScript 中,forEach 无法正确处理异步操作,并且在进入下一次迭代之前它不会等待循环内的等待完成。这可能会导致意外结果或不完整的操作。查看 Using async/await with a forEach Loop 的答案以了解详细信息。

items.forEach(async item => {
  item.bar = 'forEach';
  await item.save();
});

您可以使用以下代码片段轻松修改它,结果应该按您的预期工作。

const promises = items.map(async (item) => {
  item.bar = 'forEach';
  await item.save();
});
await Promise.all(promises);
© www.soinside.com 2019 - 2024. All rights reserved.