跳至主要内容
版本:v6 - 稳定版

迁移

就像您使用 版本控制 系统(如 Git)来管理源代码中的更改一样,您可以使用 **迁移** 来跟踪对数据库的更改。使用迁移,您可以将现有数据库转移到另一个状态,反之亦然:这些状态转换保存在迁移文件中,其中描述了如何达到新状态以及如何撤消更改以回到旧状态。

您将需要 Sequelize 命令行界面 (CLI)。CLI 提供了对迁移和项目引导的支持。

Sequelize 中的迁移是一个 JavaScript 文件,它导出两个函数 updown,它们指示如何执行迁移以及如何撤消它。您手动定义这些函数,但您不会手动调用它们;它们将由 CLI 自动调用。在这些函数中,您应该只使用 sequelize.query 以及 Sequelize 提供给您的其他方法执行所需的查询。除此之外没有其他魔法。

安装 CLI

要安装 Sequelize CLI

npm install --save-dev sequelize-cli

有关详细信息,请参阅 CLI GitHub 代码库

项目引导

要创建一个空项目,您需要执行 init 命令

npx sequelize-cli init

这将创建以下文件夹

  • config,包含配置文件,它告诉 CLI 如何连接到数据库
  • models,包含您项目的所有模型
  • migrations,包含所有迁移文件
  • seeders,包含所有种子文件

配置

在继续之前,我们需要告诉 CLI 如何连接到数据库。为此,让我们打开默认配置文件 config/config.json。它看起来像这样

{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}

请注意,Sequelize CLI 默认情况下假设使用的是 MySQL。如果您使用的是其他方言,则需要更改 "dialect" 选项的内容。

现在编辑此文件并设置正确的数据库凭据和方言。对象的键(例如“development”)在 model/index.js 中用于匹配 process.env.NODE_ENV(未定义时,“development”是默认值)。

Sequelize 将使用每种方言的默认连接端口(例如,对于 PostgreSQL,它是端口 5432)。如果您需要指定其他端口,请使用 "port" 字段(它在 config/config.js 中默认不存在,但您可以简单地添加它)。

注意: 如果您的数据库尚未存在,您只需调用 db:create 命令。如果具有适当的访问权限,它将为您创建该数据库。

创建第一个模型(和迁移)

正确配置 CLI 配置文件后,您就可以创建第一个迁移了。这就像执行一个简单的命令一样简单。

我们将使用 model:generate 命令。此命令需要两个选项

  • name:模型的名称;
  • attributes:模型属性列表。

让我们创建一个名为 User 的模型。

npx sequelize-cli model:generate --name User --attributes firstName:string,lastName:string,email:string

这将

  • models 文件夹中创建一个模型文件 user
  • migrations 文件夹中创建一个名称类似于 XXXXXXXXXXXXXX-create-user.js 的迁移文件。

注意: Sequelize 将只使用模型文件,它是表的表示。另一方面,迁移文件是该模型或更具体地说是该表的更改,由 CLI 使用。将迁移视为对数据库中的某些更改的提交或日志。

运行迁移

到目前为止,我们还没有将任何内容插入数据库。我们只是为第一个模型 User 创建了必需的模型和迁移文件。现在,要实际在数据库中创建该表,您需要运行 db:migrate 命令。

npx sequelize-cli db:migrate

此命令将执行以下步骤

  • 将确保数据库中存在名为 SequelizeMeta 的表。此表用于记录哪些迁移已在当前数据库上运行
  • 开始查找尚未运行的任何迁移文件。这可以通过检查 SequelizeMeta 表来实现。在这种情况下,它将运行 XXXXXXXXXXXXXX-create-user.js 迁移,我们在上一步中创建了它。
  • 根据其迁移文件中指定的列创建名为 Users 的表。

撤消迁移

现在,我们的表已创建并保存在数据库中。使用迁移,您可以通过运行一个命令来还原到旧状态。

您可以使用 db:migrate:undo,此命令将撤消最近一次迁移。

npx sequelize-cli db:migrate:undo

您可以通过使用 db:migrate:undo:all 命令撤消所有迁移来还原到初始状态。您还可以通过使用 --to 选项传递迁移名称来还原到特定迁移。

npx sequelize-cli db:migrate:undo:all --to XXXXXXXXXXXXXX-create-posts.js

创建第一个种子

假设我们想要默认情况下将一些数据插入一些表中。如果我们继续上一个示例,我们可以考虑为 User 表创建一个演示用户。

要管理所有数据迁移,您可以使用种子。种子文件是对数据的某些更改,可用于使用示例数据或测试数据填充数据库表。

让我们创建一个种子文件,它将向我们的 User 表添加一个演示用户。

npx sequelize-cli seed:generate --name demo-user

此命令将在 seeders 文件夹中创建一个种子文件。文件名将类似于 XXXXXXXXXXXXXX-demo-user.js。它遵循与迁移文件相同的 up / down 语义。

现在,我们应该编辑此文件以将演示用户插入 User 表。

module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.bulkInsert('Users', [
{
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
createdAt: new Date(),
updatedAt: new Date(),
},
]);
},
down: (queryInterface, Sequelize) => {
return queryInterface.bulkDelete('Users', null, {});
},
};

运行种子

在上一步中,您创建了一个种子文件;但是,它尚未提交到数据库。为此,我们运行一个简单的命令。

npx sequelize-cli db:seed:all

这将执行该种子文件,一个演示用户将被插入到 User 表中。

注意: 种子执行历史记录未存储在任何地方,这与使用 SequelizeMeta 表的迁移不同。如果您希望更改此行为,请阅读“存储”部分。

撤消种子

如果种子使用任何存储,则可以撤消它们。有两个命令可用于执行此操作

如果您希望撤消最近一次种子

npx sequelize-cli db:seed:undo

如果您希望撤消特定种子

npx sequelize-cli db:seed:undo --seed name-of-seed-as-in-data

如果您希望撤消所有种子

npx sequelize-cli db:seed:undo:all

迁移框架

以下框架显示了一个典型的迁移文件。

module.exports = {
up: (queryInterface, Sequelize) => {
// logic for transforming into the new state
},
down: (queryInterface, Sequelize) => {
// logic for reverting the changes
},
};

我们可以使用 migration:generate 生成此文件。这将在您的迁移文件夹中创建 xxx-migration-skeleton.js

npx sequelize-cli migration:generate --name migration-skeleton

传递的 queryInterface 对象可用于修改数据库。Sequelize 对象存储可用的数据类型,例如 STRINGINTEGER。函数 updown 应该返回一个 Promise。让我们看一个示例

module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Person', {
name: Sequelize.DataTypes.STRING,
isBetaMember: {
type: Sequelize.DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Person');
},
};

以下是一个迁移的示例,它对数据库执行两个更改,使用自动管理的事务来确保所有指令都成功执行或在发生错误时回滚

module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(t => {
return Promise.all([
queryInterface.addColumn(
'Person',
'petName',
{
type: Sequelize.DataTypes.STRING,
},
{ transaction: t },
),
queryInterface.addColumn(
'Person',
'favoriteColor',
{
type: Sequelize.DataTypes.STRING,
},
{ transaction: t },
),
]);
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(t => {
return Promise.all([
queryInterface.removeColumn('Person', 'petName', { transaction: t }),
queryInterface.removeColumn('Person', 'favoriteColor', {
transaction: t,
}),
]);
});
},
};

下一个示例是一个具有外键的迁移。您可以使用引用来指定外键

module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Person', {
name: Sequelize.DataTypes.STRING,
isBetaMember: {
type: Sequelize.DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
},
userId: {
type: Sequelize.DataTypes.INTEGER,
references: {
model: {
tableName: 'users',
schema: 'schema',
},
key: 'id',
},
allowNull: false,
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Person');
},
};

下一个示例是一个使用 async/await 的迁移,您可以在其中在新的列上创建一个唯一索引,并使用手动管理的事务

module.exports = {
async up(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.addColumn(
'Person',
'petName',
{
type: Sequelize.DataTypes.STRING,
},
{ transaction },
);
await queryInterface.addIndex('Person', 'petName', {
fields: 'petName',
unique: true,
transaction,
});
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
async down(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.removeColumn('Person', 'petName', { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};

以下是一个迁移的示例,它创建一个由多个字段组成的唯一索引,并带有条件。该条件允许关系多次存在,但只能有一个满足该条件。

module.exports = {
up: (queryInterface, Sequelize) => {
queryInterface
.createTable('Person', {
name: Sequelize.DataTypes.STRING,
bool: {
type: Sequelize.DataTypes.BOOLEAN,
defaultValue: false,
},
})
.then((queryInterface, Sequelize) => {
queryInterface.addIndex('Person', ['name', 'bool'], {
indicesType: 'UNIQUE',
where: { bool: 'true' },
});
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Person');
},
};

.sequelizerc 文件

这是一个特殊的配置文件。它允许您指定通常作为 CLI 参数传递的以下选项。

  • env:运行命令的环境。
  • config:配置文件的路径。
  • options-path:包含额外选项的 JSON 文件的路径。
  • migrations-path:迁移文件夹的路径。
  • seeders-path:种子文件夹的路径。
  • models-path:模型文件夹的路径。
  • url:要使用的数据库连接字符串。替代使用 --config 文件。
  • debug:如果可用,则显示各种调试信息。

您可以使用它的几种场景

  • 您想覆盖 migrationsmodelsseedersconfig 文件夹的默认路径。
  • 您想将 config.json 重命名为其他名称,例如 database.json

还有更多其他用途。让我们看看如何使用此文件进行自定义配置。

首先,让我们在项目的根目录中创建一个 .sequelizerc 文件,内容如下

// .sequelizerc

const path = require('path');

module.exports = {
config: path.resolve('config', 'database.json'),
'models-path': path.resolve('db', 'models'),
'seeders-path': path.resolve('db', 'seeders'),
'migrations-path': path.resolve('db', 'migrations'),
};

使用此配置,您告诉 CLI

  • 使用 config/database.json 文件进行配置设置;
  • 使用 db/models 作为模型文件夹;
  • 使用 db/seeders 作为种子文件夹;
  • 使用 db/migrations 作为迁移文件夹。

动态配置

默认情况下,配置文件是一个名为 config.json 的 JSON 文件。但有时您需要动态配置,例如访问环境变量或执行其他代码来确定配置。

值得庆幸的是,Sequelize CLI 可以从 .json.js 文件中读取。这可以通过 .sequelizerc 文件进行设置。您只需将 .js 文件的路径作为导出对象的 config 选项提供。

const path = require('path');

module.exports = {
config: path.resolve('config', 'config.js'),
};

现在 Sequelize CLI 将加载 config/config.js 来获取配置选项。

config/config.js 文件示例

const fs = require('fs');

module.exports = {
development: {
username: 'database_dev',
password: 'database_dev',
database: 'database_dev',
host: '127.0.0.1',
port: 3306,
dialect: 'mysql',
dialectOptions: {
bigNumberStrings: true,
},
},
test: {
username: process.env.CI_DB_USERNAME,
password: process.env.CI_DB_PASSWORD,
database: process.env.CI_DB_NAME,
host: '127.0.0.1',
port: 3306,
dialect: 'mysql',
dialectOptions: {
bigNumberStrings: true,
},
},
production: {
username: process.env.PROD_DB_USERNAME,
password: process.env.PROD_DB_PASSWORD,
database: process.env.PROD_DB_NAME,
host: process.env.PROD_DB_HOSTNAME,
port: process.env.PROD_DB_PORT,
dialect: 'mysql',
dialectOptions: {
bigNumberStrings: true,
ssl: {
ca: fs.readFileSync(__dirname + '/mysql-ca-main.crt'),
},
},
},
};

上面的示例还展示了如何将自定义方言选项添加到配置中。

使用 Babel

要启用迁移和种子中更现代的构造,您只需安装 babel-register 并将其在 .sequelizerc 的开头引用。

npm i --save-dev babel-register
// .sequelizerc

require('babel-register');

const path = require('path');

module.exports = {
config: path.resolve('config', 'config.json'),
'models-path': path.resolve('models'),
'seeders-path': path.resolve('seeders'),
'migrations-path': path.resolve('migrations'),
};

当然,结果将取决于您的 babel 配置(例如在 .babelrc 文件中)。了解更多信息,请访问 babeljs.io.

安全提示

将环境变量用于配置设置。这是因为密码等秘密信息绝不应该成为源代码的一部分(尤其是不应该提交到版本控制中)。

存储

您可以使用三种类型的存储:sequelizejsonnone

  • sequelize:将迁移和种子存储在 sequelize 数据库中的表中。
  • json:将迁移和种子存储在 json 文件中。
  • none:不存储任何迁移/种子。

迁移存储

默认情况下,CLI 将在您的数据库中创建一个名为 SequelizeMeta 的表,其中包含每个已执行迁移的条目。要更改此行为,您可以在配置文件中添加三个选项。使用 migrationStorage,您可以选择用于迁移的存储类型。如果您选择 json,则可以使用 migrationStoragePath 指定文件的路径,否则 CLI 将写入 sequelize-meta.json 文件。如果您想将信息保存在数据库中,使用 sequelize,但想使用不同的表,则可以使用 migrationStorageTableName 更改表名。您还可以通过提供 migrationStorageTableSchema 属性来定义 SequelizeMeta 表的不同架构。

{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql",

// Use a different storage type. Default: sequelize
"migrationStorage": "json",

// Use a different file name. Default: sequelize-meta.json
"migrationStoragePath": "sequelizeMeta.json",

// Use a different table name. Default: SequelizeMeta
"migrationStorageTableName": "sequelize_meta",

// Use a different schema for the SequelizeMeta table
"migrationStorageTableSchema": "custom_schema"
}
}

注意: 不建议使用 none 存储作为迁移存储。如果您决定使用它,请注意没有记录哪些迁移已执行或未执行的影响。

种子存储

默认情况下,CLI 不会保存任何已执行的种子。如果您选择更改此行为(!),则可以使用配置文件中的 seederStorage 更改存储类型。如果您选择 json,则可以使用 seederStoragePath 指定文件的路径,否则 CLI 将写入 sequelize-data.json 文件。如果您想将信息保存在数据库中,使用 sequelize,则可以使用 seederStorageTableName 指定表名,否则将默认为 SequelizeData

{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql",
// Use a different storage. Default: none
"seederStorage": "json",
// Use a different file name. Default: sequelize-data.json
"seederStoragePath": "sequelizeData.json",
// Use a different table name. Default: SequelizeData
"seederStorageTableName": "sequelize_data"
}
}

配置连接字符串

作为使用 --config 选项和定义数据库的配置文件的替代方法,您可以使用 --url 选项传入连接字符串。例如

npx sequelize-cli db:migrate --url 'mysql://root:password@mysql_host.com/database_name'

如果使用 npm 的 package.json 脚本,请确保在使用标志时在命令中使用额外的 --。例如

// package.json

...
"scripts": {
"migrate:up": "npx sequelize-cli db:migrate",
"migrate:undo": "npx sequelize-cli db:migrate:undo"
},
...

以以下方式使用命令:npm run migrate:up -- --url <url>

编程使用

Sequelize 拥有一个姊妹库,名为 umzug,用于以编程方式处理迁移任务的执行和日志记录。