跳至主要內容
版本: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 將使用每個方言的預設連線埠(例如,對於 postgres,它是 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 表格。如果您希望變更此行為,請閱讀 Storage 區段。

還原種子

如果種子使用任何儲存,則可以還原它們。有兩個可用的命令

如果您希望還原最近的種子

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'

如果將 package.json 指令碼與 npm 搭配使用,請務必在使用旗標時在指令中額外使用 --。例如:

// 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 的姊妹程式庫,用於以程式方式處理遷移任務的執行和記錄。