runoops.com

Sequelize 关联查询

Sequelize 支持标准关联关系: 一对一, 一对多 和 多对多。

为此,Sequelize 提供了 四种 关联类型,并将它们组合起来以创建关联:

  • HasOne 关联类型
  • BelongsTo 关联类型
  • HasMany 关联类型
  • BelongsToMany 关联类型

本章将讲解如何定义这四种类型的关联,然后讲解如何将它们组合来定义三种标准关联类型(一对一, 一对多 和 多对多)。

本教程实例代码:https://gitee.com/liqiang88/sequelize-test

本章节需要用到的实例代码中的MySQL 数据,需要执行:

$ node associate/create.js

来创建相应表和数据。

生成的表关系如图:


一对一关系

用户表与用户信息表
一个用户只有一张用户信息表

UserModel:

const { DataTypes } = require('sequelize');
const SequelizeConn = require('../SequelizeConn');

// 用户表
const UserModel = SequelizeConn.define('user', {
  id: {
    type: DataTypes.INTEGER,
    allowNull: false,
    primaryKey: true,
    autoIncrement: true
  },
  username: {
    type: DataTypes.STRING,
    allowNull: false
  },
  status: {
    type: DataTypes.INTEGER,
    allowNull: false,
  },
  password: {
    type: DataTypes.INTEGER,
    allowNull: false
  },
  createdAt: {
    type: DataTypes.DATE,
    allowNull: true
  },
  updatedAt: {
    type: DataTypes.DATE,
    allowNull: true
  }
}, {
  tableName: 'user'
})

module.exports = UserModel;

UserInfoModel:

const { DataTypes } = require('sequelize');
const SequelizeConn = require('../SequelizeConn');

// 用户信息表
const UserInfoModel = SequelizeConn.define('userInfo', {
    id: {
        type: DataTypes.INTEGER,
        allowNull: false,
        primaryKey: true,
        autoIncrement: true
    },
    user_id: {
        type: DataTypes.INTEGER,
        allowNull: false
    },
    birthday: {
        type: DataTypes.STRING,
        allowNull: true
    },
    nickname: {
        type: DataTypes.STRING,
        allowNull: true
    },
    createdAt: {
        type: DataTypes.DATE,
        allowNull: true
    },
    updatedAt: {
        type: DataTypes.DATE,
        allowNull: true
    }
}, {
    tableName: 'user_info'
});

module.exports = UserInfoModel;

定义一对一关系:

// 定义关系
// 1----1   一对一关系
User.hasOne(UserInfo, { foreignKey: 'user_id', sourceKey: 'id' });
// UserInfo.belongsTo(User, {foreignKey: 'user_id', targetKey: 'id'})
// User.hasOne(UserInfo, {foreignKey: 'user_id', sourceKey: 'id', as: 'user_info'})
// UserInfo.belongsTo(User, {foreignKey: 'user_id', targetKey: 'id', as: 'user_info'})

一对一查询:

(async () => {
    //查询数据
    let username = 'runoops';
    const userResult = await User.findOne({
        where: {
            username: username
        },
        include: [UserInfo]
        // include: [
        //   {model: UserInfo, as: 'user_info'},
        // ]
        // include: [{all: true}] //所有关联的信息
    });
    const userRs = userResult.toJSON();
    console.log(userRs);
})();

查询结果:

{
  id: 1,
  username: 'runoops',
  status: 1,
  password: 123,
  createdAt: 2023-05-10T03:48:15.000Z,
  updatedAt: 2023-05-10T03:48:15.000Z,
  user_info: {
    id: 1,
    user_id: 1,
    birthday: '2011-02-03',
    nickname: 'luo',
    createdAt: 2023-05-10T03:50:59.000Z,
    updatedAt: 2023-05-10T03:50:59.000Z
  }
}

注意:

如果在定义关系时,使用as,则在使用include的时候,也需要使用as,且变量需要相同
如果在定义关系是,没有使用as,则在使用include的时候,不能使用as,可以将模型直接写在数组中或者只使用model,不使用as。
不过怎么定义关系,都可以通过include: [{all: true}]的方式来拿到所有关联的信息


一对多关系

给上面的关系增加一个手机表,一个用户可以拥有多个手机

PhoneModel:

const { DataTypes } = require('sequelize');
const SequelizeConn = require('../SequelizeConn');

// 手机表
const PhoneModel = SequelizeConn.define('phone', {
  id: {
    type: DataTypes.INTEGER,
    allowNull: false,
    primaryKey: true,
    autoIncrement: true
  },
  user_id: {
    type: DataTypes.INTEGER,
    allowNull: false
  },
  number: {
    type: DataTypes.INTEGER,
    allowNull: false
  },
  createdAt: {
    type: DataTypes.DATE,
    allowNull: true
  },
  updatedAt: {
    type: DataTypes.DATE,
    allowNull: true
  }
}, {
  tableName: 'phone'
});

module.exports = PhoneModel;

定义一对多关系:

// 定义关系
// 1----n   一对多关系
User.hasMany(Phone, { foreignKey: 'user_id', sourceKey: 'id' });
Phone.belongsTo(User, { foreignKey: 'user_id', targetKey: 'id' });
// User.hasMany(Phone, {foreignKey: 'user_id', sourceKey: 'id', as: 'phone'})
// Phone.belongsTo(User, {foreignKey: 'user_id', targetKey: 'id', as: 'phone'})

一对多查询:

(async () => {
    //查询数据
    let username = 'runoops';
    const userResult2 = await User.findOne({
        where: {
            username: username
        },
        include: [UserInfo, Phone]
        // include: [
        //   {model: UserInfo, as: 'user_info'},
        //   {model: Phone, as: 'phone'},
        // ]
        // include: [{all: true}]
    });
    const userRs2 = userResult2.toJSON();
    console.log(userRs2);
})();

查询结果:

{
  id: 1,
  username: 'runoops',
  status: 1,
  password: 123,
  createdAt: 2023-05-10T03:48:15.000Z,
  updatedAt: 2023-05-10T03:48:15.000Z,
  user_info: {
    id: 1,
    user_id: 1,
    birthday: '2011-02-03',
    nickname: 'luo',
    createdAt: 2023-05-10T03:50:59.000Z,
    updatedAt: 2023-05-10T03:50:59.000Z
  },
  phones: [
    {
      id: 1,
      user_id: 1,
      number: 1878788787,
      createdAt: 2023-05-10T08:36:38.000Z,
      updatedAt: 2023-05-10T08:36:38.000Z
    },
    {
      id: 2,
      user_id: 1,
      number: 1878788788,
      createdAt: 2023-05-10T08:36:38.000Z,
      updatedAt: 2023-05-10T08:36:38.000Z
    }
  ]
}

多对多关系

新增课程表course,一门课程有多个学生,一个学生有多门课程
新增中间表user_course,用来关联学生与课程

CourseModel:

const { DataTypes } = require('sequelize');
const SequelizeConn = require('../SequelizeConn');

//课程表
const CourseModel = SequelizeConn.define('course', {
  id: {
    type: DataTypes.INTEGER,
    allowNull: false,
    primaryKey: true,
    autoIncrement: true
  },
  name: {
    type: DataTypes.STRING,
    allowNull: false
  },
  createdAt: {
    type: DataTypes.DATE,
    allowNull: true
  },
  updatedAt: {
    type: DataTypes.DATE,
    allowNull: true
  }
}, {
  tableName: 'course'
});

module.exports = CourseModel;

UserCourseModel:

const { DataTypes } = require('sequelize');
const SequelizeConn = require('../SequelizeConn');
//课程-学生关系表
const UserCourseModel = SequelizeConn.define('userCourse', {
    id: {
      type: DataTypes.INTEGER,
      allowNull: false,
      primaryKey: true,
      autoIncrement: true
    },
    user_id: {
      type: DataTypes.INTEGER,
      allowNull: false
    },
    course_id: {
      type: DataTypes.INTEGER,
      allowNull: false
    },
    createdAt: {
      type: DataTypes.DATE,
      allowNull: true
    },
    updatedAt: {
      type: DataTypes.DATE,
      allowNull: true
    }
  }, {
    tableName: 'user_course'
  });

  module.exports = UserCourseModel;

定义多对多关系:

// 定义关系
// n----n   多对多关系
Course.belongsToMany(User, { through: UserCourse, foreignKey: 'course_id' });
User.belongsToMany(Course, { through: UserCourse, foreignKey: 'user_id' });
// Course.belongsToMany(User, { through: UserCourse, foreignKey: 'course_id',targetKey: 'id', as: 'user' });
// User.belongsToMany(Course, { through: UserCourse, foreignKey: 'user_id', targetKey: 'id', as: 'course' });

多对多查询:

(async () => {
    let username = 'runoops';
    //查询用户的选课信息
    const userResult3 = await User.findOne({
        where: {
            username: username
        },
        include: [UserInfo, Phone, Course]
        // include: [
        //   {model: UserInfo, as: 'user_info'},
        //   {model: Phone, as: 'phone'},
        //   {model: Course, as: 'course'},
        // ]
        // include: [{ all: true }]
    });
    const userRs3 = userResult3.toJSON();
    console.log(userRs3);
})();

查询结果:

{
  id: 1,
  username: 'runoops',
  status: 1,
  password: 123,
  createdAt: 2023-05-10T03:48:15.000Z,
  updatedAt: 2023-05-10T03:48:15.000Z,
  user_info: {
    id: 1,
    user_id: 1,
    birthday: '2011-02-03',
    nickname: 'luo',
    createdAt: 2023-05-10T03:50:59.000Z,
    updatedAt: 2023-05-10T03:50:59.000Z
  },
  phones: [
    {
      id: 1,
      user_id: 1,
      number: 1878788787,
      createdAt: 2023-05-10T08:36:38.000Z,
      updatedAt: 2023-05-10T08:36:38.000Z
    },
    {
      id: 2,
      user_id: 1,
      number: 1878788788,
      createdAt: 2023-05-10T08:36:38.000Z,
      updatedAt: 2023-05-10T08:36:38.000Z
    }
  ],
  courses: [
    {
      id: 1,
      name: '语文',
      createdAt: 2023-05-10T08:36:38.000Z,
      updatedAt: 2023-05-10T08:36:38.000Z,
      user_course: [Object]
    },
    {
      id: 2,
      name: '数学',
      createdAt: 2023-05-10T08:36:38.000Z,
      updatedAt: 2023-05-10T08:36:38.000Z,
      user_course: [Object]
    }
  ]
}

预先加载

通过使用 include 参数 在 Sequelize 中执行预先加载。

上面查询的方式都是用预先加载的方式,详见上面例子,例如:

await User.findOne({ include: UserInfo });

延迟加载

延迟加载是指仅在确实需要时才获取关联数据的技术。

//延迟加载的方式
(async () => {
    //1. 先查询 user
    let username = 'runoops';
    const userResult4 = await User.findOne({
        where: {
            username: username
        }
    });
    // console.log(userResult4);
    // console.log(userResult4.username); //ok
    // 2. 再关联查询
    const userInfo = await userResult4.getUserInfo();
    console.log(userInfo.toJSON());
    const userPhone = await userResult4.getPhones();
    console.log(userPhone.map(item => item.toJSON()));
})();

结果如下:

$ node associate/querydelay.js
Executing (default): SELECT `id`, `username`, `status`, `password`, `createdAt`, `updatedAt` FROM `user` AS `us
er` WHERE `user`.`username` = 'runoops' LIMIT 1;
Executing (default): SELECT `id`, `user_id`, `birthday`, `nickname`, `createdAt`, `updatedAt` FROM `user_info`
AS `userInfo` WHERE `userInfo`.`user_id` = 1 LIMIT 1;
{
  id: 1,
  user_id: 1,
  birthday: '2011-02-03',
  nickname: 'luo',
  createdAt: 2023-05-10T03:50:59.000Z,
  updatedAt: 2023-05-10T03:50:59.000Z
}
Executing (default): SELECT `id`, `user_id`, `number`, `createdAt`, `updatedAt` FROM `phone` AS `phone` WHERE `
phone`.`user_id` = 1;
[
  {
    id: 1,
    user_id: 1,
    number: 1878788787,
    createdAt: 2023-05-10T08:36:38.000Z,
    updatedAt: 2023-05-10T08:36:38.000Z
  },
  {
    id: 2,
    user_id: 1,
    number: 1878788788,
    createdAt: 2023-05-10T08:36:38.000Z,
    updatedAt: 2023-05-10T08:36:38.000Z
  }
]

Captcha Code

0 笔记

分享笔记

Inline Feedbacks
View all notes