课程引导
- 🚀【eggjs实战10天入门-第1天】🚀—— 搭建项目
- 🚀【eggjs实战10天入门-第2天】🚀—— controller、service和config
- 🚀【eggjs实战10天入门-第3天】🚀—— 操作mysql
本节目标
开始将数据存储到mysql和redis中
暂时不考虑参数不存在,读写报错的情况
1、myslq
1.1、安装mysql和navicat
安装mysql
// mac下
brew install mysql
brew services start mysql
mysql和navicat安装教程挺多的,大家可以搜索下,本篇就不讲了
安装navicat 然后链接本地启动的mysql
当出现这个节目的时候,代表我们已经通过navicat链接到了本地的mysql.如果你有远程服务器,也可以安装在服务器上,这里我们就先使用本地数据库了。
1.2、项目中安装mysql和sequelize
我们直接使用ORM 框架来帮助我们管理数据层的代码,现在社区常用的有sequelize/typeOrm,现在Prisma呼声也很高,感兴趣的可以尝试下这个。我平常用sequelize多一些,这里就用sequelize作为示例了。
我们首先安装mysql2和egg-sequelize(选择mysql2是因为据说性能更高一些,只是据说,我没有做过测评)(不过npm里面mysql2的下载量确实高一些)
很奇怪的是从12月18日开始,整个的下载量开始急速下降,不知道是什么原因。有兴趣的可以分析下这个下降趋势
yarn add egg-sequelize mysql2 -S
1.3、插件(plugin)
这里要引入一个新的概念,插件。eggjs的插件其实是相对更加独立的一个迷你应用。 像orm、校验、日志等这种独立的业务逻辑都可以在插件中引入。同时我们也可以根据eggjs的规范开发自己的插件。 插件的开启特别的简单
只需要在app/config/plugin.js中配置一下就可以了。比如我们开启下sequelize插件
exports.sequelize = {
enable: true,
package: 'egg-sequelize',
};
1.4、mysql的配置
我们可以想象一下,如果我们要使用mysql,就肯定需要要写mysql的配置。比如mysql的地址,密码,库名。那我们之前讲到的config配置就派上了用场。我们可以做如下的配置
dialect: 'mysql', // 使用的数据库,支持 mysql, mariadb, postgres, mssql等数据库
database: 'test', // 数据库名称
host: 'localhost', // 服务主机地址
port: 3306, // 端口
username: 'root', // 用户名
password: '', // 密码
delegate: 'myModel', // 【可选】加载所有的模型models到 `app[delegate]` and `ctx[delegate]`对象中,进行委托, 默认是model
baseDir: 'my_model', // 【可选】加载 `app/${baseDir}`文件夹下的所有js文件作为models,默认为 `model`
exclude: 'index.js', // 【可选】加载所有模型models时,忽略 `app/${baseDir}/index.js` 文件,支持文件路径和数组
但其实我们在使用过程中,会出现链接多个库的情况,那我们该怎么处理呢
首先我们可以在navicat里面可视化操作创建两个数据库,me-help,me-blog
这里我们字符集设置为utf8mb4(这个字符集可以支持表情的)
这样我们就得到了两个空的数据库
接下来我们开始在项目中配置这两个数据库
module.exports = appInfo => {
const config = {}
config.keys = appInfo.name + '_1672833991623_8554';
config.cdn = {
AK: 'default',
SK: 'default',
BucketName: 'xxx-xxx',
DoMain: 'https://xxx.xxx.com'
}
config.security = {
csrf: {
enable: true,
headerName: 'token',
},
};
config.sequelize = {
datasources: [
{
delegate: 'blogModel',
baseDir: 'blog_model',
dialect: 'mysql',
username: 'root',
password: 'ab123456',
database: 'me-blog',
host: '127.0.0.1',
port: 3306,
timezone: '+08:00',
define: {
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
},
dialectOptions: {
dateStrings: true,
typeCast: true,
},
logging: false
},
{
delegate: 'helpModel',
baseDir: 'help_model',
dialect: 'mysql',
username: 'root',
password: 'ab123456',
database: 'me-help',
host: '127.0.0.1',
port: 3306,
timezone: '+08:00',
define: {
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
},
logging: false
}
]
}
return {
...config
};
};
1.5、开始增删改查
1.5.1、设计表结构
官方提的建议是通过sequelize-cli进行表的初始化设计。但是我们这里不准备使用官方推荐的模式。
1、不同的公司操作规范不同,我们这边是不允许通过项目去修改数据库格式的,这也是比较高危的操作。一般有运维的话,会有专门的一套工具去初始化或者修改表结构。
2、我采用的是通过navicat进行可视化表结构的创建,然后通过开发的cli把当前环境的表结构初始化为项目的model
比如我们在me-help数据库创建一个日记表(todo_list), 记录我们每天写的日记。
字段为id,title、content、update_time、create_time,我们创建5个字段。同时讲id设置为主键,做自动递增
| 字段 | 类型 | 功能 | 备注 |
|---|---|---|---|
| id | int | 主键 | 主键最多只能有一个,且值不能重复不可为空。我们一般通过表的主键可以确定唯一的一条数据 |
| title | varchar | 标题 | |
| content | text | 内容 | |
| create_time | datetime | 创建的时间 | |
| update_time | datetime | 更新的时间 |
创建好字段后点击保存,我们为这个表起名为todo_list
表名+字段名一般都有自己的规范,这里我们采用单词全小写,多组此采用下划线相连。如todo_list
1.5.2、创建model
在seqlize中,我们需要创建和线上数据库对应的模型,也就是model
一般情况下,将与数据库表字段对应的mode文件放在app/model下,egg-sequelize插件会自动加载和处理。
但是我们设置的me-help的baseDir为help_model,所以和me-help相关的model,我们都放在help_model文件夹中
app下新建文件夹help_model,并创建表todo_list对应的model文件abc.js,
为什么要使用abc.js呢,这里主要是给大家标识好每个字段的作用,暂时抛弃语义化的命名
有的教程里面到处定义的User,使用的时候也是User,新入门的时候很难搞清楚之间的对应关系
egg-sequelize开源地址(github.com/eggjs/egg-s…
比如文件路径为app/help_model/abc.js
'use strict';
module.exports = app => {
const DataTypes = app.Sequelize;
const attributes = {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
comment: "id",
field: "id"
},
title: {
type: DataTypes.STRING(255),
comment: "标题",
field: "title"
},
content: {
type: DataTypes.TEXT,
comment: "内容",
field: "content"
},
createTime: {
type: DataTypes.DATE,
field: "create_time"
},
updateTime: {
type: DataTypes.DATE,
field: "update_time"
}
};
const options = {
timestamps: false,
freezeTableName: true
};
return app.model.define("todo_list", attributes, options);
};
我们一定要关注好这一句,const TodoListModel = app.model.define("todo_list", attributes, options);
关于模型定义的参数
- 第一个参数为我们需要映射的表名,做到和数据库里面的名字完全一致
- 第二个参数attributes为各个字段的配置
- 第三个参数,是一个对象,我常用的两个参数如下
timestamps: false // 是否自动添加时间戳字段 (updatedAt, createdAt)
freezeTableName: true // 如果不设置或者为false的话,sequelize会将第一个参数转为复数形式
1.5.3、搞清楚调用关系
接下来我们会使用app.helpModel.Abc去操作这个todo_list表 这时候大家可能有2个疑问
- app.helpModel.Abc这个玩意是怎么得来的? 这里面的关系我们用一个表格来演示
一般情况下是这样 model文件 | class name | | ----------------- | ------------------------ | |
abc.js|app.model.Abc| |User.js|app.model.User|
但是如果我在config.xx.js里面定义了delegate:helpModel model文件 | class name | | ----------------- | ------------------------ | |
abc.js|app.helpModel.Abc| |user_group.js|app.helpModel.UserGroup|user/profile.js|app.model.User.Profile|
这就是app.helpModel.Abc的由来,眼睑的朋友可能看到Abc和User是大写的,这是因为Abc和user是挂载到helpModel上的一个class,sequlize会把当前文件名转化为大驼峰
- app.helpModel.Abc这是操作哪个表?
操作哪个表取决于abc.js这个文件里面的定义
在
app/help_model/abc.js最后一句我们可以看到第一个参数是todo_list
return app.helpModel.define("todo_list", attributes, options);
那就说明app.helpModel.Abc是要操作me-help数据库的todo_list表
1.5.4、增删改查
- 1、写操作
今天发现阳了,我决定记录一下,于是我新增了一条变阳的数据
使用ctx.helpModel.Abc.create进行新增数据
const { Service } = require('egg')
class BaseService extends Service {
// 我们使用create方法增加一条数据
async add({title, content}) {
const { ctx } = this;
const user = await ctx.helpModel.Abc.create({
title,
content,
createTime: new Date(),
updateTime: new Date()
});
return user
}
}
module.exports = BaseService;
这时候我们就可以看到,数据库里面有个刚从我们新增的一行数据。
过了一会,我一看温度计,卧槽,已经退烧了,那赶紧再记录一下转阴记录,我们再增加一条
- 2、读操作
使用ctx.helpModel.Abc.findAll进行查询全部
const { Service } = require('egg')
class BaseService extends Service {
async getAll() {
const { ctx } = this;
const result = await ctx.helpModel.Abc.findAll();
return result
}
}
module.exports = BaseService;
这时我们发现可以读到刚才写入的两条数据了
- 3、更新操作
在我们喜滋滋认为自己转阴的时候,又量了下体温,发现刚才是自己烧迷糊了,看错温度计了,自己还是🐑。本着认真仔细的态度,我们这时候需要把第二条的转阴记录改一下
发现我们的系统还不支持更新,于是挣扎着爬起来,继续写更新接口
使用ctx.helpModel.Abc.update进行更新
因为是更新操作,所以我们把updateTime时间也更新一下
const { Service } = require('egg')
class BaseService extends Service {
async updateTodo({ id, title, content }) {
const { ctx } = this;
const result = await ctx.helpModel.Abc.update({
title,
content,
updateTime: new Date()
}, {
where: {
id
}
});
return { result: "更新成功" }
}
}
module.exports = BaseService;
这时我们查看一下数据库,发现数据也修改成功了
- 4、删除数据
突然从睡梦中醒来,发现变阳是做了个梦,还梦游着写了几个接口
趁着梦里的知识还没忘,我赶紧爬起来,抓紧写了个删除接口
const { Service } = require('egg')
class BaseService extends Service {
async delTodo({ id }) {
const { ctx } = this;
await ctx.helpModel.Abc.destroy({
where: {
id
}
});
return { result: "删除成功" }
}
}
module.exports = BaseService;
执行后发现数据库果然空了。
1.6、小结
sequlize的中文文档(www.sequelize.cn/core-concep…
我们刚才用到的只是非常简单的几个api,先入个门再说
github源码 tag为1.2.0