NestJS 数据库

213 阅读1分钟

Nest 与数据库无关,允许您轻松地与任何 SQL 或 NoSQL 数据库集成。根据您的偏好,您有许多可用的选项。一般来说,将 Nest 连接到数据库只需为数据库加载一个适当的 Node.js 驱动程序,就像使用 Express 或 Fastify 一样。

官方文档:www.typeorm.org/

TypeORM 集成

$ npm install --save @nestjs/typeorm typeorm mysql2

将 TypeOrmModule 导入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: 'root',// 用户密码
      database: 'test',//数据库名称,需要已经建立的数据库
      // 从当前目录及其子目录中的所有 .entity 文件加载实体。
      // entities: [__dirname + '/**/*.entity{.ts,.js}'],
      entities: [],
      synchronize: true,// 自动同步数据库
    }),
  ],
})
export class AppModule {}

我们可以创建 ormconfig.json ,而不是将配置对象传递给 forRoot()。

{
  "type": "mysql",
  "host": "localhost",
  "port": 3306,
  "username": "root",
  "password": "root",
  "database": "test",
  "entities": ["dist/**/*.entity{.ts,.js}"],
  "synchronize": true
}

app.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [TypeOrmModule.forRoot()],
})
export class AppModule {}

存储库模式

TypeORM 支持存储库设计模式,因此每个实体都有自己的存储库。可以从数据库连接获得这些存储库。为了继续这个示例,我们需要至少一个实体。我们来定义User 实体。 user.entity.ts

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

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

  @Column()
  firstName: string;

  @Column()
  lastName: string;

  @Column({ default: true })
  isActive: boolean;
}

app.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './users/user.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'test',
      entities: [User],
      synchronize: true,
    }),
  ],
})
export class AppModule {}

user.module.ts

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

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

users.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

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

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

  findOne(id: string): Promise<User> {
    return this.usersRepository.findOne(id);
  }

  async remove(id: string): Promise<void> {
    await this.usersRepository.delete(id);
  }
}

事务

数据库事务代表在数据库管理系统(DBMS)中针对数据库的一组操作,这组操作是有关的、可靠的并且和其他事务相互独立的。

@Injectable()
export class UsersService {
  constructor(private connection: Connection) {}
}

......

async createMany(users: User[]) {
  const queryRunner = this.connection.createQueryRunner();

  await queryRunner.connect();
  await queryRunner.startTransaction();
  try {
    await queryRunner.manager.save(users[0]);
    await queryRunner.manager.save(users[1]);

    await queryRunner.commitTransaction();
  } catch (err) {
    //如果遇到错误,可以回滚事务
    await queryRunner.rollbackTransaction();
  } finally {
    //你需要手动实例化并部署一个queryRunner
    await queryRunner.release();
  }
}

async createMany(users: User[]) {
  await this.connection.transaction(async manager => {
    await manager.save(users[0]);
    await manager.save(users[1]);
  });
}

多个数据库

某些项目可能需要多个数据库连接。这也可以通过本模块实现。要使用多个连接,首先要做的是创建这些连接。在这种情况下,连接命名成为必填项。

const defaultOptions = {
  type: 'postgres',
  port: 5432,
  username: 'user',
  password: 'password',
  database: 'db',
  synchronize: true,
};

@Module({
  imports: [
    TypeOrmModule.forRoot({
      ...defaultOptions,
      host: 'user_db_host',
      entities: [User],
    }),
    TypeOrmModule.forRoot({
      ...defaultOptions,
      name: 'albumsConnection',
      host: 'album_db_host',
      entities: [Album],
    }),
  ],
})
export class AppModule {}

分页、排序、条件查询

分页

  async findAll(page: number, limit: number): Promise<Post[]> {
    const posts = await this.postsRepository.find({
      skip: (page - 1) * limit,
      take: limit,
    });
    return posts;
  }
 
  async count(): Promise<number> {
    return this.postsRepository.count();
  }

  // qb = qb.skip(pageParam.pageSize * (pageParam.current - 1)).take(pageParam.pageSize)

排序

  async findAllSorted(sortBy: string, order: 'ASC' | 'DESC'): Promise<User[]> {
    return this.usersRepository
      .createQueryBuilder('user')
      .orderBy(`user.${sortBy}`, order)
      .getMany();
  }

// 普通排序
qb = qb.orderBy('update_time', 'DESC')
// 自定义条件,且多重排序
// 例如:assignee字段为 ${userName} 的优先显示,然后再在此基础上再进行时间和状态的排序
qb = qb
    .orderBy(`case assignee when assignee="${userName}" then 1 else 0 end`)
    .addOrderBy('status', 'ASC')
    .addOrderBy('update_time', 'DESC')

条件查询

  async findUsersByCondition(name: string, age: number): Promise<User[]> {
    const query = this.usersRepository.createQueryBuilder('user');
 
    if (name) {
      query.where('user.name = :name', { name });
    }
 
    if (age) {
      query.andWhere('user.age = :age', { age });
    }
 
    return query.getMany();
  }

保存

  async createUser(userData: User): Promise<User> {
    const newUser = this.usersRepository.create(userData);
    return this.usersRepository.save(newUser);
  }

关联多表查询

// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { PhotoEntity } from './photo.entity';
 
@Entity('users')
export class UserEntity {
  @PrimaryGeneratedColumn()
  id: number;
 
  @Column()
  name: string;
 
  @OneToMany(() => PhotoEntity, (photo) => photo.user)
  photos: PhotoEntity[];
}
// photo.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import { UserEntity } from './user.entity';
 
@Entity('photos')
export class PhotoEntity {
  @PrimaryGeneratedColumn()
  id: number;
 
  @Column()
  title: string;
 
  @Column()
  url: string;
 
  @ManyToOne(() => UserEntity, (user) => user.photos)
  user: UserEntity;
}
// users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserEntity } from './user.entity';
 
@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(UserEntity)
    private usersRepository: Repository<UserEntity>,
  ) {}
 
  async findUserWithPhotos(userId: number): Promise<UserEntity> {
    return this.usersRepository.findOne(userId, { relations: ['photos'] });
  }
}