规划数据库模型是一个触及数据组织、结构化和存储的进程。这个进程保证了数据的完整性、削减了冗余,而且进步了查询功率。以下是规划数据库模型的进程:
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 装修器:
- @Entity()
用于界说一个类作为数据库表的映射。 - @PrimaryGeneratedColumn()
将某个字段符号为主键,而且值会主动生成。一般用于自增的ID字段。 - @Column()
界说表中的一个列。能够指定列的类型、长度、是否仅有等特点。 - @CreateDateColumn()
主动设置列的值为刺进数据的时刻。 - @UpdateDateColumn()
主动设置列的值为数据更新的时刻。 - @DeleteDateColumn()
用于软删去功能,主动设置列的值为数据被软删去的时刻。 - @VersionColumn()
用于达观锁,每次实体更新时,该列的值会主动增加。 - @ManyToOne(), @OneToMany(), @OneToOne(), @ManyToMany()
这些装修器用于界说不同类型的相相联系。 - @JoinColumn()
用于@OneToOne()和@ManyToOne()联系,指定外键列。 - @JoinTable()
用于@ManyToMany()联系,指定衔接表的信息。 - @Index()
用于为实体的某个或某些列创立数据库索引。 - @Unique()
用于界说表等级的仅有束缚。 - @Check()
用于界说表等级的检查束缚。 - @Generated()
用于界说一个生成的列,如主动创立的UUID。 - @EntityRepository()
用于自界说库房类,尽管不直接界说表结构,但与表操作严密相关。
在依赖注入 TypeORM 后,TypeORM 提供了一系列的办法来操作数据库,这些办法一般经过 Repository 目标来调用。Repository 是 TypeORM 的抽象,它答应你履行常见的持久层操作。以下是一些常用的 Repository 办法:
这些办法能够大致分为以下几类:
-
Repository 办法 – 用于对单个数据库实体进行操作的办法,包含:
- find:查找多个实体。
- findOne:查找单个实体。
- create:依据提供的目标创立一个新的实体实例,但不保存它。
- save:保存给定的实体或实体数组。
- remove:从数据库中移除给定的实体。
- update:更新数据库中的实体。
- insert:向数据库中刺进新的实体。
- delete:删去数据库中的记载。
- count:回来满足条件的实体数量。
- increment:增加实体的某个列的值。
- decrement:削减实体的某个列的值。
-
QueryBuilder – 用于构建更杂乱查询的接口,例如:
- createQueryBuilder:创立一个新的查询构建器。
- select:挑选特定的列。
- where:增加查询条件。
- orderBy:增加排序条件。
- leftJoinAndSelect:增加左衔接并挑选相关实体。
- getMany:履行查询并回来多个成果。
- getOne:履行查询并回来单个成果。
-
Entity Manager – 提供了一些更底层的数据库操作办法,例如:
- transaction:履行业务操作。
- createQueryRunner:创立一个能够用来办理单个数据库衔接的查询运转器。
- getCustomRepository:获取自界说的 repository。
-
Migration 办法 – 用于数据库搬迁,例如:
- createMigration:创立新的搬迁文件。
- runMigration:履行搬迁。
- revertMigration:回滚搬迁。
-
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 });
});
}