1.0 前言
大家看我们之前 Mysql 章节,我们和数据库交互的时候,每次都是要写 sql 的,然后处理数据返回的数据一般都是下划线的的(比如 img_url),而一般前端一般用的是驼峰写法(imgUrl),假设数据库的字段非常多,表也有很几个,每次转换起来非常麻烦,而且每次查询都要写 sql 也是很麻烦的,有什么简便的方法吗? 答案是有的,就是 ORM 库。
2.0 什么是 ORM?
ORM:对象关系映射(Object Relational Mapping,简称ORM),目的是操作对象一样操作数据库。因为数据库不是面向对象的,所以需要一个 对象来和数据库一一对应起来,方便操作数据库。
举个例子,我们 Mysql 对用户接口操作有下面几个方法:
async function list(ctx) {
const data = await ctx.mysql.queryPromise('select id,name,email,img_url from user')
ctx.body = {
data: data,
success: true
}
}
这样我们每次都要写 sql ,非常不方面,那现在假设有个对象和数据库字段映射起来,我们转变操作数据库的方式。
// app/model/user.js
伪代码如下
const User = sequelize.define("User", {
id: {
type: DataTypes.INTEGER(10),
allowNull: false,
autoIncrement: true,
primaryKey: true
},
name: String,
email: String,
imgUrl: String
}, {
tableName: 'user',
});
module.exports = User
接着我们 用户操作改成:
import User from '../model/user'
async function list(ctx) {
const data = await ctx.mysqlORM.User.find({})
// 返回的 data 自动转换成 [{id: 1, name: '', email: '', imgUrl: ''}]
ctx.body = {
data: data,
success: true
}
}
同理,新增,更新,删除操作,我们只要操作 User 对象就好了,自动生成对应的 sql,这样不就方便很多么。
那这个整个逻辑即使 ORM 库要解决的问题,本质就是对数据库做了一层抽象,通过我们容易理解,方便操作的 对象的方式来操作数据,大大减少工作量。
3.0 Sequelize 使用
在 Node.js 我们最常用的 ORM 框架就是 Sequelize 了,Sequelize 文档。
3.0.1 安装
npm install --save sequelize
npm install --save mysql2
3.0.2 创建 Sequelize 实例
Sequelize 这个库 包裹了 Mysql ,我还是还是在 /app/db/mysql 中初始化 Sequelize。
const {mySql } = require('../config')
const { Sequelize } = require('sequelize');
const { logger } = require('../log4j/logger')
const { port, host, pass, userName, database, dialect = 'mysql' } = mySql
let connection
async function initDB() {
try {
// 初始化 sequelize
const sequelize = new Sequelize(database, userName, pass, {
host: host,
port: port,
dialect: dialect
});
connection = sequelize
await sequelize.authenticate()
} catch (error) {
logger.error(error)
}
}
function getConnection() {
return connection
}
initDB()
module.exports = {
getConnection
}
3.0.3 定于 User 对应的 model
和数据库表映射的我们都放在 app/models 目录下。
app/models/user.js
const { Sequelize, DataTypes, Model } = require('sequelize')
const { getConnection } = require('../db/mysql')
class User extends Model {
}
User.init(
{
id: {
type: DataTypes.INTEGER({ length: 11, unsigned: true }), // 标记类型
autoIncrement: true, // id 是否自动增长
primaryKey: true, // 是否是主键
allowNull: false,
comment: 'id', // 描述
},
name: {
type: DataTypes.STRING,
allowNull: false,
comment: '名称',
},
email: {
type: DataTypes.STRING,
allowNull: false,
comment: '邮箱',
},
imgUrl: {
type: DataTypes.STRING,
allowNull: false,
comment: '图片url',
},
}, {
sequelize: getConnection(),
modelName: 'User',
tableName: 'user',
underscored: true, // 转下划线
comment: '用户表',
freezeTableName: true,
timestamps: true,
})
module.exports = User
如上我们就定义了好了一个 User model,接下来我们看看 查询的时候变化。
3.0.4 使用 model
我们看看看之前用户查询改成如下:
const User = require('../models/user')
async function list(ctx) {
const data = await User.findAll()
console.log('查询到:', data)
ctx.body = {
data: data,
success: true
}
}
async function detail(ctx) {
const id = ctx.params.id
const data = await User.findByPk(id)
ctx.body = {
data: data[0],
success: true
}
}
async function add(ctx) {
const { path } = ctx.request.files.file
const { name, email } = ctx.request.body // 获取 request body 字段
const imgUrl = path.split("/static")[1]
const data = await User.create({ name, email, imgUrl })
ctx.body = {
success: true,
}
}
async function remove(params) {
const id = ctx.params.id
const data = await User.destroy(id)
ctx.body = {
data: data[0],
success: true
}
}
module.exports = {
detail,
list,
add,
remove
}
我们把项目看起来,看看查询接口,控制台打印如下:
可以看到,Sequelize 会自动组装 sql ,并且返回的数据都已经自动将下划线转换成的驼峰字段了。大大节省了人力。
3.0.5 绑定到 Koa 的 ctx 对象上去
因为 models 目录下面都是我们需要的 model,我们可以获取到 这些文件,用一个对象存储起来,然后绑定到 ctx 对象上去。
// app/models/index.js
const fs = require('fs')
const path = require('path')
const { getConnection } = require('../db/mysql')
const User = require('./user')
let modelObj = {}
fs.readdirSync(__dirname).filter(function(file) {
console.log(file)
return (file.indexOf('.') !== 0) && (file !== 'index.js') && (file.slice(-3) === '.js');
})
.forEach(function(file) {
const model = require(path.join(__dirname, file))
modelObj[model.name] = model;
});
Object.keys(modelObj).forEach(function(modelName) {
if (modelObj[modelName].associate) {
modelObj[modelName].associate(modelObj)
}
});
modelObj.sequelize = getConnection()
module.exports = modelObj // 这样 modelObj = {User}
// app.js
const model = require('./app/models/index')
app.use(async (ctx, next) => {
ctx.model = model
await next()
})
如上,我们就将所有的 model 都绑定到 ctx 对象上去了,这样我们 接口可以改造如下:
// app/controller/user.js
async function list(ctx) {
const data = await ctx.model.User.findAll()
console.log('查询到:', data)
ctx.body = {
data: data,
success: true
}
}
这样就不需要 加载对应的 js 了,这种使用方式也挺方便的。
4.0 小结
这节我们学习了 ORM 的概念,Sequelize 的使用,在平常开发过程中我们都会使用到 ORM 框架,大家可以对着 Demo 源码看看,Demo地址