在EggJS中使用Sequelize做聯表查詢


內容轉自https://www.jianshu.com/p/078087c69b77,感謝

1.EggJS引用Sequelize

  1. 安裝sequelize依賴和mysql驅動
cnpm i egg-sequelize mysql2 -S
  1. 啟用sequelize插件

  在config/plugin.js里面添加

sequelize: {
    enable: true,
    package: 'egg-sequelize',
},
  1. 配置數據庫

在config/config.default.js里面添加

  config.sequelize = {
    dialect: 'mysql',  // 表示使用mysql
    host: '127.0.0.1', // 連接的數據庫主機地址
    port: 3306, // mysql服務端口
    database: 'demo', // 數據庫名
    username: 'root',  // 數據庫用戶名
    password: 'root', // 數據庫密碼
    define: {  // model的全局配置
        timestamps: true,   // 添加create,update,delete時間戳
        paranoid: true,   // 添加軟刪除
        freezeTableName: true,  // 防止修改表名為復數
        underscored: false  // 防止駝峰式字段被默認轉為下划線
    },
    timezone: '+8:00',  // 由於orm用的UTC時間,這里必須加上東八區,否則取出來的時間相差8小時
    dialectOptions: {  // 讓讀取date類型數據時返回字符串而不是UTC時間
        dateStrings: true,
        typeCast(field, next) {
            if (field.type === "DATETIME") {
                return field.string();
            }
            return next();
        }
    }
};

  

2.定義Model

  1. 剛開始使用egg-init構建的Egg項目是沒有app/model目錄的,初始的項目結構如下:
itzishu
├── README.md
├── app
│   ├── controller
│   │   └── home.js
│   └── router.js
├── appveyor.yml
├── config
│   ├── config.default.js
│   └── plugin.js
├── package.json
└── test
    └── app
        └── controller
            └── home.test.js

  

先在app目錄下新建一個目錄為model,里面用來存放所有的數據庫里面定義的表的實例對象內容。

  1. 數據庫表的內容如下:
/*
 Navicat Premium Data Transfer
 Source Server         : 系統數據庫3306
 Source Server Type    : MySQL
 Source Server Version : 50725
 Source Host           : localhost:3306
 Source Schema         : demo
 Target Server Type    : MySQL
 Target Server Version : 50725
 File Encoding         : 65001
 Date: 12/05/2019 15:11:37
*/
 
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
 
-- ----------------------------
-- Table structure for classes
-- ----------------------------
DROP TABLE IF EXISTS `classes`;
CREATE TABLE `classes` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `createdAt` datetime DEFAULT NULL,
  `updatedAt` datetime DEFAULT NULL,
  `deletedAt` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
 
-- ----------------------------
-- Records of classes
-- ----------------------------
BEGIN;
INSERT INTO `classes` VALUES (1, '軟件工程1601', '2019-05-12 13:11:43', '2019-05-12 13:11:47', NULL);
INSERT INTO `classes` VALUES (2, '網絡工程1601', '2019-05-12 13:12:10', '2019-05-12 13:12:13', NULL);
COMMIT;
 
-- ----------------------------
-- Table structure for info
-- ----------------------------
DROP TABLE IF EXISTS `info`;
CREATE TABLE `info` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `age` int(11) NOT NULL,
  `sex` tinyint(255) NOT NULL DEFAULT '1' COMMENT '1為男,0為女',
  `studentId` int(11) NOT NULL,
  `createdAt` datetime DEFAULT NULL,
  `updatedAt` datetime DEFAULT NULL,
  `deletedAt` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;
 
-- ----------------------------
-- Records of info
-- ----------------------------
BEGIN;
INSERT INTO `info` VALUES (1, '許仙', 23, 1, 1, '2019-05-12 13:25:58', '2019-05-12 13:26:01', NULL);
INSERT INTO `info` VALUES (2, '白素貞', 20, 0, 2, '2019-05-12 13:26:41', '2019-05-12 13:26:46', NULL);
INSERT INTO `info` VALUES (3, '法海', 22, 1, 3, '2019-05-12 13:27:20', '2019-05-12 13:27:22', NULL);
INSERT INTO `info` VALUES (4, '小青', 18, 0, 4, '2019-05-12 13:27:48', '2019-05-12 13:27:51', NULL);
INSERT INTO `info` VALUES (5, '金如意', 20, 0, 5, '2019-05-12 13:28:34', '2019-05-12 13:28:37', NULL);
INSERT INTO `info` VALUES (6, '景松', 23, 1, 6, '2019-05-12 13:30:07', '2019-05-12 13:30:10', NULL);
COMMIT;
 
-- ----------------------------
-- Table structure for lession
-- ----------------------------
DROP TABLE IF EXISTS `lession`;
CREATE TABLE `lession` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `createdAt` datetime DEFAULT NULL,
  `updatedAt` datetime DEFAULT NULL,
  `deletedAt` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
 
-- ----------------------------
-- Records of lession
-- ----------------------------
BEGIN;
INSERT INTO `lession` VALUES (1, '計算機網絡', '2019-05-12 13:12:32', '2019-05-12 13:12:35', NULL);
INSERT INTO `lession` VALUES (2, 'Java程序設計', '2019-05-12 13:12:50', '2019-05-12 13:12:52', NULL);
INSERT INTO `lession` VALUES (3, '軟件項目管理', '2019-05-12 13:13:07', '2019-05-12 13:13:10', NULL);
INSERT INTO `lession` VALUES (4, '網絡安全', '2019-05-12 13:13:22', '2019-05-12 13:13:25', NULL);
COMMIT;
 
-- ----------------------------
-- Table structure for lession_student
-- ----------------------------
DROP TABLE IF EXISTS `lession_student`;
CREATE TABLE `lession_student` (
  `lessionId` int(11) NOT NULL,
  `studentId` int(11) NOT NULL,
  `createdAt` datetime DEFAULT NULL,
  `updatedAt` datetime DEFAULT NULL,
  `deletedAt` datetime DEFAULT NULL,
  PRIMARY KEY (`lessionId`,`studentId`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 
-- ----------------------------
-- Records of lession_student
-- ----------------------------
BEGIN;
INSERT INTO `lession_student` VALUES (1, 1, '2019-05-12 13:20:35', '2019-05-12 13:20:40', NULL);
INSERT INTO `lession_student` VALUES (1, 2, '2019-05-12 13:20:51', '2019-05-12 13:20:53', NULL);
INSERT INTO `lession_student` VALUES (1, 3, '2019-05-12 13:21:02', '2019-05-12 13:21:05', NULL);
INSERT INTO `lession_student` VALUES (1, 4, '2019-05-12 13:21:15', '2019-05-12 13:21:19', NULL);
INSERT INTO `lession_student` VALUES (1, 5, '2019-05-12 13:21:29', '2019-05-12 13:21:32', NULL);
INSERT INTO `lession_student` VALUES (1, 6, '2019-05-12 13:21:43', '2019-05-12 13:21:45', NULL);
INSERT INTO `lession_student` VALUES (2, 1, '2019-05-12 13:23:10', '2019-05-12 13:23:13', NULL);
INSERT INTO `lession_student` VALUES (2, 3, '2019-05-12 13:23:28', '2019-05-12 13:23:31', NULL);
INSERT INTO `lession_student` VALUES (2, 4, '2019-05-12 13:23:40', '2019-05-12 13:23:43', NULL);
INSERT INTO `lession_student` VALUES (2, 5, '2019-05-12 13:23:54', '2019-05-12 13:23:57', NULL);
INSERT INTO `lession_student` VALUES (3, 1, '2019-05-12 13:24:21', '2019-05-12 13:24:24', NULL);
INSERT INTO `lession_student` VALUES (3, 4, '2019-05-12 13:24:39', '2019-05-12 13:24:42', NULL);
INSERT INTO `lession_student` VALUES (4, 2, '2019-05-12 13:24:59', '2019-05-12 13:25:03', NULL);
INSERT INTO `lession_student` VALUES (4, 6, '2019-05-12 13:25:12', '2019-05-12 13:25:15', NULL);
COMMIT;
 
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `number` varchar(12) NOT NULL COMMENT '學號',
  `password` varchar(32) NOT NULL,
  `classId` int(11) NOT NULL,
  `createdAt` datetime DEFAULT NULL,
  `updatedAt` datetime DEFAULT NULL,
  `deletedAt` datetime DEFAULT NULL,
  PRIMARY KEY (`id`,`number`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;
 
-- ----------------------------
-- Records of student
-- ----------------------------
BEGIN;
INSERT INTO `student` VALUES (1, '160101', '202cb962ac59075b964b07152d234b70', 1, '2019-05-12 13:16:09', '2019-05-12 13:16:12', NULL);
INSERT INTO `student` VALUES (2, '160201', '202cb962ac59075b964b07152d234b70', 2, '2019-05-12 13:16:32', '2019-05-12 13:16:35', NULL);
INSERT INTO `student` VALUES (3, '160102', '202cb962ac59075b964b07152d234b70', 1, '2019-05-12 13:17:17', '2019-05-12 13:17:21', NULL);
INSERT INTO `student` VALUES (4, '160103', '202cb962ac59075b964b07152d234b70', 1, '2019-05-12 13:17:51', '2019-05-12 13:17:54', NULL);
INSERT INTO `student` VALUES (5, '160104', '202cb962ac59075b964b07152d234b70', 1, '2019-05-12 13:18:13', '2019-05-12 13:18:16', NULL);
INSERT INTO `student` VALUES (6, '160202', '202cb962ac59075b964b07152d234b70', 2, '2019-05-12 13:18:36', '2019-05-12 13:18:39', NULL);
COMMIT;
 
SET FOREIGN_KEY_CHECKS = 1;
 

  

其中,各個表之間存在聯系為:

  • student與info存在一對一關系
  • classes與student存在一對多關系
  • student與lession存在多對多關系,中間表為lession_student
  1. 根據數據表的結構,我們確定關系並寫好model目錄下相關文件

    • student.js
    module.exports = app => {
    const { STRING, INTEGER } = app.Sequelize;
 
    const Student = app.model.define('student', {
        id: {
            type: INTEGER,
            autoIncrement: true,
            primaryKey: true
        },
        number: {
            type: STRING,
            allowNull: false,
        },
        password: {
            type: STRING(32),
            allowNull: false
        },
        classId: {
            type: INTEGER,
            allowNull: false
        }
    });
 
    Student.associate = function (){
        // 與Info存在一對多關系,所以是hasOne()
        app.model.Student.hasOne(app.model.Info, {foreignKey: 'studentId'});
        // 與Classes存在多對一關系,所以使用belongsTo()
        app.model.Student.belongsTo(app.model.Classes, {foreignKey: 'classId', targetKey: 'id'});
        // 與Lessison存在多對多關系,使用belongsToMany()
        app.model.Student.belongsToMany(app.model.Lession, {
            through: app.model.LessionStudent,
            foreignKey: 'studentId',
            otherKey: 'lessionId'
        });
    }
 
    return Student;
}

  info.js

    module.exports = app => {
    const { STRING, INTEGER, BOOLEAN } = app.Sequelize;
 
    const Info = app.model.define('info', {
        id: {
            type: INTEGER,
            autoIncrement: true,
            primaryKey: true
        },
        name: {
            type: STRING(50),
            allowNull: false,
        },
        age: {
            type: INTEGER,
            allowNull: false
        },
        sex: {
            type: BOOLEAN,
            allowNull: false,
            get() {
                if ( this.getDataValue('sex') ){
                    return '男';
                }else {
                    return '女';
                }
            }
        },
        studentId: {
            type: INTEGER,
            allowNull: false
        }
    });
 
    Info.associate = function (){
        app.model.Info.belongsTo(app.model.Student, {foreignKey: 'studentId', targetKey: 'id'});
    }
 
    return Info;
}

  

這里注意下,在sex字段中,有一個get(){}方法,因為在數據表里面,sex字段存了1或0 ,1為男0為女,為了直接返回"男"或"女",這里使用get方法在找到數據后先做了處理,那返回給調用的函數的數據就是我們設置的值
 
classes.js
    module.exports = app => {
    const { STRING, INTEGER, BOOLEAN } = app.Sequelize;
 
    const Classes = app.model.define('classes', {
        id: {
            type: INTEGER,
            autoIncrement: true,
            primaryKey: true
        },
        name: {
            type: STRING(50),
            allowNull: false,
        },
        age: {
            type: INTEGER,
            allowNull: false
        },
        sex: {
            type: BOOLEAN,
            allowNull: false,
            get() {
                if ( this.getDataValue('sex') ){
                    return '男';
                }else {
                    return '女';
                }
            }
        },
        studentId: {
            type: INTEGER,
            allowNull: false
        }
    });
 
    Classes.associate = function (){
        // classes與student是一對多關系,所以這里使用hasMany()
        app.model.Classes.hasMany(app.model.Student, {foreignKey: 'classId', targetKey: 'id'});
    }
 
    return Classes;
}

  lession.js

    module.exports = app => {
    const { INTEGER, STRING } = app.Sequelize;
 
    const Lession = app.model.define('lession', {
        id: {
            type: INTEGER,
            primaryKey: true,
            autoIncrement: true
        },
        name: {
            type: STRING,
            allowNull: false
        }
    });
 
    Lession.associate = function(){
        // 與student表是多對多關系
        app.model.Lession.belongsToMany(app.model.Student, {
            through: app.model.LessionStudent,
            foreignKey: 'lessionId',
            otherKey: 'studentId'
        });
    }
 
    return Lession;
}

  

lession-student.js

    module.exports = app => {
    const { INTEGER } = app.Sequelize;
 
    const LessionStudent = app.model.define('lession_student', {
        lessionId: {
            type: INTEGER,
            primaryKey: true
        },
        studentId: {
            type: INTEGER,
            primaryKey: true
        }
    });
 
    LessionStudent.associate = function(){
 
    }
 
    return LessionStudent;
}

  

  1. 總結一下Model定義的內容
  • 針對MYSQL常用的字段類型

字段類型從 app.Sequelize 獲取,對應名字如下

   Sequelize.STRING                      // VARCHAR(255)
Sequelize.STRING(1234)                // VARCHAR(1234)
Sequelize.STRING.BINARY               // VARCHAR BINARY
Sequelize.TEXT                        // TEXT
Sequelize.TEXT('tiny')                // TINYTEXT
 
Sequelize.INTEGER                     // INTEGER
Sequelize.BIGINT                      // BIGINT
Sequelize.BIGINT(11)                  // BIGINT(11)
 
Sequelize.FLOAT                       // FLOAT
Sequelize.FLOAT(11)                   // FLOAT(11)
Sequelize.FLOAT(11, 12)               // FLOAT(11,12)
 
Sequelize.DOUBLE                      // DOUBLE
Sequelize.DOUBLE(11)                  // DOUBLE(11)
Sequelize.DOUBLE(11, 12)              // DOUBLE(11,12)
 
Sequelize.DECIMAL                     // DECIMAL
Sequelize.DECIMAL(10, 2)              // DECIMAL(10,2)
 
Sequelize.DATE                        // DATETIME 針對 mysql / sqlite, TIMESTAMP WITH TIME ZONE 針對 postgres
Sequelize.DATE(6)                     // DATETIME(6) 針對 mysql 5.6.4+. 小數秒支持多達6位精度
Sequelize.DATEONLY                    // DATE 不帶時間.
Sequelize.BOOLEAN                     // TINYINT(1)

  

字段屬性值

屬性名 類型 默認值 說明 說明
type Any 數據類型
primaryKey Boolean false 主鍵
autoIncrement Boolean false 自增
allowNull Boolean false 是否允許為空
defaultValue Any 默認值
field String 字段名 自定義字段名
unique Any 約束


  • 表與表的關聯性

在sequelize中,表與表之間用代碼來說存在三種關聯關系:一對一,一對多,多對多

  1. 一對一

在該項目中,student表和info表是存在一對一關系的,一個學生有一條專屬信息。

在student.js中,使用了hasOne()方法,第一個參數為關聯的模型對象Info,第二個參數為一個對象,其包含一個屬性為foreginKey為對應的信息表中studentId字段

在info.js中,使用了belongsTo()方法,第一個參數為關聯的模型對象Student, 第二個參數也是一個對象,其有兩個屬性,foreginKey為info表中的"studentId"字段,第二個參數targetKey為student表中的"id"字段


總結: hasOne()和belongsTo()第一個參數為本表關聯的另外一個表的Model實例,第二個參數中,都有foreginKey屬性,對hasOne來說,這個屬性值是對方表與自己Id對應的字段,對belongsTo來說,這個屬性值是本表上的與對方表id對應的字段名。belongsTo比hasOne多了個targetKey屬性,其為對方表的對應主鍵名
 
  1. 一對多

classes與student是一對多的關系,一個班級有多個學生,多個學生組成一個班級。

在student.js中,使用了belongsTo(),在classes.js中,使用了hasMany(),發現hasMany()與belongsTo()所需要的參數是類似的,但是這里注意,hasMany()里面的foreginKey值是對方表的classesId。結合第上面"一對一"的分析,我們可以總結出:

has開頭的方法中,foreginKey屬性值從對方的表上找,如果有targetKey的值則是自己的主鍵;

belongs開頭的方法中,foreginKey屬性值在自身表上找,targetKey屬性值則是對方表上

  1. 多對多

分析多對多關系,一個學生有多門課,一個課有多個學生,那我們可以用一個中間表lession-student.js做這個聯系。

在student.js中,我們使用了belongsToMany()方法,lession.js文件中也是如此,通過該方法的參數內容,可以發現其多了一個through屬性,其值是中間表的Model實例。根據上面的規律,belongs開頭的方法里面foreginKey找自己,otherKey找其他,所以很容易理解。


總結: 在Model的實例里面,重寫Model的associate方法,將關聯的關系放到里面。

一對一的方法有:hasOne(Model, {foreignKey:對方,})belongsTo(Model,{foreignKey:自己,targetKey:對方})

一對多的方法有: hasMany(Model,{foreignKey:對方, targetKey:自己})belongsTo(Model,{foreignKey:自己,targetKey:對方})

多對多的方法有: belongsToMany(Model,{through:Model, targetKey:自己, otherKey:對方})



.聯表查詢

  • 一對一

在controller里面如下寫

 // 獲取學生信息 通過一對多的聯系
    async info(){
        const { ctx, app } = this;
        let result = await app.model.Student.findAll({
          include: {
            model: app.model.Info
          }
        });
        ctx.body = result;
    }

  

獲取到的值如下:

    [
        // 第一個學生
    {
        "id": 1,
        "number": "160101",
        "password": "202cb962ac59075b964b07152d234b70",
        "classId": 1,
        "createdAt": "2019-05-12 13:16:09",
        "updatedAt": "2019-05-12 13:16:12",
        "deletedAt": null,
        "info": {  // 聯表查到的信息
            "sex": "男",
            "id": 1,
            "name": "許仙",
            "age": 23,
            "studentId": 1,
            "createdAt": "2019-05-12 13:25:58",
            "updatedAt": "2019-05-12 13:26:01",
            "deletedAt": null
        }
    },
    // 第二個學生
    {
        "id": 2,
        "number": "160201",
        "password": "202cb962ac59075b964b07152d234b70",
        "classId": 2,
        "createdAt": "2019-05-12 13:16:32",
        "updatedAt": "2019-05-12 13:16:35",
        "deletedAt": null,
        "info": {
            "sex": "女",
            "id": 2,
            "name": "白素貞",
            "age": 20,
            "studentId": 2,
            "createdAt": "2019-05-12 13:26:41",
            "updatedAt": "2019-05-12 13:26:46",
            "deletedAt": null
        }
    },
    {
        "id": 3,
        "number": "160102",
        "password": "202cb962ac59075b964b07152d234b70",
        "classId": 1,
        "createdAt": "2019-05-12 13:17:17",
        "updatedAt": "2019-05-12 13:17:21",
        "deletedAt": null,
        "info": {
            "sex": "男",
            "id": 3,
            "name": "法海",
            "age": 22,
            "studentId": 3,
            "createdAt": "2019-05-12 13:27:20",
            "updatedAt": "2019-05-12 13:27:22",
            "deletedAt": null
        }
    },
    {
        "id": 4,
        "number": "160103",
        "password": "202cb962ac59075b964b07152d234b70",
        "classId": 1,
        "createdAt": "2019-05-12 13:17:51",
        "updatedAt": "2019-05-12 13:17:54",
        "deletedAt": null,
        "info": {
            "sex": "女",
            "id": 4,
            "name": "小青",
            "age": 18,
            "studentId": 4,
            "createdAt": "2019-05-12 13:27:48",
            "updatedAt": "2019-05-12 13:27:51",
            "deletedAt": null
        }
    },
    {
        "id": 5,
        "number": "160104",
        "password": "202cb962ac59075b964b07152d234b70",
        "classId": 1,
        "createdAt": "2019-05-12 13:18:13",
        "updatedAt": "2019-05-12 13:18:16",
        "deletedAt": null,
        "info": {
            "sex": "女",
            "id": 5,
            "name": "金如意",
            "age": 20,
            "studentId": 5,
            "createdAt": "2019-05-12 13:28:34",
            "updatedAt": "2019-05-12 13:28:37",
            "deletedAt": null
        }
    },
    {
        "id": 6,
        "number": "160202",
        "password": "202cb962ac59075b964b07152d234b70",
        "classId": 2,
        "createdAt": "2019-05-12 13:18:36",
        "updatedAt": "2019-05-12 13:18:39",
        "deletedAt": null,
        "info": {
            "sex": "男",
            "id": 6,
            "name": "景松",
            "age": 23,
            "studentId": 6,
            "createdAt": "2019-05-12 13:30:07",
            "updatedAt": "2019-05-12 13:30:10",
            "deletedAt": null
        }
    }
]

  

一對多

// 獲取班級名為 軟件工程1601 的班級學生
    async student(){
      const { ctx, app } = this;
      let result = await app.model.Classes.findAll({
        where: {
          name: '軟件工程1601'
        },
        include: {
          model: app.model.Student
        }
      })
      ctx.body = result;
    }

  

獲取數據如下:

    [
    {
        "id": 1,
        "name": "軟件工程1601",
        "createdAt": "2019-05-12 13:11:43",
        "updatedAt": "2019-05-12 13:11:47",
        "deletedAt": null,
        "students": [
            {
                "id": 1,
                "number": "160101",
                "password": "202cb962ac59075b964b07152d234b70",
                "classId": 1,
                "createdAt": "2019-05-12 13:16:09",
                "updatedAt": "2019-05-12 13:16:12",
                "deletedAt": null
            },
            {
                "id": 3,
                "number": "160102",
                "password": "202cb962ac59075b964b07152d234b70",
                "classId": 1,
                "createdAt": "2019-05-12 13:17:17",
                "updatedAt": "2019-05-12 13:17:21",
                "deletedAt": null
            },
            {
                "id": 4,
                "number": "160103",
                "password": "202cb962ac59075b964b07152d234b70",
                "classId": 1,
                "createdAt": "2019-05-12 13:17:51",
                "updatedAt": "2019-05-12 13:17:54",
                "deletedAt": null
            },
            {
                "id": 5,
                "number": "160104",
                "password": "202cb962ac59075b964b07152d234b70",
                "classId": 1,
                "createdAt": "2019-05-12 13:18:13",
                "updatedAt": "2019-05-12 13:18:16",
                "deletedAt": null
            }
        ]
    }
]

  

  • 多對多

從學生獲取課程信息

 

 // 獲取學生的選課內容
    async lession(){
      const { ctx, app } = this;
      let result = await app.model.Student.findAll({
        where:{
          id: 1,
        },
        include: [
          {model: app.model.Info},
          {model: app.model.Lession}
        ]
      });
      ctx.body = result;
    }

  

這里的話,注意include的值為一個數組了,這樣可以多個聯表獲取數據

數據如下:

[
    {
        "id": 1,
        "number": "160101",
        "password": "202cb962ac59075b964b07152d234b70",
        "classId": 1,
        "createdAt": "2019-05-12 13:16:09",
        "updatedAt": "2019-05-12 13:16:12",
        "deletedAt": null,
        "info": {
            "sex": "男",
            "id": 1,
            "name": "許仙",
            "age": 23,
            "studentId": 1,
            "createdAt": "2019-05-12 13:25:58",
            "updatedAt": "2019-05-12 13:26:01",
            "deletedAt": null
        },
        "lessions": [
            {
                "id": 1,
                "name": "計算機網絡",
                "createdAt": "2019-05-12 13:12:32",
                "updatedAt": "2019-05-12 13:12:35",
                "deletedAt": null,
                "lession_student": {
                    "lessionId": 1,
                    "studentId": 1,
                    "createdAt": "2019-05-12 13:20:35",
                    "updatedAt": "2019-05-12 13:20:40",
                    "deletedAt": null
                }
            },
            {
                "id": 2,
                "name": "Java程序設計",
                "createdAt": "2019-05-12 13:12:50",
                "updatedAt": "2019-05-12 13:12:52",
                "deletedAt": null,
                "lession_student": {
                    "lessionId": 2,
                    "studentId": 1,
                    "createdAt": "2019-05-12 13:23:10",
                    "updatedAt": "2019-05-12 13:23:13",
                    "deletedAt": null
                }
            },
            {
                "id": 3,
                "name": "軟件項目管理",
                "createdAt": "2019-05-12 13:13:07",
                "updatedAt": "2019-05-12 13:13:10",
                "deletedAt": null,
                "lession_student": {
                    "lessionId": 3,
                    "studentId": 1,
                    "createdAt": "2019-05-12 13:24:21",
                    "updatedAt": "2019-05-12 13:24:24",
                    "deletedAt": null
                }
            }
        ]
    }
]

  

從課程獲取選課學生:

// 獲取某個課的參課學生
    async lessionStudent(){
      const { ctx, app } = this;
      let result = await app.model.Lession.findAll({
        where:{
          name: '網絡安全'
        },
        include: {
          model: app.model.Student,
          include: {
            model: app.model.Info
          }
        }
      });
      ctx.body = result;
    }

  

這里注意,在include的下面又有一個include,第二個include是相對Student表的

數據如下:

[
    {
        "id": 4,
        "name": "網絡安全",
        "createdAt": "2019-05-12 13:13:22",
        "updatedAt": "2019-05-12 13:13:25",
        "deletedAt": null,
        "students": [
            {
                "id": 2,
                "number": "160201",
                "password": "202cb962ac59075b964b07152d234b70",
                "classId": 2,
                "createdAt": "2019-05-12 13:16:32",
                "updatedAt": "2019-05-12 13:16:35",
                "deletedAt": null,
                "lession_student": {
                    "lessionId": 4,
                    "studentId": 2,
                    "createdAt": "2019-05-12 13:24:59",
                    "updatedAt": "2019-05-12 13:25:03",
                    "deletedAt": null
                },
                "info": {
                    "sex": "女",
                    "id": 2,
                    "name": "白素貞",
                    "age": 20,
                    "studentId": 2,
                    "createdAt": "2019-05-12 13:26:41",
                    "updatedAt": "2019-05-12 13:26:46",
                    "deletedAt": null
                }
            },
            {
                "id": 6,
                "number": "160202",
                "password": "202cb962ac59075b964b07152d234b70",
                "classId": 2,
                "createdAt": "2019-05-12 13:18:36",
                "updatedAt": "2019-05-12 13:18:39",
                "deletedAt": null,
                "lession_student": {
                    "lessionId": 4,
                    "studentId": 6,
                    "createdAt": "2019-05-12 13:25:12",
                    "updatedAt": "2019-05-12 13:25:15",
                    "deletedAt": null
                },
                "info": {
                    "sex": "男",
                    "id": 6,
                    "name": "景松",
                    "age": 23,
                    "studentId": 6,
                    "createdAt": "2019-05-12 13:30:07",
                    "updatedAt": "2019-05-12 13:30:10",
                    "deletedAt": null
                }
            }
        ]
    }
]

  

4. 總結

用時4小時,調試加數據庫設置,代碼編寫,查文檔。允許我偷個懶,不想總結了,仔細閱讀內容,基本上可以了解Sequelize在聯表查詢上的基本用法了

 

2020.3.24補充

1. 模型(表)之間的關聯關系

1.1 模型關系概述

數據庫中的表之間存在一定的關聯關系,表之間的關系基於主/外鍵進行關聯、創建約束等。關系表中的數據分為1對1(1:1)、1對多(1:M)、多對多(N:M)三種關聯關系。

Sequelize中建立關聯關系,通過調用模型(源模型)的belongsTohasOnehasManybelongsToMany方法,再將要建立關系的模型(目標模型)做為參數傳入即可。這些方法會按以下規則創建關聯關系:

  • hasOne - 與目標模型建立1:1關聯關系,關聯關系(外鍵)存在於目標模型中。詳見:Model.hasOne()
  • belongsTo - 與目標模型建立1:1關聯關系,關聯關系(外鍵)存在於源模型中。詳見:Model.belongsTo()
  • hasMany - 與目標模型建立1:N關聯關系,關聯關系(外鍵)存在於目標模型中。詳見:Model.hasMany()
  • belongsToMany - 與目標模型建立N:M關聯關系,會通過sourceIdtargetId創建交叉表。詳見:Model.belongsToMany()

 

1.2 定義關系模型

為了能夠清楚說明模型關系的定義及關系模型的使用,我們定義如下4個模型對象:

  • 用戶(User)-與其它模型存在1:11:NN:M
  • 用戶登錄信息(UserCheckin)-與User存在1:1關系
  • 用戶地址(UserAddress)-與User存在N:1關系
  • 角色(Role)-與User存在N:M關系

這幾個模型的E-R結構如下:

Node.js Sequelize 模型(表)之間的關聯及關系模型的操作

2. 關系/關聯相關的API

2.1 綜合介紹

在Sequelize中創建關聯通過調用模型()的 belongsTo / hasOne / hasMany / belongsToMany方法完成,並且為這個方法第一個參數提供另一個模型(目標)。各種方法以下規則創建關聯:

  • hasOne - 添加外鍵到目標模型,並以單數關系混入到源模型
  • belongsTo - 為當前模型添加外鍵,並以單數關系混入到源模型
  • hasMany - 添加外鍵到目標模型,並以復數關系混入到源模型
  • belongsToMany - 為連接的表創建N:M關系並以復數關系混入到源模型。會通過sourceIdtargetId創建交叉表。

在創建關系時,可以通過as選項指定別名。這在對一模型引用兩次,或者對關聯模型使用定義之外的名稱時非常有用。

User.hasMany(Picture)
User.belongsTo(Picture, { as: 'ProfilePicture', constraints: false })

user.getPictures() // 獲取所有圖片
user.getProfilePicture() // 僅獲取主圖

User.findAll({
  where: ...,
  include: [
    { model: Picture }, // 加載所有圖片
    { model: Picture, as: 'ProfilePicture' }, // 加載主圖,名稱拼寫必須與關聯關系中命名相同
  ]
})

要完全控制通過Sequlize 添加的外鍵列,可以使用foreignKey選項。選項值可以是表示名稱的字符串或類似使用sequelize.define進行模型定義時對象。

User.hasMany(Picture, { foreignKey: 'uid' })

這樣外鍵列會使用uid代替默認的userId

User.hasMany(Picture, {
  foreignKey: {
    name: 'uid',
    allowNull: false
  }
})

指定uid列不能為NULL。在大多數情況下,這將覆蓋的外鍵約束,這sequelize自動創建的,這在外鍵禁用時非常有用。

當匹配關聯模型時,可限制只匹配部分模型。這些查詢條件與在find/findAll中的使用方式相同。如,只查找'jpg'格式的圖片:

user.getPictures({
  where: {
    format: 'jpg'
  }
})

 

2.2 Model.hasOne() - 擁有一個

Model.hasOne(target, [options])

創建當前模型(源)到目標模型之間的關系,外鍵會被添加到目標模型中。

名稱 類型 說明
target Model  
[options] object  
[options.hooks=false] boolean 設置為 true 時,會在關聯模型刪除時執行 before-/afterDestroy 鈎子方法
[options.as] string 當前模型(源)的別名,單數形式。如果你為一個表創建多次關聯,或者不想使用定義模型時使用的名稱,那么就應該為模型指定一個別名。
[options.foreignKey] string | object 目標表中的外鍵名或相當於定義外鍵列的對象 (語法參考 Sequelize.define )。使用對象時,應該添加一個name來設置列名。默認的外鍵命名規為源模型名+源模型主鍵名
[options.onDelete='SET NULL | CASCADE'] string 如果外允許空則 SET NULL,其它則 CASCADE
[options.onUpdate='CASCADE'] string  
[options.constraints=true] boolean 是否在刪除或更新時啟用外鍵約束

 

2.3 Model.belongsTo() - 屬於

Model.belongsTo(target, [options])

創建當前模型(源)到目標模型之間的關系,外鍵會被添加到源模型中。

名稱 類型 說明
target Model  
[options] object  
[options.hooks=false] boolean 設置為 true 時,會在關聯模型刪除時執行 before-/afterDestroy 鈎子方法
[options.as] string 當前模型(源)的別名,單數形式。如果你為一個表創建多次關聯,或者不想使用定義模型時使用的名稱,那么就應該為模型指定一個別名。
[options.foreignKey] string | object 目標表中的外鍵名或相當於定義外鍵列的對象 (語法參考 Sequelize.define )。使用對象時,應該添加一個name來設置列名。默認的外鍵命名規為源模型名+源模型主鍵名
[options.scope] object 鍵/值 集合,用於目標的創建和查找操作(sqlite 不支持 N:M)
[options.onDelete='SET NULL | NO ACTION'] string 如果外允許空則 SET NULL,其它則 CASCADE
[options.onUpdate='CASCADE'] string  
[options.constraints=true] boolean 是否在刪除或更新時啟用外鍵約束

 

2.4 Model.hasMany() - 擁有多個

Model.hasMany(target, [options])

創建當前模型(源)到目標模型之間的 1:m 的關系,外鍵會被添加到目標模型中。

名稱 類型 說明
target Model  
[options] object  
[options.hooks=false] boolean 設置為 true 時,會在關聯模型刪除時執行 before-/afterDestroy 鈎子方法
[options.as] string 當前模型(源)的別名,單數形式。如果你為一個表創建多次關聯,或者不想使用定義模型時使用的名稱,那么就應該為模型指定一個別名。
[options.foreignKey] string | object 目標表中的外鍵名或相當於定義外鍵列的對象 (語法參考 Sequelize.define )。使用對象時,應該添加一個name來設置列名。默認的外鍵命名規為源模型名+源模型主鍵名
[options.targetKey] string 用於關聯目標表的字段名。默認為目標表的主鍵。
[options.onDelete='SET NULL | NO ACTION'] string 如果外允許空則 SET NULL,其它則 CASCADE
[options.onUpdate='CASCADE'] string  
[options.constraints=true] boolean 是否在刪除或更新時啟用外鍵約束

 

2.5 Model.belongsToMany() - 多對多

Model.belongsToMany(target, [options])

創建連接表的 N:M 的關系

User.belongsToMany(Project, { through: 'UserProjects' })
Project.belongsToMany(User, { through: 'UserProjects' })

定義中指定需要through時,sequelize會嘗試自動生成名字,但生成的名字並不一定符合邏輯。

你通過自定義屬性定義一個模型,它的屬性可以用兩種方式添加/設置關聯。

var UserProjects = sequelize.define('UserProjects', {
  started: Sequelize.BOOLEAN
})
User.belongsToMany(Project, { through: UserProjects })
Project.belongsToMany(User, { through: UserProjects })
jan.addProject(homework, { started: false }) // homework 工程還未開始
jan.setProjects([makedinner, doshopping], { started: true}) // shopping和dinner 兩種方式都會啟動

如果你想設置多個目標實例,但是有不同的屬性,這時必須在實例上設置屬性:

p1.UserProjects = {
  started: true
}
user.setProjects([p1, p2], {started: false}) 

類似的,使用自定義屬性連接表時,這些屬性將做為一個對象的名稱:

user.getProjects().then(function (projects) {
  var p1 = projects[0]
  p1.UserProjects.started // Is this project started yet?
})
名稱 類型 說明
target Model  
[options] object  
[options.hooks=false] boolean 設置為 true 時,會在關聯模型刪除時執行 before-/afterDestroy 鈎子方法
[options.through] Model | string | object 在N:M 的關聯中,用於連接源 和 目標 表的名稱
[options.through.model] Model 用於連接 N:M 關系的模型
[options.through.scope] object 用於建立關聯的鍵/值集合,並通過模型查找默認值。
[options.through
.unique=true]
boolean 設置為 true時,唯一鍵會從使用的外鍵中生成
[options.as] string 當前模型(源)的別名,單數形式。如果你為一個表創建多次關聯,或者不想使用定義模型時使用的名稱,那么就應該為模型指定一個別名。
[options.foreignKey] string | object 目標表中的外鍵名或相當於定義外鍵列的對象 (語法參考 Sequelize.define )。使用對象時,應該添加一個name來設置列名。默認的外鍵命名規為源模型名+源模型主鍵名
[options.otherKey] string | object 連接表的外鍵名稱(表示目標模型)或表示其它列的類型定義(見sequelize.define語法)。使用對象時,可以添加一個name 屬性以設置目標列,默認為 目標模型名稱 + 目標主鍵的名稱
[options.onDelete='SET NULL | NO ACTION'] string 如果外允許空則 SET NULL,其它則 CASCADE
[options.onUpdate='CASCADE'] string  
[options.constraints=true] boolean 是否在刪除或更新時啟用外鍵約束

 



喜歡這篇文章?歡迎打賞~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM