Raspberry Pi 4 的後端修鍊 - sequelize-cli

  1. Raspberry Pi 4 的後端修鍊: sequelize-cli
    1. 安裝
    2. 初始化 sequelize 的基本環境
    3. 資料庫連線設定
    4. 用 ORM 創造資料庫
    5. 用 ORM 創造 Migration 步驟
      1. 執行 migration
      2. 此也可以加上 transaction
      3. 此也可以含有 forang key
      4. 一併創造資料表
    6. 用 ORM 創造資料

Raspberry Pi 4 的後端修鍊: sequelize-cli

安裝

sequelize[1]

$ npm install --save sequelize mysql2

要使用 mariadb 要安裝 mysql2
並且把 config 的 “dialect” 設成 “mysql” (後面會講)

sequelize-cli[2]

$ npm install -g --save sequelize-cli

安裝好,確定一下安裝成功

$ sequelize --version

Sequelize CLI [Node: 12.18.0, CLI: 5.5.1, ORM: 5.21.13]

5.5.1

問題: Unable to resolve sequelize package
因為 sequelize-cli 不會幫你安裝 sequelize 所以 shell 執行 sequelize 時會出現呼叫不到 sequelize

指令說明

$ npx sequelize

Sequelize CLI [Node: 12.18.0, CLI: 5.5.1, ORM: 5.22.4]

sequelize [command]

命令:
sequelize db:migrate Run pending migrations
sequelize db:migrate:schema:timestamps:add Update migration table to have timestamps
sequelize db:migrate:status List the status of all migrations
sequelize db:migrate:undo Reverts a migration
sequelize db:migrate:undo:all Revert all migrations ran
sequelize db:seed Run specified seeder
sequelize db:seed:undo Deletes data from the database
sequelize db:seed:all Run every seeder
sequelize db:seed:undo:all Deletes data from the database
sequelize db:create Create database specified by configuration
sequelize db:drop Drop database specified by configuration
sequelize init Initializes project
sequelize init:config Initializes configuration
sequelize init:migrations Initializes migrations
sequelize init:models Initializes models
sequelize init:seeders Initializes seeders
sequelize migration:generate Generates a new migration file [別名: migration:create]
sequelize model:generate Generates a model and its migration [別名: model:create]
sequelize seed:generate Generates a new seed file [別名: seed:create]

選項:
--help 顯示說明 [布林]
--version 顯示版本

分成幾個部份

初始化

指令 作用
sequelize init 初始化 project(包含下面四個)
sequelize init:config 初始化 configuration
sequelize init:migrations 初始化 migrations
sequelize init:models 初始化 models
sequelize init:seeders 初始化 seeders

資料庫 建立/刪除

使用時注意權限是否可以更動資料庫

指令 作用
sequelize db:create 從指定的 config 檔新增 database
sequelize db:drop 從指定的 config 檔刪除 database

產生各種腳本

指令 作用 參數
sequelize migration:generate 產生 new migration 檔 [別名: migration:create]
sequelize model:generate 產生 model 和它的 migration 檔 [別名: model:create]
sequelize seed:generate 產生 new seed 檔 [別名: seed:create]

資料表結構維護

指令 作用
sequelize db:migrate 執行未跑過的 migrations
sequelize db:migrate:undo 恢復一次 migration
sequelize db:migrate:undo:all 恢復所有跑過的 migrations
sequelize db:migrate:schema:timestamps:add Update migration table to have timestamps
sequelize db:migrate:status List the status of all migrations

前置資料維護

指令 作用
sequelize db:seed 執行指定的 seeder
sequelize db:seed:all 執行每一個 seeder
sequelize db:seed:undo 從 database 刪除資料
sequelize db:seed:undo:all 從 database 刪除資料

初始化 sequelize 的基本環境

sequelize 的 cli 工具,可以做到很多事情,不過大多都在處理 migration。

$ sequelize init

Sequelize CLI [Node: 12.18.0, CLI: 5.5.1, ORM: 5.22.4]

Created "config/config.json"
Successfully created models folder at "/home/pi/code/express-demo/models".
Successfully created migrations folder at "/home/pi/code/express-demo/migrations".
Successfully created seeders folder at "/home/pi/code/express-demo/seeders".

執行完會出現

project/
├- migrations/
├- seeders/
├- config/
| ├- config.json
└- models/
└- index.js
  • config, 包含 config 檔案, 提供資料庫連線給 CLI
  • models, 包含專案中所有的 models
  • migrations, 包含所有的 migration 檔案
  • seeders, 包含所有的 seed 檔案

在此出現幾個陌生的名詞

  • CLI: command line interface
  • models: MVC 的 model,用來和資料庫連動的物件定義
  • migration: 資料庫變動記錄
  • seed: 餵資料,通常是資料庫的系統初始資料

ORM 工具管理資料表變化,都會使用 migration。
它就像是 git 這種版本控制工具,只是它版控的是 table schema (只有管有哪些資料表與有哪些資料表的欄位)

資料庫連線設定

sequelize 要連線資料庫要靠 config/config.json 這裡的資訊。

config/config.json

環境變數就會對應到預設 config 的 development, test, production 三種情境,可依需要增減。

dialect: 翻譯到不同資料庫的 statement[3]

在此,我使用的是 mariadb,所以 “dialect” 設定成 “mariadb” “mysql” 才會跑。(不知道為什麼的雷點!)

如果有 operatorsAliases: false 營運商別名,保持預設值就好。(我目前是刪掉它)

{
"development": {
"username": "root",
"password": "root",
"host": "127.0.0.1",
"database": "booklibrary",
"dialect": "mysql"
},
"test": {
"username": "rasp",
"password": "rasp",
"host": "127.0.0.1",
"database": "booklibrary",
"dialect": "mysql"
},
"production": {
"username": "rasp",
"password": "rasp",
"database": "booklibrary",
"host": "127.0.0.1",
"dialect": "mysql"
}
}

三個的使用情境,我自己會這麼使用

development

用在開發者變動(新增/刪除/修改)資料庫、資料表結構

production

用在平常的執行時使用的設定 (給 CD/CI 起動時用的)

test

還沒想到。

用 ORM 創造資料庫

$ npx sequelize db:create booklibrary

Sequelize CLI [Node: 12.18.0, CLI: 5.5.1, ORM: 5.22.4]

Loaded configuration file "config/config.json".
Using environment "development".
Database booklibrary created.

因為用 cli 所以 env 要自帶在 env 參數
預設 env 參數是 “development”

db:create 要使用權限較高的帳號執行。
在此設定在 development 的 config 中。

用 ORM 創造 Migration 步驟

Migration 像是資料庫的演進過程,每一步都記錄下來的話,有助於未來再建立一次初始系統。
也有助於自動化資料表建置、修改或系統的初始資料建置。

但是 migration 畢竟一開始算是「一個動作」我們就以這個角度切入看看 sequelize-cli 幫我們打造了什麼工具又可以做到什麼吧!

一開始我們創一個 Migration 檔來執行或恢復。

$ npx sequelize-cli migration:generate --name create-empty-file

Sequelize CLI [Node: 12.18.0, CLI: 5.5.1, ORM: 5.22.4]

migrations folder at "/home/pi/code/express-demo/migrations" already exists.
New migration was created at /home/pi/code/express-demo/migrations/20210820152155-create-empty-file.js .

可以產生一個 migration 檔。

migrations/20210820152155-create-empty-file.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
'use strict';

module.exports = {
up: (queryInterface, Sequelize) => {
/*
Add altering commands here.
Return a promise to correctly handle asynchronicity.
*/

// Example:
return queryInterface.createTable('users', { id: Sequelize.INTEGER });
},

down: (queryInterface, Sequelize) => {
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
*/

// Example:
return queryInterface.dropTable('users');
}
};

配合上面的 Example (source code 產生的)

  • queryInterface: 用來修改 Database
  • Sequelize: 用來記可以用的資料型別 (ex: STRING, INTEGER)
  • function up, down: 回傳 promise

執行 migration

執行到底

sequelize-cli db:migrate

恢復到底

sequelize db:migrate:undo:all

此也可以加上 transaction

透過 queryInterface.sequelize.transaction(done => {}) 可以在 callback 裡面執行 一次 transaction 的所有行為。並且在每個行為的最後一個參數加上 , { transaction: done }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(transaction => {
return queryInterface.createTable('users', {
id: Sequelize.INTEGER
}, { transaction });
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(transaction => {
return queryInterface.dropTable('users', { transaction });
});
}
};

此也可以含有 forang key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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;
}
}
};

一併創造資料表

可以在建立 migration 的同時,也產生好對應 Model 檔案。

$ sequelize model:generate --name authors --attributes author_id:number,name_last:string,name_first:string,country:string


Sequelize CLI [Node: 12.18.0, CLI: 5.5.1, ORM: 5.22.4]

New model was created at /home/pi/code/express-demo/models/authors.js .
New migration was created at /home/pi/code/express-demo/migrations/20210501084235-authors.js .
  • author_id:number
  • name_last:string
  • name_first:string
  • country:string

產生 models/authors.js

1
2
3
4
5
6
7
8
9
10
11
12
13
'use strict';
module.exports = (sequelize, DataTypes) => {
const authors = sequelize.define('authors', {
author_id: DataTypes.NUMBER,
name_last: DataTypes.STRING,
name_first: DataTypes.STRING,
country: DataTypes.STRING
}, {});
authors.associate = function(models) {
// associations can be defined here
};
return authors;
};

產生 migrations/20210501084235-create-authors.js

這裡做一點修改,我們想要讓 id 用 author_id 表示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('authors', {
author_id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
// author_id: {
// type: Sequelize.NUMBER
// },
name_last: {
type: Sequelize.STRING
},
name_first: {
type: Sequelize.STRING
},
country: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('authors');
}
};
describe authors;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| author_id | int(11) | NO | PRI | <null> | auto_increment |
| name_last | varchar(255) | YES | | <null> | |
| name_first | varchar(255) | YES | | <null> | |
| country | varchar(255) | YES | | <null> | |
| createdAt | datetime | NO | | <null> | |
| updatedAt | datetime | NO | | <null> | |
+------------+--------------+------+-----+---------+----------------+

6 rows in set
Time: 0.759s

用 ORM 創造資料

為了在 migration 可以預先填入資料

$ npx sequelize-cli seed:generate --name base-authors

Sequelize CLI [Node: 12.18.0, CLI: 5.5.1, ORM: 5.22.4]

seeders folder at "/home/pi/code/express-demo/seeders" already exists.
New seed was created at /home/pi/code/express-demo/seeders/20210501134052-base-authors.js .

seeders/20210501134052-base-authors.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
'use strict';

module.exports = {
up: (queryInterface, Sequelize) => {
/*
自己寫入要加上去什麼資料
*/
return queryInterface.bulkInsert('authors', [{
name_first: 'Kafka',
name_last: 'Franz',
country: 'Czech Republic',
createdAt: new Date(),
updatedAt: new Date(),
}, {
name_first: 'Dostoevsky',
name_last: 'Fyodor',
country: 'Russian',
createdAt: new Date(),
updatedAt: new Date(),
}, {
name_first: 'Chris',
name_last: 'Wang',
country: 'Tainan',
createdAt: new Date(),
updatedAt: new Date(),
}, {
name_first: 'Mary',
name_last: 'Lee',
country: 'Tainan',
createdAt: new Date(),
updatedAt: new Date(),
}], {});
},

down: (queryInterface, Sequelize) => {
/*
同時要寫下返回的時候要刪掉什麼資料
*/
return queryInterface.bulkDelete('authors', null, {});
}
};

執行

$ npx sequelize db:seed

Sequelize CLI [Node: 12.18.0, CLI: 5.5.1, ORM: 5.22.4]

Loaded configuration file "config/config.json".
Using environment "development".

看資料是否有加進去資料庫

select * from authors;
+-----------+-----------+------------+----------------+---------------------+---------------------+
| author_id | name_last | name_first | country | createdAt | updatedAt |
+-----------+-----------+------------+----------------+---------------------+---------------------+
| 1 | Franz | Kafka | Czech Republic | 2021-05-01 13:36:31 | 2021-05-01 13:36:31 |
| 2 | Fyodor | Dostoevsky | Russian | 2021-05-01 13:36:31 | 2021-05-01 13:36:31 |
| 3 | Wang | Chris | Tainan | 2021-05-01 13:36:31 | 2021-05-01 13:36:31 |
| 4 | Lee | Mary | Tainan | 2021-05-01 13:36:31 | 2021-05-01 13:36:31 |
+-----------+-----------+------------+----------------+---------------------+---------------------+

4 rows in set
Time: 0.507s

返回上次填入資料的動作

$ npx sequelize-cli db:seed:undo

指定檔案返回填入資料的動作

$ npx sequelize-cli db:seed:undo --seed seeders/20210501134052-base-authors.js

返全部填入資料的動作

$ npx sequelize-cli db:seed:undo:all

  1. Sequelize ↩︎

  2. sequelize/cli: The Sequelize CLI ↩︎

  3. Manual Dialects | Sequelize ↩︎