遷移
就像您使用 版本控制 系統(如 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 將使用每個方言的預設連線埠(例如,對於 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
物件儲存可用的資料類型,例如 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'
如果將 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 的姊妹程式庫,用於以程式方式處理遷移任務的執行和記錄。