多对多

优质
小牛编辑
142浏览
2023-12-01

多对多关联模型

满足条件:一个学生可以选择多个课程,一个课程也可以被多个学生选择 示例: 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://www.xnip.cn/wp-content/uploads/2020/docimg10/1a0b8d7098ecd31596c538b090e9d265_579x360.png)
![](https://www.xnip.cn/wp-content/uploads/2020/docimg10/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;
    });
}