內容轉自https://www.jianshu.com/p/078087c69b77,感謝
1.EggJS引用Sequelize
- 安裝sequelize依賴和mysql驅動
cnpm i egg-sequelize mysql2 -S
- 啟用sequelize插件
在config/plugin.js里面添加
sequelize: {
enable: true,
package: 'egg-sequelize',
},
- 配置數據庫
在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
- 剛開始使用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,里面用來存放所有的數據庫里面定義的表的實例對象內容。
- 數據庫表的內容如下:
/* 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
-
根據數據表的結構,我們確定關系並寫好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;
}
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;
}
- 總結一下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中,表與表之間用代碼來說存在三種關聯關系:一對一,一對多,多對多
- 一對一
在該項目中,student表和info表是存在一對一關系的,一個學生有一條專屬信息。
在student.js中,使用了hasOne()方法,第一個參數為關聯的模型對象Info,第二個參數為一個對象,其包含一個屬性為foreginKey為對應的信息表中studentId字段
在info.js中,使用了belongsTo()方法,第一個參數為關聯的模型對象Student, 第二個參數也是一個對象,其有兩個屬性,foreginKey為info表中的"studentId"字段,第二個參數targetKey為student表中的"id"字段
- 一對多
classes與student是一對多的關系,一個班級有多個學生,多個學生組成一個班級。
在student.js中,使用了belongsTo(),在classes.js中,使用了hasMany(),發現hasMany()與belongsTo()所需要的參數是類似的,但是這里注意,hasMany()里面的foreginKey值是對方表的classesId。結合第上面"一對一"的分析,我們可以總結出:
has開頭的方法中,foreginKey屬性值從對方的表上找,如果有targetKey的值則是自己的主鍵;
belongs開頭的方法中,foreginKey屬性值在自身表上找,targetKey屬性值則是對方表上
- 多對多
分析多對多關系,一個學生有多門課,一個課有多個學生,那我們可以用一個中間表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中建立關聯關系,通過調用模型(源模型)的belongsTo、hasOne、hasMany、belongsToMany方法,再將要建立關系的模型(目標模型)做為參數傳入即可。這些方法會按以下規則創建關聯關系:
hasOne- 與目標模型建立1:1關聯關系,關聯關系(外鍵)存在於目標模型中。詳見:Model.hasOne()belongsTo- 與目標模型建立1:1關聯關系,關聯關系(外鍵)存在於源模型中。詳見:Model.belongsTo()hasMany- 與目標模型建立1:N關聯關系,關聯關系(外鍵)存在於目標模型中。詳見:Model.hasMany()belongsToMany- 與目標模型建立N:M關聯關系,會通過sourceId和targetId創建交叉表。詳見:Model.belongsToMany()
1.2 定義關系模型
為了能夠清楚說明模型關系的定義及關系模型的使用,我們定義如下4個模型對象:
用戶(User)-與其它模型存在1:1、1:N、N:M用戶登錄信息(UserCheckin)-與User存在1:1關系用戶地址(UserAddress)-與User存在N:1關系角色(Role)-與User存在N:M關系
這幾個模型的E-R結構如下:

2. 關系/關聯相關的API
2.1 綜合介紹
在Sequelize中創建關聯通過調用模型(源)的 belongsTo / hasOne / hasMany / belongsToMany方法完成,並且為這個方法第一個參數提供另一個模型(目標)。各種方法以下規則創建關聯:
hasOne- 添加外鍵到目標模型,並以單數關系混入到源模型belongsTo- 為當前模型添加外鍵,並以單數關系混入到源模型hasMany- 添加外鍵到目標模型,並以復數關系混入到源模型belongsToMany- 為連接的表創建N:M關系並以復數關系混入到源模型。會通過sourceId和targetId創建交叉表。
在創建關系時,可以通過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 | 是否在刪除或更新時啟用外鍵約束 |
喜歡這篇文章?歡迎打賞~~

