⚡️ 深入数据之海:SQL 基础与 ORM 的应用

大家好,我是大布布将军。

在上一篇中,我们的 BFF 服务还是一个"中介",它不存储自己的数据。但一个完整的全栈应用,或一个复杂的 BFF,往往需要存储一些自身特有的数据,比如会话信息、BFF 配置、或者一些临时业务数据。这就要求我们掌握数据库知识。

本篇,我们聚焦于最主流的数据库类型:关系型数据库(如 MySQL, PostgreSQL) ,以及如何在 Node.js 中优雅地操作它们。

1. 关系型数据库(SQL)基础:后端思维的基石

关系型数据库的核心是"表"(Table),数据以行和列的方式存储,并通过键(Keys) 建立关联关系(Relationship)。这是后端工程师的"语言"。

SQL 基础概念 对应 Node.js 实践 作用
CRUD 增 (POST), 查 (GET), 改 (PUT/PATCH), 删 (DELETE) 数据库操作的四个基本动作,与 RESTful API 一一对应。
JOIN 关联查询(如左连接 LEFT JOIN) 在 Service 层中,通过一次查询获取关联表的数据,避免多次请求。
WHERE 过滤条件 在 Service 层中,根据用户 ID、状态等进行精确查询。

【思考转变】 :作为前端,我们习惯的是操作对象(JSON)。作为后端,你必须学会用 SQL 语句思考数据之间的关系

2. Node.js 操作数据库:告别手动拼接 SQL

在早期的 Node.js 开发中,开发者需要手动拼接 SQL 字符串,比如 'SELECT * FROM users WHERE status = "' + status + '"'。这不仅低效,而且容易导致SQL 注入等严重安全问题。

现代 Node.js 后端开发几乎全部使用 ORM (Object-Relational Mapping) ,即对象关系映射

  • ORM 的作用: 它将数据库中的"表"映射成我们熟悉的 TypeScript/JavaScript 对象(Entity) ,让我们能用面向对象的方式(例如 userRepository.find({ where: { status: 'active' } }))来操作数据库,而不需要写一行 SQL。
  • 主流选择: 推荐 TypeORM (功能强大,与 NestJS 结合紧密)或 Prisma(近年新兴的 ORM,体验现代化)。

3. 实践项目:基于 TypeORM 的 CRUD 模块

我们以 TypeORM 为例,来实现对 Order 数据的基本 CRUD 操作。

步骤一:定义实体 (Entity)

在 TypeORM 中,我们使用 TypeScript 类来定义数据库中的表结构:

TypeScript

复制代码
// 📁 src/order/order.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';

@Entity('orders') // 对应数据库中的 orders 表
export class Order {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    userId: number; // 哪个用户下的订单

    @Column({ type: 'decimal', precision: 10, scale: 2 })
    totalAmount: number;

    @Column({ default: 'pending' })
    status: 'pending' | 'paid' | 'shipped';

    @CreateDateColumn()
    createdAt: Date; // 自动创建时间
}
步骤二:在 Service 层执行 CRUD

在 Service 层中,我们注入 Repository 来进行操作:

TypeScript

复制代码
// 📁 src/order/order.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Order } from './order.entity';

@Injectable()
export class OrderService {
    // 依赖注入:获取 Order 表的操作句柄 (Repository)
    constructor(
        @InjectRepository(Order)
        private orderRepository: Repository<Order>,
    ) {}

    // 1. C - Create (创建订单)
    async createOrder(data: Partial<Order>): Promise<Order> {
        const newOrder = this.orderRepository.create(data); // 实例化实体
        return this.orderRepository.save(newOrder); // 保存到数据库
    }

    // 2. R - Read (读取订单详情)
    async findOrderById(id: number): Promise<Order | null> {
        // 使用 findOne 方法,以 WHERE 条件查询
        return this.orderRepository.findOne({ where: { id: id, status: 'paid' } });
    }

    // 3. U - Update (更新订单状态)
    async updateOrderStatus(id: number, status: 'shipped'): Promise<void> {
        await this.orderRepository.update(id, { status: status });
    }
}

4. 高级应用:JOIN 查询与数据关联

在实际业务中,我们经常需要同时获取订单和其对应的用户信息。

在 ORM 中,我们通常通过在实体上定义关系(例如 @OneToOne, @OneToMany)来实现 JOIN 查询,从而仅用一次数据库操作 就取回所有关联数据。这比在 Service 层写两个 findOne 再手动合并数据高效得多。

核心要点: 当你需要的数据分散在两个或更多表时,优先考虑使用 ORM 的关联查询(本质是 SQL JOIN)。

总结

掌握 SQL 基础和 ORM 是从 BFF 迈向全栈的决定性一步。它让你从一个 API 编排者,升级为一个数据所有者和管理者

通过 ORM,我们安全且优雅地完成了数据的持久化操作。下一篇,我们将探讨如何利用 NoSQL 数据库(Redis) ,将数据存储的效率进一步提升,实现 BFF 接口的高性能缓存

相关推荐
zhqiok15 小时前
React中类似于Vue中Pinia的轻量级状态管理神器——Zustand
前端
Mintopia15 小时前
促成高端技术方案形成的关键要素与实践路径
前端
摸鱼的春哥16 小时前
春哥的Agent通关秘籍13:实现RAG查询
前端·javascript·后端
明月_清风17 小时前
滚动锁定:用户向上翻看历史时,如何阻止 AI 新消息把它“顶”下去?
前端·javascript
明月_清风17 小时前
当高阶函数遇到 AI:如何自动化生成业务层面的逻辑拦截器
前端·javascript·函数式编程
NAGNIP1 天前
轻松搞懂全连接神经网络结构!
人工智能·算法·面试
NAGNIP1 天前
一文搞懂激活函数!
算法·面试
moshuying1 天前
别让AI焦虑,偷走你本该有的底气
前端·人工智能
GIS之路1 天前
ArcPy,一个基于 Python 的 GIS 开发库简介
前端
可夫小子1 天前
OpenClaw基础-为什么会有两个端口
前端