在使用 Sequelize ORM(对象关系映射)时,设计数据库表的外键关系是一个重要的任务,它直接影响到数据的完整性和查询性能。本文将介绍 Sequelize 外键的几种基本设计模式,以及它们的实现方式和优缺点,希望对前端开发者能有一定的指导意义。
什么是外键?
在关系型数据库中,一个数据表通常会包含与其他表的关联信息,这些联系通常通过外键来实现。一个外键是指一个表的字段或一组字段,其值必须与另一个表的相应字段匹配。
例如,在一个博客应用中,我们可以定义一个帖子(post)表和一个评论(comment)表。评论必须属于某个帖子,因此我们可以在评论表中添加一个指向帖子表的外键,将二者建立关联。帖子表的主键通常是一个自增长的 ID,而评论表的外键则是指向帖子表 ID 的一个整数字段,其值表示该评论所属的帖子 ID。
外键的设计模式
在 Sequelize 中,主要有以下几种外键的设计模式:
1. 手动定义外键
手动定义外键是一种最基础的设计模式。在 Sequelize 中,可以通过添加 foreignKey
属性来手动指定外键映射关系。例如,在上文中的评论表模型定义中,我们可以使用如下语句来创建一条外键关联:
Comment.belongsTo(Post, { foreignKey: 'postId' });
其中,Comment
和 Post
分别是评论和帖子模型,postId
是评论表中指向帖子表 ID 的字段名。此时,若我们要查询某个帖子下面的所有评论,可以使用如下语句:
Post.hasMany(Comment, { foreignKey: 'postId' });
这样 Sequelize 会自动根据外键关系查询出对应的评论,以 Comment
的集合形式返回。
不过,在手动定义外键时需要注意,我们需要手动维护外键的完整性约束,即插入、更新、删除操作前需要对外键关联进行检查和处理,以保证数据的完整性。
2. 间接关联表法
间接关联表法是一种表关联设计的拓展模式。在这种模式下,我们会建立一张中间表(通常称之为关联表或者连接表),用于连接两个数据表的关联。这种设计模式相对于手动定义外键,可以在保持数据完整性的前提下,更加灵活地定制关联关系。
例如,在博客应用中,我们可以使用一个 post_tags
表来连接帖子和标签两个表。post_tags
表可以包含两个字段:postId
和 tagId
,分别表示帖子 ID 和标签 ID,二者组成了一个复合主键。通过这种方式,我们可以方便地为每个帖子关联多个标签,同时连接表也可以扩展为一个元数据表,存储额外的关联信息。
在 Sequelize 中,可以定义间接关联表的条件,例如:
Post.belongsToMany(Tag, { through: 'post_tags' }); Tag.belongsToMany(Post, { through: 'post_tags' });
这里,我们通过 through
属性来指定关联表的名称,在这个例子中就是 post_tags
。Sequelize 将会自动创建一个 post_tags
表,并将其作为多对多关联的中间表。使用这种方式,Sequelize 会自动维护关联表的外键关系,并能够执行基于多对多关系的查询操作。
3. 聚合外键法
聚合外键法是一种特殊的表关联模式,它将数据表按照其属性的相似性划分为多个子集,并在每个子集中定义外键关系。这种设计模式通常适用于属性较多或有大量冗余数据的表,以提高查询性能和减小数据存储空间。
例如,在一个电商应用中,商品可能会包含多个属性信息,例如品牌、型号、颜色等。为了减少重复数据的存储,我们可以将商品拆分为多个子表,例如:
product_master
表:存储商品基本信息,如商品名、价格、发货地址。product_brand
表:存储商品品牌信息,如品牌名、品牌地址。product_model
表:存储商品型号信息,如型号名、适用机型、运行内存。product_color
表:存储商品颜色信息,如颜色名、颜色代码、RGB 值等。
在这种设计模式下,不同子表之间会存在外键关系,以保证数据的完整性。例如,品牌关联到商品的方式可以通过以下方式实现:
Brand.hasMany(ProductModel, { foreignKey: 'brandId' }); ProductModel.belongsTo(Brand, { foreignKey: 'brandId' });
使用这种方式,可以方便地对商品的属性进行查询操作,例如:
ProductModel.findAll({ include: [Brand, Color], where: { price: { [Op.lt]: 1000 } }, order: ['price'] })
这会查询出价格低于 1000 元的所有型号信息,并将其品牌和颜色信息包含进来。
总结
在 Sequelize 中,在设计数据表时,外键的选择和使用是一个重要的环节。我们需要首先分析业务需求,然后选择适合的外键设计方式,以达到保证数据完整性和查询性能的目的。在掌握了基本的外键设计模型后,我们可以结合具体业务场景,灵活选择最合适的关联方式,提高代码的可维护性和可扩展性。
完整示例代码:(数据库中需提前设置 post 表)
const { Sequelize, DataTypes, Op } = require('sequelize'); const sequelize = new Sequelize({ dialect: 'sqlite', storage: ':memory:', }); const Post = sequelize.define('Post', { title: DataTypes.STRING, content: DataTypes.TEXT }); const Comment = sequelize.define('Comment', { content: DataTypes.TEXT, creator: DataTypes.STRING }); Comment.belongsTo(Post, { foreignKey: 'postId' }); Post.hasMany(Comment, { foreignKey: 'postId' }); async function test() { await sequelize.sync({ force: true }); await Post.create({ title: 'Hello', content: 'This is a test post.' }); await Comment.create({ content: 'This is a test comment', creator: 'Alice', postId: 1 }); const post = await Post.findOne({ where: { title: 'Hello' } }); console.log(JSON.stringify(post, null, 2)); } test();
参考资料:
- Sequelize Official Documents:https://sequelize.org/
- 《Node.js:实战应用》
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/659e2984add4f0e0ff73a3ec