迁移
就像您使用 版本控制 系统(如 Git)来管理源代码中的更改一样,您可以使用 **迁移** 来跟踪对数据库的更改。使用迁移,您可以将现有数据库转移到另一个状态,反之亦然:这些状态转换保存在迁移文件中,其中描述了如何达到新状态以及如何撤消更改以回到旧状态。
您将需要 Sequelize 命令行界面 (CLI)。CLI 提供了对迁移和项目引导的支持。
Sequelize 中的迁移是一个 JavaScript 文件,它导出两个函数 up
和 down
,它们指示如何执行迁移以及如何撤消它。您手动定义这些函数,但您不会手动调用它们;它们将由 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
对象存储可用的数据类型,例如 STRING
或 INTEGER
。函数 up
或 down
应该返回一个 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
:如果可用,则显示各种调试信息。
您可以使用它的几种场景
- 您想覆盖
migrations
、models
、seeders
或config
文件夹的默认路径。 - 您想将
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.
安全提示
将环境变量用于配置设置。这是因为密码等秘密信息绝不应该成为源代码的一部分(尤其是不应该提交到版本控制中)。
存储
您可以使用三种类型的存储:sequelize
、json
和 none
。
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,用于以编程方式处理迁移任务的执行和日志记录。