Sequelize用法詳解(MySQL)
接着上一篇《Js全棧開發之koa2中間件分離與數據層搭建》來繼續學習koa2,上一篇着重總結了數據庫的訪問及模型層的搭建,以及從koa2項目中拆分出中間件的具體步驟。還略微講解了ORM框架Sequelize的單表用法,但並沒有仔細的講解,本篇將重點彙總Sequelize框架的具體用法,總結一些平時比較常用的api及語法。
文章目錄
1. 安裝Sequelize及數據庫驅動包
npm install --save sequelize
# 選擇以下之一:
$ npm install --save pg pg-hstore # Postgres
$ npm install --save mysql2
$ npm install --save mariadb
$ npm install --save sqlite3
$ npm install --save tedious # Microsoft SQL Server
2. 連接數據庫
var Sequelize = require('sequelize');
sequelize = new Sequelize({
database: 'koa2learn', //數據庫名稱
username: 'root', //數據庫用戶名
password: 'xxxxxx', //數據庫密碼
host: 'xxx.xxx.xxx.xxx', //數據庫主機IP localhost
dialect: 'mysql', //數據庫類型 'mysql'|'mariadb'|'sqlite'|'postgres'|'mssql',
pool: { //連接池配置
max: 5, //最大連接數
min: 0, //最小連接數
acquire: 500, //請求超時時間
idle: 10000 //斷開連接後,連接實例在連接池保持的時間
},
logging: console.log //輸出日誌信息 true or false
});
// 測試連接
module.exports = {
testConn: async function(){
sequelize.authenticate().then(() => {
console.log("連接建立成功");
})
.catch(err => {
throw new Error(`無法連接數據庫:${err.message}`);
});
},
}
3. 定義數據模型
3.1 define方法定義
const { Sequelize, DataTypes } = require('sequelize');
const User = sequelize.define('User', {
id:{
type: DataTypes.UUID,
defaultValue: Sequelize.UUID,
primaryKey: true
},
firstName: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: "Dahlin"
},
lastName: {
type: DataTypes.STRING
// allowNull 默認爲 true
}
}, {
tableName: 'User',
timestamps: false
});
3.2 擴展Model定義
const { Sequelize, DataTypes, Model } = require('sequelize');
class User extends Model {}
User.init({
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING
// allowNull 默認爲 true
}
}, {
// 這是其他模型參數
sequelize, // 我們需要傳遞連接實例
modelName: 'User' // 我們需要選擇模型名稱
});
// 定義的模型是類本身
console.log(User === sequelize.models.User); // true
3.3 同步數據
(async () => {
await sequelize.sync({ force: true });
console.log("所有模型均已成功同步.");
})();
3.4 模型實例
class User extends Model {
static classLevelMethod() {
return 'foo';
}
instanceLevelMethod() {
return 'bar';
}
getFullname() {
return [this.firstname, this.lastname].join(' ');
}
}
User.init({
firstname: DataTypes.STRING,
lastname: DataTypes.STRING
}, { sequelize });
console.log(User.classLevelMethod()); // 'foo'
const user = User.build({ firstname: 'Jane', lastname: 'Doe' });
console.log(user.instanceLevelMethod()); // 'bar'
console.log(user.getFullname()); // 'Jane Doe'
// 創建實例
const dahlin = User.build({lastName:"badnothing"});
console.log(dahlin instanceof User);
console.log(dahlin.firstName);
await sequelize.sync({ force: true });
console.log("所有模型均已成功同步.");
const jane = await User.create({ firstName: "Jane" });
jane.lastName = "Ada";
// 實例json序列化
console.log(jane.toJSON()); // 這樣最好!
console.log(JSON.stringify(jane, null, 4)); // 這樣也不錯!
// 保存實例
await jane.save();
// 保存實例字段
//await jane.save({ fields: ['lastName '] });
// 刪除實例
//await jane.destroy();
// 重新加載實例
//await jane.reload();
4. 增刪改查基礎
4.1 插入
const user = await User.create(
{
firstName: "adaa",
lastName: "ffdf"
},
{ fields: ['firstName','lastName'] }
);
console.log("Jane's auto-generated ID:", user.id);
console.log(user.firstName); // 'alice123'
console.log(user.isAdmin); // false
// 批量插入
const user2 = await User.bulkCreate([
{ firstName: 'Jack', lastName:'Sparrow' },
{ firstName: 'Davy' , lastName:'Jones' }
]);
console.log(user2.length); // 2
console.log(user2[0] instanceof User); // true
console.log(user2[0].id); // 1 // (或另一個自動生成的值)
4.2 查詢
// 查詢所有用戶
const users = await User.findAll({
attributes: ['firstName','lastName'],
where:
{
firstName: 'ada',
lastName: 'fff'
}
});
console.log(users.every(user => user instanceof User)); // true
console.log("All users:", JSON.stringify(users, null, 4));
4.3 刪改
// 將所有沒有姓氏的人更改爲 "Doe"
await User.update({ lastName: "Doe" }, {
where: {
lastName: null
}
});
// 刪除所有名爲 "Jane" 的人
await User.destroy({
where: {
firstName: "Jane"
}
});
// 截斷表格
await User.destroy({
truncate: true
});
5. Finders模型查詢
- findByPk 方法使用提供的主鍵從表中僅獲得一個條目.
- findOne 方法獲得它找到的第一個條目(它可以滿足提供的可選查詢參數).
- findOrCreate
除非找到一個滿足查詢參數的結果,否則方法 findOrCreate 將在表中創建一個條目.
const [user, created] = await User.findOrCreate({
where: { username: 'sdepold' },
defaults: {
job: 'Technical Lead JavaScript'
}
});
console.log(user.username); // 'sdepold'
console.log(user.job); // 這可能是也可能不是 'Technical Lead JavaScript'
console.log(created); // 指示此實例是否剛剛創建的布爾值
if (created) {
console.log(user.job); // 這裏肯定是 'Technical Lead JavaScript'
}
- findAndCountAll
結合了 findAll 和 count 的便捷方法. 在處理與分頁有關的查詢時非常有用,在分頁中,你想檢索帶有 limit 和 offset 的數據,但又需要知道與查詢匹配的記錄總數.findAndCountAll 方法返回一個具有兩個屬性的對象:
count - 一個整數 - 符合查詢條件的記錄總數
rows - 一個數組對象 - 獲得的記錄
const { count, rows } = await Project.findAndCountAll({
where: {
title: {
[Op.like]: 'foo%'
}
},
offset: 10,
limit: 2
});
console.log(count);
console.log(rows);
6. 獲取器/設置器/虛擬字段
6.1 獲取器
獲取器是爲模型定義中的一列定義的 get() 函數
const User = sequelize.define('user', {
// 假設我們想要以大寫形式查看每個用戶名,
// 即使它們在數據庫本身中不一定是大寫的
username: {
type: DataTypes.STRING,
get() {
const rawValue = this.getDataValue(username);
return rawValue ? rawValue.toUpperCase() : null;
}
}
});
// 使用
const user = User.build({ username: 'SuperUser123' });
console.log(user.username); // 'SUPERUSER123'
console.log(user.getDataValue(username)); // 'SuperUser123'
6.2 設置器
設置器是爲模型定義中的一列定義的 set() 函數. 它接收要設置的值
const User = sequelize.define('user', {
username: DataTypes.STRING,
password: {
type: DataTypes.STRING,
set(value) {
// 在數據庫中以明文形式存儲密碼是很糟糕的.
// 使用適當的哈希函數來加密哈希值更好.
// 使用用戶名作爲鹽更好.
this.setDataValue('password', hash(this.username + value));
}
}
});
// 使用
const user = User.build({ username: 'dahlin', password: '123456' });
console.log(user.password);
console.log(user.getDataValue(password));
6.3 虛擬字段
虛擬字段是 Sequelize 在後臺填充的字段,但實際上它們不存在於數據庫中.
const { DataTypes } = require("sequelize");
const User = sequelize.define('user', {
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
fullName: {
type: DataTypes.VIRTUAL,
get() {
return `${this.firstName} ${this.lastName}`;
},
set(value) {
throw new Error('不要嘗試設置 `fullName` 的值!');
}
}
});
7. 原始查詢
// 刪改語句
const [results, metadata] = await sequelize
.query("UPDATE User SET firstName = :fistName WHERE lastName = :lastName",{
replacements: { fistName: 'Jack1' ,lastName:'Sparrow'},
});
console.log(results);
console.log(metadata);
// 查詢語句
const users = await sequelize.query("SELECT * FROM User where firstName=:firstName",
{ type: QueryTypes.SELECT ,
replacements: { firstName: 'Jack1' },
});
console.log(users);
// 查詢並映射成對象實例
const users = await sequelize.query("SELECT * FROM User where firstName=:firstName",
{ type: QueryTypes.SELECT ,
replacements: { firstName: 'Jack1' },
model: User,
mapToModel: true // 如果你有任何映射字段,則在此處傳遞 true
});
console.log(users);
console.log("All users:", JSON.stringify(users, null, 4));
8. 關聯查詢
8.1 默認設置
const Ship = sequelize.define('ship', {
name: DataTypes.TEXT,
crewCapacity: DataTypes.INTEGER,
amountOfSails: DataTypes.INTEGER
}, { timestamps: false });
const Captain = sequelize.define('captain', {
name: DataTypes.TEXT,
skillLevel: {
type: DataTypes.INTEGER,
validate: { min: 1, max: 10 }
}
}, { timestamps: false });
Captain.hasOne(Ship);
Ship.belongsTo(Captain); // 這將在 Ship 中創建 `captainId` 外鍵.
// 通過將模型傳遞給 `include` 來完成預先加載:
console.log((await Ship.findAll({ include: Captain })).toJSON());
// 或通過提供關聯的模型名稱:
console.log((await Ship.findAll({ include: 'captain' })).toJSON());
// 同樣,實例獲得用於延遲加載的 `getCaptain()` 方法:
const ship = Ship.findOne();
console.log((await ship.getCaptain()).toJSON());
8.2 直接提供外鍵名稱
Ship.belongsTo(Captain, { foreignKey: 'bossId' }); // 這將在 Ship 中創建 `bossId` 外鍵.
// 通過將模型傳遞給 `include` 來完成預先加載:
console.log((await Ship.findAll({ include: Captain })).toJSON());
// 或通過提供關聯的模型名稱:
console.log((await Ship.findAll({ include: 'Captain' })).toJSON());
// 同樣,實例獲得用於延遲加載的 `getCaptain()` 方法:
const ship = Ship.findOne();
console.log((await ship.getCaptain()).toJSON());
8.3 定義別名
Ship.belongsTo(Captain, { as: 'leader' }); // 這將在 Ship 中創建 `leaderId` 外鍵.
Ship.belongsTo(Captain, { as: 'leader', foreignKey: 'bossId' }); // 這將在 Ship 中創建 `bossId` 外鍵.
console.log((await Ship.findAll({ include: 'leader' })).toJSON());
// 或者,你可以傳遞一個指定模型和別名的對象:
console.log((await Ship.findAll({
include: {
model: Captain,
as: 'leader'
}
})).toJSON());
// 同樣,實例獲得用於延遲加載的 `getLeader()`方法:
const ship = Ship.findOne();
console.log((await ship.getLeader()).toJSON());
8.4 多對多關係
// 字符串創建方式
const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
Movie.belongsToMany(Actor, { through: 'ActorMovies' });
Actor.belongsToMany(Movie, { through: 'ActorMovies' });
// 傳遞模型方式創建
const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
const ActorMovies = sequelize.define('ActorMovies', {
MovieId: {
type: DataTypes.INTEGER,
references: {
model: Movie, // 'Movies' 也可以使用
key: 'id'
}
},
ActorId: {
type: DataTypes.INTEGER,
references: {
model: Actor, // 'Actors' 也可以使用
key: 'id'
}
}
});
Movie.belongsToMany(Actor, { through: 'ActorMovies' });
Actor.belongsToMany(Movie, { through: 'ActorMovies' });
8.5 延遲加載與預先加載
延遲加載是指僅在確實需要時才獲取關聯數據的技術. 另一方面,預先加載是指從一開始就通過較大的查詢一次獲取所有內容的技術.
- 延遲加載示例
const awesomeCaptain = await Captain.findOne({
where: {
name: "Jack Sparrow"
}
});
// 用獲取到的 captain 做點什麼
console.log('Name:', awesomeCaptain.name);
console.log('Skill Level:', awesomeCaptain.skillLevel);
// 現在我們需要有關他的 ship 的信息!
const hisShip = await awesomeCaptain.getShip();
// 用 ship 做點什麼
console.log('Ship Name:', hisShip.name);
console.log('Amount of Sails:', hisShip.amountOfSails);
- 預先加載示例
const awesomeCaptain = await Captain.findOne({
where: {
name: "Jack Sparrow"
},
include: Ship
});
// 現在 ship 跟着一起來了
console.log('Name:', awesomeCaptain.name);
console.log('Skill Level:', awesomeCaptain.skillLevel);
console.log('Ship Name:', awesomeCaptain.ship.name);
console.log('Amount of Sails:', awesomeCaptain.ship.amountOfSails);
9. 官方中文文檔
- Sequelize Docs 中文版
https://github.com/demopark/sequelize-docs-Zh-CN