🌰举几个栗子,用Koa两天入门Node.js后端开发(三)
前言
选用Koa作为web框架,用三篇文章讲解Node.js编写接口、服务端渲染(SSR)和MySQL数据库
- 🌰举几个栗子,用Koa两天入门Node.js后端开发(一) | 接口篇
- 🌰举几个栗子,用Koa两天入门Node.js后端开发(二) | 服务端渲染篇
- 🌰举几个栗子,用Koa两天入门Node.js后端开发(三) | 数据库篇
前两篇已经讲了接口和服务端渲染,接下来学习后端最重要的内容 —— 数据库
预备知识
本篇的基础是您熟悉了第一篇的内容
🌰举几个栗子,用Koa两天入门Node.js后端开发(一) | 接口篇
🚩学习目标
用Koa + Mysql实现4个接口,数据保存到数据中
- 新增用户(POST请求)
- 查询用户信息(GET请求)
- 修改用户密码(GET请求)
- 删除用户(GET请求)
1. 什么是数据库?
“按照数据结构来组织、储存和管理数据的仓库”
🌰简单举个栗子
-
注册账号需要数据库存储信息
-
登录账号需要读取数据库信息
-
修改密码需要修改数据库信息
数据库有4个主要操作【增、删、改、查】
- 增:在数据库中 新增 一条/多条数据
- 删: …… 删除 ……
- 改: …… 修改 ……
- 查: …… 查询 ……
举几个常用的数据库
数据库 | 费用 | 模型 | 下载地址 |
---|---|---|---|
MySQL | 社区版免费 | 关系型数据库 | downloads.mysql.com/archives/in… |
Oracle | 收费 | 关系型数据库 | |
SQL Server | 收费 | 关系型数据库 | |
MongoDB | 社区版免费 | 非关系数据库 | www.mongodb.com/try/downloa… |
Redis | 社区版免费 | 非关系数据库 | redis.io/download |
Access | 收费 | 关系型数据库 |
数据库模型主要有两种:关系型数据库和非关系型数据库
关系型(SQL)和非关系型(NOSQL)数据库的区别?
二者区别非常多,详细的区别请自行百度
🌰举个不恰当的栗子
关系型 :数据储存成表格形式,就像上面的表格一样,有行和列
非关系型 :数据储存的是信息组合,比如对象,键值对等
本文以MySQL为例,结合Koa讲解后端的接口和数据库使用
2. 准备工作
🚧MySQL 8.0
没有安装MySQL?🤔
安装步骤参考以下掘友的文章,我就不重复写了
💥 一定记好设置的密码!
💥 一定记好设置的密码!
在cmd命令窗口中使用命令查看是否安装完成
mysql -V
输出MySQL的版本就表示安装完成
登录到MySQL
mysql -u root -p
如图所示结果就链接成功
确认MySQL运行没问题,关掉命令行。后面使用软件操作,不使用命令行
🚦 启动报错
ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost:3306' (10061)
解决:检查MySQL的服务是否启动
🚧MySQL 管理工具
推荐使用 Navicat PreMium 15
免费试用14天
-
运行Navicat,新建连接
-
填写链接信息
连接名:随便写
主机:localhost
端口:3306
用户名:root
密码:安装时设置的密码
💡 左下角【测试连接】按钮点击,弹出“连接成功”表示连接没问题
-
展开连接就能看到MySQL的一些系统数据库,后面将新建一个自己的库
-
执行SQL语句
-
在数据上右击 --> 新建查询
-
弹出的查询窗口中输入SQL语句
-
点击
运行
按钮
💡 创建数据库可以在
本地
上右击或使用可视化窗口 -
💥执行SQL后记得【右击 - 刷新】
-
编辑数据
编辑表格里的数据,需要点击下方的【对号】提交更改,否则不会生效
🚧Node.js
Node.js 版本 >= 10
需要安装Koa-generator
和 Koa
如果您不了解Koa,建议先看这篇文章
🚧请求工具
我用Hoppscotch(原名postwoman)你也可以用其他请求工具,postman,apifox,apipost等
👉 GitHub
3. 一些知识
开始写代码之前,先了解一些数据库的知识和概念。
😜简单且通俗
3.1 字段和纪录
MySQL以表的形式存储数据,表中的行叫做纪录,表中的列叫做字段
🌰举个栗子
在Excel中,通常说年龄
列,在数据库中叫做年龄
字段。
在Excel中,通常说张三
这一行,在数据库中叫做张三
这条纪录。
一个数据库中可以有多个表,表和表之间能通过字段建立关联
🌰举个栗子
表a
和表b
通过各自的字段1
建立了关联。有了关联就能联查数据,比如查询下表中张三的年龄和性别,就需要关联年龄表
和性别表
。
年龄表
姓名 | 年龄 |
---|---|
张三 | 18 |
性别表
姓名 | 性别 |
---|---|
张三 | 男 |
💥注意:
实际开发中不要使用中文表名和字段名
3.2 主键
完整名称是“主键约束” (PRIMARY KEY),一般情况每张表都会设置一个主键。
💡目的是确保该条纪录的唯一性,表和表之间建立关联需要主键
- 每个表只能定义一个主键
- 主键不能重复且不能为NULL
- 两个字段联合可以作为一个主键
🌰举个栗子
将2.1表中的姓名
作为年龄表
的主键,姓名
作为性别表
的主键。两表可以使用姓名
建立关联
实际开发中因为重名的问题,姓名是不能作为主键的
3.3 数据类型
数据库中同样有数据类型和长度,通常在创建表时指定字段类型和字段长度
几个常用的数据类型
类型 | 大小 | 格式 | 用途 |
---|---|---|---|
int | 4 Bytes | 大整数值 | |
float | 4 Bytes | 单精度浮点数值 | |
double | 8 Bytes | 双精度浮点数值 | |
varchar | 0-65535 bytes | 变长字符串 | |
blob | 0-65 535 bytes | 二进制形式的长文本数据 | |
date | 3 Bytes | YYYY-MM-DD | 日期值 |
datetime | 8 Bytes | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
timestamp | 4 Bytes | YYYYMMDD HHMMSS | 时间戳 |
详细的取值范围去菜鸟教程里看吧 👉 MySQL 数据类型
🌰 举个栗子
# 定义3位的整型变量age
age int(3)
# 定义20位的字段串name
name varchar(20)
💡 注意:int(M)中的长度M与你存放的数值型的数的大小无关
3.4 SQL写法
通过前面的知识,知道SQL语句操作的是 表、字段、纪录(ps:当然还有其他的,但不是本文的内容😜)
从增删改查四个方面看SQL语句的写法,简单拆分SQL语句,由3部分组成
动作
(新建表,修改表,查看表....)操作的表
(表名,字段...)条件
(where...)
以下语句在Navicat中可依次执行查看效果
3.4.1 增
-
新建数据库 - 名称:example
CREATE DATABASE example;
-
新建表 - 名称:people,包括字段name,age(name是主键)
CREATE TABLE people(name varchar(25) PRIMARY KEY, age int(3));
-
增加字段 - 在people表中增加sex字段
ALTER TABLE people ADD sex varchar(2);
-
增加一条纪录 - 在people表中增加“张三,18,男”
insert into people (name, age, sex) values('张三', 18, '男');
💡 SQL语句中的关键字不区分大小写
3.4.2 改
-
修改表名 - 修改people表名为person
ALTER TABLE people RENAME TO person;
-
修改字段 - 在person表中修改age字段为years,类型不变
ALTER TABLE person CHANGE age years int(3);
-
修改字段 - 在person表中修改years字段类型长度为2
ALTER TABLE person MODIFY years int(3);
💡 修改字段名用CHANGE,修改数据类型用MODIFY。
😁详细区别自行百度
-
修改纪录 - 修改person表中“张三”的年龄为20
UPDATE person SET years=20 WHERE name='张三'
修改多个字段值用逗号
,
隔开years=20, sex='女'
3.4.3 查
-
查询纪录(全部结果) - 查询person表中所有数据
SELECT * FROM person
-
查询纪录(部分字段) - 查询person表中name是“张三”的年龄
SELECT years FROM person WHERE name='张三'
-
查询纪录 - 查询person表中name是“张三”,sex是“男”的数据
SELECT * FROM person WHERE name='张三' AND sex='男'
-
查询纪录 - 多表联查
# 现在只有person一张表,需要扩充一下 # 新建 mail 表(name是主键) CREATE TABLE mail(name varchar(25) PRIMARY KEY, msg varchar(200)); # 插入一条数据 INSERT INTO mail (name, msg) values('张三', '请周末来加班!'); # 联查 所有人的年龄和信息 SELECT person.name, person.years, mail.msg FROM person, mail WHERE person.name = mail.name
联查有多种方法,比如
inner join
left join
right join
😁详细用法自行百度
3.4.4 删
-
删除字段 - 删除person表中sex字段
ALTER TABLE person DROP COLUMN sex;
-
删除纪录 - 删除person中里名为“张三”的纪录
DELETE FROM person WHERE name='张三';
-
删除表 - 删除person表
DROP TABLE person;
-
删除数据库
DROP DATABASE example;
MySQL知识非常多,各种语句要多练习。了解简单的写法以后,我们来看在Node.js中怎样写SQL语句。
4. Node.js中使用MySQL
🌰举几个栗子,用Koa两天入门Node.js后端开发(一)中学习了Koa怎样写接口,继续在之前的项目中写本篇的代码
用Koa+MySQL实现增删改查4个功能
- 增加用户
- 查询用户信息
- 修改用户信息
- 删除用户
4.1 连接数据库
首先,新建一个数据库koadb
CREATE DATABASE koadb;
安装MySQL的npm包mysql2
为什么是mysql2?
npm主页 🤡更快,更高,更强
npm i mysql2
在项目的根目录创建数据的连接文件pool.js
const mysql = require('mysql2')
// 注意修改成你的连接信息
const config = {
database: 'koadb', // 数据库
username: 'root', // 用户
password: '000000', // 密码
port: '3306', // MySQL端口号
host: 'localhost' // MySQL地址
}
const pool = mysql.createPool(config)
module.exports = conn
注意修改config
中的连接信息,conn就创建了一个数据连接
4.2 调用SQL
首先引用pool.js
,然后参考下面的写法
无参数SQL
const [rows, fields] = await pool.query('SELECT * FROM USER')
传参SQL
const [rows, fields] = await pool.query('SELECT * FROM USER WHERE NAME = ?',['张三'])
预先生成SQL
const sql = pool.format('SELECT * FROM USER WHERE NAME = ?',['张三'])
const [rows, fields] = await pool.query(sql)
错误处理
const sql = pool.format('SELECT * FROM USER WHERE NAME = ?',['张三'])
pool.query(sql,(err, rows, fields)=>{
//...
})
4.3 设计数据表
根据4个功能,共设计2张数据表,一张用户表
,一张密码表
user - 用户表
字段 | 数据类型 | 长度 | 含义 |
---|---|---|---|
id | int | 5 | 主键 |
name | varchar | 32 | 用户名 |
level | int | 1 | 等级 |
pwd - 密码表
字段 | 数据类型 | 长度 | 含义 |
---|---|---|---|
id | int | 5 | 主键 |
name | varchar | 32 | 用户名 |
password | varchar | 18 | 密码 |
两表通过id
字段关联
4.4 创建数据表
新建 user
- 用户表
CREATE TABLE USER (
id INT ( 5 ) PRIMARY KEY,
name VARCHAR ( 32 ),
level INT ( 1 ));
双击表名,查看一下创建完成的表,确认没有问题
新建pwd
- 密码表
CREATE TABLE PWD (
id INT ( 5 ) PRIMARY KEY,
name VARCHAR ( 32 ),
password VARCHAR ( 18 ));
双击表名,查看一下创建完成的表,确认没有问题
4.5 事务
写代码前还需要了解最后一个概念事务
事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。
🌰 举个栗子
新增用户需要在user
和pwd
两表中各添加一条数据,使用事务以后,要么两条都插入成功,要么两条都失败。
不会出现一条成功,一条失败,这样保证了数据的一致性
// 省略...
//开启事务
const conn = await pool.getConnection()
await conn.beginTransaction()
try {
// ...
// MySQL操作
// ...
await conn.commit() //提交事务
conn.release() //释放连接
} catch (e) {
console.log(e)
conn.rollback() //回滚事务
}
5. 接口
有了数据库连接以后,创建4个接口。
- 新增用户 -
/sql/addUser
- 查询信息 -
/sql/getUser
- 修改密码 -
/sql/changUserPassword
- 删除用户 -
/sql/deleteUser
根据🌰举几个栗子,用Koa两天入门Node.js后端开发(一)学习的内容,新建sql.js
引用之前新建的query.js
const router = require('koa-router')()
const pool = require('../pool')
router.prefix('/sql')
module.exports = router
在app.js
中添加调用
// ……省略
const sql = require('./routes/sql')
// ……省略
app.use(sql.routes(), sql.allowedMethods())
💡 写接口之前,需要先写出SQL语句,然后根据SQL语句再定义接口参数
5.1 新增用户(POST)
📢 在sql.js
中新增addUser
接口,用于创建新用户。这个接口也是4个接口中最复杂的接口。
每个用户到两张表 user
和pwd
来存储level和password
新建用户的SQL语句
# 在用户表插入一条
INSERT INTO USER ( id, name, level )
VALUES
( '1', 'Maokai', 0 );
# 在密码表插入一条
INSERT INTO PWD ( id, name, password )
VALUES
( '1', '张三', '123456$' );
从SQL看出一共需要传4个值 id
,name
,level
,password
,其中id需要自动生成,其他值由传参拿到。
请求参数:
{ name:'', password:'', level:'' }
📘 id生成逻辑: 取user
表里当前最大的id,新id在此基础上加1,用SQL语句表示。(也可以在建表时设置自增id)
SELECT
COALESCE( max( id ), 0 ) + 1 id
FROM USER
✨接口代码
/*
* 新增用户
*/
router.post('/addUser', async (ctx, next) => {
const { name, password, level } = ctx.request.body
try {
if (!(name && password && level)) {
throw '缺少参数'
}
// 获取新id
const idSQL = 'SELECT COALESCE( max( id ), 0 ) + 1 id FROM USER'
const [[{ id }]] = await pool.query(idSQL)
// 获取MySQL连接
const conn = await pool.getConnection()
await conn.beginTransaction()
const userSQL = pool.format('INSERT INTO USER ( id, name, level ) VALUES( ?, ?, ? )', [id, name, level])
const pwdSQL = pool.format('INSERT INTO PWD ( id, name, password ) VALUES( ?, ?, ? )', [id, name, password])
await conn.query(userSQL)
await conn.query(pwdSQL)
// 提交事务
await conn.commit()
// 释放MySQL连接
conn.release()
ctx.body = `增加用户成功,id:${id},name:${name}`
} catch (e) {
conn.rollback()
ctx.body = e
}
})
使用Hoppscotch 测试接口(POST)
http://localhost:3000/sql/addUser
请求参数
{
"name": "MaoKai",
"password": "123456$",
"level": 1
}
可以看到正确返回了name
和id
图片比较大,耐心等待👇
5.2 查询信息(GET)
📢 在sql.js
中新增getUser
接口,查询用户信息。
每个用户信息存到两张表中 user
和pwd
,但是查询接口仅需user
表
查询信息的SQL语句
SELECT
*
FROM USER
WHERE
id = 1
从SQL看出只需要传1个值 id
使用Koa的路由参数:
/getUser/1
✨接口代码
/*
* 查询信息
*/
router.get('/getUser/:id', async (ctx, next) => {
const { id } = ctx.params
try {
if (!id) {
throw '缺少参数'
}
const userSQL = pool.format('SELECT * FROM USER WHERE ID = ?', [id])
const [rows] = await pool.query(userSQL)
const { name, level } = rows[0]
ctx.body = `用户信息:\n id:${id}\n name:${name}\n level:${level}`
} catch (e) {
ctx.body = e
}
})
使用Hoppscotch 测试接口(POST)
http://localhost:3000/sql/getUser/1
可以看到正确返回了用户信息
5.3 修改密码(POST)
📢 在sql.js
中新增changPassword
接口,用于修改密码。逻辑是先校验旧密码,如果正确则修改,错误则返回
修改密码只需要pwd
表
修改密码的SQL语句
# 查询旧密码
SELECT password
FROM pwd
WHERE
id = 1
# 修改成新密码
UPDATE pwd
SET password = '123'
WHERE
id = 1
从SQL看出一共需要传3个值 id
,password
,newPassword
请求参数:
{ id:'', newPassword:'', password:'' }
✨接口代码
/*
* 修改密码
*/
router.post('/changPassword', async (ctx, next) => {
const { id, password, newPassword } = ctx.request.body
try {
if (!(id && password && newPassword)) {
throw '缺少参数'
}
const pwdSQL = pool.format('SELECT password current FROM PWD WHERE ID = ?', [id])
const [[{ current }]] = await pool.query(pwdSQL)
if (password !== current) {
throw '密码错误,请检查'
}
const newPwdSQL = pool.format('UPDATE pwd SET password = ? WHERE ID = ?', [newPassword, id])
await pool.query(newPwdSQL)
ctx.body = `id:${id} 密码修改成功, 新密码:${newPassword}`
} catch (e) {
ctx.body = e
}
})
使用Hoppscotch 测试接口(POST)
http://localhost:3000/sql/changPassword
请求参数
{
"id": 1,
"password": "123456$",
"newPassword": "@@@"
}
可以看到数据库中的pasword
字段被更新了
图片比较大,耐心等待👇
5.4 删除用户(GET)
📢 在sql.js
中新增deleteUser
接口,删除用户。
每个用户信息存到两张表中 user
和pwd
,删除用户时需要同时删除。
删除的SQL语句,这次用一个连接语句
DELETE
user,pwd
FROM
user,pwd
WHERE
user.id = pwd.id
AND user.id = '1'
从SQL看出只需要传1个值 id
使用Koa的路由参数:
/deleteUser/1
✨接口代码
/*
* 删除用户
*/
router.get('/deleteUser/:id', async (ctx, next) => {
const { id } = ctx.params
try {
if (!id) {
throw '缺少参数'
}
const userSQL = pool.format('SELECT * FROM USER WHERE ID = ?', [id])
const [rows] = await pool.query(userSQL)
const { name, level } = rows[0]
ctx.body = `用户信息:\n id:${id}\n name:${name}\n level:${level}`
} catch (e) {
ctx.body = e
}
})
使用Hoppscotch 测试接口(POST)
http://localhost:3000/sql/getUser/1
可以看到删除了用户(注意:即使用户不存在也不会报错)
最后
到这里,Koa后端快速入门就结束了,3篇文章简单写了一些Koa的知识,掘友们在这方面也有诸多优秀文章,还要大家自己去发现。
- 🌰举几个栗子,用Koa两天入门Node.js后端开发(一) | 接口篇
- 🌰举几个栗子,用Koa两天入门Node.js后端开发(二) | 服务端渲染篇
- 🌰举几个栗子,用Koa两天入门Node.js后端开发(三) | 数据库篇。
😜万水千山总是情,点个关注行不行~
转载自:https://juejin.cn/post/7067887882458890253