【问题标题】:How does sequelize.sync() work, specifically the force option?sequelize.sync() 是如何工作的,特别是 force 选项?
【发布时间】:2014-01-30 18:00:58
【问题描述】:

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

sequelize.sync({
    force: true
});

具体来说,我有兴趣知道 force: false 的作用是什么?它不会将架构与数据库同步吗?

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

【问题讨论】:

    标签: sequelize.js


    【解决方案1】:

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

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

    【讨论】:

    • 另外:如果您更改实体的表名(已存在),则该表将保留数据,并创建另一个具有新名称的表。
    • 这不是在回答问题。问题问force: false 做什么,而不是force: true 做什么。
    • 我调用了 sequelize.sync({force: false}),但仍然重新创建了表。这是为什么呢?
    • 链接已失效。这就是为什么应避免答案中的(独立)链接的原因。
    【解决方案2】:

    OP 询问 force: false 做了什么,这也是我想知道的,所以剩下的就在这里。

    对我来说,主要的收获是各个字段没有同步(这是我所希望的,来自 Waterline ORM)。这意味着,如果您有 force: false 并且该表存在,则您所做的任何字段添加/修改/删除都不会被执行。

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

    这是current code from the 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);
    };
    

    【讨论】:

      【解决方案3】:

      最小可运行示例

      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();
      })();
      

      设置:

      npm install sequelize@6.5.1 sqlite3@5.0.2.
      

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

      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);
      

      如果我们使用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);
      

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

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

      测试:

      npm install sequelize@6.5.1 sqlite3@5.0.2.
      

      force: false单独不同步架构,需要alter: true

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

      为了既保留现有数据又更新架构,除了force: false,我们还必须使用alter: true

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

      const assert = require('assert')
      const { Sequelize, DataTypes } = require('sequelize');
      
      (async () => {
      {
        const sequelize = new Sequelize({
          dialect: 'sqlite',
          storage: 'tmp.sqlite',
        });
        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 = new Sequelize({
          dialect: 'sqlite',
          storage: 'tmp.sqlite',
        });
        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();
      }
      })();
      

      如果我们删除 alter: true 它会爆炸,因为新列不存在:

      SequelizeDatabaseError: SQLITE_ERROR: table IntegerNames has no column named nameEs
      

      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));
      

      它不使用 ALTER 语句,因为 SQLite 目前不支持它们。然后,这打破了 sequelize 不会根据需要自动删除和重新创建的约束:Process of changing a table with sequelize migration if foreign key constraint is active 这很可悲。

      迁移往往是在实际项目中更新架构的推荐方式:Sequelize: Changing model schema on production

      【讨论】:

      • 2022 年 2 月,这是最详尽和描述性最强的答案。
      猜你喜欢
      • 2017-12-11
      • 2011-09-20
      • 1970-01-01
      • 1970-01-01
      • 2011-10-28
      • 1970-01-01
      • 1970-01-01
      • 2015-11-28
      • 2011-04-23
      相关资源
      最近更新 更多