前言,最近在研究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的创建、更新、查询有更好建议的欢迎留言,我也在摸索中。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。