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 sequelizenpm 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' } = mySqllet connectionasync function initDB() {try {// 初始化 sequelizeconst sequelize = new Sequelize(database, userName, pass, {host: host,port: port,dialect: dialect});connection = sequelizeawait 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.jsconst { 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.idconst data = await User.findByPk(id)ctx.body = {data: data[0],success: true}}async function add(ctx) {const { path } = ctx.request.files.fileconst { 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.idconst 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.jsconst 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.jsconst model = require('./app/models/index')app.use(async (ctx, next) => {ctx.model = modelawait next()})
如上,我们就将所有的 model 都绑定到 ctx 对象上去了,这样我们 接口可以改造如下:
// app/controller/user.jsasync 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地址
