NestJS数据库集成 (Database Integration)

学习目标

  • 理解数据库集成在NestJS中的作用和地位
  • 掌握TypeORM的基本使用方法
  • 学会使用Prisma进行数据库操作
  • 理解MongoDB的集成方式
  • 能够实现基本的数据库操作(增删改查)

核心知识点

1. 数据库集成概述

NestJS支持多种数据库集成方式,主要包括:

  • TypeORM:一个功能强大的ORM库,支持多种关系型数据库
  • Prisma:一个现代的ORM工具,提供类型安全的数据库访问
  • MongoDB:通过Mongoose或MongoDB驱动进行集成
  • 原生驱动:直接使用数据库的原生驱动

选择合适的数据库集成方式取决于项目需求、团队熟悉度和性能要求。

2. TypeORM集成

TypeORM是NestJS官方推荐的ORM库,它支持多种关系型数据库,如MySQL、PostgreSQL、SQLite等。

安装依赖

npm install @nestjs/typeorm typeorm mysql2
# 或
npm install @nestjs/typeorm typeorm pg
# 或
npm install @nestjs/typeorm typeorm sqlite3

配置TypeORM

在根模块中配置TypeORM:

// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'password',
      database: 'nestjs-db',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true, // 开发环境使用,生产环境应关闭
    }),
    UsersModule,
  ],
})
export class AppModule {}

创建实体

创建一个用户实体:

// users/entities/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

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

  @Column()
  name: string;

  @Column()
  email: string;

  @Column()
  password: string;

  @Column({ default: () => 'CURRENT_TIMESTAMP' })
  created_at: Date;

  @Column({ default: () => 'CURRENT_TIMESTAMP' })
  updated_at: Date;
}

创建用户模块

// users/users.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { User } from './entities/user.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

创建用户服务

// users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    const user = this.usersRepository.create(createUserDto);
    return await this.usersRepository.save(user);
  }

  async findAll(): Promise<User[]> {
    return await this.usersRepository.find();
  }

  async findOne(id: number): Promise<User> {
    const user = await this.usersRepository.findOne({ where: { id } });
    if (!user) {
      throw new NotFoundException(`User with id ${id} not found`);
    }
    return user;
  }

  async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
    const user = await this.findOne(id);
    Object.assign(user, updateUserDto);
    return await this.usersRepository.save(user);
  }

  async remove(id: number): Promise<void> {
    const result = await this.usersRepository.delete(id);
    if (result.affected === 0) {
      throw new NotFoundException(`User with id ${id} not found`);
    }
  }
}

3. Prisma集成

Prisma是一个现代的ORM工具,提供类型安全的数据库访问,支持PostgreSQL、MySQL、SQLite等多种数据库。

安装依赖

npm install prisma @prisma/client
npx prisma init

配置Prisma

编辑prisma/schema.prisma文件:

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model User {
  id        Int      @id @default(autoincrement())
  name      String
  email     String   @unique
  password  String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

生成Prisma客户端

npx prisma migrate dev --name init
npx prisma generate

创建Prisma服务

// prisma.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
  async onModuleInit() {
    await this.$connect();
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }
}

注册Prisma服务

// app.module.ts
import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
import { UsersModule } from './users/users.module';

@Module({
  imports: [UsersModule],
  providers: [PrismaService],
  exports: [PrismaService],
})
export class AppModule {}

使用Prisma服务

// users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Injectable()
export class UsersService {
  constructor(private prisma: PrismaService) {}

  async create(createUserDto: CreateUserDto) {
    return this.prisma.user.create({
      data: createUserDto,
    });
  }

  async findAll() {
    return this.prisma.user.findMany();
  }

  async findOne(id: number) {
    const user = await this.prisma.user.findUnique({
      where: { id },
    });
    if (!user) {
      throw new NotFoundException(`User with id ${id} not found`);
    }
    return user;
  }

  async update(id: number, updateUserDto: UpdateUserDto) {
    return this.prisma.user.update({
      where: { id },
      data: updateUserDto,
    });
  }

  async remove(id: number) {
    try {
      return await this.prisma.user.delete({
        where: { id },
      });
    } catch (error) {
      throw new NotFoundException(`User with id ${id} not found`);
    }
  }
}

4. MongoDB集成

NestJS支持通过Mongoose或MongoDB驱动集成MongoDB。

安装依赖

npm install @nestjs/mongoose mongoose

配置MongoDB

在根模块中配置MongoDB:

// app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UsersModule } from './users/users.module';

@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost:27017/nestjs-db'),
    UsersModule,
  ],
})
export class AppModule {}

创建模式

创建一个用户模式:

// users/schemas/user.schema.ts
import { Schema } from 'mongoose';

export const UserSchema = new Schema({
  name: String,
  email: String,
  password: String,
  createdAt: { type: Date, default: Date.now },
  updatedAt: { type: Date, default: Date.now },
});

注册模式

// users/users.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { UserSchema } from './schemas/user.schema';

@Module({
  imports: [MongooseModule.forFeature([{ name: 'User', schema: UserSchema }])],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

使用MongoDB

// users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User } from './interfaces/user.interface';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Injectable()
export class UsersService {
  constructor(@InjectModel('User') private userModel: Model<User>) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    const createdUser = new this.userModel(createUserDto);
    return createdUser.save();
  }

  async findAll(): Promise<User[]> {
    return this.userModel.find().exec();
  }

  async findOne(id: string): Promise<User> {
    const user = await this.userModel.findById(id).exec();
    if (!user) {
      throw new NotFoundException(`User with id ${id} not found`);
    }
    return user;
  }

  async update(id: string, updateUserDto: UpdateUserDto): Promise<User> {
    const user = await this.userModel.findByIdAndUpdate(id, updateUserDto, { new: true }).exec();
    if (!user) {
      throw new NotFoundException(`User with id ${id} not found`);
    }
    return user;
  }

  async remove(id: string): Promise<void> {
    const result = await this.userModel.findByIdAndDelete(id).exec();
    if (!result) {
      throw new NotFoundException(`User with id ${id} not found`);
    }
  }
}

5. 数据库连接配置

使用环境变量

在实际应用中,应该使用环境变量来配置数据库连接:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    ConfigModule.forRoot(),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        type: 'mysql',
        host: configService.get('DATABASE_HOST'),
        port: +configService.get('DATABASE_PORT'),
        username: configService.get('DATABASE_USERNAME'),
        password: configService.get('DATABASE_PASSWORD'),
        database: configService.get('DATABASE_NAME'),
        entities: [__dirname + '/**/*.entity{.ts,.js}'],
        synchronize: configService.get('DATABASE_SYNCHRONIZE') === 'true',
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

连接池配置

对于生产环境,应该配置数据库连接池:

// app.module.ts
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: 'nestjs-db',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: false,
      
      // 连接池配置
      poolSize: 10,
      connectorPackage: 'mysql2',
      extra: {
        authPlugin: 'mysql_native_password',
      },
    }),
  ],
})
export class AppModule {}

6. 实体关系

一对一关系

// entities/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn } from 'typeorm';
import { Profile } from './profile.entity';

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

  @Column()
  name: string;

  @OneToOne(() => Profile, profile => profile.user, { cascade: true })
  @JoinColumn()
  profile: Profile;
}

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

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

  @Column()
  bio: string;

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

一对多关系

// entities/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { Post } from './post.entity';

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

  @Column()
  name: string;

  @OneToMany(() => Post, post => post.user)
  posts: Post[];
}

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

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

  @Column()
  title: string;

  @ManyToOne(() => User, user => user.posts)
  user: User;
}

多对多关系

// entities/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany, JoinTable } from 'typeorm';
import { Role } from './role.entity';

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

  @Column()
  name: string;

  @ManyToMany(() => Role)
  @JoinTable()
  roles: Role[];
}

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

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

  @Column()
  name: string;

  @ManyToMany(() => User)
  users: User[];
}

7. 事务管理

TypeORM事务

// users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Transaction, TransactionManager, EntityManager } from 'typeorm';
import { User } from './entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}

  @Transaction()
  async createWithTransaction(
    createUserDto: CreateUserDto,
    @TransactionManager() manager?: EntityManager,
  ): Promise<User> {
    const user = manager.create(User, createUserDto);
    return await manager.save(user);
  }
}

Prisma事务

// users.service.ts
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma.service';
import { CreateUserDto } from './dto/create-user.dto';

@Injectable()
export class UsersService {
  constructor(private prisma: PrismaService) {}

  async createWithTransaction(createUserDto: CreateUserDto) {
    return this.prisma.$transaction(async (prisma) => {
      const user = await prisma.user.create({
        data: createUserDto,
      });
      // 可以在这里执行其他数据库操作
      return user;
    });
  }
}

8. 数据库迁移

TypeORM迁移

# 生成迁移
npx typeorm migration:generate -d src/data-source.ts src/migrations/CreateUsersTable

# 运行迁移
npx typeorm migration:run -d src/data-source.ts

# 回滚迁移
npx typeorm migration:revert -d src/data-source.ts

Prisma迁移

# 创建迁移
npx prisma migrate dev --name add-users-table

# 应用迁移到生产环境
npx prisma migrate deploy

# 查看迁移状态
npx prisma migrate status

9. 性能优化

  • 使用索引:为频繁查询的列添加索引
  • 使用分页:避免一次性查询大量数据
  • 使用查询构建器:优化复杂查询
  • 使用缓存:缓存频繁访问的数据
  • 避免N+1查询:使用预加载(eager loading)

10. 最佳实践

  • 使用环境变量:存储数据库连接信息
  • 分离配置:将数据库配置与业务逻辑分离
  • 使用事务:确保数据一致性
  • 错误处理:妥善处理数据库错误
  • 日志记录:记录数据库操作日志
  • 定期备份:定期备份数据库

实践案例分析

案例:用户管理系统

需求分析

我们需要创建一个用户管理系统,包括:

  • 用户的增删改查操作
  • 使用TypeORM进行数据库集成
  • 支持用户与角色的多对多关系
  • 实现基本的事务管理
  • 提供类型安全的数据库访问

实现步骤

  1. 安装必要的依赖
  2. 配置数据库连接
  3. 创建实体
  4. 实现服务层
  5. 创建控制器
  6. 测试系统功能

代码实现

1. 安装依赖
npm install @nestjs/typeorm typeorm pg
2. 配置数据库连接
// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { RolesModule } from './roles/roles.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'postgres',
      password: 'password',
      database: 'nestjs-db',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),
    UsersModule,
    RolesModule,
  ],
})
export class AppModule {}
3. 创建实体
// users/entities/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany, JoinTable } from 'typeorm';
import { Role } from '../../roles/entities/role.entity';

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

  @Column()
  name: string;

  @Column({ unique: true })
  email: string;

  @Column()
  password: string;

  @Column({ default: () => 'CURRENT_TIMESTAMP' })
  createdAt: Date;

  @Column({ default: () => 'CURRENT_TIMESTAMP' })
  updatedAt: Date;

  @ManyToMany(() => Role)
  @JoinTable()
  roles: Role[];
}
// roles/entities/role.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany } from 'typeorm';
import { User } from '../../users/entities/user.entity';

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

  @Column({ unique: true })
  name: string;

  @ManyToMany(() => User)
  users: User[];
}
4. 创建DTO
// users/dto/create-user.dto.ts
export class CreateUserDto {
  name: string;
  email: string;
  password: string;
  roleIds?: number[];
}

// users/dto/update-user.dto.ts
export class UpdateUserDto {
  name?: string;
  email?: string;
  password?: string;
  roleIds?: number[];
}
// roles/dto/create-role.dto.ts
export class CreateRoleDto {
  name: string;
}

// roles/dto/update-role.dto.ts
export class UpdateRoleDto {
  name?: string;
}
5. 实现服务层
// roles/roles.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Role } from './entities/role.entity';
import { CreateRoleDto } from './dto/create-role.dto';
import { UpdateRoleDto } from './dto/update-role.dto';

@Injectable()
export class RolesService {
  constructor(
    @InjectRepository(Role)
    private rolesRepository: Repository<Role>,
  ) {}

  async create(createRoleDto: CreateRoleDto): Promise<Role> {
    const role = this.rolesRepository.create(createRoleDto);
    return await this.rolesRepository.save(role);
  }

  async findAll(): Promise<Role[]> {
    return await this.rolesRepository.find();
  }

  async findOne(id: number): Promise<Role> {
    return await this.rolesRepository.findOne({ where: { id } });
  }

  async update(id: number, updateRoleDto: UpdateRoleDto): Promise<Role> {
    const role = await this.rolesRepository.findOne({ where: { id } });
    Object.assign(role, updateRoleDto);
    return await this.rolesRepository.save(role);
  }

  async remove(id: number): Promise<void> {
    await this.rolesRepository.delete(id);
  }
}
// users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Transaction, TransactionManager, EntityManager } from 'typeorm';
import { User } from './entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { RolesService } from '../roles/roles.service';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
    private rolesService: RolesService,
  ) {}

  @Transaction()
  async create(
    createUserDto: CreateUserDto,
    @TransactionManager() manager?: EntityManager,
  ): Promise<User> {
    const user = manager.create(User, {
      name: createUserDto.name,
      email: createUserDto.email,
      password: createUserDto.password,
    });

    const savedUser = await manager.save(user);

    if (createUserDto.roleIds && createUserDto.roleIds.length > 0) {
      const roles = await this.rolesService.findAll();
      const userRoles = roles.filter(role => createUserDto.roleIds.includes(role.id));
      savedUser.roles = userRoles;
      await manager.save(savedUser);
    }

    return savedUser;
  }

  async findAll(): Promise<User[]> {
    return await this.usersRepository.find({ relations: ['roles'] });
  }

  async findOne(id: number): Promise<User> {
    const user = await this.usersRepository.findOne({ where: { id }, relations: ['roles'] });
    if (!user) {
      throw new NotFoundException(`User with id ${id} not found`);
    }
    return user;
  }

  @Transaction()
  async update(
    id: number,
    updateUserDto: UpdateUserDto,
    @TransactionManager() manager?: EntityManager,
  ): Promise<User> {
    const user = await this.findOne(id);

    Object.assign(user, {
      name: updateUserDto.name,
      email: updateUserDto.email,
      password: updateUserDto.password,
    });

    if (updateUserDto.roleIds) {
      const roles = await this.rolesService.findAll();
      const userRoles = roles.filter(role => updateUserDto.roleIds.includes(role.id));
      user.roles = userRoles;
    }

    return await manager.save(user);
  }

  async remove(id: number): Promise<void> {
    const result = await this.usersRepository.delete(id);
    if (result.affected === 0) {
      throw new NotFoundException(`User with id ${id} not found`);
    }
  }
}
6. 创建控制器
// roles/roles.controller.ts
import { Controller, Get, Post, Body, Put, Delete, Param } from '@nestjs/common';
import { RolesService } from './roles.service';
import { CreateRoleDto } from './dto/create-role.dto';
import { UpdateRoleDto } from './dto/update-role.dto';
import { Role } from './entities/role.entity';

@Controller('roles')
export class RolesController {
  constructor(private rolesService: RolesService) {}

  @Post()
  create(@Body() createRoleDto: CreateRoleDto): Promise<Role> {
    return this.rolesService.create(createRoleDto);
  }

  @Get()
  findAll(): Promise<Role[]> {
    return this.rolesService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: number): Promise<Role> {
    return this.rolesService.findOne(id);
  }

  @Put(':id')
  update(@Param('id') id: number, @Body() updateRoleDto: UpdateRoleDto): Promise<Role> {
    return this.rolesService.update(id, updateRoleDto);
  }

  @Delete(':id')
  remove(@Param('id') id: number): Promise<void> {
    return this.rolesService.remove(id);
  }
}
// users/users.controller.ts
import { Controller, Get, Post, Body, Put, Delete, Param } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './entities/user.entity';

@Controller('users')
export class UsersController {
  constructor(private usersService: UsersService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto): Promise<User> {
    return this.usersService.create(createUserDto);
  }

  @Get()
  findAll(): Promise<User[]> {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: number): Promise<User> {
    return this.usersService.findOne(id);
  }

  @Put(':id')
  update(@Param('id') id: number, @Body() updateUserDto: UpdateUserDto): Promise<User> {
    return this.usersService.update(id, updateUserDto);
  }

  @Delete(':id')
  remove(@Param('id') id: number): Promise<void> {
    return this.usersService.remove(id);
  }
}
7. 创建模块
// roles/roles.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { RolesController } from './roles.controller';
import { RolesService } from './roles.service';
import { Role } from './entities/role.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Role])],
  controllers: [RolesController],
  providers: [RolesService],
  exports: [RolesService],
})
export class RolesModule {}
// users/users.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { User } from './entities/user.entity';
import { RolesModule } from '../roles/roles.module';

@Module({
  imports: [TypeOrmModule.forFeature([User]), RolesModule],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

测试结果

1. 创建角色

请求

POST /roles
Content-Type: application/json

{
  "name": "admin"
}

响应

{
  "id": 1,
  "name": "admin"
}
2. 创建用户

请求

POST /users
Content-Type: application/json

{
  "name": "John Doe",
  "email": "john@example.com",
  "password": "password123",
  "roleIds": [1]
}

响应

{
  "id": 1,
  "name": "John Doe",
  "email": "john@example.com",
  "password": "password123",
  "createdAt": "2023-10-01T10:00:00.000Z",
  "updatedAt": "2023-10-01T10:00:00.000Z",
  "roles": [
    {
      "id": 1,
      "name": "admin"
    }
  ]
}
3. 获取所有用户

请求

GET /users

响应

[
  {
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com",
    "password": "password123",
    "createdAt": "2023-10-01T10:00:00.000Z",
    "updatedAt": "2023-10-01T10:00:00.000Z",
    "roles": [
      {
        "id": 1,
        "name": "admin"
      }
    ]
  }
]
4. 更新用户

请求

PUT /users/1
Content-Type: application/json

{
  "name": "John Doe Updated",
  "email": "john.updated@example.com"
}

响应

{
  "id": 1,
  "name": "John Doe Updated",
  "email": "john.updated@example.com",
  "password": "password123",
  "createdAt": "2023-10-01T10:00:00.000Z",
  "updatedAt": "2023-10-01T10:05:00.000Z",
  "roles": [
    {
      "id": 1,
      "name": "admin"
    }
  ]
}
5. 删除用户

请求

DELETE /users/1

响应

204 No Content

代码解析

  1. 数据库配置

    • 使用TypeOrmModule配置PostgreSQL数据库连接
    • 设置自动同步实体到数据库(开发环境)
  2. 实体定义

    • 创建User和Role实体
    • 定义多对多关系
    • 使用装饰器定义字段属性
  3. 服务实现

    • 使用@InjectRepository注入Repository
    • 实现CRUD操作
    • 使用@Transaction装饰器实现事务管理
    • 处理实体关系
  4. 控制器实现

    • 创建RESTful API端点
    • 处理HTTP请求和响应
    • 调用服务层方法
  5. 模块组织

    • 按功能划分模块
    • 正确配置模块依赖
    • 导出需要共享的服务

互动思考问题

  1. 思考:TypeORM、Prisma和MongoDB的区别是什么?它们各自的使用场景是什么?

  2. 讨论:在实际应用中,如何选择合适的数据库集成方式?需要考虑哪些因素?

  3. 实践:尝试使用Prisma实现一个博客系统,包括用户、文章和评论的CRUD操作。

  4. 挑战:如何实现数据库的读写分离,提高系统性能?

  5. 扩展:了解NestJS的@nestjs/event-emitter模块,思考如何结合数据库操作实现事件驱动架构。

小结

本集我们学习了NestJS数据库集成的核心概念和使用方法,包括:

  • 数据库集成的基本概念和重要性
  • TypeORM的安装和配置
  • Prisma的使用方法
  • MongoDB的集成方式
  • 实体定义和关系映射
  • 事务管理
  • 数据库迁移
  • 性能优化和最佳实践

通过实践案例,我们创建了一个完整的用户管理系统,展示了如何使用TypeORM实现用户和角色的CRUD操作,以及如何处理它们之间的多对多关系。数据库集成是NestJS应用开发中的重要组成部分,选择合适的数据库集成方式可以提高开发效率和系统性能。

在下一集中,我们将学习NestJS的数据库关系与查询,了解如何实现更复杂的数据库操作和查询优化。

« 上一篇 NestJS配置管理 (Configuration) 下一篇 » 14-database-relations