如果小伙伴还不会使用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表

vs mysql 实体数据模型 mysql实体关系_前端

再来创建一个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 {}

这样我们就创建好了表

vs mysql 实体数据模型 mysql实体关系_数据库_02


接下来我们来创建表和表之间的关系

vs mysql 实体数据模型 mysql实体关系_vs mysql 实体数据模型_03


通过这个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类里面的内容

vs mysql 实体数据模型 mysql实体关系_前端_04

多对一/一对多的关系

多对一/一对多是指 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"和外键。

vs mysql 实体数据模型 mysql实体关系_vs mysql 实体数据模型_05

多对多的关系

多对多是一种 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[];
}

vs mysql 实体数据模型 mysql实体关系_数据库_06


到此为止 我们介绍了 一对一,一对多,多对多的关系,当然也都是从0到1 创建表的过程。恭喜,你成功进步了一大截。那么企业中都是0到1的场景吗?当然不是,那要是在原有的库上增加数据我们该怎么办呢?

接下来我们要介绍 旧项目中已有数据库我们怎么使用TypeORM 那么我们需要了解一下一个库:typeorm-model-generator

vs mysql 实体数据模型 mysql实体关系_User_07


局部安装

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"

开始执行

vs mysql 实体数据模型 mysql实体关系_前端_08


项目中会生成一个 entities文件夹

vs mysql 实体数据模型 mysql实体关系_mysql_09


注意

如果是旧项目 还是放在 entities 里面,后面新项目还是单独放

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