Js全栈开发之Sequelize用法详解(MySQL)
接着上一篇《Js全栈开发之koa2中间件分离与数据层搭建》来继续学习koa2,上一篇着重总结了数据库的访问及模型层的搭建,以及从koa2项目中拆分出中间件的具体步骤。还略微讲解了ORM框架Sequelize的单表用法,但并没有仔细的讲解,本篇将重点汇总Sequelize框架的具体用法,总结一些平时比较常用的api及语法。
·
Sequelize用法详解(MySQL)
接着上一篇《Js全栈开发之koa2中间件分离与数据层搭建》来继续学习koa2,上一篇着重总结了数据库的访问及模型层的搭建,以及从koa2项目中拆分出中间件的具体步骤。还略微讲解了ORM框架Sequelize的单表用法,但并没有仔细的讲解,本篇将重点汇总Sequelize框架的具体用法,总结一些平时比较常用的api及语法。
文章目录
1. 安装Sequelize及数据库驱动包
npm install --save sequelize
# 选择以下之一:
$ npm install --save pg pg-hstore # Postgres
$ npm install --save mysql2
$ npm install --save mariadb
$ npm install --save sqlite3
$ npm install --save tedious # Microsoft SQL Server
2. 连接数据库
var Sequelize = require('sequelize');
sequelize = new Sequelize({
database: 'koa2learn', //数据库名称
username: 'root', //数据库用户名
password: 'xxxxxx', //数据库密码
host: 'xxx.xxx.xxx.xxx', //数据库主机IP localhost
dialect: 'mysql', //数据库类型 'mysql'|'mariadb'|'sqlite'|'postgres'|'mssql',
pool: { //连接池配置
max: 5, //最大连接数
min: 0, //最小连接数
acquire: 500, //请求超时时间
idle: 10000 //断开连接后,连接实例在连接池保持的时间
},
logging: console.log //输出日志信息 true or false
});
// 测试连接
module.exports = {
testConn: async function(){
sequelize.authenticate().then(() => {
console.log("连接建立成功");
})
.catch(err => {
throw new Error(`无法连接数据库:${err.message}`);
});
},
}
3. 定义数据模型
3.1 define方法定义
const { Sequelize, DataTypes } = require('sequelize');
const User = sequelize.define('User', {
id:{
type: DataTypes.UUID,
defaultValue: Sequelize.UUID,
primaryKey: true
},
firstName: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: "Dahlin"
},
lastName: {
type: DataTypes.STRING
// allowNull 默认为 true
}
}, {
tableName: 'User',
timestamps: false
});
3.2 扩展Model定义
const { Sequelize, DataTypes, Model } = require('sequelize');
class User extends Model {}
User.init({
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING
// allowNull 默认为 true
}
}, {
// 这是其他模型参数
sequelize, // 我们需要传递连接实例
modelName: 'User' // 我们需要选择模型名称
});
// 定义的模型是类本身
console.log(User === sequelize.models.User); // true
3.3 同步数据
(async () => {
await sequelize.sync({ force: true });
console.log("所有模型均已成功同步.");
})();
3.4 模型实例
class User extends Model {
static classLevelMethod() {
return 'foo';
}
instanceLevelMethod() {
return 'bar';
}
getFullname() {
return [this.firstname, this.lastname].join(' ');
}
}
User.init({
firstname: DataTypes.STRING,
lastname: DataTypes.STRING
}, { sequelize });
console.log(User.classLevelMethod()); // 'foo'
const user = User.build({ firstname: 'Jane', lastname: 'Doe' });
console.log(user.instanceLevelMethod()); // 'bar'
console.log(user.getFullname()); // 'Jane Doe'
// 创建实例
const dahlin = User.build({lastName:"badnothing"});
console.log(dahlin instanceof User);
console.log(dahlin.firstName);
await sequelize.sync({ force: true });
console.log("所有模型均已成功同步.");
const jane = await User.create({ firstName: "Jane" });
jane.lastName = "Ada";
// 实例json序列化
console.log(jane.toJSON()); // 这样最好!
console.log(JSON.stringify(jane, null, 4)); // 这样也不错!
// 保存实例
await jane.save();
// 保存实例字段
//await jane.save({ fields: ['lastName '] });
// 删除实例
//await jane.destroy();
// 重新加载实例
//await jane.reload();
4. 增删改查基础
4.1 插入
const user = await User.create(
{
firstName: "adaa",
lastName: "ffdf"
},
{ fields: ['firstName','lastName'] }
);
console.log("Jane's auto-generated ID:", user.id);
console.log(user.firstName); // 'alice123'
console.log(user.isAdmin); // false
// 批量插入
const user2 = await User.bulkCreate([
{ firstName: 'Jack', lastName:'Sparrow' },
{ firstName: 'Davy' , lastName:'Jones' }
]);
console.log(user2.length); // 2
console.log(user2[0] instanceof User); // true
console.log(user2[0].id); // 1 // (或另一个自动生成的值)
4.2 查询
// 查询所有用户
const users = await User.findAll({
attributes: ['firstName','lastName'],
where:
{
firstName: 'ada',
lastName: 'fff'
}
});
console.log(users.every(user => user instanceof User)); // true
console.log("All users:", JSON.stringify(users, null, 4));
4.3 删改
// 将所有没有姓氏的人更改为 "Doe"
await User.update({ lastName: "Doe" }, {
where: {
lastName: null
}
});
// 删除所有名为 "Jane" 的人
await User.destroy({
where: {
firstName: "Jane"
}
});
// 截断表格
await User.destroy({
truncate: true
});
5. Finders模型查询
- findByPk 方法使用提供的主键从表中仅获得一个条目.
- findOne 方法获得它找到的第一个条目(它可以满足提供的可选查询参数).
- findOrCreate
除非找到一个满足查询参数的结果,否则方法 findOrCreate 将在表中创建一个条目.
const [user, created] = await User.findOrCreate({
where: { username: 'sdepold' },
defaults: {
job: 'Technical Lead JavaScript'
}
});
console.log(user.username); // 'sdepold'
console.log(user.job); // 这可能是也可能不是 'Technical Lead JavaScript'
console.log(created); // 指示此实例是否刚刚创建的布尔值
if (created) {
console.log(user.job); // 这里肯定是 'Technical Lead JavaScript'
}
- findAndCountAll
结合了 findAll 和 count 的便捷方法. 在处理与分页有关的查询时非常有用,在分页中,你想检索带有 limit 和 offset 的数据,但又需要知道与查询匹配的记录总数.findAndCountAll 方法返回一个具有两个属性的对象:
count - 一个整数 - 符合查询条件的记录总数
rows - 一个数组对象 - 获得的记录
const { count, rows } = await Project.findAndCountAll({
where: {
title: {
[Op.like]: 'foo%'
}
},
offset: 10,
limit: 2
});
console.log(count);
console.log(rows);
6. 获取器/设置器/虚拟字段
6.1 获取器
获取器是为模型定义中的一列定义的 get() 函数
const User = sequelize.define('user', {
// 假设我们想要以大写形式查看每个用户名,
// 即使它们在数据库本身中不一定是大写的
username: {
type: DataTypes.STRING,
get() {
const rawValue = this.getDataValue(username);
return rawValue ? rawValue.toUpperCase() : null;
}
}
});
// 使用
const user = User.build({ username: 'SuperUser123' });
console.log(user.username); // 'SUPERUSER123'
console.log(user.getDataValue(username)); // 'SuperUser123'
6.2 设置器
设置器是为模型定义中的一列定义的 set() 函数. 它接收要设置的值
const User = sequelize.define('user', {
username: DataTypes.STRING,
password: {
type: DataTypes.STRING,
set(value) {
// 在数据库中以明文形式存储密码是很糟糕的.
// 使用适当的哈希函数来加密哈希值更好.
// 使用用户名作为盐更好.
this.setDataValue('password', hash(this.username + value));
}
}
});
// 使用
const user = User.build({ username: 'dahlin', password: '123456' });
console.log(user.password);
console.log(user.getDataValue(password));
6.3 虚拟字段
虚拟字段是 Sequelize 在后台填充的字段,但实际上它们不存在于数据库中.
const { DataTypes } = require("sequelize");
const User = sequelize.define('user', {
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
fullName: {
type: DataTypes.VIRTUAL,
get() {
return `${this.firstName} ${this.lastName}`;
},
set(value) {
throw new Error('不要尝试设置 `fullName` 的值!');
}
}
});
7. 原始查询
// 删改语句
const [results, metadata] = await sequelize
.query("UPDATE User SET firstName = :fistName WHERE lastName = :lastName",{
replacements: { fistName: 'Jack1' ,lastName:'Sparrow'},
});
console.log(results);
console.log(metadata);
// 查询语句
const users = await sequelize.query("SELECT * FROM User where firstName=:firstName",
{ type: QueryTypes.SELECT ,
replacements: { firstName: 'Jack1' },
});
console.log(users);
// 查询并映射成对象实例
const users = await sequelize.query("SELECT * FROM User where firstName=:firstName",
{ type: QueryTypes.SELECT ,
replacements: { firstName: 'Jack1' },
model: User,
mapToModel: true // 如果你有任何映射字段,则在此处传递 true
});
console.log(users);
console.log("All users:", JSON.stringify(users, null, 4));
8. 关联查询
8.1 默认设置
const Ship = sequelize.define('ship', {
name: DataTypes.TEXT,
crewCapacity: DataTypes.INTEGER,
amountOfSails: DataTypes.INTEGER
}, { timestamps: false });
const Captain = sequelize.define('captain', {
name: DataTypes.TEXT,
skillLevel: {
type: DataTypes.INTEGER,
validate: { min: 1, max: 10 }
}
}, { timestamps: false });
Captain.hasOne(Ship);
Ship.belongsTo(Captain); // 这将在 Ship 中创建 `captainId` 外键.
// 通过将模型传递给 `include` 来完成预先加载:
console.log((await Ship.findAll({ include: Captain })).toJSON());
// 或通过提供关联的模型名称:
console.log((await Ship.findAll({ include: 'captain' })).toJSON());
// 同样,实例获得用于延迟加载的 `getCaptain()` 方法:
const ship = Ship.findOne();
console.log((await ship.getCaptain()).toJSON());
8.2 直接提供外键名称
Ship.belongsTo(Captain, { foreignKey: 'bossId' }); // 这将在 Ship 中创建 `bossId` 外键.
// 通过将模型传递给 `include` 来完成预先加载:
console.log((await Ship.findAll({ include: Captain })).toJSON());
// 或通过提供关联的模型名称:
console.log((await Ship.findAll({ include: 'Captain' })).toJSON());
// 同样,实例获得用于延迟加载的 `getCaptain()` 方法:
const ship = Ship.findOne();
console.log((await ship.getCaptain()).toJSON());
8.3 定义别名
Ship.belongsTo(Captain, { as: 'leader' }); // 这将在 Ship 中创建 `leaderId` 外键.
Ship.belongsTo(Captain, { as: 'leader', foreignKey: 'bossId' }); // 这将在 Ship 中创建 `bossId` 外键.
console.log((await Ship.findAll({ include: 'leader' })).toJSON());
// 或者,你可以传递一个指定模型和别名的对象:
console.log((await Ship.findAll({
include: {
model: Captain,
as: 'leader'
}
})).toJSON());
// 同样,实例获得用于延迟加载的 `getLeader()`方法:
const ship = Ship.findOne();
console.log((await ship.getLeader()).toJSON());
8.4 多对多关系
// 字符串创建方式
const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
Movie.belongsToMany(Actor, { through: 'ActorMovies' });
Actor.belongsToMany(Movie, { through: 'ActorMovies' });
// 传递模型方式创建
const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
const ActorMovies = sequelize.define('ActorMovies', {
MovieId: {
type: DataTypes.INTEGER,
references: {
model: Movie, // 'Movies' 也可以使用
key: 'id'
}
},
ActorId: {
type: DataTypes.INTEGER,
references: {
model: Actor, // 'Actors' 也可以使用
key: 'id'
}
}
});
Movie.belongsToMany(Actor, { through: 'ActorMovies' });
Actor.belongsToMany(Movie, { through: 'ActorMovies' });
8.5 延迟加载与预先加载
延迟加载是指仅在确实需要时才获取关联数据的技术. 另一方面,预先加载是指从一开始就通过较大的查询一次获取所有内容的技术.
- 延迟加载示例
const awesomeCaptain = await Captain.findOne({
where: {
name: "Jack Sparrow"
}
});
// 用获取到的 captain 做点什么
console.log('Name:', awesomeCaptain.name);
console.log('Skill Level:', awesomeCaptain.skillLevel);
// 现在我们需要有关他的 ship 的信息!
const hisShip = await awesomeCaptain.getShip();
// 用 ship 做点什么
console.log('Ship Name:', hisShip.name);
console.log('Amount of Sails:', hisShip.amountOfSails);
- 预先加载示例
const awesomeCaptain = await Captain.findOne({
where: {
name: "Jack Sparrow"
},
include: Ship
});
// 现在 ship 跟着一起来了
console.log('Name:', awesomeCaptain.name);
console.log('Skill Level:', awesomeCaptain.skillLevel);
console.log('Ship Name:', awesomeCaptain.ship.name);
console.log('Amount of Sails:', awesomeCaptain.ship.amountOfSails);
9. 官方中文文档
- Sequelize Docs 中文版
https://github.com/demopark/sequelize-docs-Zh-CN
更多推荐
已为社区贡献1条内容
所有评论(0)