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

第六部分:第五节 - 数据持久化 (基础):管理厨房的原材料库存

一个真正的后端应用通常需要与数据库交互,存储和读取数据。数据库就像中央厨房的原材料仓库,所有的食材都整齐地存放在这里,并有专门的管理系统。

在 Node.js 生态中,有多种数据库可供选择(如 PostgreSQL, MySQL, MongoDB 等),也有多种库可以帮助我们与数据库交互。常用的方式是使用 ORM (Object-Relational Mapper) 处理关系型数据库(如 TypeORM, Sequelize)或 ODM (Object-Document Mapper) 处理 NoSQL 数据库(如 Mongoose 处理 MongoDB)。ORM/ODM 就像仓库管理员提供的标准化接口,让我们可以用面向对象的方式来操作数据库中的数据,而无需直接写复杂的 SQL 语句或处理底层数据库驱动。

NestJS 并没有内置强制使用某个特定的 ORM/ODM,但它提供了良好的集成能力。通常的做法是:

  1. 安装数据库驱动和 ORM/ODM 库。
  2. 配置数据库连接。 这通常在一个专门的数据库模块中完成。
  3. 定义数据模型(Entities/Schemas)。 描述数据在数据库中的结构。
  4. 在 Service 中使用 ORM/ODM 提供的 Repository 或 Model 来进行数据操作(CRUD)。 Service 作为业务逻辑层,负责协调数据访问。

Service 与数据交互的角色:

在 NestJS 中,Service (Provider) 是进行数据访问的理想位置。控制器负责处理 HTTP 请求和调用 Service,而 Service 则负责执行具体的业务逻辑,包括与数据库进行交互。这符合关注点分离的原则:控制器只关心如何接收请求和返回响应,Service 则关心如何实现某个功能(包括数据读写)。

想象一下,控制器(服务员)把订单交给服务(厨师长),厨师长根据食谱(业务逻辑)决定需要哪些原材料,然后通过仓库管理员(ORM/ODM)从仓库(数据库)获取原材料,最后完成菜品。

模拟数据存储(使用内存数组):

在这个基础教程阶段,为了避免引入复杂的数据库安装和配置,我们可以先在 Service 中使用一个简单的内存数组来模拟数据存储。这让我们能够专注于 NestJS 的核心架构,理解 Service 如何作为数据交互层。

小例子:修改 UsersService,使用内存数组存储用户数据

修改 src/users/users.service.ts

// src/users/users.service.ts
import { Injectable } from '@nestjs/common';// 定义一个简单的用户接口,用于类型安全
interface User {id: number;name: string;age: number;
}@Injectable()
export class UsersService {// 使用内存数组模拟用户数据存储private users: User[] = [{ id: 1, name: 'Alice', age: 28 },{ id: 2, name: 'Bob', age: 32 },];// 获取所有用户 (Read)findAll(): User[] {return this.users;}// 获取单个用户 (Read)findOne(id: number): User | undefined {return this.users.find(user => user.id === id);}// 创建用户 (Create) - 简单实现create(userData: { name: string; age: number }): User {const newUser = {id: this.users.length > 0 ? this.users[this.users.length - 1].id + 1 : 1, // 简单生成 ID...userData,};this.users.push(newUser);return newUser;}// 更新用户 (Update) - 简单实现update(id: number, updateData: Partial<User>): User | undefined {const userIndex = this.users.findIndex(user => user.id === id);if (userIndex === -1) {return undefined; // 找不到用户}this.users[userIndex] = { ...this.users[userIndex], ...updateData };return this.users[userIndex];}// 删除用户 (Delete) - 简单实现remove(id: number): boolean {const initialLength = this.users.length;this.users = this.users.filter(user => user.id !== id);return this.users.length < initialLength; // 判断是否删除了元素}
}

修改 src/users/users.controller.ts 以使用这些方法:

// src/users/users.controller.ts (修改)
import { Controller, Get, Post, Put, Delete, Param, Body, NotFoundException, HttpCode, HttpStatus } from '@nestjs/common';
import { UsersService } from './users.service';
import { ParseIntPipe } from '@nestjs/common'; // 导入 ParseIntPipe// 定义一个简单的 DTO 用于创建用户 (后面会详细讲 DTO)
class CreateUserDto {name: string;age: number;
}@Controller('api/users')
export class UsersController {constructor(private readonly usersService: UsersService) {}@Get() // GET /api/usersfindAll() {return this.usersService.findAll();}@Get(':id') // GET /api/users/:idfindOne(@Param('id', ParseIntPipe) id: number) {const user = this.usersService.findOne(id);if (!user) {throw new NotFoundException(`找不到 ID 为 ${id} 的用户`); // 抛出 HttpException}return user;}@Post() // POST /api/users@HttpCode(HttpStatus.CREATED) // 返回 201 Created 状态码create(@Body() createUserDto: CreateUserDto) {const newUser = this.usersService.create(createUserDto);return newUser;}@Put(':id') // PUT /api/users/:idupdate(@Param('id', ParseIntPipe) id: number, @Body() updateUserDto: Partial<CreateUserDto>) {const updatedUser = this.usersService.update(id, updateUserDto);if (!updatedUser) {throw new NotFoundException(`找不到 ID 为 ${id} 的用户`);}return updatedUser;}@Delete(':id') // DELETE /api/users/:id@HttpCode(HttpStatus.NO_CONTENT) // 返回 204 No Content 状态码remove(@Param('id', ParseIntPipe) id: number) {const removed = this.usersService.remove(id);if (!removed) {// 如果删除不成功(比如 ID 不存在),可以选择抛出 404throw new NotFoundException(`找不到 ID 为 ${id} 的用户,无法删除`);}// NestJS 会自动处理返回 204,因为方法没有返回值,且我们设置了 @HttpCode(HttpStatus.NO_CONTENT)// 如果不设置 @HttpCode,默认会返回 200}
}

小结: 数据库是后端应用存放数据的“原材料仓库”。ORM/ODM 提供了与数据库交互的便捷方式。在 NestJS 中,Service 负责与数据源(可以是内存数组、数据库、其他服务)进行交互,而控制器则通过调用 Service 来获取或修改数据。使用内存数组是学习阶段模拟数据持久化的简单方法。

练习:

  1. 在你的 my-backend 项目中,确认你已经创建了 products 模块和服务。
  2. 修改 products.service.ts,使用一个内存数组来存储产品数据(包含 ID、名称、价格等属性)。
  3. ProductsService 中实现以下方法,操作这个内存数组:
    • findAll(): 返回所有产品。
    • findOne(id: number): 根据 ID 查找单个产品。
    • create(productData: { name: string; price: number }): 添加新产品,并简单生成一个唯一 ID。
    • update(id: number, updateData: Partial<{ name: string; price: number }>): 根据 ID 更新产品。
    • remove(id: number): 根据 ID 删除产品。
  4. 修改 products.controller.ts,更新你的路由处理函数 (findAll, findOne, create, update, remove),使其调用 ProductsService 中对应的方法来处理数据。
  5. findOne, update, remove 方法中,如果通过 ID 找不到产品,则抛出 NotFoundException
  6. 运行应用,使用 Postman 等工具测试你的产品 API 的增删改查功能。
http://www.xdnf.cn/news/594307.html

相关文章:

  • NACOS2.3.0开启鉴权登录
  • 基于深度学习的无线电调制识别系统
  • 数据库基础面试题(回答思路和面试建议)
  • 小林八股Java集合笔记(8k字概要版)
  • 【调优】Java 调优学习笔记之字符串
  • ollama接口数据返回格式化数据,商品标题,商品详情
  • 八、Linux进程和计划任务管理
  • 【Dify学习笔记】:dify通过ollama加载DeepSeek-R1-32B模型无法加载!终于解决了!!
  • C++ QT生成GIF,处理原始图像RGBA数据,窗口生成简单的动画
  • 练习小项目7:天气状态切换器
  • db_ha执行ha_isready报错authentication method 13 not supported
  • 同步/异步电路;同步/异步复位
  • 从法律视角看湖北理元理律师事务所的债务优化实践
  • Qt5、C++11 获取wifi列表与wifi连接
  • vue3商城类源码分享 期末作业 注册登录,状态管理,搜索,购物车订单页面
  • v3.0 YOLO篇-如何通过YOLO进行实验
  • Redis 中的缓存击穿、缓存穿透和缓存雪崩是什么?
  • 比较连续型自变量和从连续型变量转换成了三分类变量的因变量的关系
  • Gitee PPM:智能化项目管理如何重塑软件工厂的未来格局
  • Scaled Dot-Product Attention 中的缩放操作
  • Spring Cloud生态与技术选型指南:如何构建高可用的微服务系统?
  • C语言:gcc 或 g++ 数组边界检查方法
  • 山东大学软件学院创新项目实训开发日志——第十二周
  • 2021~2025:特斯拉人形机器人Optimus发展进程详解
  • UV-python环境管理工具 入门教程
  • 时源芯微|电源、地线的处理
  • 技术篇-2.4.Python应用场景及开发工具安装
  • JMeter JDBC请求Query Type实测(金仓数据库版)
  • springboot3+vue3融合项目实战-大事件文章管理系统-本地存储及阿里云oss程序集成
  • 一文读懂Agent智能体,从概念到应用—Agent百科