【Nestjs实操】typeorm一对多如何配置

一、解决场景

有两张表,分别是MenuPage,一个menu下会关联有多个page,如下图:

image-20240331225216836

要实现这种一对多的关系,我们要使用typeorm@OneToMany@ManyToOne这两个装饰器,具体如下:

  • Menu实体类(entity)中使用@OneToMany,申明关联到多个Page实例。
  • Page实体类(entity)中使用@ManyToOne,同时申明关联到一个Menu实例。

二、实现步骤

1、Menu实体类(menu.entity.ts)

有一张Menu表,代码如下:

import { Page } from '@/module/page/entities/page.entity';
import { ApiProperty } from '@nestjs/swagger';
import {
  Column,
  CreateDateColumn,
  Entity,
  OneToMany,
  PrimaryGeneratedColumn,
  UpdateDateColumn
} from 'typeorm';

@Entity({
  name: 'menu',
})
export class Menu {
  @PrimaryGeneratedColumn()
  id: number;

  @ApiProperty({
    description: '菜单名称',
    required: true,
  })
  @Column({
    name: 'name',
    length: 255,
    comment: '菜单名称',
  })
  name: string;

  @ApiProperty({
    description: '是否叶子节点',
    required: true,
  })
  @Column({
    name: 'is_leaf',
    default: true,
    comment: '是否叶子节点',
  })
  isLeaf: boolean;

  @ApiProperty({
    description: '外链或页面唯一标识',
    required: true,
  })
  @Column({
    name: 'link',
    default: "",
    length: 255,
    comment: '外链或页面唯一标识',
  })
  link: string;

  @ApiProperty({
    description: '打开页面方式',
    required: true,
  })
  @Column({
    name: 'target',
    default: '_self',
    length: 50,
    comment: '打开页面方式',
  })
  target: string;

  @ApiProperty({
    description: '菜单图标',
  })
  @Column({
    name: 'icon',
    length: 50,
    comment: '菜单图标',
  })
  icon: string;

  @ApiProperty({
    description: '父菜单Id',
  })
  @Column({
    name: 'parent_id',
    default: 0,
    comment: '父菜单Id',
  })
  parentId: number;

  @ApiProperty({
    description: '菜单路径',
  })
  @Column({
    name: 'parent_arr',
    default: "0",
    comment: '菜单路径',
  })
  parentArr: string;

  @ApiProperty({
    description: '所属站点Id',
  })
  @Column({
    name: 'site_id',
    comment: '所属站点Id',
  })
  siteId: number;

  @ApiProperty({
    description: '是否隐藏',
  })
  @Column({
    name: 'is_hide',
    default: false,
    type: 'boolean',
    comment: '是否隐藏',
  })
  isHide: boolean;

  @CreateDateColumn({
    name: 'create_time',
    type: 'timestamp',
    select: false,
    comment: '创建时间',
  })
  createTime: Date;

  @UpdateDateColumn({
    name: 'update_time',
    type: 'timestamp',
    select: false,
    comment: '修改时间',
  })
  updateTime: Date;

  @OneToMany((type) => Page, (p) => p.menu)
  pages: Page[];
}

2、Page实体类(page.entity.ts)

import { Menu } from '@/module/menu/entities/menu.entity';
import { ApiProperty } from '@nestjs/swagger';
import {
  Column,
  CreateDateColumn,
  Entity,
  JoinColumn,
  JoinTable,
  ManyToMany,
  ManyToOne,
  OneToOne,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
  VersionColumn,
} from 'typeorm';

@Entity({
  name: 'page',
})

export class Page {
  @PrimaryGeneratedColumn()
  id: number;

  @ApiProperty({
    description: '页面名称',
    required: true,
  })
  @Column({
    name: 'name',
    length: 255,
    comment: '页面名称',
  })
  name: string;

  @ApiProperty({
    description: '所属站点标识',
  })
  @Column({
    name: 'siteid',
    nullable: true,
    comment: '所属站点ID'
  })
  siteid: number;

  @ApiProperty({
    description: '所属站点名字',
  })
  @Column({
    name: 'site_name',
    nullable: true,
    comment: '所属站点名字'
  })
  siteName: string;

  @ApiProperty({
    description: '页面Schema数据内容',
  })
  @Column({
    name: 'schema_content',
    nullable: true,
    comment: '页面Schema数据内容',
    type:"text"
  })
  schemaContent: string;

  @ApiProperty({
    description: '删除',
  })
  @Column({
    name: 'is_delete',
    nullable: true,
    default: false,
    comment: '删除',
  })
  isDelete: boolean;

  @ApiProperty({
    description: '创建人ID',
  })
  @Column({
    name: 'create_user_id',
    nullable: false,
    comment: '创建人ID',
  })
  createUserId: number;

  @ApiProperty({
    description: '创建人',
  })
  @Column({
    name: 'create_user_nickname',
    nullable: false,
    comment: '创建人昵称',
  })
  createUserNickName: string;

  @CreateDateColumn({
    name: 'create_time',
    type: 'datetime',
    // select: false,
    nullable: true,
    comment: '创建时间',
  })
  createTime: Date;

  @ApiProperty({
    description: '更新人ID',
  })
  @Column({
    name: 'update_user_id',
    nullable: true,
    comment: '更新人ID',
  })
  updateUserId: number;

  @ApiProperty({
    description: '更新人',
  })
  @Column({
    name: 'update_user_nickname',
    nullable: true,
    comment: '更新人昵称',
  })
  updateUserNickName: string;

  @UpdateDateColumn({
    name: 'update_time',
    type: 'datetime',
    // select: false,
    nullable: true,
    comment: '修改时间',
  })
  updateTime: Date;

  @ManyToOne((type) => Menu, (c) => c.pages)
  menu?: Menu;
}

按照上述的menu.entity.tspage.entity.ts实体类申明,会自动创建好menupage这两张表,并且会自动在page中创建menuId外键字段。

image-20240331230436223

3、创建数据

//---->创建page
//1、创建page实例
const page = new Page();
page.name = "页面";

//2、创建menu实例
const menu = new Menu();
menu.id = page?.menuId;

//3、赋值给page.menu
page.menu = menu;

//4、开启保存事务
await this.pageRepository.save(page);
return true;
//---->创建menu
//1、创建menu实例
const menu = new Menu();
menu.name = "菜单一";

//2、创建page1、page2实例
const page1 = new Page();
page1.name = "页面1";

const page2 = new Page();
page2.name = "页面2";

//2、赋值给menu.pages
menu.pages = [page1, page2]

//3、开启保存事务
await this.menuRepository.save(menu);

更多保存方式,请查看typeorm官网(many-to-one-one-to-many-relations)

4、数据查询

//查询page列表
let [data, total] = await this.pageRepository.findAndCount({
    skip: (current - 1) * pageSize,
    take: pageSize,
    order: { [sortBy]: sortOrder },
    relations: {
        menu: true,
    },
    select: {
        'id': true,
        'name': true,
        'createUserId': true,
        'createUserNickName': true,
        'createTime': true,
        'updateUserId': true,
        'updateUserNickName': true,
        'updateTime': true,
        'menu': {
            'name': true,
        }
    },
    where,
});

返回的数据结构如下:

image-20240331231416840

可以看到,返回的page列表数据中,对应的menu数据单独包裹返回。
更多查询方式,请查看typeorm官网(find-options)

注意:在使用typeorm必须要知道背后执行的sql是什么?才能对写法做优化。这里贴一张上述写法某次访问日志打印的sql,可以看到里面用到left join,所以还是要谨慎使用实体关联,合理设计数据库结构~~~
在这里插入图片描述

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
nestjs是一个基于Node.js的Web框架,它提供了一种现代化的、模块化的、可扩展的方式来构建服务端应用程序。typeorm是一个强大的ORM(对象关系映射)库,它可以将数据库的关系数据映射为对象,并且提供了一些便利的方法来操作这些对象。 nestjs整合typeorm可以帮助我们更方便地操作数据库,以下是nestjs整合typeorm的步骤: 1. 安装typeorm和nest-typeorm依赖: ``` npm install --save typeorm @nestjs/typeorm ``` 2. 在app.module.ts引入typeorm和nest-typeorm: ``` import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { User } from './user.entity'; // 引入实体类 @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: '123456', database: 'test', entities: [User], // 实体类列表 synchronize: true, }), TypeOrmModule.forFeature([User]), // 导入要用到的实体类 ], controllers: [AppController], providers: [AppService], }) export class AppModule {} ``` 3. 创建实体类: ``` import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() age: number; @Column() email: string; } ``` 4. 在服务使用: ``` import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from './user.entity'; @Injectable() export class UserService { constructor( @InjectRepository(User) private readonly userRepository: Repository<User>, ) {} async findAll(): Promise<User[]> { return await this.userRepository.find(); } async create(user: User): Promise<User> { return await this.userRepository.save(user); } async update(id: number, user: User): Promise<void> { await this.userRepository.update(id, user); } async delete(id: number): Promise<void> { await this.userRepository.delete(id); } } ``` 以上是nestjs整合typeorm的基本步骤,通过使用nestjs整合typeorm可以更方便地操作数据库,并且nestjs提供了很多其他功能,例如异常处理、日志等。如果您有任何问题或者需要更详细的介绍,请告诉我。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT飞牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值