2

背景

最近在学习NestJs,但是不从实际需求出发,没有项目的依托,感觉总是个入门而已,只有在解决一个个项目上的问题才能进一步加深和巩固所学知识,故想将搭建一个博客后台系统的需求为出发点,巩固所学知识,同时也作为学习新知识、难题解决及注意事项的记录。

一、安装依赖

yarn add @nestjs/typeorm typeorm mysql

二、连接数据库(MySQL)

在app.modules中用TypeOrmModule实现数据库连接
// src/app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      useFactory: () => ({
        type: 'mysql',
        host: 'localhost',
        port: 3306,
        username: 'root',
        password: '',
        database: 'blogs',
        timezone: 'UTC',
        charset: 'utf8mb4',
        entities: ['./**/*.entity.js'],
        synchronize: true,
        logging: true,
    })}),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

三、创建entity

entity(实体对象)映射数据库中的表,entity中的Column与表中的字段相对应
// src/entities/article.entity.ts

import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';

@Entity('article')
export class ArticleEntity {
  @PrimaryGeneratedColumn({
    type: 'int',
    comment: '主键id',
  })
  id: number;

  @Column('varchar', {
    nullable: false,
    comment: '文章标题',
  })
  title: string;

  @Column('varchar', {
    nullable: false,
    comment: '文章内容',
  })
  content: string;

  @Column({
    nullable: false,
    name: 'category_id',
    comment: '文章类别',
  })
  categoryID: string;

  @Column('varchar', {
    nullable: true,
    comment: '文章简介',
  })
  intro: string;

  @Column('varchar', {
    nullable: true,
    comment: '文章封面',
  })
  cover: string;

  @Column('varchar', {
    nullable: true,
    comment: '文章标签',
  })
  tags: string;

  @Column('enum', {
    nullable: false,
    default: 0,
    enum: [0, 1, 2],
    comment: '文章状态,0为编辑中,1为已发布,2为不可用',
  })
  status: number;

  @CreateDateColumn({
    type: 'timestamp',
    name: 'created_at',
    comment: '创建时间',
  })
  createdAt: Date;

  @UpdateDateColumn({
    type: 'timestamp',
    name: 'updated_at',
    comment: '最后更新时间',
  })
  updatedAt: Date;

  @DeleteDateColumn({
    type: 'timestamp',
    name: 'delete_at',
    comment: '删除',
  })
  deleteAt: Date;
}
// src/entities/article-category.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';

@Entity('article_category')
export class ArticleCategoryEntity {
  @PrimaryGeneratedColumn({
    type: 'int',
    comment: '主键id',
  })
  id: number;

  @Column('varchar', {
    nullable: false,
    unique: true,
    name: 'category_name',
    comment: '文章类别名称',
  })
  categoryName: string;

  @CreateDateColumn({
    type: 'timestamp',
    name: 'created_at',
    comment: '创建时间',
  })
  createdAt: Date;

  @UpdateDateColumn({
    type: 'timestamp',
    name: 'updated_at',
    comment: '最后更新时间',
  })
  updatedAt: Date;

  @DeleteDateColumn({
    type: 'timestamp',
    name: 'delete_at',
    comment: '删除',
  })
  deleteAt: Date;
}

Tips:entity要放在/src/entities目录下,才能在dist目录下生成.entity.js格式的文件,直接加载.entity.ts文件则会报语法错误,之前试过新建libs,然后将数据库模块放到libs下,希望将数据库这部分独立出来,方便维护;不过发现最后编译只会在dist生成一个main.js文件,缺少.entity.js,查看main.js内容发现*.entity.ts编译的结果也直接放入main.js中。

四、数据库的CURD

EntityManager: 像放一个实体存储库的集合的地方,你可以管理(insert, update, delete, load等)任何实体;可以通过getManager()或Connection访问实体管理器。

Repository: 像EntityManager一样,但其操作仅限于具体实体;可以通过getRepository(Entity),Connection#getRepository访问存储库。

下面通过Repository提供的API来实现对数据库的增删改查,以及左联查询

// src/service/article/article.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { ArticleCategoryEntity } from 'src/entities/article-category.entity';
import { ArticleEntity } from 'src/entities/article.entity';
import { Repository, createQueryBuilder, getConnection, getRepository } from 'typeorm';

@Injectable()
export class ArticleService {
    constructor(
        @InjectRepository(ArticleCategoryEntity)
        private readonly articleCategoryRepo: Repository<ArticleCategoryEntity>,
        @InjectRepository(ArticleEntity)
        private readonly articleRepo: Repository<ArticleEntity>,
    ) {

    }

    /**
     * 获取文章分类
     */
    getArticleCate() {
        return this.articleCategoryRepo.find();
    }

    /**
     * 创建文章分类
     * @param body
     */
    createArticleCate(body): Promise<any> {
        const userEntity = this.articleCategoryRepo.create(body);
        return this.articleCategoryRepo.save(userEntity);
    }

    /**
     * 删除文章分类
     * @param id 文章分类ID
     */
    delArticleCate(id): Promise<any> {
        return this.articleCategoryRepo.delete(id);
    }

    /**
     * 获取文章
     * @param id 文章ID
     */
    getArticle(id) {
        // 利用leftJoinAndMapOne方法进行左联查询
        // 两个未关联的实体,通过getRawMany获取原始查询数据
        if (id) {
            return createQueryBuilder(ArticleEntity, 'article')
                .leftJoinAndMapOne('article.cate_name', ArticleCategoryEntity, 'cate', 'cate.id = article.category_id')
                .where('article.id = :id', { id })
                .select(['article.*', 'cate.category_name'])
                .getRawMany(); // 获得原始结果
        } else {
            return createQueryBuilder(ArticleEntity, 'article')
                .leftJoinAndMapOne('article.cate_name', ArticleCategoryEntity, 'cate', 'cate.id = article.category_id')
                .getRawMany(); // 获得原始结果
        }
    }

    /**
     * 创建文章
     * @param body
     */
    createArticle(body) {
        const articleEntity = this.articleRepo.create(body);
        return this.articleRepo.save(articleEntity);
    }

    /**
     * 更新文章
     * @param id
     * @param body
     */
    updateArticle(id, body) {
        return this.articleRepo.update(id, body);
    }

    /**
     * 删除文章
     * @param id 文章ID
     */
    delArticle(id) {
        return this.articleRepo.delete(id);
    }
}

wy丶
98 声望8 粉丝

全干工程师