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

相关推荐
山峰哥9 分钟前
数据库工程与SQL调优——从索引策略到查询优化的深度实践
数据库·sql·性能优化·编辑器
较劲男子汉20 分钟前
CANN Runtime零拷贝传输技术源码实战 彻底打通Host与Device的数据传输壁垒
运维·服务器·数据库·cann
java搬砖工-苤-初心不变25 分钟前
MySQL 主从复制配置完全指南:从原理到实践
数据库·mysql
不像程序员的程序媛1 小时前
Nginx日志切分
服务器·前端·nginx
星哥说事1 小时前
跨平台打包神器,免费将网页、Vue、React秒变桌面 APP,仅需 1 个 Github Token!
经验分享
北原_春希1 小时前
如何在Vue3项目中引入并使用Echarts图表
前端·javascript·echarts
尽意啊1 小时前
echarts树图动态添加子节点
前端·javascript·echarts
吃面必吃蒜1 小时前
echarts 极坐标柱状图 如何定义柱子颜色
前端·javascript·echarts
O_oStayPositive1 小时前
Vue3使用ECharts
前端·javascript·echarts
竹秋…1 小时前
echarts自定义tooltip中的内容
前端·javascript·echarts