Sequelize 學習筆記

_沒有好名字了_發表於2018-12-28

Sequelize 學習筆記

在 Node 中使用 MYSQL

  • 訪問 MYSQL 需要通過網路請求給 MYSQL 伺服器,對於 Node.js 程式,這個訪問 MySQL 伺服器的軟體包通常稱為 MySQL 驅動程式。不同的程式語言需要實現自己的驅動,MySQL 官方提供了各種多種的驅動程式。目前社群使用的比較廣泛的 Mysql Nodejs 驅動程式是 mysqlmysql2 (與mysql的聯絡:相容 mysql 大多數 API 以及效能更加優異)
  • node-mysql 是一個實現了 MySQL 協議的 Node.js JavaScript 客戶端,通過這個模組可以與 MySQL 資料庫建立連線、執行查詢等操作。如果直接使用 mysql 包提供的介面進行操作,編寫的程式碼就比較底層,基本類同直接的 sql,例如查詢操作
var mysql = require('mysql')// 建立與資料庫的連線var connection = mysql.createConnection({ 
host: 'example.com', user: 'example', password: 'secret-password'
})// 通過 mysql提供的介面進行查詢connection.query('SELECT * FROM project WHERE id = ?', ['1'], function( err, rows) {
if (err) {
// error
} else {
for (let row in rows) {
// 業務程式碼
}
}
})複製程式碼

ORM (Object Relational Mapping)

  • ORM框架的作用就是就是可以把關係型資料庫表結構 對映為javascript物件。例如

Table project

名稱 型別 允許空 預設值 主鍵 說明
id int(11) Y Y
content text Y N 專案描述
title varchar(90) Y N 專案名稱
priority_status int(11) Y 1 N 優先順序id
owner int(11) Y 0 N 專案經理(PM)id
team_id int(11) Y 0 N 小組id
process_status vint(11) Y N 專案狀態
schedule double(11, 2) Y 0.00 N 專案進度
prepayDate date Y N 預交時間
createDate date Y N 建立時間
deadline date Y N 截止時間
  // 就可以對映為,表中每一列對映為物件中一個鍵值對,每一行對映為一個js物件。  { 
id:'', content:'', title:'', priority_status:'', priority_status:'', owner:'' ...
}複製程式碼
  • 通過ORM框架, 對資料庫進行 CURD時,不需要直接去書寫 SQL語句,而是通過操作物件,進而由框架轉化為具體的SQL語句進行查詢, 將物件轉化我表中的一條記錄。m
  • Sequelize 是Nodejs中 比較常用的ORM框架

Sequelize基礎使用

建立資料庫連線

let Sequelize = require("sequelize");
let sequelize = new Sequelize( 'sample', // 資料庫名 'root', // 使用者名稱 'password', // 使用者密碼 {
dialect: 'mysql', // 資料庫使用mysql host: 'localhost', // 資料庫伺服器ip port: 3306, // mysql預設埠 define: {
'underscored': true // 欄位以下劃線(_)來分割(預設是駝峰命名風格)
}, pool: {
max: 20, // 連線池最大連線數量 min: 0, // 連線池最小連線數量 idle: 10000 // 每個執行緒最長等待時間
}
});
// freezeTableName: true,複製程式碼

定義模型

  • 模型:用來表述(描述)資料庫表欄位資訊的物件,每一個模型物件表示資料庫中的一個表,後續對資料庫的操作都是通過對應的模型物件來完成
// public define(modelName: String, attributes: Object, options: Object): Model// modelName:模型名稱,自定義 attributes:模型中包含都資料,每一個資料對映對應表中都每一個欄位 options:模型(表)的設定var project = sequelize.define(  'project', // 預設表名(一般這裡寫單數),生成時會自動轉換成複數形式。在模型訪問時的model.name  { 
id: {
type: Sequelize.INTEGER(11), // 欄位型別 allowNull: false, // 是否允許為NULL primaryKey: true, // 欄位是主鍵 autoIncrement: true, // 是否自增
}, content: {
type: Sequelize.TEXT, allowNull: true, unique: false // 欄位是否UNIQUE
}, title: {
type: Sequelize.STRING(90), allowNull: true, validate: {
//模型驗證 當前欄位值發生改變的時候進行驗證 is: ["^[a-z]+$",'i'], // 只允許字母 not: ["[a-z]",'i'], // 不能使用字母 isEmail: true
}, field: 'project_title' // 資料庫中欄位的實際名稱
}
}, {
tableName: 'project', // 手動設定表的實際名稱 timestamps: false, // 是否給每條記錄新增 createdAt 和 updatedAt 欄位,並在新增新資料和更新資料的時候自動設定這兩個欄位的值,預設為true paranoid: true, // 設定 deletedAt 欄位,當刪除一條記錄的時候,並不是真的銷燬記錄,而是通過該欄位來標示,即保留資料,進行假刪除,預設為false freezeTableName: false, // 禁用修改表名;
預設情況下,sequelize將自動將所有傳遞的模型名稱(define的第一個引數)轉換為複數。 預設為false
indexes: [] // 定義表索引
})// 表同步:沒有就新建,有就不變// project.sync();
// 表同步:沒有就新建,有就先刪除再新建// project.sync({// force: true//
});
複製程式碼

  • 一個模型類對應一個表,一個模型例項物件(DAO)就是一條對應的表記錄,通過操作這個物件來關聯操作對應的表中的資料,操作模型類就是操作表,操作模型類物件就是操作該表中的某條記錄,既有如下對應關係
    • 模型類 – 表
    • 模型例項 – 記錄
    • DAO(Data Access Objec) 這些例項物件擁有一系列運算元據的方法(saveupdatedestroy
// 建立模型例項物件 public static build(options: Object): Model | Model[]// options:一個物件,對應的就是表中的欄位(模型中定義的屬性),需要注意的是對該物件的操作不會立即反應到實際的資料庫中,需要通過後續的操作進行同步比如saveattr= { 
id:"test", content:"iscontent", titile:"istitle"
}let projectInstance = Project.build(attr) // 建立例項物件// 例項方法projectInstance.save() // 驗證該例項,如果通過驗證,則持久化到資料庫中projectInstance.update(updates: Object) // updates:要更新的欄位,呼叫該方法等同於呼叫.set()然後.save()projectInstance.destroy() // 銷燬該例項(假刪除或真刪除)// public static create(values: Object, options: Object): Promise<
Model>
// 構建一個新的模型例項,並進行儲存。與build()方法不同的是,此方法除建立新例項外,還會將其儲存到對應資料庫表中。await Project.create(attr)複製程式碼

  • sequelize除了通過模型建立出來的例項對單條資料進行操作,也可以通過模型類對整個對應的表進行操作
// 根據主鍵搜尋單條記錄: 模型.findById(id: Number | String | Buffer) Project.findById(1);
// 根據條件搜尋一條記錄: 模型.findOne(options: Object) Project.findOne({
where:{id:1
}
});
//搜尋特定記錄或建立它(如果沒有對應記錄): 模型.findOrCreate(options: Object)//在資料庫中搜尋多個記錄,返回資料和總計數: 模型.findAll(findOptions: Object)Project.findAll({
where: {
id:1, content: 'iscontent'
}, // 搜尋條件 limit: 10, // 查詢記錄條數限制 offset: 2, // 查詢時的偏移/起始位置,一般用於分頁查詢時 order: [ "id", //根據id排序 ["id","desc"] //根據id倒序 ], attributes:["title","owner"], //返回的欄位
})// 在資料庫中搜尋多個記錄,返回資料和總計數: 模型.findAndCountAll(findOptions: Object)// 與findAll類似,但是返回值包含 count 屬性 - 返回資料與總計數let result = await Project.findAndCountAll({
'limit': 20, 'offset': 0
});
// 返回的result物件將包含2個欄位:result.count是資料總數,result.rows是符合查詢條件的所有資料。複製程式碼
  • 限制欄位
  • 欄位重新命名
  • where 子句
  • 複合過濾查詢
    • 在sequelize中,還存在一個Op物件,用於處理複雜的條件操作。
    • 常見的Op及解釋
// 限制查詢結果物件中的欄位let result = await Project.findAndCountAll({ 
attributes:["title","owner"],
});
// =>
{
title: 'istitle', owner: 1
} 而不是返回所有的欄位
// 欄位重新命名let result = await Project.findAndCountAll({
attributes:[ ["title","projectTitle" ],"owner"],
});
// =>
{
projectTitle: 'istitle', owner: 1
} 而不是返回所有的欄位
// where 子句let result = await Project.findAndCountAll({
where:{
id: [1, 2, 3], title: 'a', content: 'iscontent'
}
});
// 查詢projects表中滿足 (id ===1 || id ===2 || id ===3) &
&
title === 'a' &
&
content === 'iscontent' 條件的記錄
// 複合過濾const Op = Sequelize.OpProject.findAll({
where: {
title: 'a', id: {
[Op.eq]: 1
}, // id為1 [Op.or]: [{
priority_status: [1, 2]
}, {
owner: {
[Op.gt]: 10
}
}] // (priority_status === 1 ||priority_status === 2)&
&
owner >
10

}
})// [常見的Op及解釋][5][Op.and]: {a: 5
} // 且 (a = 5)[Op.or]: [{a: 5
}, {a: 6
}] // (a = 5 或 a = 6)[Op.gt]: 6, // id >
6
[Op.gte]: 6, // id >
= 6
[Op.lt]: 10, // id <
10
[Op.lte]: 10, // id <
= 10
[Op.ne]: 20, // id != 20[Op.eq]: 3, // = 3[Op.not]: true, // 不是 TRUE[Op.between]: [6, 10], // 在 6 和 10 之間[Op.notBetween]: [11, 15], // 不在 11 和 15 之間[Op.in]: [1, 2], // 在 [1, 2] 之中[Op.notIn]: [1, 2], // 不在 [1, 2] 之中[Op.like]: '%hat', // 包含 '%hat'[Op.notLike]: '%hat' // 不包含 '%hat'[Op.iLike]: '%hat' // 包含 '%hat' (不區分大小寫) (僅限 PG)[Op.notILike]: '%hat' // 不包含 '%hat' (僅限 PG)[Op.regexp]: '^[h|a|t]' // 匹配正規表示式/~ '^[h|a|t]' (僅限 MySQL/PG)[Op.notRegexp]: '^[h|a|t]' // 不匹配正規表示式/!~ '^[h|a|t]' (僅限 MySQL/PG)[Op.iRegexp]: '^[h|a|t]' // ~* '^[h|a|t]' (僅限 PG)[Op.notIRegexp]: '^[h|a|t]' // !~* '^[h|a|t]' (僅限 PG)[Op.like]: {
[Op.any]: ['cat', 'hat']
} // 包含任何陣列['cat', 'hat'] - 同樣適用於 iLike 和 notLike[Op.overlap]: [1, 2] // &
&
[1, 2] (PG陣列重疊運算子)
[Op.contains]: [1, 2] // @>
[1, 2] (PG陣列包含運算子)
[Op.contained]: [1, 2] // <
@ [1, 2] (PG陣列包含於運算子)
[Op.any]: [2,3] // 任何陣列[2, 3]::INTEGER (僅限PG)複製程式碼
  • 常見統計操作API
    • model.count({
      where: {
      }
      }) 統計查詢個數
    • model.max(field, {where:{
      }
      }) 指定欄位查詢最大值
    • model.min(field, {where:{
      }
      }) 指定欄位查詢最小值
    • model.sum(field, {where: {
      }
      }) 求和

// public static  update(values: Object, options: Object): Promise<
Array<
affectedCount, affectedRows>
>
const result = await Project.update( {
content: "newContent"
}, {
where: {
id: projectId, user_id: userId
}
} )複製程式碼

  • model.destroy()
  • 需要注意的是,如果我們開啟了paranoid模式,destroy的時候不會執行DELETE語句,而是執行一個UPDATE語句將deleted_at欄位設定為當前時間(一開始此欄位值為NULL)。我們可以使用model.destroy({force: true
    })來強制刪除,從而真正從表中刪除該條記錄。
// public destroy(options: Object): Promise<
undefined>
const result = await Project.destroy({
where: {
id: projectId, user_id: userId
}
})複製程式碼

來源:https://juejin.im/post/5c259cf46fb9a049eb3bff49#comment