手把手教你彻底学会Sequelize表关联关系!少走弯路!

0.前言

我们在 Node 项目中使用 Sequelize 操作数据库时,通常需要处理多个表之间的关联关系。例如用户和订单之间的一对多关系、用户和角色之间的多对多关系。

网上的教程大部分都是给两行代码,缺少很多细节。

本文将通过详细的代码案例来讲解 Sequelize 中的一对一、一对多、多对多关系。绝对细节满满、通俗易懂。

1. 一对一

这里以用户和资料为例讲解一对一关系。一个用户对应一个资料。

1.1 创建数据库表

用户表

css 复制代码
CREATE TABLE `z_user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `age` int DEFAULT NULL,
  `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `status` tinyint DEFAULT NULL,
  `create_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `email` (`email`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;

资料表

css 复制代码
CREATE TABLE `z_profile` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int DEFAULT NULL COMMENT '用户id',
  `birthday` datetime DEFAULT NULL COMMENT '生日',
  `work` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '工作',
  PRIMARY KEY (`id`),
  KEY `foreign_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

1.2 定义模型(实体)

User 模型

css 复制代码
const User = sequelize.define('User', {
  id: {
    type: Sequelize.INTEGER, // 类型
    primaryKey: true, // 主键
    autoIncrement: true // 是否允许自增
  },
  name: {
    type: Sequelize.STRING(50),
    allowNull: false, // 是否允许为空
    unique: true // 唯一
  },
  age: {
    type: Sequelize.INTEGER,
    allowNull: false, // 是否允许为空
  },
  email: {
    type: Sequelize.STRING,
  },
  status: {
    type: Sequelize.INTEGER,
    defaultValue: 1
  }
}, {
  sequelize, // 我们需要传递连接实例
  tableName: 'z_user',// 对应的数据库表
  timestamps: false
});

Profile 实体

css 复制代码
const Profile = sequelize.define('Profile', {
  id: {
    type: Sequelize.INTEGER, // 类型
    primaryKey: true, // 主键
    autoIncrement: true // 是否允许自增
  },
  birthday: {
    type: Sequelize.DATE,
    allowNull: false, // 是否允许为空
  },
  work: {
    type: Sequelize.STRING(50),
    allowNull: false, // 是否允许为空
  },
}, {
  sequelize, // 我们需要传递连接实例
  tableName: 'z_profile',// 对应的数据库表
  timestamps: false
});

1.3 定义一对一关系

我们在 profile 的模型里面定义关联关系:

1. 基本版本

css 复制代码
// 一个用户拥有一个资料
User.hasOne(Profile);
// 一个资料只属于一个用户
Profile.belongsTo(User);

测试:

css 复制代码
// 获取用户详情
router.get('/detail', async (req, res) => {
  try {
    let result = await User.findOne({ include: Profile, where: { id: req.query.id } });
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

findOne 可以通过 id 查询信息,include 表示查询结果包含 Profile 的信息。

但是我们发现测试结果报错。

这是因为 hasOne 和 belongsTo 方法如果只传递一个模型参数,那 sequelize 默认会认为 Profile 模型对应数据库表的外键就是 userId 。而我们设计的外键字段是 user_id

这时候我们把 z_profile 表中的 user_id 改为 userId,再次测试:

成功!

2. 自定义外键版

那如果不想修改表中的字段,外键字段还想是 user_id 呢?

只需要在设置关联关系时,通过 foreignKey 指定外键是 user_id 即可:

测试结果同上。

2.一对多

这里以用户和订单为例讲解一对多关系。一个用户对应多个订单。

2.1 创建数据库表

订单表

css 复制代码
CREATE TABLE `z_order` (
  `id` int NOT NULL AUTO_INCREMENT,
  `number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '编号',
  `user_id` int DEFAULT NULL COMMENT '用户id',
  `order_amount` decimal(10,2) DEFAULT NULL COMMENT '订单总金额',
  `status` tinyint DEFAULT '0' COMMENT '订单状态',
  `create_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `email` (`order_amount`) USING BTREE,
  KEY `order_ibfk_1` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;

2.2 定义模型(实体)

Order 模型

css 复制代码
const Order = sequelize.define('Order', {
  id: {
    type: Sequelize.INTEGER, // 类型
    primaryKey: true, // 主键
    autoIncrement: true // 是否允许自增
  },
  number: {
    type: Sequelize.STRING(50),
    allowNull: false, // 是否允许为空
    unique: true // 唯一
  },
  orderAmount: {
    type: Sequelize.DECIMAL,
    defaultValue: 0,
    field: "order_amount"
  },
  status: {
    type: Sequelize.INTEGER,
    defaultValue: 1
  }
}, {
  sequelize, // 我们需要传递连接实例
  tableName: 'z_order',// 对应的数据库表
  timestamps: false
});

注:如果数据库订单表中金额是 order_amount,js 模型中是 orderAmount,需要通过 field 属性指定表的属性。

2.3 定义一对多关系

1.基本版

在 order.js 模型中定义一对多的关联关系

如果 z_order 表中用户外键是 userId,可以使用以下方式指定关联关系:

css 复制代码
// 一个用户拥有多个订单
User.hasMany(Order);
// 一个订单属于一个用户
Order.belongsTo(User);

2.自定义外键版

如果 z_order 表中用户外键是 user_id,可以使用指定 foreignKey 的方式指定关联关系:

测试:

css 复制代码
// 获取用户订单列表
router.get('/getOrderList', async (req, res) => {
  try {
    const result = await User.findOne({ include: Order, where: { id: req.query.id } });
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

3.多对多

这里以用户和角色为例讲解多对多关系。一个用户对应多个角色。一个角色属于多个用户。

3.1 创建数据库表

角色表

css 复制代码
CREATE TABLE `z_role` (
  `id` int NOT NULL AUTO_INCREMENT,
  `role_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;

用户角色关联表

css 复制代码
CREATE TABLE `z_user_role` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int NOT NULL,
  `role_id` int NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;

3.2 定义模型(实体)

Role 模型

css 复制代码
const Role = sequelize.define('Role', {
  id: {
    type: Sequelize.INTEGER, // 类型
    primaryKey: true, // 主键
    autoIncrement: true // 是否允许自增
  },
  roleName: {
    type: Sequelize.STRING(50),
    allowNull: false, // 是否允许为空
    field: "role_name"
  },
}, {
  sequelize, // 我们需要传递连接实例
  tableName: 'z_role',// 对应的数据库表
  timestamps: false
});

UserRole 模型

css 复制代码
const { Sequelize } = require('sequelize');
const sequelize = require('../database/sequelize');
const User = require("./user")
const Role = require("./role")
const UserRole = sequelize.define('UserRole', {
  userId: {
    type: Sequelize.INTEGER,
    field: "user_id",
    references: {
      model: User,
      key: 'id',
    },
  },
  roleId: {
    type: Sequelize.INTEGER,
    field: "role_id",
    references: {
      model: Role,
      key: 'id',
    },
  },
}, {
  sequelize, // 我们需要传递连接实例
  tableName: 'z_user_role',// 对应的数据库表
  timestamps: false
});

references 该属性定义了外键约束的具体表和字段,确保 user_id 和 role_id 字段分别与 z_user 表和 z_role 表的主键字段进行关联。

3.3 定义多对多关系

1.基本版

在 role.js 模型中定义多对多的关联关系

如果 z_user_role 表中外键是 userIdroleId,可以使用以下方式指定关联关系:

css 复制代码
User.belongsToMany(Role, {
  through: UserRole        // 指定中间表
});

Role.belongsToMany(User, {
  through: UserRole    // 指定中间表
});

2.自定义外键版

如果 z_user_role 表中外键是 user_idrole_id,可以使用指定 foreignKey 和 otherKey 的方式指定关联关系:

css 复制代码
User.belongsToMany(Role, {
  through: UserRole,          // 指定中间表
  foreignKey: "user_id",    // 本模型的外键
  otherKey: "role_id",       // 目标模型的外键
});
// 'user_id' 在 UserRole 中指向 'z_user' 表的主键(通常是 id)
// 'role_id' 在 UserRole 中指向 'z_role' 表的主键(通常是 id)

Role.belongsToMany(User, {
  through: UserRole,     // 指定中间表
  foreignKey: "role_id",    // 本模型的外键
  otherKey: "user_id",       // 目标模型的外键
});
  • through:指定中间表模型,这里是 UserRole。
  • foreignKey:表示当前模型用于关联的外键字段。例如,在 User.belongsToMany(Role) 中,foreignKey 是 user_id,表示 User 的 id 在 UserRole 中的字段。
  • otherKey:表示目标模型用于关联的外键字段。在 User.belongsToMany(Role) 中,otherKey 是 role_id,表示 Role 的 id 在 UserRole 中的字段。

测试:

css 复制代码
// 获取用户和角色
// 获取用户和角色
router.get('/getUserAndRoles', async (req, res) => {
  try {
    const result = await User.findOne({
      include: [
        {
          model: Role,
          attributes: ["id", "role_name"], // 只返回需要的字段
          through: {
            attributes: [], // 不包含中间表字段
          },
          where: {
            status: 0, // 只查询启用的角色
          },
        },
      ], where: { id: req.query.id }
    });
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});
  • User.findOne:根据用户的ID查找用户。
  • include:包含关联的角色信息。
    • model: Role:指定要包含的模型。
    • through: { attributes: [] }:指定不包含中间表 UserRole 的任何额外信息。
    • attributes: ["id", "role_name"]:只返回角色的指定字段。
    • where: { status: 0 }:添加条件,只查找状态为0(启用)的角色。

注:z_role 表这里又额外增加了一个 status 字段

4.完整代码

css 复制代码
链接: https://pan.baidu.com/s/1RTbgaCmt6vRSqn7ACvrzPg?pwd=6666 
提取码: 6666 

拿到代码之后记得:

  • npm install 安装依赖
  • 修改 Sequelize.js 里面连接 MySQL 数据库的账号密码等配置项
  • npm app.js 启动后端项目下x
相关推荐
sir76116 小时前
Redisson分布式锁实现原理
后端
大学生资源网16 小时前
基于springboot的万亩助农网站的设计与实现源代码(源码+文档)
java·spring boot·后端·mysql·毕业设计·源码
苏三的开发日记16 小时前
linux端进行kafka集群服务的搭建
后端
苏三的开发日记16 小时前
windows系统搭建kafka环境
后端
爬山算法17 小时前
Netty(19)Netty的性能优化手段有哪些?
java·后端
Tony Bai17 小时前
Cloudflare 2025 年度报告发布——Go 语言再次“屠榜”API 领域,AI 流量激增!
开发语言·人工智能·后端·golang
想用offer打牌17 小时前
虚拟内存与寻址方式解析(面试版)
java·后端·面试·系统架构
無量17 小时前
AQS抽象队列同步器原理与应用
后端
9号达人17 小时前
支付成功订单却没了?MyBatis连接池的坑我踩了
java·后端·面试
用户4973573379818 小时前
【轻松掌握通信协议】C#的通信过程与协议实操 | 2024全新
后端