完成服务器对数据库的操作是必要的知识和能力,这里需要具有一定数据库语句知识以及代码中出现的async和await等知识。
egg服务器想要操作数据库,进行增删改查,同样需要插件实现。
下载插件egg-mysql
终端执行该指令(注意终端位置)
npm i --save egg-mysql
启动插件
添加启动代码在config/plugin.js中(参考cors插件)
...
mysql: {
enable: true,
package: 'egg-mysql',
},
...
配置mysql插件
提示:按照
mysql的步骤这里不讲解,建议提前学习积累一定使用经验,并安装好,记下用户名、数据库名、密码等信息以备使用。
配置在config.default.js文件的config对象内。
- 单个数据库配置
...
const config = exports = {
...
mysql: {
// 单数据库信息配置
client: {
// host -- localhost 或 数据库所在ip 或 host名
host: 'localhost',
// 端口号
port: '3306',
// 用户名 ---数据库的用户名
user: 'root',
// 密码 ---数据库密码
password: '123456',
// 数据库名
database: 'test',
},
// 是否加载到 app 上,默认开启
app: true,
// 是否加载到 agent 上,默认关闭
agent: false,
},
};
...
- 数据库参考图
- 多个数据库配置(
clients对象成为数据库配置对象的集合对象,参考单个数据库写法)
clients: {
// clientId, 获取client实例,需要通过 app.mysql.get('clientId') 获取
db1: {
host: 'xxx',
port: '3306',
user: 'root',
password: 'xxx',
database: '数据库名1',
},
db2: {
host: 'xxx',
port: '3307',
user: 'test_user',
password: 'xxx',
database: '数据库名2',
},
// ...
},
构建Service层
这里我们提出一个Service概念,Service 就是在复杂业务场景下用于做业务逻辑封装的一个抽象层。不用强硬理解,我们避免不了学习理解不了的知识,如果没有好的资源帮助理解,我们可以先会用,积累一定使用经验后我们慢慢就会理解。
1.创建Service(初始化)
我们创建Service就和Controller结构类似,app下创建一个文件夹service以及index.js文件,并初始化代码:(我们承接上次登录持久化的项目代码)
我们设计数据库中表login,id为主键。
2.搭建对应的controller(初始化)
ctx可以调用Service层接口,一一对应Service方法。
这里我们其实实现了查询功能。
数据库增删改查
- 我们先创建一个静态网页(
mysql.html)不断补充内容,对应四个路由四个请求(增删改查):
<!-- http://juejinlogin.com/public/mysql.html -->
<form action="" onsubmit="select(event)">
<label for="">id:<input type="text" id="id"></label>
<input type="submit" id="btn" value="查询">
</form>
<div class="mysql">
还未查询
</div>
<script>
function select(event){
event.preventDefault();
let id = document.querySelector('#id').value;
let xhr = new XMLHttpRequest()
xhr.open('get','http://juejinlogin.com/mysql_find?id='+id)
xhr.timeout = 5000;
xhr.onload=()=>{
console.log('接收到服务器响应');
console.log(JSON.parse(xhr.responseText));
show(JSON.parse(xhr.responseText).user);
}
xhr.onerror=()=>{
console.log('请求失败')
}
xhr.send(null);
}
function show(user){
if(user)
name = `查询到:用户${user.name},他的密码是${user.password}`
else
name=`查无此人`
document.querySelector('.mysql').innerHTML=name
}
</script>
1.查询
- 关键mysql插件代码:
const user = await this.app.mysql.get('login', { id: uid });
//等效于:select * from `login` where `id` = `uid值`,多个关键字就再第二参数对象内再添加新的属性就行了
- 首先我们手动在数据库中增加一条信息,并且按照如下格式:
- 直接沿用Service和controller初始化代码,添加一个路由关联:
router.get('/mysql_find', controller.mysql.index);
- 实现效果:
get方法只能获取到一条数据,并且是满足条件的第一条数据。多条数据获取使用select方法(请看最下面)。
2.增加
- 关键mysql插件代码:
const result = await this.app.mysql.insert('posts', { title: 'Hello World' });
insert即插入添加,除了通过一个
关键字增加数据外,还可以一次增加多个数据,以及通过多个关键字增加数据。
//insert方法第二参数的另外写法:
insert('posts', { title: 'Hello World', name: '2333' }); //多个关键字
insert('posts', [{ title: 'Hello World1' },{ title: 'Hello World2' }]); //多条数据
- 编写静态页面交互内容:
...
<form action="" onsubmit="add(event)">
<label for="">name:<input type="text" id="addname"></label>
<label for="">password:<input type="password" id="addpwd"></label>
<input type="submit" id="btn" value="增加">
</form>
...
function add(event){
event.preventDefault();
let name = document.querySelector('#addname').value;
let pwd = document.querySelector('#addpwd').value;
let xhr = new XMLHttpRequest()
xhr.open('post','http://juejinlogin.com/mysql_add')
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded')
xhr.timeout = 5000;
xhr.onload=()=>{
console.log('接收到服务器响应');
console.log(JSON.parse(xhr.responseText));
}
xhr.onerror=()=>{
console.log('请求失败')
}
xhr.send(`name=${name}&pwd=${pwd}`);
}
效果:
- 设置Service和controller代码:
//controller\mysql.js内新增方法。
async add() {
const { ctx } = this;
const res = await ctx.service.index.add(ctx.request.body);
ctx.body = {
msg: 'add ok',
result: res,
};
}
//service\index.js内新增方法。
async add(msg) {
const result = await this.app.mysql.insert('login', { name: msg.name, password: msg.password });//这里可以直接msg作为参数二,只是阅读性差。
return { result };
}
- 添加路由和静态网页新的结合和请求。
router.post('/mysql_add', controller.mysql.add);
- 效果:
3.删除
- 关键mysql插件代码:
const result = await this.app.mysql.delete('posts', { author: 'fengmk2' });
//等效于 delete form `posts` where `author` = `fengmk2`
- 其他部分新增代码基本是和上面的写法模式类似,只是关联的是这个delete方法:
<form action="" onsubmit="del(event)">
<label for="">name:<input type="text" id="delname"></label>
<label for="">password:<input type="password" id="delpwd"></label>
<input type="submit" id="btn" value="删除">
</form>
function del(event){
event.preventDefault();
let name = document.querySelector('#delname').value;
let pwd = document.querySelector('#delpwd').value;
let xhr = new XMLHttpRequest()
xhr.open('post','http://juejinlogin.com/mysql_del')//主要是路由不一样。
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded')
xhr.timeout = 5000;
xhr.onload=()=>{
console.log('接收到服务器响应');
console.log(JSON.parse(xhr.responseText));
}
xhr.onerror=()=>{
console.log('请求失败')
}
xhr.send(`name=${name}&password=${pwd}`);
}
//controller\mysql.js内新增方法。
async del() {
const { ctx } = this;
const res = await ctx.service.index.del(ctx.request.body);
ctx.body = {
msg: 'del ok',
result: res,
};
}
//service\index.js内新增方法。
async del(msg) {
// const result = await this.app.mysql.delete('login', { name: msg.name, password: msg.password });
const result = await this.app.mysql.delete('login', msg);
return { result };
}
//路由
router.post('/mysql_del', controller.mysql.del);
- 效果:
4.修改
update操作是比较复杂的操作。
- 关键mysql插件代码
const result = await this.app.mysql.update('posts', row, options);
//这里row是用更新的数据,不包含主键id,而option是起where语句效果。
const result = await this.app.mysql.update('posts', row);
//这里row是用更新的数据,因为包含id主键,所以自动进行找到进行更新。
我们跳过繁杂的设计过程(请参考上面写法),我们主要集中在service层的实现,以写代学。
- service层实现:
async update(msg) {
const option = {
where: {
name: msg.oldname,
},
};
const result = await this.app.mysql.update('login', { name: msg.name, password: msg.password }, option);
return { result };
}
主要注意的就是更新是三个参数(特殊用法最后讲),第三个参数的用于where,以及特殊的结构。
- 效果:
5.特殊写法补充:
补充两个方法:select、query,和特殊的literals函数。
- 查询整个表 或 特定集合的数据:
const results = await this.app.mysql.select('posts');
let list = await this.app.mysql.select('index',{where: { module: module }})
从
get条件查询方法变成select全查询方法,通过参数二的配置对象可以实现多个数据的查询。
- 手写数据库语句方法:(通过
query,我们可以直接书写并使用数据库语句)
await this.app.mysql.query('INSERT INTO `posts`(`title`) VALUES('Hello World')');
//等效于:await this.app.mysql.insert('posts', { title: 'Hello World' });
- 数据库语句
占位符写法:(通过?字符来进行数据库语句的占位)
await this.app.mysql.query('INSERT INTO `posts`(`title`) VALUES(?)',['Hello World']);
//等效于:await this.app.mysql.insert('posts', { title: 'Hello World' });
- 数据库的常用函数:(具体需求请查文档)
this.app.mysql.literals.now
//等效于:数据库中的now()函数。
官方文档
因为是egg-mysql插件,所以我们即使具备数据库和egg.js知识,也并不能直接得知对应数据库操作的方法api,虽然我们可以用query直接写mysql语句,但是还是要学会使用官网文档:
这部分代码量并不大,但是重复的结构较多,需要理解其中每个代码的前后和因果关系,这样方能举一反三,同时方便查错。