[TOC]
### 多对多关联模型
满足条件:一个学生可以选择多个课程,一个课程也可以被多个学生选择
示例:
![](https://box.kancloud.cn/1e840ce844769077e3dda3a4f40c18ad_699x178.png)
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}]
}
~~~
![](https://box.kancloud.cn/1a0b8d7098ecd31596c538b090e9d265_579x360.png)
![](https://box.kancloud.cn/56daf427b4950f9183c79563df4d443a_477x194.png)
## 四、关联查询
~~~
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类