I encountered a strange behavior with Sequelize when using MySQL. This script creates a table with one column and then seeds a record into this table. It then uses .forEach()
and for (const ... of ...)
to iterate over the results of .findAll()
and update each record (in this case, only one record).
(node.js 20.12.2 + sequelize 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();
});
I have set up 4 console.log()
statements to observe the data changes. In SQLite, the behavior is as expected:
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
However, in MySQL, the forEach
update does not seem to work:
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
In SQLite, both .forEach()
and for-of
loops correctly update the record. However, in MySQL, the .forEach()
loop does not seem to apply the update, while the for-of
loop does.