Sequelize是Nodejs生態中一個比較出名的ORM框架。通過ORM框架,可以使用物件來運算元據庫表資料,提高了開發效率和程式碼可讀性,也方便後期維護。
今天主要介紹通過遷移[Migration]
來建立資料庫,表。
遷移的好處,可以類比git。通過每次建立遷移檔案,來支援更新,回滾資料庫表結構,也方便協同開發,也避免人工手動去直接修改資料庫,用程式碼自動管理。換個電腦,也不用去拷貝資料庫,直接執行遷移就可以完全恢復開發環境,極大減輕了心智負擔。
1. 建立專案, 安裝node package依賴
mkdir node_work
cd node_work
mkdir app
npm init -y
npm i sequelize-cli sequelize mysql2 koa
複製程式碼
2. 初始化Sequelize
npx sequelize init
複製程式碼
執行之後,會產生四個目錄:
config, migrations, models, seeders
config:
{
"development": {
"username": "root",
"password": "root",
"database": "app_development",
"host": "127.0.0.1",
"port": 8889,
"dialect": "mysql",
"timezone": "+08:00"
},
"test": {
"username": "root",
"password": null,
"database": "app_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "app_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
複製程式碼
環境env => {配置}
不同環境,對應不同的配置,也可以自定義環境,比如home
env指的是process.env.NODE_ENV
,
可以通過設定環境變數來改變,比如export NODE_ENV=production
;
遷移時候,也可以指定環境:npx sequelize db:migrate --env production
,來連線production對應配置的資料庫
建立資料庫:
npx sequelize db:create
複製程式碼
說明
npx
是npm5.2之後,自帶的一個命令。可以不用全域性安裝sequelize,使用時候,如果本地沒有,就去npm倉庫下載;下載完後或者本地已經下載過,就執行指令碼命令。這樣可以避免本地全域性包過期,環境問題,每次都使用最新版本
migrations: 遷移檔案
npx sequelize model:generate --name User --attributes username:string
複製程式碼
執行後,會生成20180918055558-create-user.js
遷移檔案,和models/user.js
模型檔案
其他欄位可以在遷移檔案中補全,最後再執行npx sequelize db:migrate
,就可以在資料庫中看到生成了users表
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
username: {
type: Sequelize.STRING(20),
allowNull: false
},
password: {
type: Sequelize.CHAR(32),
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
}, {
tableName: 'users',
charset: 'utf8mb4',
collate: 'utf8mb4_bin',
define: {
timestamps: true
}
}).then(() => {
// 新增索引
return queryInterface.addIndex('users', {
name: 'username',
unique: true,
fields: ['username']
});
});
},
// 回退時執行,刪除表
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
複製程式碼
執行遷移:
npx sequelize db:migrate
npx sequelize db:migrate:all
複製程式碼
撤銷遷移:
npx sequelize db:migrate:undo 最近一次的
npx sequelize db:migrate:undo:all
npx sequelize db:migrate:undo:all --to XXXXXXXXXXXXXX-create-posts.js
複製程式碼
--from, --to
引數,可以指定遷移檔案
models: 模型檔案
model:generate
生成的model都在這個目錄中
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
username: {
type: Sequelize.STRING(20),
allowNull: false
},
password: {
type: Sequelize.CHAR(32),
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
},
{
tableName: 'users',
charset: 'utf8mb4',
collate: 'utf8mb4_bin',
}).then(() => {
return queryInterface.addIndex('users', {
name: 'username',
unique: true,
fields: ['username']
});
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
複製程式碼
模型物件建立,預設會自動賦值,更新createdAt, updatedAt兩個timestamps欄位。下邊會給出完整示例。
seeders: 填充資料檔案
建立seed檔案:
npx sequelize seed:generate --name demo-user
複製程式碼
執行之後,會得到20180918090545-demo-user.js
'use strict';
const md5 = require('md5')
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.bulkInsert('Users', [
{
username: 'Kimoo',
password: md5('123456'),
createdAt: new Date(),
updatedAt: new Date(),
},
{
username: 'Reci',
password: md5('123321'),
createdAt: new Date(),
updatedAt: new Date(),
}
], {});
},
down: (queryInterface, Sequelize) => {
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.bulkDelete('Person', null, {});
*/
return queryInterface.bulkDelete('Users', null, {});
}
};
複製程式碼
填充資料:
npx sequelize db:seed:all
複製程式碼
撤銷資料:
npx sequelize db:seed:undo 最近一次的
npx sequelize db:seed:undo --seed name-of-seed-as-in-data 具體某個
npx sequelize db:seed:undo:all
複製程式碼
3. 具體實踐
app.js
(async function() {
const Koa = require('koa');
const KoaStaticCache = require('koa-static-cache');
const KoaBodyParser = require('koa-bodyparser');
const router = require('./routers/main');
const Session = require('koa-session');
const app = new Koa();
// app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
app.keys = ['app'];
app.use( Session({
key: 'koa:sess',
maxAge: 86400000,
autoCommit: true,
overwrite: true,
httpOnly: true,
signed: true,
rolling: false,
renew: false
}, app) );
// app.use( async (ctx, next) => {
// ctx.set('Access-Control-Allow-Origin','*');
// await next();
// } );
app.use( KoaStaticCache('./public', {
prefix: 'public',
gzip: true
}) );
app.use( KoaBodyParser() );
app.use( router.routes() );
app.listen(8088);
})();
複製程式碼
models/index.js
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
// 自動匯入 models 資料夾下的所有檔案,比如user.js這個模型檔案
// 自動載入模型並執行
// let users = require('./users');
// let UsersModel = users(sequelize, Sequelize);
// db[UsersModel.name] = UsersModel; // db['Users'] = UsersModel;
// 下面通過fs自動載入所有的檔案,並執行,同時生成的模型物件掛載到db物件下面,最後返回出去
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = sequelize['import'](path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
複製程式碼
routers/main.js
const KoaRouter = require('koa-router');
const md5 = require('md5');
const Models = require('../models');
const Sequelize = require('sequelize');
const router = new KoaRouter();
router.post('/register', async ctx => {
// console.log(ctx.request.body);
let username = ctx.request.body.username.trim();
let password = ctx.request.body.password.trim();
let repassword = ctx.request.body.repassword.trim();
if (username=='' || password == '' || repassword == '') {
return ctx.body = {
code: 1,
data: '使用者名稱或密碼不能為空'
}
}
if (password != repassword) {
return ctx.body = {
code: 2,
data: '兩次輸入的密碼不一致'
}
}
let user = await Models.Users.findOne({
where: {
username
}
});
if (user !== null) {
return ctx.body = {
code: 3,
data: '當前使用者已經被註冊了'
}
}
let newUser = await Models.Users.build({
username,
password: md5(password)
}).save();
ctx.body = {
code: 0,
data: {
id: newUser.get('id'),
username: newUser.get('username')
}
}
});
router.post('/login', async ctx => {
let username = ctx.request.body.username;
let password = ctx.request.body.password;
let user = await Models.Users.findOne({
where: {
username
}
});
if (user === null) {
return ctx.body = {
code: 1,
data: '不存在該使用者'
}
}
if (user.get('password') !== md5(password)) {
return ctx.body = {
code: 1,
data: '密碼錯誤'
}
}
// ctx.cookies.set('uid', user.get('id'), {
// httpOnly: false
// });
// 服務端傳送一個約定好的cookie,來表示當前是登入
// ctx.cookies.set('uid', user.get('id'), {
// // httpOnly,表示當前的cookie是否允許客戶端進行操作(js),如果為true,那麼就表示這個cookie是能使用者http協議的資料傳輸
// httpOnly: true,
// signed: true
// });
ctx.cookies.set('username', user.get('username'), {
httpOnly: false
});
ctx.session.uid = 1;
ctx.body = {
code: 0,
data: {
id: user.get('id'),
username: user.get('username')
}
}
});
})
module.exports = router;
複製程式碼
4. 測試介面,註冊使用者,新增資料
可以在postman
中測試介面,地址http://localhost:8088/register
,註冊使用者
node app.js
複製程式碼