前端也想写后端(2)ORM框架以及数据库关系和操作

ORM框架以及数据库关系和操作

提到后端肯定离不开数据库和数据的相关操作,最原始的办法我们就是直接写SQL语句进行查询,但是这样写起来比较麻烦,而且容易出错,所以我们需要一个框架来帮助我们操作数据库,这个框架就是ORM框架。

ORM(Object-Relational Mapping,对象关系映射)框架是一种编程技术,用于在面向对象的编程语言和关系型数据库之间建立映射关系,从而简化数据库操作。比如 java 中的 MyBatis,Python 中的 SQLAlchemy,Node.js中的 TypeORM等。

Node.js有很多可用的ORM框架,有 TypeORMSequelizePrisma 等,其中 TypeORM 的用户也是很多的,后面我们将以这个ORM框架来开展。

数据库

在具体讲解ORM框架之前,我们先来说一下数据库,当前流行的数据库很多,可分为关系型数据库和非关系型数据库。

维度 关系型数据库 非关系型数据库
数据模型 基于 "表 - 行 - 列" 的二维结构,数据之间通过外键建立关联(如 "用户表" 与 "订单表" 通过用户 ID 关联)。 数据模型多样,常见类型: 1.文档型(如 MongoDB,存储 JSON-like 文档) 2.键值型(如 Redis,key-value 键值对 3. 图型(如 Neo4j,节点 - 边关系) 4. 列族型(如 HBase,按列族存储)。
Schema(结构) 严格固定,表的字段、类型、长度需预先定义,修改结构(如加字段)需执行 DDL 语句,可能影响现有数据。 灵活可变,无需预先定义结构,同一份数据可以有不同字段(如 MongoDB 的文档可动态增删字段),适合数据结构频繁变化的场景。
事务与一致性 强支持 ACID 特性(原子性、一致性、隔离性、持久性),适合需要严格数据一致性的场景(如金融交易)。 多数不支持完整 ACID,或仅支持最终一致性(如 MongoDB 4.0 后支持多文档事务,但功能有限),更注重可用性和分区容错性。
查询方式 使用标准化 SQL 语言,支持复杂查询(如多表关联、分组、聚合等),查询能力强大 无统一查询语言,依赖各自 API(如 MongoDB 用查询文档,Redis 用命令行),复杂关联查询能力较弱(需业务层实现)
扩展性 垂直扩展为主(升级服务器硬件),水平扩展(分布式)较复杂(需分库分表中间件支持) 天生支持水平扩展(通过分片、复制),可通过增加节点轻松扩容,适合海量数据场景
适用数据类型 结构化数据(如用户信息、订单、财务数据等,格式固定且关系明确) 非结构化 / 半结构化数据(如日志、图片、JSON 数据)、高频读写的简单数据(如缓存、计数器)

在考虑关系与非关系数据库时,需要根据具体业务需求来选择,比如从数据结构是否固定、数据量和并发量、查询复杂度、一致性要求等方面来考虑。

我们后面关系型数据库用我们最熟悉的MySQL,非关系型数据库用最常用的Redis,MongoDB等。

我们先来安装一下MySQL

MySQL下载

安装的过程就在这里不细说了,不会安装或者有疑惑的人,可以看看别人的MySQL安装教程。

安装完成后,我们还需要有一个 MySQL 客户端,比如我们常用的 Navicat,这个之前是收费的,现在也有免费版了,但是只能使用一种数据库,如果需要使用多种数据库,就需要购买专业版了,当然我们可有别的办法免费使用专业版(doge),或者你也可以使用 HeidiSQL,这个是免费软件,且开源,且支持 MySQLMariaDBSQL ServerPostgreSQL 等数据库管理。

HeidiSQL下载

打开之后我们连接我们的MySQL数据库,然后创建一个数据库,我们这里创建一个 nest_demo 的数据库。

TypeORM

TypeORM是一个功能强大的ORM框架,它支持多种数据库,包括 MySQL、PostgreSQL、SQLite、Microsoft SQL Server 等,并且支持事务、关系、查询构建器等高级功能。

TypeORM 中文文档

我们先来安装一下

bash 复制代码
pnpm i @nestjs/typeorm typeorm mysql2

然后在我们的app.module.ts中引入TypeORM模块,并配置数据库连接信息

ts 复制代码
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql', // 数据库类型
      username: 'root',
      password: '123456',
      host: '127.0.0.1',
      port: 3306,
      database: 'nest_demo', // 数据库名称
      autoLoadEntities: true, // 是否自动将实体类自动同步到数据库  (实体文件)
      synchronize: true, // 定义数据库表结构与实体类字段同步
    }),
    UserModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

然后打开我们上节生成的user模块中的 user/entities/user.entity.ts 文件,在这个文件里我们可以定义我们的实体类,也就是数据库中的表结构,我们这里定义一个用户表:

ts 复制代码
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity('d_user')
export class User {
  @PrimaryGeneratedColumn({
    type: 'int',
    comment: '用户id',
  })
  id: number;

  @Column({
    type: 'varchar',
    comment: '用户昵称',
  })
  username: string;

  @Column({
    type: 'varchar',
    comment: '密码',
    select: false,
  })
  password: string;

  @Column({
    type: 'datetime',
    comment: '创建时间',
    default: () => 'CURRENT_TIMESTAMP',
  })
  createTime: Date;
}

Entity 装饰器用于定义实体类,PrimaryGeneratedColumn 装饰器用于定义主键列,Column 装饰器用于定义普通列。 Entity 可以接收一个字符串参数,表示实体类对应的数据库表名,如果不传,默认会使用类名作为表名。

然后我们进入到我们user模块中的 user.module.ts 文件,引入我们刚刚定义的实体类,并配置到 TypeORM 模块中

ts 复制代码
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import  { User } from './entities/user.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

这时候我们打开我们的数据库,可以看到已经自动生成了我们定义的表结构

关系

在数据库中,关系是指两个或多个表之间的关联,比如一个商品上市需要注册,每一个商品都有对应的注册信息,反之一个注册信息对应一件商品,这就是一对一的关系;我们在网上买东西,一个用户账号可以有很多个订单,这些订单都是属于这个用户的,这就是一对多的关系;我们一个订单可以有很多个商品,一个商品也可以出现在多个订单中,这就是多对多的关系。

那这些关系在 typeorm 中是如何表示的呢?

我们这边来单独演示表的关系,所以我们单独在src下创建个文件夹entity,里面放所有我们演示的数据库实体类。

一对一

我们创建一个product.entity.ts和register.entity.ts,分别表示商品和注册信息,然后我们创建一个一对一的关系,一个商品只能有一个注册信息,一个注册信息也只能对应一个商品。

register.ts

ts 复制代码
import { Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm';

@Entity('t_register')
export class Register {
  @PrimaryGeneratedColumn('uuid', {
    comment: '注册信息id',
  })
  id: string;

  @Column({
    type: 'varchar',
    length: 50,
    comment: '注册信息名称',
  })
  register_name: string;

  @Column({
    type: 'varchar',
    length: 255,
    comment: '注册信息描述',
  })
  register_desc: string;

  @Column({
    type: 'varchar',
    length: 255,
    comment: '注册信息类别',
  })
  register_category: string;
}

product.entity.ts

ts 复制代码
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn, ManyToMany } from 'typeorm';
import { Register } from './register.entity'

@Entity('t_product')
export class Product {
  @PrimaryGeneratedColumn('uuid', {
    comment: '商品id',
  })
  id: string;

  @Column({
    type: 'varchar',
    length: 50,
    comment: '商品名称',
  })
  product_name: string;

  @Column({
    type: 'varchar',
    length: 255,
    comment: '商品描述',
  })
  product_desc: string;

  @Column({
    type: 'varchar',
    length: 255,
    comment: '商品类别',
  })
  product_category: string;

  @Column({
    type: 'decimal',
    precision: 10,
    scale: 2,
    comment: '商品价格',
  })
  product_price: number;

  // 一对一关系,表示一个产品对应一个注册信息
  @OneToOne(() => Register)
  @JoinColumn({
    name: 'register_id',
  }) // 表示这个实体将存储关联的外键
  register: Register;
}

我们通过 @OneToOne 装饰器来定义一对一关系,第一个参数是要关联的实体类,第二个参数是关联的外键字段。 @JoinColumn 装饰器表示这个实体将存储关联的外键。

我们在 app.module.ts 中引入我们刚刚定义的实体类,并配置到 TypeORM 模块中

在实际的nest项目中,我们一般会在模块中引入实体类,然后在模块中配置 TypeORM 模块,这样就可以在模块中使用实体类了。我们这里为了演示方便,直接在 app.module.ts 中引入实体类。

在nest项目中,我们推荐使用 @nestjs/typeorm 来配置 TypeORM 模块,这个模块提供了 TypeOrmModule.forRootTypeOrmModule.forFeature 两个方法,分别用于配置全局数据库连接和模块数据库连接。使用 @nestjs/typeorm 因为它与 NestJS 的依赖注入系统深度集成,更符合框架设计理念。

ts 复制代码
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Product } from './entity/product.entity';
import { Register } from './entity/register.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql', // 数据库类型
      username: 'root',
      password: '123456',
      host: '127.0.0.1',
      port: 3306,
      database: 'nest_demo', // 数据库名称
      autoLoadEntities: true, // 是否自动将实体类自动同步到数据库
      synchronize: true, // 定义数据库表结构与实体类字段同步
    }),
    TypeOrmModule.forFeature([Product, Register]),
    UserModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

然后我们打开我们的数据库,可以看到已经自动生成了我们定义的表结构

并且在 t_product 表中多了一个 register_id 字段,这个字段就是关联的外键字段。

我们给 t_productt_register 表插入一条数据,然后我们通过 register_id 字段来查询关联的 t_register 表中的数据。

在实际的nest项目中,我们一般会在 service 中使用实体类,在实体中我们通过依赖注入获取表的 Repository,通过Repository 来操作数据库,然后在 controller 中调用 service 中的方法。这边我们直接在 app.service 中演示

ts 复制代码
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Product } from './entity/product.entity'
import { Register } from './entity/register.entity'
import { Repository } from 'typeorm';

@Injectable()
export class AppService {
  constructor(
    @InjectRepository(Product)
    private readonly productRepository: Repository<Product>,
    @InjectRepository(Register)
    private readonly registerRepository: Repository<Register>,
  ) {}

  async inertData() {
    const register = this.registerRepository.create({
      register_name: 'XXX薯片',
      register_desc: 'XXXX公司生产的薯片',
      register_category: '食品',
    })
    await this.registerRepository.save(register)
    const product = this.productRepository.create({
      product_name: '薯片',
      product_desc: 'XXX薯片,香脆可口',
      product_category: '食品',
      product_price: 12.5,
      register: register
    })
    await this.productRepository.save(product)
    return 'success'
  }
}

我们在controller 中调用这两个方法,为了方便起见,我直接使用Get请求了,这样直接在浏览器访问就可以调用了

ts 复制代码
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  inertData() {
    return this.appService.inertData();
  }
}

我们直接在浏览器访问 http://localhost:3000 之后触发 inertData 方法,我们打开数据库就可以看到我们插入的数据了。

那怎么查询呢?我们直接在 app.service 中添加一个查询方法

ts 复制代码
getProducts() {
  const products = this.productRepository.createQueryBuilder('product')
  .leftJoinAndSelect('product.register', 'register')
  .getMany()
  return products;
}

我们在 app.controller 中添加一个查询方法

ts 复制代码
@Get('get')
getData() {
  return this.appService.getProducts();
}

我们直接在浏览器访问 http://localhost:3000/get 就可以看到我们查询的数据了。

一对多/多对一

我们创建一个 account.entity.ts 和 order.entity.ts,分别表示账户和订单,然后我们创建一个一对多的关系,一个订单可以有多个订单详情,一个订单详情只能对应一个订单。

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

@Entity('t_account')
export class Account {
  @PrimaryGeneratedColumn('uuid', {
    comment: '账户id',
  })
  id: number;

  @Column({
    type: 'varchar',
    length: 50,
    comment: '账户名称',
  })
  account_name: string;

  @Column({
    type: 'varchar',
    length: 255,
    comment: '账户描述',
  })
  account_desc: string;

  @CreateDateColumn({
    type: 'datetime',
    name: 'created_at',
    comment: '开户时间',
  })
  createdAt: Date;

  @OneToMany(() => Order, (order) => order.account)
  orders: Order[];
}
ts 复制代码
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn } from 'typeorm';
import { Account } from './account.entity';

// 订单状态
export enum OrderStatus {
  PENDING = 'PENDING', // 待支付
  PAID = 'PAID', // 已支付
  CANCELED = 'CANCELED', // 已取消
}

@Entity('t_order')
export class Order {
  @PrimaryGeneratedColumn('uuid', {
    comment: '订单id',
  })
  id: number;

  @Column({
    type: 'varchar',
    length: 50,
    comment: '订单名称',
  })
  order_name: string;

  @Column({
    type: 'varchar',
    length: 255,
    comment: '订单描述',
  })
 order_desc: string;

  @Column({
    type: 'decimal',
    precision: 12, // 总位数
    scale: 2, // 小数位数
    comment: '订单总金额(元)',
  })
  total_amount: number;

  @Column({
    type: 'enum',
    default: OrderStatus.PENDING,
    enum: OrderStatus,
    comment: '订单状态',
  })
  status: OrderStatus;

  /*
    订单创建时间(自动生成,无需手动设置)
    使用 CreateDateColumn 会自动在插入时设置为当前时间
  */
  @CreateDateColumn({
    type: 'datetime',
    name: 'created_at',
    comment: '订单创建时间',
  })
  createdAt: Date;

  /* 
    自动在数据更新时刷新
  */
  @Column({
    type: 'datetime',
    name: 'updated_at',
    nullable: true,
    comment: '订单更新时间',
  })
  updatedAt?: Date;

  @ManyToOne(() => Account, (account) => account.orders)
  @JoinColumn({
    name: 'account_id',
  })
  account: Account;
}

我们通过 @OneToMany@ManyToOne 装饰器来定义一对多/多对一关系,第一个参数是要关联的实体类,第二个参数是关联的外键字段。@ManyToOne 装饰器表示这个实体将存储关联的外键。

你可以在 @ManyToOne / @OneToMany 关系中省略 @JoinColumn,除非你需要自定义关联列在数据库中的名称。@ManyToOne 可以单独使用,但 @OneToMany 必须搭配 @ManyToOne 使用。

我们在app.module.ts中将刚才这两个实体引入

ts 复制代码
// ....
import { Product } from './entity/product.entity';
import { Register } from './entity/register.entity';
import { Order } from './entity/order.entity'
import { Account } from './entity/account.entity'

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      // .....
    }),
    TypeOrmModule.forFeature([Product, Register, Order, Account]),
    UserModule
  ],
  controllers: [AppController],
  providers: [AppService],
})

我们来看一下数据库

并且在 t_order 表中多了一个 account_id 字段,这个字段就是关联的外键字段。

我们给 t_ordert_account 表插入一些数据

ts 复制代码
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Product } from './entity/product.entity'
import { Register } from './entity/register.entity'
import { Account } from './entity/account.entity';
import { Order } from './entity/order.entity';
import { Repository } from 'typeorm';
import { OrderStatus } from './entity/order.entity';

@Injectable()
export class AppService {
  constructor(
    @InjectRepository(Product)
    private readonly productRepository: Repository<Product>,
    @InjectRepository(Register)
    private readonly registerRepository: Repository<Register>,
    @InjectRepository(Account)
    private readonly accountRepository: Repository<Account>,
    @InjectRepository(Order)
    private readonly orderRepository: Repository<Order>,
  ) {}

  /* async inertData() {
    const register = this.registerRepository.create({
      register_name: 'XXX薯片',
      register_desc: 'XXXX公司生产的薯片',
      register_category: '食品',
    })
    await this.registerRepository.save(register)
    const product = this.productRepository.create({
      product_name: '薯片',
      product_desc: 'XXX薯片,香脆可口',
      product_category: '食品',
      product_price: 12.5,
      register: register
    })
    await this.productRepository.save(product)
    return 'success'
  } */

  async inertData() {
    const account = this.accountRepository.create({
      account_name: '张三',
      account_desc: '张三的账户',
    });
    await this.accountRepository.save(account);
    const order = this.orderRepository.create({
      order_name: '订单1',
      order_desc: '订单1的描述',
      total_amount: 100,
      status: OrderStatus.PENDING,
      account,
    });
    const order2 = this.orderRepository.create({
      order_name: '订单2',
      order_desc: '订单2的描述',
      total_amount: 200,
      status: OrderStatus.PAID,
      account,
    });
    await this.orderRepository.save([order, order2]);
    return 'success'
  }
}

我们在浏览器访问之后来看一下数据库中的数据

那么我们如何查询呢?

app.service.ts

ts 复制代码
getALlAccountOrders() {
  const orders = this.accountRepository.createQueryBuilder('account')
    .leftJoinAndSelect('account.orders', 'orders')
    .getMany();
  return orders;
}

app.controller.ts

ts 复制代码
@Get('order')
getDataById() {
  return this.appService.getALlAccountOrders();
}

我们查询一下看看 http://localhost:3000/order

如果我想查询某个人账户下的所有订单该怎么写?

ts 复制代码
getTargetAccountOrders(name: string) {
  const orders = this.accountRepository.createQueryBuilder('account')
    .leftJoinAndSelect('account.orders', 'orders')
    .where('account.account_name = :name', { name })
    .getMany();
  return orders;
}
ts 复制代码
@Get('order/:name')
getDataByName(@Param('name') name: string) {
  return this.appService.getTargetAccountOrders(name);
}

我们就可以通过http://localhost:3000/order/XXX查询了

多对多

一个订单可以有多个商品,一个商品也可以属于多个订单,这就是多对多的关系。

ts 复制代码
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, ManyToMany, JoinColumn, CreateDateColumn, JoinTable } from 'typeorm';
import { Account } from './account.entity';
import { Product } from './product.entity';

// 订单状态
export enum OrderStatus {
  PENDING = 'PENDING', // 待支付
  PAID = 'PAID', // 已支付
  CANCELED = 'CANCELED', // 已取消
}

@Entity('t_order')
export class Order {
  @PrimaryGeneratedColumn('uuid', {
    comment: '订单id',
  })
  id: number;

  // ...

  @ManyToOne(() => Account, (account) => account.orders)
  @JoinColumn({
    name: 'account_id',
  })
  account: Account;

  @ManyToMany(() => Product)
  @JoinTable({
    name: 't_order_product', // 中间表名称
    joinColumn: {
      name: 'order_id',
      referencedColumnName: 'id',
    },
    inverseJoinColumn: {
      name: 'product_id',
      referencedColumnName: 'id',
    },
  })
  products: Product[];
}

我们通过 @ManyToMany 装饰器来定义多对多关系,@JoinTable 装饰器表示这个实体将存储关联的外键。

我们保存之后,在数据库中查看一下

可以看到,多对多关系在数据库中会生成一个中间表,这个中间表会存储两个实体之间的关联关系。

那我们怎么去保存商品和订单这种多对多关系的数据呢?

ts 复制代码
  async inertData() {
    const product1 = this.productRepository.create({
      product_name: '锅巴',
      product_desc: 'XXX锅巴,香的窜天',
      product_category: '食品',
      product_price: 10.5,
    })
    const product2 = this.productRepository.create({
      product_name: '辣条',
      product_desc: 'XXX辣条,辣你两头',
      product_category: '食品',
      product_price: 6.5,
    })
    await this.productRepository.save([product1, product2])
    // 先找出张三的账户,创建新的订单,并关联上
    const account = await this.accountRepository.findOne({
      where: { account_name: '张三' }
    })
    if(account) {
      const order = this.orderRepository.create({
        order_name: '多对多的订单',
        order_desc: '多对多的订单的描述',
        total_amount: 100,
        status: OrderStatus.PENDING,
        products: [product1, product2],
        account
      })
      await this.orderRepository.save(order)
      return 'success'
    } else {
      return 'error'
    }
  }

我们通过 products: [product] 来保存多对多关系的数据。

同样我们通过浏览器来调用我们的接口然后插入数据,这时候我们看一下我们的数据库

可以看到,我们的中间表已经保存了订单和商品之间的关系,一个订单有两个商品。

这时候我们想查询某个订单下的所有商品该怎么写呢?

ts 复制代码
// 获取指定订单下的所有产品
getProductsByOrder(id: string) {
  const products = this.orderRepository.createQueryBuilder('order')
  .leftJoinAndSelect('order.products', 'products')
  .where('order.id = :id', { id })
  .getMany()
  return products;
}
ts 复制代码
@Get('getProductsByOrder/:id')
getOrderById(@Param('id') id: string) {
  return this.appService.getProductsByOrder(id);
}

我们查询一下看看 http://localhost:3000/getProductsByOrder/f8e287f2-03df-4f08-b089-166443c5d1ec

那我们可以反过来,想查询某个商品存在于哪些订单中,该怎么写呢?

在这之前,我们发现在做多对多关系的时候,只给product的实体中定义了@ManyToMany,但是没有给Order实体定义@ManyToMany,这是因为,在多对多关系中,只需要在一边定义即可,另一边会自动生成,但是这时候,我们想查询某个商品存在于哪些订单中,就需要在Order实体中定义@ManyToMany了,但是这边就不能写@JoinTable了,我们已经生成了中间表,所以这边就不需要再生成一次了。

product.entity.ts

ts 复制代码
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn, ManyToMany } from 'typeorm';
import { Register } from './register.entity'
import { Order } from './order.entity'

@Entity('t_product')
export class Product {
  @PrimaryGeneratedColumn('uuid', {
    comment: '商品id',
  })
  id: string;

  // ...

  // 一对一关系,表示一个产品对应一个注册信息
  @OneToOne(() => Register)
  @JoinColumn({
    name: 'register_id',
  }) // 表示这个实体将存储关联的外键
  register: Register;

  @ManyToMany(() => Order, (order) => order.products)
  orders: Order[];
}

我们还得把 order.entity.ts 中 @ManyToMany 这块修改一下

ts 复制代码
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, ManyToMany, JoinColumn, CreateDateColumn, JoinTable } from 'typeorm';
import { Account } from './account.entity';
import { Product } from './product.entity';

// 订单状态
export enum OrderStatus {
  PENDING = 'PENDING', // 待支付
  PAID = 'PAID', // 已支付
  CANCELED = 'CANCELED', // 已取消
}

@Entity('t_order')
export class Order {
  @PrimaryGeneratedColumn('uuid', {
    comment: '订单id',
  })
  id: number;

  // ...

  @ManyToMany(() => Product, (product) => product.orders)
  @JoinTable({
    name: 't_order_product', // 中间表名称
    joinColumn: {
      name: 'order_id',
      referencedColumnName: 'id',
    },
    inverseJoinColumn: {
      name: 'product_id',
      referencedColumnName: 'id',
    },
  })
  products: Product[];
}

这下我们可以写查询方法了

ts 复制代码
  // 获取包含该产品的所有订单
  getOrdersByProduct(id: string) {
    const orders = this.productRepository.createQueryBuilder('product')
    .leftJoinAndSelect('product.orders', 'orders')
    .where('product.id = :id', { id })
    .getMany()
    return orders;
  }
ts 复制代码
  @Get('getOrdersByProduct/:id')
  getOrdersByProduct(@Param('id') id: string) {
    return this.appService.getOrdersByProduct(id);
  }

我们查询一下看看 http://localhost:3000/getOrdersByProduct/0ba4c21f-c553-4c35-a077-2aa256993e9d

可以看到,我们获取到了该商品存在于哪些订单中。

ps. 最上面的一对一反着查询的时候跟这块是一样的,就不回头写了。
本专栏源码地址

相关推荐
FairyDiana11 小时前
解锁Node.js核心装备库:全局API通关指南
面试·node.js·trae
Moment12 小时前
面试官:为什么文件上传时要用 MD5 重命名,而不是时间戳❓❓❓
前端·后端·node.js
艾小码1 天前
Node.js 中的 Gzip 压缩:加速你的 Web 应用传输
性能优化·node.js
BitSmith1 天前
如何使用Node实现一个简单的chatgpt
node.js
Code季风1 天前
GORM 一对多关联(Has Many)详解:从基础概念到实战应用
数据库·go·orm
阿虎儿2 天前
Modern Node.js Patterns for 2025(2025 年现代 Node.js 模式)
javascript·node.js
guidovans2 天前
node.js 零基础入门
node.js·编辑器·vim
Ares-Wang2 天前
Node.js 》》bcryptjs 加密
开发语言·javascript·node.js
太阳伞下的阿呆2 天前
mac安装node.js
macos·node.js