规划数据库模型是一个触及数据组织、结构化和存储的进程。这个进程保证了数据的完整性、削减了冗余,而且进步了查询功率。以下是规划数据库模型的进程:

1. 承认有哪些实体

实体是实际国际中能够区别的目标或事物,它们一般对应于数据库中的表。在承认实体时,你需求辨认出业务流程中的主要组件。例如,假如你正在规划一个电子商务数据库,实体或许包含“客户”、“产品”、“订单”等。

2. 承认有哪些相关的实体

实体之间往往存在相关,这些相关界说了实体怎么彼此相关。例如,在电子商务数据库中,“客户”或许与“订单”有相关,由于客户会下订单。同样,“订单”与“产品”有相关,由于订单包含一个或多个产品。

3. 承认相相联系

相相联系描绘了实体之间的交互办法,这些联系能够是1对1、一对多或多对多。例如,“客户”和“订单”之间一般是一对多的联系,由于一个客户能够有多个订单,但每个订单只能属于一个客户。承认相相联系是规划数据库模型中非常要害的一步,由于它影响了数据的整合和查询的杂乱性。

4. 承认实体特点

每个实体都会有一系列的特点,这些特点描绘了实体的特征。例如,关于“客户”实体,特点或许包含客户ID、姓名、地址和电子邮箱等。在承认特点时,重要的是要保证特点的原子性,即每个特点都是不可分割的根本单位。

5. 不断优化细节

数据库模型的规划是一个迭代进程,需求不断地优化。这包含正规化进程,以削减数据冗余和进步数据一致性。正规化一般触及到将一个大表分解成多个小表,并经过外键相关。此外,还需求考虑索引的运用以进步查询功率,以及怎么处理业务、并发和安全性问题。

示例场景:图书馆办理体系

在软件规划和数据库建模中,从用户角色动身来承认实体及其相关实体、相相联系和实体特点是一个常见的办法,这一般触及到用户故事或用例剖析。下面经过一个例子来说明这个进程:
假定咱们要规划一个图书馆办理体系,咱们的用户角色包含图书办理员和读者。

进程 1:界说用户故事或用例

首先,咱们需求界说用户故事或用例来描绘用户角色的需求。例如:

  • 作为一个图书办理员,我想要增加新书本,以便扩充图书馆的藏书。
  • 作为一个读者,我想要查找图书,以便借阅我感兴趣的书本。

进程 2:从用例中辨认实体

接下来,咱们从每个用户故事或用例中辨认出触及的实体。

  • 图书办理员用例触及的实体或许有:

    • 图书(Book)
  • 读者用例触及的实体或许有:

    • 图书(Book)
    • 读者(Reader)

进程 3:承认相相联系

最后,咱们需求承认实体之间的相相联系。

  • **图书(Book)读者(Reader)**之间的联系或许是:
    • 读者能够借阅多本图书(一对多联系:一个 Reader 能够有多个 Book)
    • 一本图书能够被多个读者借阅(多对一联系:一个 Book 能够被多个 Reader 借阅)

进程 4:界说实体特点

然后为每个实体界说特点。

  • **图书(Book)**的特点或许包含:
    • 书名(Title)
    • 作者(Author)
    • ISBN 编号(ISBN)
    • 出版日期(Publication Date)
    • 类别(Genre)
  • **读者(Reader)**的特点或许包含:
    • 姓名(Name)
    • 读者编号(Reader ID)
    • 借阅记载(Borrowing Record)

进程 5:制作实体联系图(ERD)

依据上述信息,咱们能够制作一个实体联系图(ERD),来可视化实体、特点和联系。

[Reader] --<借阅>-- [Book]
|         |
|         |-> 姓名(Name)
|         |-> 读者编号(Reader ID)
|         |-> 借阅记载(Borrowing Record)
|
|-> 书名(Title)
|-> 作者(Author)
|-> ISBN编号(ISBN)
|-> 出版日期(Publication Date)
|-> 类别(Genre)

经过这个进程,咱们从用户角色和运用路径动身,辨认了体系中的要害实体(图书和读者),实体之间的相相联系(如借阅联系)以及它们的特点(如书名、作者、读者编号等)。这样的剖析有助于咱们构建一个结构化的数据库模型,从而支撑图书馆办理体系的开发。

运用 TypeORM 生成实体和特点

TypeORM 是一个开源的目标联系映射(ORM)库,运用函数的操作映射数据库底层的 sql 履行。所以使得开发人员不必学习杂乱的 sql 语句,从而操作数据库。
TypeORM 支撑多种数据库,包含 MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL 等。
在 Nest.js 中运用 TypeORM 界说 MySQL 数据库表结构并完结 CRUD(创立、读取、更新、删去)操作需求几个进程。下面是一个简化的流程:

1. 装置必要的包

首先,装置 Nest.js CLI(假如还没有装置的话)和必要的 npm 包:

npm i -g @nestjs/cli
npm i @nestjs/typeorm typeorm mysql2

这儿 mysql2 是 MySQL 的 Node.js 驱动。

2. 创立数据库衔接

app.module.ts 或相关的模块文件中装备 TypeORM:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'your_username',
      password: 'your_password',
      database: 'your_database',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true, // 注意:在出产环境中不主张运用此选项,由于它或许会导致数据丢掉
    }),
    // ... other modules
  ],
  // ... controllers, providers
})
export class AppModule {}

3. 界说实体

创立一个 TypeScript 类作为实体,并运用装修器界说表结构:

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;
  @Column({ length: 500 })
  name: string;
  @Column('text')
  description: string;
  // 其他列...
}

4. 创立服务

创立一个服务来处理 CRUD 操作:

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();
  }
  findOne(id: string): Promise<User> {
    return this.userRepository.findOne(id);
  }
  async create(user: User): Promise<User> {
    return this.userRepository.save(user);
  }
  async update(id: string, user: User): Promise<void> {
    await this.userRepository.update(id, user);
  }
  async delete(id: string): Promise<void> {
    await this.userRepository.delete(id);
  }
}

5. 创立控制器

创立一个控制器来处理 HTTP 请求:

import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common';
import { UserService } from './user.service';
import { User } from './user.entity';
@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}
  @Get()
  getAll(): Promise<User[]> {
    return this.userService.findAll();
  }
  @Get(':id')
  getOne(@Param('id') id: string): Promise<User> {
    return this.userService.findOne(id);
  }
  @Post()
  create(@Body() user: User): Promise<User> {
    return this.userService.create(user);
  }
  @Put(':id')
  update(@Param('id') id: string, @Body() user: User): Promise<void> {
    return this.userService.update(id, user);
  }
  @Delete(':id')
  delete(@Param('id') id: string): Promise<void> {
    return this.userService.delete(id);
  }
}

6. 注册模块

在你的模块(例如 app.module.ts)中注册 TypeOrmModule.forFeature 和控制器:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { User } from './user.entity';
@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

这些进程概述了在 Nest.js 应用程序中运用 TypeORM 完结 CRUD 操作的根本流程。
具体完成或许会依据具体需求有所不同。

在运用 TypeORM 结合 Nest.js 界说表结构时,会用到一系列的装修器(decorators)来界说实体(Entity)和表之间的映射联系。以下是一些常用的 TypeORM 装修器:

  1. @Entity()
    用于界说一个类作为数据库表的映射。
  2. @PrimaryGeneratedColumn()
    将某个字段符号为主键,而且值会主动生成。一般用于自增的ID字段。
  3. @Column()
    界说表中的一个列。能够指定列的类型、长度、是否仅有等特点。
  4. @CreateDateColumn()
    主动设置列的值为刺进数据的时刻。
  5. @UpdateDateColumn()
    主动设置列的值为数据更新的时刻。
  6. @DeleteDateColumn()
    用于软删去功能,主动设置列的值为数据被软删去的时刻。
  7. @VersionColumn()
    用于达观锁,每次实体更新时,该列的值会主动增加。
  8. @ManyToOne(), @OneToMany(), @OneToOne(), @ManyToMany()
    这些装修器用于界说不同类型的相相联系。
  9. @JoinColumn()
    用于@OneToOne()和@ManyToOne()联系,指定外键列。
  10. @JoinTable()
    用于@ManyToMany()联系,指定衔接表的信息。
  11. @Index()
    用于为实体的某个或某些列创立数据库索引。
  12. @Unique()
    用于界说表等级的仅有束缚。
  13. @Check()
    用于界说表等级的检查束缚。
  14. @Generated()
    用于界说一个生成的列,如主动创立的UUID。
  15. @EntityRepository()
    用于自界说库房类,尽管不直接界说表结构,但与表操作严密相关。

在依赖注入 TypeORM 后,TypeORM 提供了一系列的办法来操作数据库,这些办法一般经过 Repository 目标来调用。Repository 是 TypeORM 的抽象,它答应你履行常见的持久层操作。以下是一些常用的 Repository 办法:
这些办法能够大致分为以下几类:

  1. Repository 办法 – 用于对单个数据库实体进行操作的办法,包含:

    • find:查找多个实体。
    • findOne:查找单个实体。
    • create:依据提供的目标创立一个新的实体实例,但不保存它。
    • save:保存给定的实体或实体数组。
    • remove:从数据库中移除给定的实体。
    • update:更新数据库中的实体。
    • insert:向数据库中刺进新的实体。
    • delete:删去数据库中的记载。
    • count:回来满足条件的实体数量。
    • increment:增加实体的某个列的值。
    • decrement:削减实体的某个列的值。
  2. QueryBuilder – 用于构建更杂乱查询的接口,例如:

    • createQueryBuilder:创立一个新的查询构建器。
    • select:挑选特定的列。
    • where:增加查询条件。
    • orderBy:增加排序条件。
    • leftJoinAndSelect:增加左衔接并挑选相关实体。
    • getMany:履行查询并回来多个成果。
    • getOne:履行查询并回来单个成果。
  3. Entity Manager – 提供了一些更底层的数据库操作办法,例如:

    • transaction:履行业务操作。
    • createQueryRunner:创立一个能够用来办理单个数据库衔接的查询运转器。
    • getCustomRepository:获取自界说的 repository。
  4. Migration 办法 – 用于数据库搬迁,例如:

    • createMigration:创立新的搬迁文件。
    • runMigration:履行搬迁。
    • revertMigration:回滚搬迁。
  5. Subscriber 和 Listener – 答应你监听和订阅特定的数据库事件,例如:

    • beforeInsert:在刺进之前触发。
    • afterInsert:在刺进之后触发。
    • beforeUpdate:在更新之前触发。
    • afterUpdate:在更新之后触发。
    • beforeRemove:在删去之前触发。
    • afterRemove:在删去之后触发。

以下是运用 TypeORM 进行数据库操作的一些代码示例,基于 Nest.js 框架:

  • 查找记载
async findAllUsers(): Promise<User[]> {
  return await this.userRepository.find();
}
async findOneUser(id: number): Promise<User> {
  return await this.userRepository.findOne(id);
}
  • 创立和保存记载
async createUser(userData: Partial<User>): Promise<User> {
  const user = this.userRepository.create(userData);
  return await this.userRepository.save(user);
}
  • 更新记载
async updateUser(id: number, updateData: Partial<User>): Promise<void> {
  await this.userRepository.update(id, updateData);
}
  • 删去记载
async deleteUser(id: number): Promise<void> {
  await this.userRepository.delete(id);
}
  • 运用 QueryBuilder 进行杂乱查询
async findUsersWithPosts(): Promise<User[]> {
  return this.userRepository
    .createQueryBuilder('user') // 'user' 是给 User 实体起的别名
    .leftJoinAndSelect('user.posts', 'post') // 假定 User 实体有一个 posts 相关
    .where('user.isActive = :isActive', { isActive: true })
    .andWhere('post.published = :published', { published: true })
    .orderBy('user.createdAt', 'DESC')
    .getMany();
}
// 关于更杂乱的查询,你能够运用子查询和多种不同的条件。例如:
async findUsersWithRecentPosts(): Promise<User[]> {
  return this.userRepository
    .createQueryBuilder('user')
    .leftJoinAndSelect('user.posts', 'post')
    .where('user.isActive = :isActive', { isActive: true })
    .andWhere(new Brackets(qb => {
      qb.where('post.createdAt > :date', { date: new Date(/* 一些日期 */) })
        .orWhere('post.title LIKE :title', { title: '%特定标题%' });
    }))
    .getMany();
}
// 咱们运用了 Brackets 来创立一个嵌套的条件块,这答应咱们在一个子查询中组合多个条件。
  • 运用 Entity Manager 进行业务操作
import { EntityManager } from 'typeorm';
async updateUserAndLogTransaction(user: User, logMessage: string): Promise<void> {
  await this.userRepository.manager.transaction(async (entityManager: EntityManager) => {
    await entityManager.save(user);
    await entityManager.insert(Log, { message: logMessage });
  });
}