[TOC]
### 多對多關聯模型
滿足條件:一個學生可以選擇多個課程,一個課程也可以被多個學生選擇
示例:

student 學生表
class 課程表
student_has_class 中間表
>[danger] 多對多,需要借助第三個中間表,中間表包含了2個外鍵,分別是兩個表的主鍵
*****
我們假設有這樣的一個場景,文章(Post)可以有多個標簽(Tag),同樣,一個Tag也可以對應多個Post,我們需要一張關聯表PostTag來記錄Post和Tag之間的關系。
## 一、model 建立3張模型表
* [ ] tag 表
* [ ] post 表
* [ ] post_tag 表
## 二、建立表關系
~~~
文章表
Post.belongsToMany(Tag, {
through: {
model: PostTag,
unique: false,
},
foreignKey: 'postId', //通過外鍵postId
constraints: false
});
標簽表
Tag.belongsToMany(Post, {
through: {
model: PostTag,
unique: false,
},
foreignKey: 'tagId', //通過外鍵tagId
constraints: false
});
~~~
>[danger] 建議關聯關系之后,Post會自動添加addTags、getTags、setTags方法。
Tag會自動添加addPosts、getPosts、setPosts方法。
## 三、添加
~~~
static async create(data) {
//例如我們tag表有2條數據,[{id:1,name:'標簽1'},{id:2,name:'標簽2'}]
//傳遞進來的data = {name:'文章1',tagIds:[1,2]}
let newPost = await Post.create({name: data.name}); //返回創建的post對象
let tags = await Tag.findAll({where: {id: data['tagIds']}})//找到對應的tagId對象
await newPost.setTags(tags) //通過setTags方法在postTag表添加記錄
return true
//以上操作會給post表創建一條新的記錄,{id:1,name:'文章1'}
//給postTag表添加2條記錄,[{id:1,postId:1,tagId:1},{id:2,post:1,tagId:2}]
}
~~~


## 四、關聯查詢
~~~
static async allPost() {
return await Post.findAll({
include: [
{model: Tag, attributes: ['id', 'name']}
]
});
}
//查詢結果
{
"code": 200,
"msg": "查詢成功!",
"data": [
{
"createdAt": "2018-12-10 13:18:11",
"updatedAt": "2018-12-10 13:18:11",
"id": 1,
"name": "文章1",
"tags": [
{
"createdAt": "2018-12-10 13:21:37",
"updatedAt": "2018-12-10 13:21:37",
"id": 1,
"name": "標簽1",
"postTag": {
"createdAt": "2018-12-10 13:18:11",
"updatedAt": "2018-12-10 13:18:11",
"id": 1,
"postId": 1,
"tagId": 1
}
},
{
"createdAt": "2018-12-10 13:21:37",
"updatedAt": "2018-12-10 13:21:37",
"id": 2,
"name": "標簽2",
"postTag": {
"createdAt": "2018-12-10 13:18:11",
"updatedAt": "2018-12-10 13:18:11",
"id": 2,
"postId": 1,
"tagId": 2
}
}
]
}
]
~~~
## 更新
~~~
static async updatePost(id, data) {
//id為需要修改的ID,data = {name:'修改文章',tagIds:[1]}
let tags = await Tag.findAll({where: {id: data['tagIds']}})
Post.findByPk(id).then(function (post) {
post.update({name: data.name})
post.setTags(tags)
})
return true
}
~~~
## 事務
多表更新中,我們總擔心那一步出錯,導致后期難以維護,這里可以使用transaction事務來處理。一旦那一步出錯,自動回滾。我拿創建那一步寫個范例。
~~~
static async create(data) {
let tags = await Tag.findAll({where: {id: data['tagIds']}})
return Sequelize.transaction(function (t) {
return Post.create({name: data.name}, {transaction: t})
.then(function (post) {
return post.setTags(tags, {transaction: t})
});
}).then(function (result) {
// 事務已被提交 result 是 promise 鏈返回到事務回調的結果
}).catch(function (err) {
// 事務已被回滾 throw 拋出錯誤
throw err;
});
}
~~~
- 概述
- 起步
- 跨域配置
- 路徑別名
- 路由
- api版本控制
- 錯誤和異常
- 全局異常處理
- 數據庫
- 創建遷移文件
- sequelize數據類型
- 配置
- 新增
- 查詢
- 條件查詢
- 模糊查詢
- 排序查詢
- 聚合查詢
- 分組查詢
- 分頁查詢
- 修改
- 刪除
- 獲取器
- 修改器
- 靜態屬性
- 字段驗證
- 外鍵約束
- 關聯模型
- 一對一
- 一對多
- 左外連接
- 多對多
- 字段顯示隱藏
- 事務
- 字段自增
- 驗證層
- egg-validate
- indicative驗證器
- egg-validate-plus
- betterValidate
- 校驗規則
- 中間件
- 安全
- 數據加密
- 單向加密
- 示例代碼
- 封裝egg加密
- 上傳
- path模塊
- 單文件上傳
- 多文件上傳
- 按照日期存儲
- 工具函數
- egg常用工具函數
- 緩存
- 配置緩存插件
- 設置緩存
- 獲取緩存
- 刪除緩存
- 消息隊列
- rabbitMQ
- 安裝
- 簡單隊列
- 工作隊列
- 工作隊列(dispach分發)
- 消息應答和持久化
- redis
- 數據類型
- 字符串類型(String)
- 哈希類型(Hash)
- 列表(List)
- 無序集合(Set)
- 可排序集合(Zset)
- 郵件系統
- nodeMailer
- 第三方模塊
- 生成隨機數
- JWT
- JWT鑒權
- 生成Token
- 短信服務
- 阿里大魚短信驗證碼
- 發送短信邏輯
- 阿里短信Node類