一般在操作数据库时,并不会直接使用sql语句来操作,而是使用ORM框架与数据库建立映射关系,Sequelize就是nodejs服务中常用的一个orm库,orm框架具备以下特点:
1、只需要面向对象编程, 不需要面向数据库编写代码: 对数据库的操作都转化成对类属性和方法的操作,不用编写各种数据库的sql语句
2、实现了数据模型与数据库的解耦,屏蔽了不同数据库操作的差异: 不在关注用的是mysql、oracle….等,通过简单的配置就可以轻松更换数据库,而不需要修改代码。
1、相比较直接使用SQL语句操作数据库,有性能损失.
2、根据对象的操作转换成SQL语句,根据查询的结果转化成对象, 在映射过程中有性能损失.。
如果是在eggjs中使用的话,参考eggjs文档,比较齐全。练习搭建练习环境如下:
mkdir Sequelize-demo
npm init -y
npm install --save sequelize
// 使用mysql安装mysql2
npm install --save mysql2
const Sequelize = require('sequelize')
const sequelize = new Sequelize('music_api', 'root', '123456', {
host: 'localhost',
dialect: 'mysql' /* one of 'mysql' | 'mariadb' | 'postgres' | 'mssql' */
})
字符串类型,传入参数可指定长度,如:
Sequelize.STRING(60)
文本类型,相当于转成数据库中的类型如下:
Sequelize.TEXT // TEXT
Sequelize.TEXT('tiny') // TINYTEXT
Sequelize.CITEXT // CITEXT PostgreSQL and SQLite only.
整数数类型,int
Sequelize.INTEGER // INTEGER
Sequelize.BIGINT // BIGINT
Sequelize.BIGINT(11) // BIGINT(11)
浮点数类型,float
Sequelize.FLOAT // FLOAT
Sequelize.FLOAT(11) // FLOAT(11)
Sequelize.FLOAT(11, 10) // FLOAT(11,10)
日期类型
Sequelize.DATE // DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
Sequelize.DATE(6) // DATETIME(6) for mysql 5.6.4+. Fractional seconds support with up to 6 digits of precision
Sequelize.DATEONLY // DATE without time.
布尔值类型
Sequelize.BOOLEAN // TINYINT(1)
modelName: 数据表的名字; timestamps: false:不将timestamps设置成false的话,会自动有两个时间createdAt和updatedAt; freezeTableName: true:不将freezeTableName设置成false,查询时直接表名就是复数形式的了
class Singer extends Sequelize.Model { }
Singer.init({
id: {
type: Sequelize.STRING,
primaryKey: true, // 主键
allowNull: false // 不允许空值
},
name: Sequelize.STRING(50)
}, { sequelize, modelName: 'singer', timestamps: false, freezeTableName: true });
查询符合条件的一条记录,相当于加了个 limit 1 的条件查询
Singer.findOne().then(singers=>{
console.log(singers)
})
还能添加条件,排序方式,查询字段
Singer.findOne({
attributes: ['name', 'avatar'],
where:{},
order: [['id', 'asc']]
}).then(singers=>{
console.log(singers)
})
相当于查询详情,传入id
Singer.findByPk(1).then(singers => {
console.log(singers)
})
查询符合条件的全部记录
Singer.findAll().then(singers => {
console.log(singers)
})
查询符合条件的全部记录,并且统计记录条数,在分页是常用
Singer.findAndCountAll({
limit: 5,
offset: 2
}).then(res => {
let { count, rows } = res
console.log(count, rows)
})
统计相关的函数
Project.count({ where: {'id': {[Op.gt]: 25}} }).then(c => {
console.log("There are " + c + " projects with an id greater than 25.")
})
Project.max('age', { where: { age: { [Op.lt]: 20 } } }).then(max => {
// will be 10
})
Project.min('age', { where: { age: { [Op.gt]: 5 } } }).then(min => {
// will be 10
})
Project.sum('age', { where: { age: { [Op.gt]: 5 } } }).then(sum => {
// will be 50
})
Op集成了多种条件查询,模糊查询,in语法,大于,小于,范围之间等等
const Op = Sequelize.Op
[Op.and]: {a: 5} // AND (a = 5)
[Op.or]: [{a: 5}, {a: 6}] // (a = 5 OR a = 6)
[Op.gt]: 6, // > 6
[Op.gte]: 6, // >= 6
[Op.lt]: 10, // < 10
[Op.lte]: 10, // <= 10
[Op.ne]: 20, // != 20
[Op.eq]: 3, // = 3
[Op.not]: true, // IS NOT TRUE
[Op.between]: [6, 10], // BETWEEN 6 AND 10
[Op.notBetween]: [11, 15], // NOT BETWEEN 11 AND 15
[Op.in]: [1, 2], // IN [1, 2]
[Op.notIn]: [1, 2], // NOT IN [1, 2]
[Op.like]: '%hat', // LIKE '%hat'
[Op.notLike]: '%hat' // NOT LIKE '%hat'
[Op.iLike]: '%hat' // ILIKE '%hat' (case insensitive) (PG only)
[Op.notILike]: '%hat' // NOT ILIKE '%hat' (PG only)
[Op.startsWith]: 'hat' // LIKE 'hat%'
[Op.endsWith]: 'hat' // LIKE '%hat'
[Op.substring]: 'hat' // LIKE '%hat%'
[Op.regexp]: '^[h|a|t]' // REGEXP/~ '^[h|a|t]' (MySQL/PG only)
[Op.notRegexp]: '^[h|a|t]' // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)
[Op.iRegexp]: '^[h|a|t]' // ~* '^[h|a|t]' (PG only)
[Op.notIRegexp]: '^[h|a|t]' // !~* '^[h|a|t]' (PG only)
[Op.like]: { [Op.any]: ['cat', 'hat']}
// LIKE ANY ARRAY['cat', 'hat'] - also works for iLike and notLike
[Op.overlap]: [1, 2] // && [1, 2] (PG array overlap operator)
[Op.contains]: [1, 2] // @> [1, 2] (PG array contains operator)
[Op.contained]: [1, 2] // <@ [1, 2] (PG array contained by operator)
[Op.any]: [2,3] // ANY ARRAY[2, 3]::INTEGER (PG only)
[Op.col]: 'user.organization_id' // = "user"."organization_id", with dialect specific column identifiers, PG in this example
一对一,一对多,多对多关联参考文章
foreignKey: 外键字段名称; sourceKey:当前表的字段名; constraints: false,不同步建立外键关系
class Singer extends Sequelize.Model { }
Singer.init({
id: {
type: Sequelize.STRING,
primaryKey: true,
allowNull: false
},
name: Sequelize.STRING(50),
singer_id: Sequelize.STRING,
avatar: Sequelize.STRING,
created: Sequelize.INTEGER(10),
updated: Sequelize.INTEGER(10)
}, { sequelize, modelName: 'singer', timestamps: false, freezeTableName: true });
const Audio = sequelize.define('audio', {
// attributes
id: {
type: Sequelize.STRING,
primaryKey: true,
allowNull: false
},
name: Sequelize.STRING(50)
}, { sequelize, modelName: 'audio', timestamps: false, freezeTableName: true });
Singer.hasMany(Audio, {
foreignKey: 'singer_id',
sourceKey: 'singer_id'
});
Audio.belongsTo(Singer, {
foreignKey: 'singer_id',
sourceKey: 'singer_id'
})
Singer.findAndCountAll({
include: [Audio],
limit: 1,
offset: 2
}).then(res => {
let { count, rows } = res
console.log(rows)
// rows.forEach(v=>{
// v.audios.forEach(e=>{
// console.log(e)
// })
// })
})
create方法,传入数据
async create() {
const ctx = this.ctx;
const { name, age } = ctx.request.body;
const user = await ctx.model.User.create({ name, age });
ctx.status = 201;
ctx.body = user;
}
update方法,传入数据
async update() {
const ctx = this.ctx;
const id = toInt(ctx.params.id);
const user = await ctx.model.User.findByPk(id);
if (!user) {
ctx.status = 404;
return;
}
const { name, age } = ctx.request.body;
await user.update({ name, age });
ctx.body = user;
}
destroy方法
async destroy() {
const ctx = this.ctx;
const id = toInt(ctx.params.id);
const user = await ctx.model.User.findByPk(id);
if (!user) {
ctx.status = 404;
return;
}
await user.destroy();
ctx.status = 200;
}
const Controller = require('egg').Controller;
function toInt(str) {
if (typeof str === 'number') return str;
if (!str) return str;
return parseInt(str, 10) || 0;
}
class UserController extends Controller {
async index() {
const ctx = this.ctx;
const query = { limit: toInt(ctx.query.limit), offset: toInt(ctx.query.offset) };
ctx.body = await ctx.model.User.findAll(query);
}
async show() {
const ctx = this.ctx;
ctx.body = await ctx.model.User.findByPk(toInt(ctx.params.id));
}
async create() {
const ctx = this.ctx;
const { name, age } = ctx.request.body;
const user = await ctx.model.User.create({ name, age });
ctx.status = 201;
ctx.body = user;
}
async update() {
const ctx = this.ctx;
const id = toInt(ctx.params.id);
const user = await ctx.model.User.findByPk(id);
if (!user) {
ctx.status = 404;
return;
}
const { name, age } = ctx.request.body;
await user.update({ name, age });
ctx.body = user;
}
async destroy() {
const ctx = this.ctx;
const id = toInt(ctx.params.id);
const user = await ctx.model.User.findByPk(id);
if (!user) {
ctx.status = 404;
return;
}
await user.destroy();
ctx.status = 200;
}
}
module.exports = UserController;
config.sequelize = {
datasources: [
{
delegate: 'model', // load all models to app.model and ctx.model
baseDir: 'model', // load models from `app/model/*.js`
dialect: 'mysql',
database: process.env.EGG_MYSQL_DATABASE || 'seq_test_staging',
host: process.env.EGG_MYSQL_HOST || '127.0.0.1',
port: process.env.EGG_MYSQL_PORT || '3306',
username: process.env.EGG_MYSQL_USERNAME,
password: process.env.EGG_MYSQL_PASSWORD,
operatorsAliases: Op,
pool: {
max: 20,
min: 0,
idle: 10000,
},
},
{
delegate: 'testModel', // load all models to app.adminModel and ctx.adminModel
baseDir: 'test_model', // load models from `app/admin_model/*.js`
dialect: 'mysql',
database: process.env.EGG_MYSQL_DATABASE || 'test',
host: process.env.EGG_MYSQL_HOST || '127.0.0.1',
port: process.env.EGG_MYSQL_PORT || '3306',
username: process.env.EGG_MYSQL_USERNAME,
password: process.env.EGG_MYSQL_PASSWORD,
operatorsAliases: Op,
pool: {
max: 20,
min: 0,
idle: 10000,
},
},
],
};