sequelize.sync()如何工作,特别是强制选项?

Nar*_*esh 55 sequelize.js

sequelize.sync()上的force选项有什么作用?

sequelize.sync({
    force: true
});
Run Code Online (Sandbox Code Playgroud)

具体来说,我有兴趣知道什么力:假吗?它不会将模式与数据库同步吗?

是否有任何正式的续集文档?我只能在文档中找到示例.

Jan*_*ier 45

(或多或少)正式文档和API参考可以在http://sequelize.readthedocs.org/en/latest/api/sequelize/#sync找到

对于你的问题:在尝试创建表之前force: true添加一个DROP TABLE IF EXISTS- 如果你强制,现有的表将被覆盖.

  • 这不是在回答问题。问题问的是 `force: false` 的作用,而不是 `force: true` 的作用。 (7认同)

Tyl*_*ier 33

OP正在询问是什么force: false,这也是我想知道的,所以这是剩下的.

对我来说,主要的问题是个别领域没有同步(这是我希望的,来自Waterline ORM).意思是,如果您有force: false并且表存在,则不会执行任何字段添加/修改/删除.

  • beforeSync 钩子运行
  • 如果表被删除 force: true
  • 表是用.创建的 if not exists
  • 必要时添加索引
  • afterSync 钩子运行

以下是来自github repo的当前代码供参考:

lib.model.js

Model.prototype.sync = function(options) {
  options = options || {};
  options.hooks = options.hooks === undefined ? true : !!options.hooks;
  options = Utils._.extend({}, this.options, options);

  var self = this
    , attributes = this.tableAttributes;

  return Promise.try(function () {
    if (options.hooks) {
      return self.runHooks('beforeSync', options);
    }
  }).then(function () {
    if (options.force) {
      return self.drop(options);
    }
  }).then(function () {
    return self.QueryInterface.createTable(self.getTableName(options), attributes, options, self);
  }).then(function () {
    return self.QueryInterface.showIndex(self.getTableName(options), options);
  }).then(function (indexes) {
    // Assign an auto-generated name to indexes which are not named by the user
    self.options.indexes = self.QueryInterface.nameIndexes(self.options.indexes, self.tableName);

    indexes = _.filter(self.options.indexes, function (item1) {
      return !_.some(indexes, function (item2) {
        return item1.name === item2.name;
      });
    });

    return Promise.map(indexes, function (index) {
      return self.QueryInterface.addIndex(self.getTableName(options), _.assign({logging: options.logging, benchmark: options.benchmark}, index), self.tableName);
    });
  }).then(function () {
    if (options.hooks) {
      return self.runHooks('afterSync', options);
    }
  }).return(this);
};
Run Code Online (Sandbox Code Playgroud)


Cir*_*四事件 6

最小可运行示例

const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize({
  dialect: 'sqlite',
  storage: 'tmp.sqlite',
});
(async () => {
const IntegerNames = sequelize.define('IntegerNames', {
  value: { type: DataTypes.INTEGER, },
  name: { type: DataTypes.STRING, },
}, {});
//await IntegerNames.sync({force: true})
await IntegerNames.create({value: 2, name: 'two'});
await sequelize.close();
})();
Run Code Online (Sandbox Code Playgroud)

设置:

npm install sequelize@6.5.1 sqlite3@5.0.2.
Run Code Online (Sandbox Code Playgroud)

在标准输出上我们可以看到它所做的查询:

Executing (default): DROP TABLE IF EXISTS `IntegerNames`;
Executing (default): CREATE TABLE IF NOT EXISTS `IntegerNames` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `value` INTEGER, `name` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);
Executing (default): PRAGMA INDEX_LIST(`IntegerNames`)
Executing (default): INSERT INTO `IntegerNames` (`id`,`value`,`name`,`createdAt`,`updatedAt`) VALUES (NULL,$1,$2,$3,$4);
Run Code Online (Sandbox Code Playgroud)

如果我们使用相反的方法,我们会得到相同的结果,只是开头force: false没有:DROP

Executing (default): CREATE TABLE IF NOT EXISTS `IntegerNames` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `value` INTEGER, `name` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);
Executing (default): PRAGMA INDEX_LIST(`IntegerNames`)
Executing (default): INSERT INTO `IntegerNames` (`id`,`value`,`name`,`createdAt`,`updatedAt`) VALUES (NULL,$1,$2,$3,$4);
Run Code Online (Sandbox Code Playgroud)

如果我们删除同步,则根本不会创建表:

Executing (default): INSERT INTO `IntegerNames` (`id`,`value`,`name`,`createdAt`,`updatedAt`) VALUES (NULL,$1,$2,$3,$4);
Run Code Online (Sandbox Code Playgroud)

结论:force

  • force: false:如果该表存在,则由于IF NOT EXISTSin不会发生任何事情CREATE TABLE IF NOT EXISTS。因此,任何表架构修改都会被忽略。
  • force: true:首先删除表,因此任何架构更改都会生效CREATE TABLE IF NOT EXISTS。但你会丢失所有数据

alter: true, force: false实际上尝试保留数据并修改数据库

使用force: true,数据库将被删除并重新创建,因此它当然会匹配最新的架构,但您会丢失所有数据。

为了尝试保留现有数据并更新架构,我们必须使用alter: trueexcept force: false。

以下工作和标准输出显示了用于通过创建临时数据库使其工作的复杂查询序列IntegerNames_backup:

const assert = require('assert')
const { Sequelize, DataTypes } = require('sequelize');

function getSequelize() {
  if (process.argv[2] === 'p') {
    return new Sequelize('tmp', undefined, undefined, {
      dialect: 'postgres',
      host: '/var/run/postgresql',
    })
  } else {
    return new Sequelize({
      dialect: 'sqlite',
      storage: 'tmp.sqlite',
    })
  }
}

(async () => {
{
  const sequelize = getSequelize()
  const IntegerNames = sequelize.define('IntegerNames', {
    value: { type: DataTypes.INTEGER, },
    name: { type: DataTypes.STRING, },
  }, {});
  await IntegerNames.sync({force: true})
  await IntegerNames.create({value: 2, name: 'two'});
  await IntegerNames.create({value: 3, name: 'three'});
  await sequelize.close();
}

// Alter by adding column.
{
  const sequelize = getSequelize()
  const IntegerNames = sequelize.define('IntegerNames', {
    value: { type: DataTypes.INTEGER, },
    name: { type: DataTypes.STRING, },
    nameEs: { type: DataTypes.STRING, },
  }, {});
  await IntegerNames.sync({
    alter: true,
    force: false,
  })
  await IntegerNames.create({value: 5, name: 'five' , nameEs: 'cinco'});
  await IntegerNames.create({value: 7, name: 'seven', nameEs: 'siete'});
  const integerNames = await IntegerNames.findAll({
    order: [['value', 'ASC']],
  });
  assert(integerNames[0].value  === 2);
  assert(integerNames[0].name   === 'two');
  assert(integerNames[0].nameEs === null);
  assert(integerNames[1].name   === 'three');
  assert(integerNames[1].nameEs === null);
  assert(integerNames[2].name   === 'five');
  assert(integerNames[2].nameEs === 'cinco');
  assert(integerNames[3].name   === 'seven');
  assert(integerNames[3].nameEs === 'siete');
  await sequelize.close();
}
})();
Run Code Online (Sandbox Code Playgroud)

如果我们删除alter: true它,它就会崩溃,因为新列不存在:

SequelizeDatabaseError: SQLITE_ERROR: table IntegerNames has no column named nameEs
Run Code Online (Sandbox Code Playgroud)

在 PostgreSQL 上它实际上会生成ALTER语句,这非常棒:

Executing (default): ALTER TABLE "public"."IntegerNames" ADD COLUMN "nameEs" VARCHAR(255);
Executing (default): ALTER TABLE "IntegerNames" ALTER COLUMN "value" DROP NOT NULL;ALTER TABLE "IntegerNames" ALTER COLUMN "value" DROP DEFAULT;ALTER TABLE "IntegerNames" ALTER COLUMN "value" TYPE INTEGER;
Executing (default): ALTER TABLE "IntegerNames" ALTER COLUMN "name" DROP NOT NULL;ALTER TABLE "IntegerNames" ALTER COLUMN "name" DROP DEFAULT;ALTER TABLE "IntegerNames" ALTER COLUMN "name" TYPE VARCHAR(255);
Executing (default): ALTER TABLE "IntegerNames" ALTER COLUMN "createdAt" SET NOT NULL;ALTER TABLE "IntegerNames" ALTER COLUMN "createdAt" DROP DEFAULT;ALTER TABLE "IntegerNames" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE;
Executing (default): ALTER TABLE "IntegerNames" ALTER COLUMN "updatedAt" SET NOT NULL;ALTER TABLE "IntegerNames" ALTER COLUMN "updatedAt" DROP DEFAULT;ALTER TABLE "IntegerNames" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE;
Run Code Online (Sandbox Code Playgroud)

我们看到它只是ALTER单独为每个列运行一个语句,即使该列已经存在,在这种情况下它不会以任何方式修改。

在 SQLite 上,alter 的工作方式是创建一个新表,然后通过以下方式将所有数据从旧表移动到新表:

Executing (default): CREATE TABLE IF NOT EXISTS `IntegerNames_backup` (`id` INTEGER PRIMARY KEY, `value` INTEGER, `name` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `nameEs` VARCHAR(255));
Executing (default): INSERT INTO `IntegerNames_backup` SELECT `id`, `value`, `name`, `createdAt`, `updatedAt`, `nameEs` FROM `IntegerNames`;
Executing (default): DROP TABLE `IntegerNames`;
Executing (default): CREATE TABLE IF NOT EXISTS `IntegerNames` (`id` INTEGER PRIMARY KEY, `value` INTEGER, `name` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `nameEs` VARCHAR(255));
Run Code Online (Sandbox Code Playgroud)

TODO 为什么不使用ALTER语句?SQLite 似乎支持它们:How do you rename a table in SQLite 3.0? 然后,这打破了续集不会根据需要自动删除和重新创建的约束:如果外键约束处于活动状态,则使用续集迁移更改表的过程是非常悲伤的。

结论:alter

alter: true允许我们轻松添加新列,或修改现有列的属性。

这不能完全取代迁移的原因:Sequelize:更改生产中的模型架构

  • 它根本无法处理表或列重命名,它只是没有足够的信息来知道新旧列是否相同。我认为它也不会处理 PostgreSQL 上的列删除。
  • 它无法智能地添加/删除/修改迁移期间可能需要的数据(显然)。作为一个简单的示例,添加NOT NULL约束可能需要您使用某些选择值填充一些先前存在 NULL 错误的行。

  • 2022 年 2 月,这是最详尽、最具描述性的答案。 (3认同)