关于Sequelize的碎碎念(可省略)
我今天也是第一次接触到这个Sequelize,给我打开了一个新世界的大门。在学校做课程练手项目的时候都是直接写SQL语句操作数据库的,今天才觉得我当时像一个原始人一样(捂脸)。
今天看着这个Sequelize,给我的感觉是:很像python在操作MongoDB。你看,获取数据的时候不是直接用SELECT语句,而是用findall这个api,而且配置条件的时候也跟MongoDB差不多:.findall({ where: { ... } })。
总之,有一种亲切感在里面哈哈哈哈哈。
然后我练手的数据库是当时学MySQL的数据库,其中有两个库(Employee,User)是继承关系。我就在想怎么在Sequelize里面表达继承关系。经过一系列搜索求证,我将它总结在下面:
Sequelize中的继承关系
(PS: 省流:文章最后面有总结)
Sequelize提供了一些表示关系的API:
hasOne:一对一hasMany:一对多belongsTo:属于belognsToMany:属于多个
很明显,我们这里会使用到 belongsTo 来表达 Employee ISA User。但是明显归明显,有一些细节还是要注意的。
测试数据
首先,来介绍一下我用的数据吧 Employee 和 User:
User(uid, name, age, street, city, state, zipcode)
Employee(uid, hireDate, hourlyRate)
User
const { DataTypes, Model } = require("sequelize");
const { sequelize } = require("./db");
class User extends Model {}
User.init(
{
uid: { type: DataTypes.INTEGER, allowNull: false, primaryKey: true },
name: { type: DataTypes.STRING, allowNull: true },
age: { type: DataTypes.INTEGER, allowNull: true },
street: { type: DataTypes.STRING, allowNull: true },
city: { type: DataTypes.STRING, allowNull: true },
state: { type: DataTypes.STRING, allowNull: true },
zipcode: { type: DataTypes.INTEGER, allowNull: true },
},
{
sequelize,
modelName: "User",
tableName: "User", // 为了不破坏之前的数据库
timestamps: false, // 为了不破坏之前的数据库
}
);
// 没写sync是为了不破坏之前的数据库
module.exports = User;
Employee
const { DataTypes, Model } = require("sequelize");
const { sequelize } = require("./db");
const User = require("./user");
class Employee extends Model {
// (optional) 定义这个,可以在获取 name 的时候,直接 employee.name,而不需要 employee.User.name
get name() {
return this.dataValues.name;
}
}
Employee.init(
{
uid: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
references: { model: User, key: "uid" }, // 声明这玩意是个 foreignKey
},
hireDate: {
type: DataTypes.DATE,
},
hourlyRate: {
type: DataTypes.FLOAT,
},
},
{
sequelize,
modelName: "Employee",
tableName: "Employee", // 为了不破坏之前的数据库
timestamps: false, // 为了不破坏之前的数据库
}
);
// { foreignKey: "uid" } 可以自定义连接两个表的 foreignKey, 这里设定为 uid
Employee.belongsTo(User, { foreignKey: "uid" });
// 没写sync是为了不破坏之前的数据库
module.exports = Employee;
细节1:子表Model记得要填上belongsTo,可以配置连接两个表的 foreignKey。
子表.belongsTo(父表, { foreignKey: "自定义的外键" });- 像我这里我就这样子了:
Employee.belongsTo(User, { foreignKey: "uid" });,表示Employee继承自User,用uid相关联。 - 默认会以
表名+Id来作为foreignKey来连接两个表。如果我在Employee里面的35行没有加{ foreignKey: "uid" }的话,就会用EmployeeId来关联。
细节2:通过 子表 查询记录在 父表 的数据,要配置 include:
include是一个数组,里面放着要JOIN的表- 然后在获取 父表 的数据的时候,记得要在中间多一个
父表才行:子表结果.父表.父表元素。
(async () => {
const e = await Employee.findOne({
include: [User],
});
console.log(e.User.name);
})();
细节3:(optional) 其实也有办法省略中间的那个 父表
- 原理:观察子表结果我们发现取元素要么从 dataValues 中取本表的数据,要么从 父表 中取父表的数据。于是就衍生出来两种方法:1. 通过
dataValues获取,2. 直接通过父表获取
Employee {
dataValues: {
uid: 75,
hireDate: '2019-11-16',
hourlyRate: '15.75',
User: ...
},
User: User {
dataValues: {
uid: 75,
name: 'Raina Simonutti',
age: 79,
street: '9163 Badeau Circle',
city: 'Honolulu',
state: 'Hawaii',
zipcode: 96845
},
...
}
...
}
-
方法一:通过
dataValues获取- 在定义子表Model的时候可以给它多加一个 getter 方法:
get 父表属性() { return this.dataValues.父表属性 }
class Employee extends Model { // (optional) 定义这个,可以在获取 name 的时候,直接 employee.name,而不需要 employee.User.name get name() { return this.dataValues.name; } }- 然后在查询的时候加一个
attributes字样,并把'User.name'重命名为'name',于是在查询属性的时候就可以直接子表结果.父表元素
(async () => { const e = await Employee.findOne({ include: [User], attributes: { include: [ "uid", "hireDate", "hourlyRate", [Sequelize.col("User.name"), "name"], ], }, }); console.log(e.name); })();还有一个方法甚至可以省略
attributes重命名的操作 - 在定义子表Model的时候可以给它多加一个 getter 方法:
-
方法二:直接通过父表获取(可以省略
attributes重命名的操作)- 在定义子表Model的时候可以给它多加一个 getter 方法:
get 父表属性() { return this.父表.父表属性 }
class Employee extends Model { // (optional) 定义这个,可以在获取 name 的时候,直接 employee.name,而不需要 employee.User.name get name() { return this.User.name; } }- 然后在查询的时候可以直接
子表结果.父表元素
(async () => { const e = await Employee.findOne({ include: [User], }); console.log(e.name); })(); - 在定义子表Model的时候可以给它多加一个 getter 方法:
- 个人感觉挺麻烦的,不如直接在中间加
父表呢,又方便,又可以避免重复,还能清楚的知道数值的来源
细节4:(optional) 用 references 可以规定哪个元素是 foreignKey
还有一个小细节,就是在 子表.init() 的时候,用 references 可以规定哪个元素是 foreignKey,虽然只要有 .belongsTo 就可以正常运作了,但是我总觉得加上这个可读性更强
Employee.init(
{
uid: {
...,
references: { model: User, key: "uid" }, // 声明这玩意是个 foreignKey
},
...
},
{...}
);
总结
直接在子表Model里面添加 子表.belongsTo(父表, { foreignKey: "自定义的外键" }); 就完事儿了。然后查询的时候记得在 findBalabala 里面加一个配置 includes: [父表1, 父表2, ...],还有获取数据的时候不要省略中间的 父表 就可以了。
PS:第一次接触 Sequelize,如果有什么讲的不对的地方,请千万不吝赐教。直接在评论区指明或者私信我都可以,千万不要有所顾忌,大佬们的每一次指点都是我宝贵的进步资源。