【NestJs】使用MySQL创建多个实体

本文介绍了如何使用NestJs框架和typeorm库连接MySQL数据库,通过@OneToOne(),@OneToMany(),@ManyToOne(),@ManyToMany()等装饰器建立一对一、一对多、多对一和多对多的关系。同时,文章提到了使用Navicat工具查看创建的数据库表,并展示了如何在app.module.ts中注册实体。最后,文章提及了在已有数据库上使用typeorm-model-generator生成模型文件的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果小伙伴还不会使用nestjs连接数据库的话 可以看我的上一篇文章 NestJs使用连接mysql企业级开发规范

关系

关系是指两个或多个表之间的联系。关系基于每个表中的常规字段,通常包含主键和外键。关系有三种:

名称说明
一对一主表中的每一行在外部表中有且仅有一个对应行。使用@OneToOne()装饰器来定义这种类型的关系
一对多/多对一主表中的每一行在外部表中有一个或多的对应行。使用@OneToMany()和@ManyToOne()装饰器来定义这种类型的关系
多对多主表中的每一行在外部表中有多个对应行,外部表中的每个记录在主表中也有多个行。使用@ManyToMany()装饰器来定义这种类型的关系

user.entity.ts

import {
  Column,
  Entity,
  PrimaryGeneratedColumn,
} from 'typeorm';


@Entity() // 最终实现实体类
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  password: string;
}

可以使用navicat 查看创建的user表
在这里插入图片描述

再来创建一个profile.entity.ts

import {
  Column,
  Entity,
  JoinColumn,
  OneToOne,
  PrimaryGeneratedColumn,
} from 'typeorm';

@Entity()
export class Profile {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  gender: number;

  @Column()
  photo: string;

  @Column()
  address: string;
}

再来创建一个roles.entity.ts

import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Roles {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;
}

再来创建一个logs.entity.ts

import {
  Column,
  Entity,
  JoinColumn,
  ManyToOne,
  PrimaryGeneratedColumn,
} from 'typeorm';

@Entity()
export class Logs {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  path: string;

  @Column()
  methods: string;

  @Column()
  data: string;

  @Column()
  result: number;
}

创建完之后别忘记把这个四个实体 添加到app.module.ts

import { Module } from '@nestjs/common';
import { UserModule } from './user/user.module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import * as dotenv from 'dotenv';
import * as Joi from 'joi';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import { ConfigEnum } from './enum/config.enum';

import { User } from './user/user.entity';
import { Profile } from './user/profile.entity';
import { Logs } from './logs/logs.entity';
import { Roles } from './roles/roles.entity';

const envFilePath = `.env.${process.env.NODE_ENV || `development`}`;

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath,
      load: [() => dotenv.config({ path: '.env' })],
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
          .valid('development', 'production')
          .default('development'),
        DB_PORT: Joi.number().default(3306),
        DB_HOST: Joi.string().ip(),
        DB_TYPE: Joi.string().valid('mysql', 'postgres'),
        DB_DATABASE: Joi.string().required(),
        DB_USERNAME: Joi.string().required(),
        DB_PASSWORD: Joi.string().required(),
        DB_SYNC: Joi.boolean().default(false),
      }),
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) =>
        ({
          type: configService.get(ConfigEnum.DB_TYPE),
          host: configService.get(ConfigEnum.DB_HOST),
          port: configService.get(ConfigEnum.DB_PORT),
          username: configService.get(ConfigEnum.DB_USERNAME),
          password: configService.get(ConfigEnum.DB_PASSWORD),
          database: configService.get(ConfigEnum.DB_DATABASE),
          entities: [User, Profile, Logs, Roles],
          // 同步本地的schema与数据库 -> 初始化的时候去使用
          synchronize: configService.get(ConfigEnum.DB_SYNC),
          logging: process.env.NODE_ENV === 'development',
          // logging: ['error'],
        } as TypeOrmModuleOptions),
    }),
    // TypeOrmModule.forRoot({
    //   type: 'mysql',
    //   host: 'localhost',
    //   port: 3306,
    //   username: 'root',
    //   password: 'example',
    //   database: 'testdb',
    //   entities: [],
    //   // 同步本地的schema与数据库 -> 初始化的时候去使用
    //   synchronize: true,
    //   logging: ['error'],
    // }),
    UserModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

这样我们就创建好了表
在这里插入图片描述
接下来我们来创建表和表之间的关系

在这里插入图片描述
通过这个er图我们很容易看出表与表之间的关系。
接下来我们现在profile.entity.ts中创创建和user表的关系

一对一

一对一是一种 A 只包含一个 B 实例,而 B 只包含一个 A 实例的关系。 我们以User和Profile实体为例。

用户只能拥有一个配置文件,并且一个配置文件仅由一个用户拥有。

import {
  Column,
  Entity,
  JoinColumn,
  OneToOne,
  PrimaryGeneratedColumn,
} from 'typeorm';
import { User } from './user.entity';

@Entity()
export class Profile {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  gender: number;

  @Column()
  photo: string;

  @Column()
  address: string;

  @OneToOne(() => User)
  @JoinColumn()  // 会默认使用小驼峰 创建一个字段进行连接
  //  @JoinColumn({name:'userId'}) 
  user: User;
}

这里我们将@OneToOne添加到user并将目标关系类型指定为User。 我们还添加了@JoinColumn,这是必选项并且只能在关系的一侧设置。 你设置@JoinColumn的哪一方,哪一方的表将包含一个"relation id"和目标实体表的外键。
同样,@JoinColumn必须仅设置在关系的一侧且必须在数据库表中具有外键的一侧。

 @OneToOne(() => User)
 // 使用函数 我们在需要的地方再次调用
 // 我们并不需要关系User类里面的内容

在这里插入图片描述

多对一/一对多的关系

多对一/一对多是指 A 包含多个 B 实例的关系,但 B 只包含一个 A 实例。 让我们以User 和 Logs实体为例。 User 可以拥有多张 Logs,但每张 Log 仅由一位 user 拥有。

import {
  Column,
  Entity,
  PrimaryGeneratedColumn,
  OneToMany,
  ManyToMany,
  JoinTable,
  OneToOne,
} from 'typeorm';
import { Logs } from '../logs/logs.entity';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  password: string;

  // typescript -> 数据库 关联关系 Mapping
  @OneToMany(() => Logs, (logs) => logs.user)
  logs: Logs[];
}

logs.entity.ts

import {
  Column,
  Entity,
  JoinColumn,
  ManyToOne,
  PrimaryGeneratedColumn,
} from 'typeorm';
import { User } from '../user/user.entity';

@Entity()
export class Logs {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  path: string;

  @Column()
  methods: string;

  @Column()
  data: string;

  @Column()
  result: number;
  // typescript 数据库 关联关系
  @ManyToOne(() => User, (user) => user.logs)
  //   user.logs 建立 OneToMany 的关系
  @JoinColumn()
  user: User;
}

这里我们将@OneToMany添加到logs属性中,并将目标关系类型指定为Logs。 你可以在@ManyToOne / @OneToMany关系中省略@JoinColumn,除非你需要自定义关联列在数据库中的名称。 @ManyToOne可以单独使用,但@OneToMany必须搭配@ManyToOne使用。 如果你想使用@OneToMany,则需要@ManyToOne。 在你设置@ManyToOne的地方,相关实体将有"关联 id"和外键。

在这里插入图片描述

多对多的关系

多对多是一种 A 包含多个 B 实例,而 B 包含多个 A 实例的关系。 我们以User和 Roels实体为例。

user.entity.ts

import {
  Column,
  Entity,
  PrimaryGeneratedColumn,
  OneToMany,
  ManyToMany,
  JoinTable,
  OneToOne,
} from 'typeorm';
import { Logs } from '../logs/logs.entity';
import { Roles } from '../roles/roles.entity';
import { Profile } from './profile.entity';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  password: string;

  // typescript -> 数据库 关联关系 Mapping
  @OneToMany(() => Logs, (logs) => logs.user)
  logs: Logs[];

  @ManyToMany(() => Roles, (roles) => roles.users)
  // 建立中间表
  @JoinTable({ name: 'users_roles' })
  roles: Roles[];

  @OneToOne(() => Profile, (profile) => profile.user)
  profile: Profile;
}

roles.entity.ts

import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
import { User } from '../user/user.entity';

@Entity()
export class Roles {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @ManyToMany(() => User, (user) => user.roles)
  users: User[];
}

在这里插入图片描述
到此为止 我们介绍了 一对一,一对多,多对多的关系,当然也都是从0到1 创建表的过程。恭喜,你成功进步了一大截。那么企业中都是0到1的场景吗?当然不是,那要是在原有的库上增加数据我们该怎么办呢?
接下来我们要介绍 旧项目中已有数据库我们怎么使用TypeORM
那么我们需要了解一下一个库:typeorm-model-generator
在这里插入图片描述
局部安装

pnpm i -D typeorm-model-generator

然后 使用 package.json 新增一个命令

"generate:models": "typeorm-model-generator -h 127.0.0.1 -p 3306 -d testdb -u root -x 123456 -e mysql -o ./src/entities"

开始执行
在这里插入图片描述
项目中会生成一个 entities文件夹

在这里插入图片描述
注意
如果是旧项目 还是放在 entities 里面,后面新项目还是单独放

启动项目,启动成功,之前库也都存在了。
下面一篇文章介绍 创建实体功能NestJs使用MySQL创建多个实体

### 集成和配置 MySQL 数据库 #### 安装必要的依赖项 要在 NestJS 项目中集成 MySQL 数据库,首先需要安装一些必需的 npm 包。这些包包括 `@nestjs/typeorm`、`typeorm` 和特定于 MySQL 的驱动程序。 ```bash npm install @nestjs/typeorm typeorm mysql2 --save ``` 此命令会将所需的软件包添加到项目的依赖列表中[^3]。 #### 创建数据库模块 接下来,在应用程序中创建一个新的模块来管理数据库连接。通常这个模块被称为 `DatabaseModule` 或者直接命名为 `TypeOrmModule`: ```typescript import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'test_db', entities: [__dirname + '/**/*.entity{.ts,.js}'], synchronize: true, // 生产环境中应设置为 false 并使用迁移工具 }), ], }) export class DatabaseModule {} ``` 这段代码定义了一个新的模块并导入了 `TypeOrmModule` 来初始化与 MySQL 数据库之间的连接。注意这里的配置参数可以根据实际情况调整,比如主机名、端口、用户名、密码以及目标数据库名称等[^1]。 #### 实体类定义 为了让 ORM 映射工作正常运行,还需要编写实体类文件描述表结构。假设有一个简单的用户表,则对应的实体可能如下所示: ```typescript import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column({ length: 50 }) name: string; } ``` 上述代码片段展示了如何通过装饰器的方式声明列属性及其约束条件。每个实体都对应着一张物理上的表格,并且可以通过 Repository API 进行 CRUD 操作。 #### 注册实体和服务层逻辑 最后一步是在适当的地方注册新创建好的实体,并实现业务服务层的功能。这通常是通过控制器 (Controller) 和服务 (Service) 组件完成的。下面是一个简单例子说明怎样查询所有用户的记录: ```typescript // user.service.ts 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 userRepository: Repository<User>, ) {} findAll(): Promise<User[]> { return this.userRepository.find(); } } // user.controller.ts import { Controller, Get } from '@nestjs/common'; import { UserService } from './user.service'; @Controller('users') export class UserController { constructor(private readonly userService: UserService) {} @Get() async getAllUsers() { const users = await this.userService.findAll(); return users; } } ``` 以上就是关于在 NestJS 应用程序内集成了 MySQL 数据库所需的主要步骤概述。当然实际开发过程中还涉及到更多细节处理,例如错误捕获机制的设计或是性能优化等方面的内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

嘴巴嘟嘟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值