当前位置: 首页 > news >正文

NodeJs学习日志(3):express,sequelize进行增删改查(CRUD)

NodeJs学习日志(3):express,sequelize进行增删改查(CRUD)

问题与解答

Q: 使用事务和悲观锁可以防止数据竞争和不一致状态?
A:

  • 事务:保证多个操作的原子性,要么全部成功,要么全部回滚。
  • 悲观锁:在操作期间锁定记录,防止其他并发修改,适用于高并发写场景。

Q: 发送很多HTTP请求,同时操作一个表,会不会所有请求都必须等待前面的操作结束?
A: 不会。

  • 如果操作的是不同行,通常可以并发执行,互不等待。
  • 如果操作的是同一行,可能会因锁机制产生等待或阻塞。
  • 只有在极端情况(如全表扫描、表锁)下,才会出现全局等待。

Q: HTTP请求参数有哪些?如何获取?
A:

  • 查询参数:req.query,如 GET /search?q=node&page=1
  • 路由参数:req.params,如 GET /users/123 中的 123
  • 请求体:req.body,如put>:put:http://localhost:3000/articles/func1/1
    body:{“title”: “New Title”, “content”: “Updated Content”}

环境准备

  • 第8回:使用 express-generator 创建正式项目,nodemon 监听代码变动
    https://clwy.cn/chapters/node-express-generator#content

  • 第9回:MySQL 与 Sequelize ORM 的使用
    https://clwy.cn/chapters/node-express-sequelize#content

  • 第10回:模型(Model)、迁移(Migration)与种子(Seeders)
    https://clwy.cn/chapters/node-express-model#content

  • 第11回:查询文章列表接口,promise 还是 async/await
    https://clwy.cn/chapters/node-express-promise#content

日志内容

模型导入方式

导入整个模型文件,或者解构赋值导入特定模型。

const models = require("../models");//导入整个模型文件
const { Article } = require("../models");//解构赋值导入特定模型
//导入整个模型文件的查询方式
//https://localhost:3000/func1
router.get("/func1", async (req, res) => {try {const articles = await models.Article.findAll();res.json({ articles: articles });}catch {res.status(500).json({ error: 'Internal Server Error' });}
});//模型导入方式不同,
//https://localhost:3000/func4
router.get("/func4", async function (req, res) {try {const articles = await Article.findAll();res.json({ articles: articles });}catch {res.status(500).json({ error: 'Internal Server Error' });}
});

async/await 与 .then().catch()

//使用,try-catch,async/await
//https://localhost:3000/func1
router.get("/func1", async (req, res) => {try {const articles = await models.Article.findAll();res.json({ articles: articles });}catch {res.status(500).json({ error: 'Internal Server Error' });}
});//使用then,catch
//https://localhost:3000/func2
router.get("/func2", function (req, res) {models.Article.findAll().then(articles => res.json({ articles })).catch(() => res.status(500).json({ error: 'Internal Server Error' }));
});

参数获取

get请求在url中获取参数
//在请求url中添加参数
//https://cn.bing.com/search?q=vscode&page=1
router.get("/search", (req, res) => {try {// 示例:/search?q=vscode&page=1const { q, page } = req.query; // q = "vscode", page = "1"res.json({ query: q, page });}catch {res.status(500).json({ error: 'Internal Server Error' });}
});//路由参数
//https://store.epicgames.com/zh-CN
router.get("/:lang", (req, res) => {try {const { lang } = req.params; //lang= "zh-CN"res.json({ "语言是": lang });}catch {res.status(500).json({ error: 'Internal Server Error' });}
});
请求体中获取
  1. 使用传统的对象属性赋值方式
  2. 使用ES6的解构赋值
//从请求体(body)中获取参数
//post: http://localhost:3000/get-args-1
//body: { title: "Hello", content: "World" }
router.post('/get-args-1', async function (req, res) {const data = {title: req.body.title,content: req.body.content};var article = await models.Article.create(data);res.json({ article: article });
});//从请求体(body)中获取参数
//post: http://localhost:3000/get-args-2
//body: { title: "Java", content: "java is a script language" }
router.post("/get-args-2", async (req, res) => {const { title, content } = req.body; // 从请求体获取const article = await models.Article.create({ title, content });res.json(article);
});

条件查询

//使用orderby进行添加条件的查询
//http://localhost:3000/func3
router.get("/func3", async (req, res) => {try {const data = await models.Article.findAll({order: [['id', 'DESC']]});res.json({ "data": data });}catch {res.status(500).json({ error: `Internal Server Error>${error}` });}
});

使用sql语句进行查询

语句意义
replacements: { id: articleId },将 :id 这个命名参数替换为变量 articleId 的值
type: sequelize.QueryTypes.SELECT告诉 Sequelize:这是一个查询数据的操作(SELECT)
// 使用原始SQL查询
app.get('/ray_sql/:articleId', async (req, res) => {const articleId = req.params; // 获取查询字符串中的 idtry {const [results] = await sequelize.query("SELECT * FROM articles WHERE id = :id", {replacements: { id: articleId },type: sequelize.QueryTypes.SELECT});if (!results || results.length === 0) {return res.status(404).send('Article not found');}res.json(results);} catch (error) {console.error('Error executing query:', error);res.status(500).send('Server error');}
});
多参数sql插入
语句意义
“SELECT * FROM articles WHERE id = :id AND title = :title”,:id 与:title 都为占位符号
replacements: { id: articleId, title: articleTitle },将占位符替换为实际的参数
const [results] = await sequelize.query("SELECT * FROM articles WHERE id = :id AND title = :title", {replacements: { id: articleId, title: articleTitle },type: sequelize.QueryTypes.SELECT}
);

使用数据库事务,悲观锁

先查询再修改

//修改用户数据-返回受到影响行数
//PUT http://localhost:3000/articles/func1/1 
//body:{"title": "New Title", "content": "Updated Content"}
router.put("/articles/func1/:id", async (req, res) => {// 创建事务const transaction = await models.sequelize.transaction();try {const { id } = req.params;const { title, content } = req.body;// 1. 使用悲观锁查询记录const article = await models.Article.findOne({where: { id },lock: true, // 启用行级锁transaction // 关联事务});if (!article) {await transaction.rollback();return res.status(404).json({ error: "文章不存在" });}// 2. 执行更新操作const [affectedRows] = await models.Article.update({ title, content },{ where: { id },transaction // 关联事务});// 3. 提交事务await transaction.commit();res.json({ affectedRows });} catch (error) {// 4. 发生错误时回滚事务if (transaction) await transaction.rollback();res.status(500).json({ error: "更新失败", details: error.message });}
});

//修改用户数据
//PUT http://localhost:3000/articles/func2/1
//body:{“title”: “New Title”, “content”: “Updated Content”}

router.put("/articles/func2/:id", async (req, res) => {const transaction = await models.sequelize.transaction();try {const { id } = req.params;const { title, content } = req.body;// 1. 使用悲观锁查询记录const article = await models.Article.findOne({where: { id },lock: true, // 行级锁transaction});if (!article) {await transaction.rollback();return res.status(404).json({ error: "文章不存在" });}// 2. 执行更新const [affectedRows] = await models.Article.update({ title, content },{ where: { id },transaction});if (affectedRows <= 0) {await transaction.rollback();return res.json({ "data": "没有数据被更新" });}// 3. 重新查询更新后的数据(在事务中)const updatedArticle = await models.Article.findByPk(id, { transaction });// 4. 提交事务await transaction.commit();res.json(updatedArticle);} catch (error) {if (transaction) await transaction.rollback();res.status(500).json({ error: "更新失败", details: error.message });}
});

//先查询再删除
//DELETE: http://localhost:3000/func1/1

router.delete("/func1/:id", async (req, res) => {const transaction = await models.sequelize.transaction();try {const { id } = req.params;// 1. 使用悲观锁查询记录const article = await models.Article.findOne({where: { id },lock: true, // 行级锁transaction});if (!article) {await transaction.rollback();return res.status(404).json({ error: "文章不存在" });}// 2. 执行删除await article.destroy({ transaction });// 3. 提交事务await transaction.commit();res.json({ success: true });} catch (error) {if (transaction) await transaction.rollback();res.status(500).json({ error: "删除失败", details: error.message });}
});

//直接删除
////DELETE: http://localhost:3000/func1/1

router.delete("/func2/:id", async (req, res) => {const transaction = await models.sequelize.transaction();try {const { id } = req.params;// 1. 使用悲观锁查询记录const article = await models.Article.findOne({where: { id },lock: true, // 行级锁transaction});if (!article) {await transaction.rollback();return res.status(404).json({ error: "文章不存在" });}// 2. 执行删除await models.Article.destroy({ where: { id },transaction});// 3. 提交事务await transaction.commit();res.json({ success: true });} catch (error) {if (transaction) await transaction.rollback();res.status(500).json({ error: "删除失败", details: error.message });}
});
http://www.dtcms.com/a/322255.html

相关文章:

  • 【QT】QMainWindow:打造专业级桌面应用的基石
  • java之父-新特性
  • 数据结构(一)顺序表
  • 【JVM】深入解析Java虚拟机
  • Ubuntu下搭建LVGL模拟器
  • react之React.cloneElement()
  • 深入剖析C++ STL原理:打开高效编程大门的钥匙
  • [每周一更]-(第155期):深入Go反射机制:架构师视角下的动态力量与工程智慧
  • Web3: DeFi借贷的安全基石, 了解喂价与清算机制的原理与重要性
  • Typora上传图片保存到assets目录下
  • ARM CPU 安全更新:Training Solo(关于 Spectre-v2 攻击中域隔离机制的局限性)
  • 学习:JS[8]本地存储+正则表达式
  • Matlab系列(004) 一 Matlab分析正态分布(高斯分布)
  • 《C++进阶之继承多态》【普通类/模板类的继承 + 父类子类的转换 + 继承的作用域 + 子类的默认成员函数】
  • pgAdmin 仪表盘的system部分不能显示,报SYSTEM_STATS扩展没有安装
  • git命令详解
  • TensorFlow深度学习实战(29)——强化学习(Reinforcement learning,RL)
  • elementui input无法输入问题
  • JAVA基础-使用BIO / NIO实现聊天室功能
  • Day 36: 复习
  • 康养休闲旅游服务虚拟仿真实训室:助力康养人才培养的创新引擎
  • 《算法导论》第 14 章 - 数据结构的扩张
  • SupChains团队:Animalcare公司供应链需求预测模型案例分享(十三)
  • [激光原理与应用-203]:光学器件 - 增益晶体 - 增益晶体的使用方法
  • GitCode 疑难问题诊疗:让你的开发流程重回正轨
  • 2025年渗透测试面试题总结-10(题目+回答)
  • C语言:构造类型
  • 【Python 语法糖小火锅 · 第 5 涮 · 完结】
  • 使用小诺框架报错:NoResourceFoundException: No static resource exercise/tag/page.
  • Go语言接口实战指南