Migrations - 迁移

就像你使用版本控制Git 来管理源代码的更改一样,你可以使用迁移来跟踪数据库的更改. 通过迁移,你可以将现有的数据库转移到另一个状态,反之亦然:这些状态转换将保存在迁移文件中,它们描述了如何进入新状态以及如何还原更改以恢复旧状态.

你将需要 Sequelize CLI. CLI支持迁移和项目引导.

Sequelize 中的 Migration 是一个 javascript 文件,它导出两个函数 updown,这些函数指示如何执行迁移和撤消它. 你可以手动定义这些功能,但不必手动调用它们; 它们将由 CLI 自动调用. 在这些函数中,你应该借助 sequelize.query 以及 Sequelize 提供给你的其他任何方法,简单地执行所需的任何查询. 除此之外,没有其他神奇的事情.

安装 CLI

要安装 Sequelize CLI,请执行以下操作:

  1. npm install --save-dev sequelize-cli

有关详细信息,请参见 CLI GitHub 库.

项目启动

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

  1. npx sequelize-cli init

这将创建以下文件夹

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

结构

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

  1. {
  2. "development": {
  3. "username": "root",
  4. "password": null,
  5. "database": "database_development",
  6. "host": "127.0.0.1",
  7. "dialect": "mysql"
  8. },
  9. "test": {
  10. "username": "root",
  11. "password": null,
  12. "database": "database_production",
  13. "host": "127.0.0.1",
  14. "dialect": "mysql"
  15. },
  16. "production": {
  17. "username": "root",
  18. "password": null,
  19. "database": "database_test",
  20. "host": "127.0.0.1",
  21. "dialect": "mysql"
  22. }
  23. }

请注意,默认情况下,Sequelize CLI 假定使用 mysql. 如果你使用其他方言,则需要更改 "dialect" 参数的内容.

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

Sequelize 将为每个方言使用默认的连接端口(例如,对于postgres,它是端口5432). 如果需要指定其他端口,请使用 port 字段(默认情况下它不在 config/config.js 中,但你可以简单地添加它).

注意: 如果你的数据库还不存在,你可以调用 db:create 命令. 通过正确的访问,它将为你创建该数据库.

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

一旦你正确配置了CLI配置文件,你就可以首先创建迁移. 它像执行一个简单的命令一样简单.

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

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

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

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

这将发生以下事情

  • models 文件夹中创建了一个 user 模型文件;
  • migrations 文件夹中创建了一个名字像 XXXXXXXXXXXXXX-create-user.js 的迁移文件.

注意: _Sequelize 将只使用模型文件,它是表描述.另一边,迁移文件是该模型的更改,或更具体的是说 CLI 所使用的表. 处理迁移,如提交或日志,以进行数据库的某些更改. _

运行迁移

直到这一步,CLI没有将任何东西插入数据库. 我们刚刚为我们的第一个模型 User 创建了必需的模型和迁移文件. 现在要在数据库中实际创建该表,需要运行 db:migrate 命令.

  1. npx sequelize-cli db:migrate

此命令将执行这些步骤

  • 将在数据库中确保一个名为 SequelizeMeta 的表. 此表用于记录在当前数据库上运行的迁移
  • 开始寻找尚未运行的任何迁移文件. 这可以通过检查 SequelizeMeta 表. 在这个例子中,它将运行我们在最后一步中创建的 XXXXXXXXXXXXXX-create-user.js 迁移,.
  • 创建一个名为 Users 的表,其中包含其迁移文件中指定的所有列.

撤消迁移

现在我们的表已创建并保存在数据库中. 通过迁移,只需运行命令即可恢复为旧状态.

你可以使用 db:migrate:undo,这个命令将会恢复最近的迁移.

  1. npx sequelize-cli db:migrate:undo

通过使用 db:migrate:undo:all 命令撤消所有迁移,可以恢复到初始状态. 你还可以通过将其名称传递到 --to 选项中来恢复到特定的迁移.

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

创建第一个种子

假设我们希望在默认情况下将一些数据插入到几个表中. 如果我们跟进前面的例子,我们可以考虑为 User 表创建演示用户.

要管理所有数据迁移,你可以使用 seeders. 种子文件是数据的一些变化,可用于使用样本数据或测试数据填充数据库表.

让我们创建一个种子文件,它会将一个演示用户添加到我们的 User 表中.

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

这个命令将会在 seeders 文件夹中创建一个种子文件.文件名看起来像是 XXXXXXXXXXXXXX-demo-user.js,它遵循相同的 up/down 语义,如迁移文件.

现在我们应该编辑这个文件,将演示用户插入User表.

  1. module.exports = {
  2. up: (queryInterface, Sequelize) => {
  3. return queryInterface.bulkInsert('Users', [{
  4. firstName: 'John',
  5. lastName: 'Doe',
  6. email: 'example@example.com',
  7. createdAt: new Date(),
  8. updatedAt: new Date()
  9. }]);
  10. },
  11. down: (queryInterface, Sequelize) => {
  12. return queryInterface.bulkDelete('Users', null, {});
  13. }
  14. };

运行种子

在上一步中,你创建了一个种子文件. 但它还没有保存到数据库. 为此,我们需要运行一个简单的命令.

  1. npx sequelize-cli db:seed:all

这将执行该种子文件,你将有一个演示用户插入 User 表.

注意: 与使用 SequelizeMeta 表的迁移不同,Seeder 执行历史记录不会存储在任何地方. 如果你想更改此行为,请阅读 存储 部分

撤销种子

Seeders 如果使用了任何存储那么就可以被撤消. 有两个可用的命令

如果你想撤消最近的种子

  1. npx sequelize-cli db:seed:undo

如果你想撤消特定的种子

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

如果你想撤消所有的种子

  1. npx sequelize-cli db:seed:undo:all

高级专题

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

  1. module.exports = {
  2. up: (queryInterface, Sequelize) => {
  3. // 转变为新状态的逻辑
  4. },
  5. down: (queryInterface, Sequelize) => {
  6. // 恢复更改的逻辑
  7. }
  8. }

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

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

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

  1. module.exports = {
  2. up: (queryInterface, Sequelize) => {
  3. return queryInterface.createTable('Person', {
  4. name: Sequelize.DataTypes.STRING,
  5. isBetaMember: {
  6. type: Sequelize.DataTypes.BOOLEAN,
  7. defaultValue: false,
  8. allowNull: false
  9. }
  10. });
  11. },
  12. down: (queryInterface, Sequelize) => {
  13. return queryInterface.dropTable('Person');
  14. }
  15. };

以下是一个迁移示例,该迁移使用自动管理的事务来在数据库中执行两次更改,以确保成功执行所有指令或在发生故障时回滚所有指令:

  1. module.exports = {
  2. up: (queryInterface, Sequelize) => {
  3. return queryInterface.sequelize.transaction(t => {
  4. return Promise.all([
  5. queryInterface.addColumn('Person', 'petName', {
  6. type: Sequelize.DataTypes.STRING
  7. }, { transaction: t }),
  8. queryInterface.addColumn('Person', 'favoriteColor', {
  9. type: Sequelize.DataTypes.STRING,
  10. }, { transaction: t })
  11. ]);
  12. });
  13. },
  14. down: (queryInterface, Sequelize) => {
  15. return queryInterface.sequelize.transaction(t => {
  16. return Promise.all([
  17. queryInterface.removeColumn('Person', 'petName', { transaction: t }),
  18. queryInterface.removeColumn('Person', 'favoriteColor', { transaction: t })
  19. ]);
  20. });
  21. }
  22. };

下一个是具有外键的迁移示例. 你可以使用 references 来指定外键:

  1. module.exports = {
  2. up: (queryInterface, Sequelize) => {
  3. return queryInterface.createTable('Person', {
  4. name: Sequelize.DataTypes.STRING,
  5. isBetaMember: {
  6. type: Sequelize.DataTypes.BOOLEAN,
  7. defaultValue: false,
  8. allowNull: false
  9. },
  10. userId: {
  11. type: Sequelize.DataTypes.INTEGER,
  12. references: {
  13. model: {
  14. tableName: 'users',
  15. schema: 'schema'
  16. },
  17. key: 'id'
  18. },
  19. allowNull: false
  20. },
  21. });
  22. },
  23. down: (queryInterface, Sequelize) => {
  24. return queryInterface.dropTable('Person');
  25. }
  26. }

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

  1. module.exports = {
  2. async up(queryInterface, Sequelize) {
  3. const transaction = await queryInterface.sequelize.transaction();
  4. try {
  5. await queryInterface.addColumn(
  6. 'Person',
  7. 'petName',
  8. {
  9. type: Sequelize.DataTypes.STRING,
  10. },
  11. { transaction }
  12. );
  13. await queryInterface.addIndex(
  14. 'Person',
  15. 'petName',
  16. {
  17. fields: 'petName',
  18. unique: true,
  19. transaction,
  20. }
  21. );
  22. await transaction.commit();
  23. } catch (err) {
  24. await transaction.rollback();
  25. throw err;
  26. }
  27. },
  28. async down(queryInterface, Sequelize) {
  29. const transaction = await queryInterface.sequelize.transaction();
  30. try {
  31. await queryInterface.removeColumn('Person', 'petName', { transaction });
  32. await transaction.commit();
  33. } catch (err) {
  34. await transaction.rollback();
  35. throw err;
  36. }
  37. }
  38. };

.sequelizerc 文件

这是一个特殊的配置文件. 它允许你指定通常作为参数传递给CLI的各种选项:

  • env: 在其中运行命令的环境
  • config: 配置文件的路径
  • options-path: 带有其他参数的 JSON 文件的路径
  • migrations-path: migrations 文件夹的路径
  • seeders-path: seeders 文件夹的路径
  • models-path: models 文件夹的路径
  • url: 要使用的数据库连接字符串. 替代使用 —config 文件
  • debug: 可用时显示各种调试信息

在某些情况下,你可以使用它:

  • 你想要覆盖到 migrations, models, seedersconfig 文件夹的路径.
  • 你想要重命名 config.json 成为别的名字比如 database.json

还有更多的, 让我们看一下如何使用这个文件进行自定义配置.

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

  1. // .sequelizerc
  2. const path = require('path');
  3. module.exports = {
  4. 'config': path.resolve('config', 'database.json'),
  5. 'models-path': path.resolve('db', 'models'),
  6. 'seeders-path': path.resolve('db', 'seeders'),
  7. 'migrations-path': path.resolve('db', 'migrations')
  8. };

通过这个配置你告诉CLI:

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

动态配置

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

值得庆幸的是,Sequelize CLI 可以从 .json.js 文件中读取. 可以使用 .sequelizerc 文件来设置. 你只需提供 .js 文件的路径作为导出对象的 config 参数即可:

  1. const path = require('path');
  2. module.exports = {
  3. 'config': path.resolve('config', 'config.js')
  4. }

现在,Sequelize CLI 将加载 config/config.js 以获取配置参数.

一个 config/config.js 文件的例子

  1. const fs = require('fs');
  2. module.exports = {
  3. development: {
  4. username: 'database_dev',
  5. password: 'database_dev',
  6. database: 'database_dev',
  7. host: '127.0.0.1',
  8. port: 3306,
  9. dialect: 'mysql',
  10. dialectOptions: {
  11. bigNumberStrings: true
  12. }
  13. },
  14. test: {
  15. username: process.env.CI_DB_USERNAME,
  16. password: process.env.CI_DB_PASSWORD,
  17. database: process.env.CI_DB_NAME,
  18. host: '127.0.0.1',
  19. port: 3306,
  20. dialect: 'mysql',
  21. dialectOptions: {
  22. bigNumberStrings: true
  23. }
  24. },
  25. production: {
  26. username: process.env.PROD_DB_USERNAME,
  27. password: process.env.PROD_DB_PASSWORD,
  28. database: process.env.PROD_DB_NAME,
  29. host: process.env.PROD_DB_HOSTNAME,
  30. port: process.env.PROD_DB_PORT,
  31. dialect: 'mysql',
  32. dialectOptions: {
  33. bigNumberStrings: true,
  34. ssl: {
  35. ca: fs.readFileSync(__dirname + '/mysql-ca-master.crt')
  36. }
  37. }
  38. }
  39. };

上面的示例还显示了如何向配置中添加自定义方言参数.

使用 Babel

为了在你的迁移和 seeder 中实现更现代的构造,你可以简单地安装 babel-register 并在 .sequelizerc 开始时 require 它:

  1. npm i --save-dev babel-register
  1. // .sequelizerc
  2. require("babel-register");
  3. const path = require('path');
  4. module.exports = {
  5. 'config': path.resolve('config', 'config.json'),
  6. 'models-path': path.resolve('models'),
  7. 'seeders-path': path.resolve('seeders'),
  8. 'migrations-path': path.resolve('migrations')
  9. }

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

安全提示

使用环境变量进行配置设置. 这是因为诸如密码之类的机密绝不应该是源代码的一部分(尤其是不要提交给版本控制).

存储

你可以使用三种类型的存储:sequelize,jsonnone.

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

迁移存储

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

  1. {
  2. "development": {
  3. "username": "root",
  4. "password": null,
  5. "database": "database_development",
  6. "host": "127.0.0.1",
  7. "dialect": "mysql",
  8. // 使用其他存储类型. 默认: sequelize
  9. "migrationStorage": "json",
  10. // 使用其他文件名. 默认: sequelize-meta.json
  11. "migrationStoragePath": "sequelizeMeta.json",
  12. // 使用其他表格名称. 默认: SequelizeMeta
  13. "migrationStorageTableName": "sequelize_meta",
  14. // 对 SequelizeMeta 表使用其他架构
  15. "migrationStorageTableSchema": "custom_schema"
  16. }
  17. }

Note: 不建议将 none 存储作为迁移存储. 如果你决定使用它,请注意没有任何迁移进行或未运行的记录的含义.

种子储存

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

  1. {
  2. "development": {
  3. "username": "root",
  4. "password": null,
  5. "database": "database_development",
  6. "host": "127.0.0.1",
  7. "dialect": "mysql",
  8. // 使用其他存储. 默认: none
  9. "seederStorage": "json",
  10. // 使用其他文件名称. 默认: sequelize-data.json
  11. "seederStoragePath": "sequelizeData.json",
  12. // 使用其他表格名称. 默认: SequelizeData
  13. "seederStorageTableName": "sequelize_data"
  14. }
  15. }

配置连接字符串

配置连接字符串作为配置文件定义数据库的 --config 参数的替代方法,可以使用 --url 参数来传递连接字符串. 例如:

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

程序用法

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