⚡️ 深入数据之海: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 接口的高性能缓存

相关推荐
唐人街都是苦瓜脸2 小时前
踩坑记:ORA-01722 无效数字错误排查与解决(附实战案例)
sql·oracle
川贝枇杷膏cbppg2 小时前
Redis 的 RDB 持久化
前端·redis·bootstrap
JIngJaneIL2 小时前
基于java+ vue农产投入线上管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
东东的脑洞3 小时前
【面试突击二】JAVA基础知识-volatile、synchronized与ReentrantLock深度对比
java·面试
LYFlied3 小时前
【每日算法】LeetCode 153. 寻找旋转排序数组中的最小值
数据结构·算法·leetcode·面试·职场和发展
源代码•宸3 小时前
goframe框架签到系统项目(BITFIELD 命令详解、Redis Key 设计、goframe 框架教程、安装MySQL)
开发语言·数据库·经验分享·redis·后端·mysql·golang
天外天-亮3 小时前
v-if、v-show、display: none、visibility: hidden区别
前端·javascript·html
川贝枇杷膏cbppg3 小时前
Redis 的 AOF
java·数据库·redis
jump_jump3 小时前
手写一个 Askama 模板压缩工具
前端·性能优化·rust