2021-10-18

5-1 MySql 介绍

  • mysql 介绍、安装和使用
  • nodejs连接 mysql
  • API 连接 mysql

先用 mysql 再用 Mongodb

  • 做 server 端,mysql 和 MongoDB 都要学习
  • 先用 mysql 再用 MongoDB,虽然后者学习成本低

安装 workbench
image.png

5-2 数据库操作(创建和增、删、查)

  • 建库
  • 建表
  • 表操作

建库

  • 创建 myblog 数据库
  • 执行 show databases; 查询
  1. CREATE SCHEMA `myblog` ;

建表

image.png
id 是标识,不能重复
createtime 是 13位的整数

比如 users 表
image.png
varchar 表示字符串类型,20 是长度,主键就是保证不能重复,自动增加可以用来确保不能重复
image.png
在 workbench 中这样创建
image.png

  1. CREATE TABLE `myblog`.`users` (
  2. `id` INT NOT NULL AUTO_INCREMENT,
  3. `username` VARCHAR(20) NOT NULL,
  4. `password` VARCHAR(20) NOT NULL,
  5. `realname` VARCHAR(10) NOT NULL,
  6. PRIMARY KEY (`id`));

同样的创建blogs table

  1. CREATE TABLE `myblog`.`blogs` (
  2. `id` INT NOT NULL AUTO_INCREMENT,
  3. `title` VARCHAR(50) NOT NULL,
  4. `content` LONGTEXT NOT NULL,
  5. `createtime` BIGINT(20) NOT NULL DEFAULT 0,
  6. `author` VARCHAR(20) NOT NULL,
  7. PRIMARY KEY (`id`));

表操作

  • 增 删 改 查
  • 使用 sql 语句(入门简单,一学就会)

增加数据

因为 password 是关键字,所以使用反引号把它包裹

  1. insert into users(username,`password`,realname) values ('zhangsan','123','张三');
  2. insert into users(username,`password`,realname) values ('lisi','123','李四');

查询一下
image.png
其他一些查询语句

  1. select id username from users;
  2. select * from users where username like '%zhang%';
  3. select * form users where password like '%1%' order by id desc;

5-3 数据库操作(更新)

更新操作

更新之前会出现一个问题:权限不够
这个 Error Code:1175
image.png
解决:
使用一次这个,就可以成功 update 了

  1. SET SQL_SAFE_UPDATES = 0;

更新操作:

  1. update users set realname='李四2' where username='lisi';

修改成功
image.png

删除操作

  1. delete from users where username='wangwu';

image.png
执行成功!

软删除

其实在真正的操作中,想要删除一条数据不是真正的删除它,而是做标记
修改表的结构,给他加一行 state,默认值为 1
image.png
代码:

  1. ALTER TABLE `myblog`.`users`
  2. ADD COLUMN `state` INT NOT NULL DEFAULT 1 AFTER `realname`;

然后操作

  1. select * from users where state=1;
  2. update users set state=0 where username='lisi';

因此,只要 state 为0则证明已经被删除了

添加 blogs 的数据

  1. insert into blogs(title,content,createtime,author) values ('标题B','内容B',1634557734333,'lisi');

总结

  • 如何建库 如何建表
  • 建表时常用的数据类型(int bigint varchar longtext)
  • sql 语句实现增、删、改、查

5-4 nodejs 操作 mysql

  • 示例:用 demo 演示,不考虑使用
  • 封装:将其封装为系统可用的工具
  • 使用:让 API 直接操作数据库,不再使用假数据

操作:
新建一个 mysql-test 这样一个空白目录
然后 npm init -y 空白目录
然后新建对应的 index.js
然后 npm i mysql

然后在 index.js 中写一整套查询逻辑

  1. const mysql = require('mysql')
  2. // 创建连接对象
  3. const connnectionObject = mysql.createConnection({
  4. host: 'localhost',
  5. user: 'root',
  6. password: 'admin123',
  7. port: '3306',
  8. database: 'myblog'
  9. })
  10. // 开始连接
  11. connnectionObject.connect()
  12. // 执行 SQL 语句
  13. const sqlStatement = 'select * from users;'
  14. // 查询
  15. connnectionObject.query(sqlStatement, (err, result) => {
  16. if (err) {
  17. console.error(err)
  18. return
  19. }
  20. console.log(result)
  21. })
  22. // 关闭连接
  23. connnectionObject.end()

然后尝试连接
image.png
试试更新语句
image.png
因此在假数据的那个地方,就可以根据 changedRows 和 affectedRows 来判断是否更新或者删除成功

试试 插入
返回 id 为 3

  1. const sqlStatement = `insert into blogs(title,content,createtime,author) values ('标题C','内容C',1634607744531,'wangwu');`

image.png

2021-10-19

5-5 nodejs 链接 mysql 做成工具

回到 blog-1 安装 mysql 然后启动
创建conf文件夹,创建db.js(/src/conf/db.js )

db.js 用来配置环境参数

  1. const env = process.env.NODE_ENV // 环境参数
  2. // 配置 mysql 的值
  3. let MYSQL_CONF
  4. if (env === 'dev') {
  5. MYSQL_CONF = {
  6. host: 'localhost',
  7. user: 'root',
  8. password: 'admin123',
  9. port: '3306',
  10. database: 'myblog'
  11. }
  12. }
  13. if (env === 'production') {
  14. // 因为 production 是用本地环境做模拟的,所以和 dev 环境设置的一样
  15. MYSQL_CONF = {
  16. host: 'localhost',
  17. user: 'root',
  18. password: 'admin123',
  19. port: '3306',
  20. database: 'myblog'
  21. }
  22. }
  23. module.exports = {
  24. MYSQL_CONF
  25. }

然后创建 db 文件夹和 mysql.js 文件(/src/db/mysql.js )
借用之前创建的 demo 代码来完成
其中回调函数使用 promise 来处理

  1. const mysql = require('mysql')
  2. const { MYSQL_CONF } = require('../conf/db')
  3. // 创建连接对象
  4. const connectionObject = mysql.createConnection(MYSQL_CONF)
  5. // 开始连接
  6. connectionObject.connect()
  7. // 统一执行 sql 的函数
  8. function exec(sqlStatement) {
  9. const promise = new Promise((resolve, reject) => {
  10. connectionObject.query(sqlStatement, (err, result) => {
  11. if (err) {
  12. reject(err)
  13. return
  14. }
  15. resolve(result)
  16. })
  17. })
  18. return promise
  19. }
  20. // 要保持连接,不能断开连接,即单例函数
  21. // connectionObject.end()
  22. module.exports = {
  23. exec
  24. }

5-6 API对接 mysql(博客列表)

将假数据全部改为真数据
将 controller/blog.js 下的数据分别修改

这里的 1 =1 的作用为:
当没有 author 和 keyword 时,sql语句就会变成 select * from blogs where order by createtime desc
就会直接报错
如果没有 where 当有 author 或者有 keyword 时 就会报错

  1. const getList = (author, keyword) => {
  2. let sql = `select * from blogs where 1=1 `
  3. if (author) {
  4. sql += `and author='${author}'`
  5. }
  6. if (keyword) {
  7. sql += `and title like '%${author}%' `
  8. }
  9. sql += `order by createtime desc;`
  10. // 返回 promise
  11. return exec(sql)
  12. }

同样的,如果拼接数据 xxx.html?k1=v1&k2=v2&k3=v3 时也可以使用这样的操作手段
获取数据:
router/blog.js

  1. // 获取博客列表
  2. if (method === 'GET' && req.path === '/api/blog/list') {
  3. // 获取地址栏中 query 中的数据
  4. const author = req.query.author || ''
  5. const keyword = req.query.keyword || ''
  6. // 然后根据 query 的值查询 mock 数据
  7. // const listData = getList(author, keyword)
  8. // return new SuccessModel(listData)
  9. const result = getList(author, keyword)
  10. return result.then(listData => {
  11. return new SuccessModel(listData)
  12. })
  13. }

试一下 http://localhost:8000/api/blog/list
image.png
成功!

2021-10-20

5-7 API对接mysql(博客详情和新建)

忘记测试模糊查询,一样也可以,比如输入keyword=A
只展示标题里含A的
image.png
同样的 查询 author为lisi 的
image.png
继续获取博客详情

博客详情

首先修改获取数据的逻辑 即 controller/blog.js
修改 getDetail 方法

  1. const getDetail = (id) => {
  2. // 根据 id 来获取某条博客的具体内容
  3. const sql = `select * from blogs where id='${id}'`
  4. // 因为返回的数组,所以讲数组的括号去掉,变成对象的形式
  5. return exec(sql).then(rows => {
  6. return rows[0]
  7. })
  8. }

然后修改路由信息 即 router/blog.js
修改

  1. // 获取博客详情
  2. if (method === 'GET' && req.path === '/api/blog/detail'){
  3. // const listData = getDetail(id)
  4. // return new SuccessModel(listData)
  5. // 拿到 id 即为 req.query.id
  6. const result = getDetail(id)
  7. return result.then(listData => {
  8. return new SuccessModel(listData)
  9. })
  10. }

测试一下
image.png
成功!

新建博客

新建博客的逻辑在之前的假数据中返回的是 插入的 id

修改获取数据的逻辑 即 controller/blog.js
修改 getDetail 方法

  1. const newBlog = (blogData = {}) => {
  2. // blogData 是一个博客对象,包含 title content author属性
  3. const title = blogData.title
  4. const content = blogData.content
  5. const author = blogData.author
  6. const createtime = Date.now()
  7. const sql = `
  8. insert into blogs (title, content, author, createtime)
  9. values ('${title}', '${content}', '${author}', ${createtime});
  10. `
  11. return exec(sql).then(insertData => {
  12. console.log('insertData', insertData)
  13. return {
  14. id: insertData.insertId
  15. }
  16. })
  17. // return {
  18. // id: 3 // 表示新建博客,插入到数据表里面的 id
  19. // }
  20. }

然后修改路由信息 即 router/blog.js
修改
这里注意修改 author 数据为假数据,后期添加了 users 的逻辑再改

  1. // 新建一篇博客
  2. if (method === 'POST' && req.path === '/api/blog/new') {
  3. // const blogData = req.body
  4. // const data = newBlog(blogData)
  5. // return new SuccessModel(data)
  6. // 因为逻辑是登录之后(即获取 author 值之后)才能新建博客
  7. // 而此时我们没写author的逻辑,所以先用假数据
  8. const author = 'zhangsan'
  9. req.body.author = author // 假数据,待开发登录时再改为真数据
  10. const result = newBlog(req.body)
  11. // 这里的 data 就是返回新建成功的博客的 id
  12. return result.then(data => {
  13. return new SuccessModel(data)
  14. })
  15. }

然后去 postman 试下 post 请求
image.png
这里返回成功,再去 workbench 试下
image.png
成功!

5-8 API对接mysql(博客更新和删除)

博客更新

在假数据中,我们设置的返回逻辑是 true 或者 false,传入更新的数据和id

  1. const updateBlog = (id, blogData = {}) => {
  2. // blogData 是一个博客对象,包含 title content 属性
  3. // 更新时只需要更新blogData的 title content 属性
  4. const title = blogData.title
  5. const content = blogData.content
  6. const sql = `
  7. update blogs set title='${title}',content='${content}' where id=${id}
  8. `
  9. return exec(sql).then(updateData => {
  10. console.log('updateData', updateData);
  11. if (updateData.affectedRows > 0) {
  12. return true
  13. }
  14. return false
  15. })
  16. // console.log('blogData,id', blogData, id);
  17. // return true
  18. }

更新 router

  1. // 更新一篇博客
  2. if (method === 'POST' && req.path === '/api/blog/update') {
  3. const result = updateBlog(id, req.body)
  4. return result.then(data => {
  5. if (data) {
  6. return new SuccessModel()
  7. } else{
  8. return new ErrorModel('update failed')
  9. }
  10. })
  11. // if (result) {
  12. // return new SuccessModel()
  13. // } else{
  14. // return new ErrorModel('update failed')
  15. // }
  16. }

在 postman 中试一下
image.png
成功
image.png
数据库中内容也一致

博客删除

删除逻辑和更新完全一样
更新一下 SQL 语句

  1. const sql = `
  2. delete from blogs where id=${id}
  3. `

删除的时候要加 author 增强博客删除的安全性

5-9 API对接mysql(登录)

登录博客

修改三个文件
app.js 用 promise 的形式来处理路由
image.png
controller user.js

  1. const { exec } = require('../db/mysql')
  2. const loginCheck = (username, password) =>{
  3. const sql = `
  4. select username,realname from users where username='${username}' and password='${password}'
  5. `
  6. return exec(sql).then(rows => {
  7. return rows[0] || {}
  8. })
  9. }
  10. module.exports = {
  11. loginCheck
  12. }

router user.js

  1. if (method === 'POST' && req.path === '/api/user/login') {
  2. const { username, password } = req.body
  3. const result = loginCheck(username, password)
  4. return result.then(data => {
  5. if (data.username) {
  6. return new SuccessModel()
  7. }
  8. return new ErrorModel('login failed')
  9. })
  10. }

测试一下
image.png
成功

总结

  • nodejs 连接 mysql ,如何执行 SQL 语句
  • 根据 NODE_ENV 来区分配置
  • 封装 exec 函数,API 使用 exec 操作数据库

5-10 总结

image.png