Sequelize 多对多的创建、查询、更新

前言,最近在研究nodeJs,新手上车,使用mysql的ORM库Sequelize,其中多对多,belongsToMany的创建、查询、更新摸索了几天,写下以下。
既然使用Sequelize,也就是不想原生sql语句,所以本文对原生sql大佬毫无作用,对Sequelize大佬毫无作用(反倒希望你们指点一二),只希望对刚上手Sequelize的胖友多少有点借鉴意义。
Sequelize的安装、链接mysql、简单的增删改查我就不累赘了,到处能搜索到,以下只针对belongsToMany。

我们假设有这样的一个场景,文章(Post)可以有多个标签(Tag),同样,一个Tag也可以对应多个Post,我们需要一张关联表PostTag来记录Post和Tag之间的关系。

一、表结构

//tag表

sequelize.define('tag', {
            name: {
                type: DataTypes.STRING(20),
                allowNull: false
            },
            createdAt: {
                type: DataTypes.DATE,
                get() {
                    return moment(this.getDataValue('createdAt')).format('YYYY-MM-DD HH:mm:ss');
                }
            },
            updatedAt: {
                type: DataTypes.DATE,
                get() {
                    return moment(this.getDataValue('updatedAt')).format('YYYY-MM-DD HH:mm:ss');
                }
            }
        },
        {
            freezeTableName: true
        })

//Post 表

 sequelize.define('post', {
                name: {
                    type: DataTypes.STRING(20),
                    allowNull: false
                },
                createdAt: {
                    type: DataTypes.DATE,
                    get() {
                        return moment(this.getDataValue('createdAt')).format('YYYY-MM-DD HH:mm:ss');
                    }
                },
                updatedAt: {
                    type: DataTypes.DATE,
                    get() {
                        return moment(this.getDataValue('updatedAt')).format('YYYY-MM-DD HH:mm:ss');
                    }
                }
            },
            {
                freezeTableName: true
            })

//PostTag表


    sequelize.define('postTag', {
                id: {
                    type: DataTypes.INTEGER,
                    primaryKey: true,
                    autoIncrement: true
                },
    
                postId: {
                    type: DataTypes.INTEGER,
                    allowNull:false,
                    unique: 'postTag'
                },
                tagId:{
                    type: DataTypes.INTEGER,
                    unique: 'postTag',
                    allowNull:false,
                    references: null
                },
                createdAt: {
                    type: DataTypes.DATE,
                    get() {
                        return moment(this.getDataValue('createdAt')).format('YYYY-MM-DD HH:mm:ss');
                    }
                },
                updatedAt: {
                    type: DataTypes.DATE,
                    get() {
                        return moment(this.getDataValue('updatedAt')).format('YYYY-MM-DD HH:mm:ss');
                    }
                }
            },
            {
                freezeTableName: true
            })
        

二、建立关联关系

const Sequelize = require('../config/db')
const PostTag = Sequelize.import('../schema/postTag.js')
const Tag = Sequelize.import('../schema/tag.js')
const Post = Sequelize.import('../schema/post.js')
// 表需要先sync 请自行处理,
//例如 PostTag.sync({force: false});

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
});


建议关联关系之后,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;
        });
    }

//关于transaction的更具体内容,可以查看Sequelize文档(尽管是全英 :))。

以上,仅供大家学习参考,如对 belongsToMany的创建、更新、查询有更好建议的欢迎留言,我也在摸索中。

阅读 5.7k

推荐阅读