# 1. 前言
我们知道,HTTP
的请求方法 GET
、POST
、PUT
和 DELETE
分别对应着对数据的增、删、改和查。既然是对数据的操作,那肯定少不了数据库。那么在本篇文章中,我们就来介绍一下在 koa
中如何使用数据库以及如何打通 GET
、POST
、PUT
和 DELETE
这四种请求方法与数据库的增删改查之间的链路。
数据库有关系型数据库和非关系型数据库两种,其中关系型数据库的典型代表是 MySQL
,非关系型数据库的典型代表通常是 MongoDB
,接下来,我们就以这两种数据库为例来介绍下在 koa
中如何使用数据库。
# 2. 关系型数据库 MySQL
关系型数据库需要通过 SQL
语言来存取数据,但书写 SQL
语句需要一定的技术能力,并且不恰当的 SQL
语句还会带来 SQL
注入漏洞。为了快捷开发,社区出现了一系列的 ORM(Object Relational Mapping)
类库。ORM
的字面意思为对象关系映射,它提供了概念性的、易于理解的模型化数据的方法。通过 ORM
,可以降低操作数据库的本。开发者不需要通过编写 SQL
脚本来操作数据库,直接通过访问对象的方式来查询、更新数据即可。这样做极大地提升了开发效率,降低了开发的门槛。但 ORM
也不是万能的,由于ORM
统一封装了 SQL
查询,在某些情况下 ORM
生成的 SQL
并不高效,依旧需要开发者编写 SQL
查询语句来提升性能。在 Node.js
中,一般采用 Sequelize
这个 ORM
类库来操作数据库。Sequelize
支持多种数据库,如 PostgreSQL
、MySQL
、SQLite
和 MSSQL
。
# 2.1 Sequelize 安装及使用
由于 Sequelize
属于第三方类库,所以老规矩,使用前先安装:
npm i Sequelize
由于我们要使用 Sequelize
来操作 MySQL
数据库,所以我们还要安装以下 MySQL
的驱动,如下:
npm i mysql2
以上两个都安装完成之后,我们就可以来使用 Sequelize
了。代码如下:
import * as Koa from "koa";
import * as Router from "koa-router";
import * as bodyParser from "koa-bodyparser";
// 引入 Sequelize
import { Sequelize } from "Sequelize";
/**
* databaseName: 数据库名称
* userName: 数据库登录名
* password: 数据库登录密码
* host: 数据库服务地址
* dialect: 数据库类型,这里是mysql
*/
const sequelize = new Sequelize(databaseName, userName, password, {
host: "localhost",
dialect: "mysql",
});
app.listen("3000", () => {
sequelize
.sync({ force: false })
.then(() => {
console.log("sequelize connect success");
console.log("server is running at http://localhost:3000");
})
.catch((err) => {
console.log(err);
});
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
将以上代码复制进 app.ts
文件中,启动服务,如果看到控制台输出如下内容,就表示与数据库连接成功了。
sequelize connect success
server is running at http://localhost:3000
2
ok,连接成功后,接下来我们就可以给数据库里添加数据了。
# 2.2 定义模型
我们知道,在关系型数据库中,数据是存在数据表里的,所以想要给数据库添加数据,我们第一件事就应该是创建数据表。创建数据表之前得先定义表结构,在 Sequelize
中,把一张数据表的表结构称作表的「模型」。可以使用 define
方法来定义模型,代码如下:
import { Sequelize, DataTypes } from "Sequelize";
const UserModel = sequelize.define("user", {
// 定义名为 user 的表
name: DataTypes.STRING, // 定义 name 字段,类型为string
age: DataTypes.NUMBER, // 定义 age 字段,类型为number
});
2
3
4
5
6
Sequelize
会默认为创建的数据表自动创建 createdAt
和 updatedAt
字段。同时,Sequelize
也提供了配置项来禁用或修改这些字段,代码如下:
const UserModel = sequelize.define(
"user", // 定义名为 user 的表
{
name: DataTypes.STRING, // 定义 name 字段,类型为string
age: DataTypes.NUMBER, // 定义 age 字段,类型为number
},
{
timestamps: false, // 禁止创建 createdAt 和 updatedAt 字段
updatedAt: "updateTime", // 创建 updateTime 字段,替代 updatedAt 字段
tableName: "myUser", // 修改创建的表名为 myUser
}
);
2
3
4
5
6
7
8
9
10
11
12
模型定义好之后,接下里就可以将模型同步到数据库中以创建数据表。在 Sequelize
中,可以通过 sync
方法将定义的模型同步到数据库中。既可以同步单个表,也可以同步全部表,代码如下:
sequelize
.sync({ force: false })
.then(() => {
console.log("sequelize connect success");
console.log("server is running at http://localhost:3000");
})
.catch((err) => {
console.log(err);
});
2
3
4
5
6
7
8
9
如果数据库中已经存在某一个表,同步时将默认不同步这个表。可以在调用 sync
方法时,开启传递参数 force
进行强制同步。
注意:如果开启 force
,同步时会删除已经存在的数据表。
# 2.3 sequelize 增删改查
在 Sequelize
中,定义的模型上会为我们提供常用的对数据表的操作方法,如增、删、改、查。接下来,我们分别来看一下:
增
Sequelize
的模型类上提供了create
方法,该方法向数据表中增加一条记录,如下:const user = await UserModel.create({ name: "nlrx", age: 18, });
1
2
3
4删
删除数据有两种方式:可以调用模型类上的
destroy
方法删除指定数据,也可以先根据指定条件查出要删除的数据,然后调用数据实例上的destroy
方法来删除自身。如下:// 调用模型类上的 `destroy` 方法删除指定数据 await UserModel.destroy({ where: { name: "nlrx", }, }); // 调用数据实例上的 `destroy` 方法来删除自身 const user = await UserModel.create({ name: "nlrx", age: 18, }); await user.destroy();
1
2
3
4
5
6
7
8
9
10
11
12
13
改
修改数据也有两种方式:可以调用模型类上的
update
方法修改指定数据,也可以先根据指定条件查出要修改的数据,然后调用数据实例上的update
方法来修改自身。如下:// 调用模型类上的 `update` 方法修改指定数据 await UserModel.update( { age: 19, //修改的字段对应的内容 }, { where: { name: "nlrx", //查询条件 }, } ); // 调用数据实例上的 `update` 方法来修改自身 const user = await UserModel.create({ name: "nlrx", age: 18, }); await user.update({ age: 19, //修改的字段对应的内容 });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20查
数据库的主要功能就是对数据的查询,所以
sequelize
提供了非常多的数据查询方法,在这里因为篇幅限制,我们只介绍两个:查找表中全部数据findAll
和根据查询条件查询单条数据findOne
,如下:// fingAll 查询全部 await UserModel.findAll(); // findOne 查询单条 await UserModel.findOne({ where: { name: "nlrx" }, });
1
2
3
4
5
6
7
# 2.4 与路由结合使用
通常情况下,koa-router
提供的 .get
、 .post
、 .put
和 .del
方法分别对应着对数据库的增、删、改和查。那么接下来,我们就来介绍下如何将路由与数据库的操作对应结合使用。
我们假设数据库里有一张 user
表,表中的字段分别是 name
和 age
,那么通过路由对 user
表的增删改查如下:
增 POST
我们通过对路由
/user
发送POST
请求,来创建一个新用户并将创建出来的新用户返回,如下:router.post("/user", async (ctx: Koa.Context, next: Koa.Next) => { const { name, age } = ctx.request.body; const user = await UserModel.create({ name, age: Number(age), }); ctx.body = { msg: "添加成功", data: user, }; });
1
2
3
4
5
6
7
8
9
10
11服务启动后,我们使用
postman
对127.0.0.1:3000/user
发送POST
请求,可以看到,数据已经成功被插入到数据库里了。查 GET
我们通过对路由
/user
发送GET
请求,并携带需要查找的用户的用户名作为参数,如下:router.get("/user/:name", async (ctx: Koa.Context, next: Koa.Next) => { const { name } = ctx.params; const user = await UserModel.findOne({ where: { name }, }); if (user) { ctx.body = { data: user, }; } else { ctx.body = { msg: "该用户不存在", }; } });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15可以看到,当我们查找用户名为
nlrx
的用户时可以被成功查到,但是当查找用户名为aa
的用户时会返回「该用户不存在」的提示。改 PUT
我们通过对路由
/user
发送PUT
请求,并携带需要修改的用户的用户名作为参数,如下:router.put("/user/:name", async (ctx: Koa.Context, next: Koa.Next) => { const { name } = ctx.params; let res: { msg: string; data: any } = { msg: "", data: null, }; const user = await UserModel.findOne({ where: { name }, }); if (user) { await user.update({ age: 19, //修改的字段对应的内容 }); res.msg = "修改成功"; res.data = user; } else { res.msg = "您要修改的用户不存在"; res.data = null; } ctx.body = res; });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23在上述代码中,我们将用户名为
nlrx
的用户的age
字段改成了19
,执行数据修改操作之前我们先查找该用户是否存在,如果存在,再执行修改操作;如果不存在,则提示「要修改的用户不存在」。修改成功之后返回了该用户被修改后的信息,我们发现该用户的age
字段确实被改成了19
。删 DELETE
我们通过对路由
/user
发送DELETE
请求,并携带需要删除的用户的用户名作为参数,如下:router.del("/user/:name", async (ctx: Koa.Context, next: Koa.Next) => { const { name } = ctx.params; let res: { msg: string; data: any } = { msg: "", data: null, }; const user = await UserModel.findOne({ where: { name }, }); if (user) { await user.destroy(); res.msg = "删除成功"; res.data = user; } else { res.msg = "您要删除的用户不存在"; res.data = null; } ctx.body = res; });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20同样,执行数据删除操作之前我们先查找该用户是否存在,如果存在,再执行删除操作;如果不存在,则提示「要删除的用户不存在」。删除成功之后返回了被删除用户的信息。
删除成功后当我们再查找该用户时,发现该用户已经不存在了,说明我们真的已经将该用户删除了。