阅读 1479
NestJS 搭建博客系统(三)— 使用TypeORM+Mysql实现数据持久化

NestJS 搭建博客系统(三)— 使用TypeORM+Mysql实现数据持久化

NestJS 搭建博客系统(三)— 使用TypeORM+Mysql实现数据持久化

前言

这一节我们使用 TypeORM + Mysql 实现数据持久化。

TypeORM 是一个 ORM 框架,它可以运行在 NodeJS、Browser、Cordova、PhoneGap、Ionic、React Native、Expo 和 Electron 平台上,可以与 TypeScript 和 JavaScript (ES5,ES6,ES7,ES8)一起使用。 它的目标是始终支持最新的 JavaScript 特性并提供额外的特性以帮助你开发任何使用数据库的(不管是只有几张表的小型应用还是拥有多数据库的大型企业应用)应用程序。

你也可以选择 Squelize ORM

环境配置

安装 Mysql

这里我不详细展开 Mysql 的安装方法。顺便安利个简便的配置方式,同时也是我正在使用的方式。使用 docker 运行 mysql。 github.com/huihuipan/e… 这个库里面配置了 Mysql、Mongodb、Redis、nginx、jenkins、yapi 等服务,可以根据自己需要选择自己的服务。

软件部分

软件会用到数据库工具,如 navicat / dbeaver 调试请求会用到 postman 或者也可以使用上面的 environment 里面提供的 yapi

编写代码

安装依赖

使用 TypeORM 和 mysql 需要在项目中安装以下包

npm install --save @nestjs/typeorm typeorm mysql2
复制代码

引用配置

首先我们在 app.module 中引用 TypeOrmModule,TypeOrmModule 由 @nestjs/typeorm 提供

// src/app.modules.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ArticleModule } from './modules/article/article.module';
import { TypeOrmModule } from '@nestjs/typeorm'
@Module({
  imports: [
    // 使用 TypeORM 配置数据库
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: '888888',
      database: 'test',
      entities: ["dist/modules/**/*.entity{.ts,.js}"],
      synchronize: true,
    }),
    ArticleModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

复制代码

创建实体 Entity

Entity 是由 @Entity 装饰器装饰的模型。 TypeORM 会为此类模型创建数据库表。

新建文件 src/modules/article/entity/article.entity.ts

// src/modules/article/entity/article.entity.ts

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

@Entity()
export class Article {
    // 主键id
    @PrimaryGeneratedColumn()
    id: number;
  
    // 创建时间
    @CreateDateColumn()
    createTime: Date
  
    // 更新时间
    @UpdateDateColumn()
    updateTime: Date
  
    // 软删除
    @Column({
      default: false
    })
    isDelete: boolean
  
    // 更新次数
    @VersionColumn()
    version: number
  
    // 文章标题
    @Column('text')
    title: string;

    // 文章描述
    @Column('text')
    description: string;

    // 文章内容
    @Column('text')
    content: string;
}
复制代码

使用 DTO

关于 DTO,NestJS 是这么说的

DTO(数据传输对象)模式。DTO是一个对象,它定义了如何通过网络发送数据。我们可以通过使用 TypeScript 接口(Interface)或简单的类(Class)来定义 DTO 模式。有趣的是,我们在这里推荐使用类。为什么?类是 JavaScript ES6 标准的一部分,因此它们在编译后的 JavaScript 中被保留为实际实体。另一方面,由于 TypeScript 接口在转换过程中被删除,所以 Nest 不能在运行时引用它们。这一点很重要,因为诸如管道(Pipe)之类的特性为在运行时访问变量的元类型提供更多的可能性。

在 java 里面

DTO: 数据传输对象,原先是为分布式提供粗粒度的数据实体,减少调用次数来提升性能和降低网络压力。

简单来说,就是定义一个数据,作用类似于定义方法的入参,而且还能方便我们做其他事情。比如我们的表单验证就可以在 DTO 中使用。

创建以下文件

// src/modules/article/dto/list.dto.ts

export class ListDTO {
  readonly page: number;
  readonly pageSize: number;
}
复制代码
// src/modules/article/dto/id.dto.ts

export class IdDTO {
  readonly id: number
}
复制代码
// src/modules/article/dto/article-create.dto.ts

export class ArticleCreateDTO {
  readonly title: string;
  readonly description: string;
  readonly content: string;
}
复制代码
// src/modules/article/dto/article-edit.dto.ts

export class ArticleEditDTO {
  readonly id: number;
  readonly title: string;
  readonly description: string;
  readonly content: string;
}
复制代码

控制器中使用 DTO

import { 
  Controller, 
  Body, 
  Query,
  Get, 
  Post,
} from '@nestjs/common';
import { ArticleService } from './article.service';
import { ArticleCreateDTO } from './dto/article-create.dto';
import { ArticleEditDTO } from './dto/article-edit.dto';
import { IdDTO } from './dto/id.dto';
import { ListDTO } from './dto/list.dto';

@Controller('article')
export class ArticleController {
  constructor(
    private articleService: ArticleService
  ) {}

  @Get('list')
  getMore(
    @Query() listDTO: ListDTO,
  ) {
    return this.articleService.getMore(listDTO)
  }

  @Get('info')
  getOne(
    @Query() idDto: IdDTO
  ) {
    return this.articleService.getOne(idDto)
  }

  @Post('create')
  create(
    @Body() articleCreateDTO: ArticleCreateDTO
  ) {
    return this.articleService.create(articleCreateDTO)
  }

  @Post('edit')
  update(
    @Body() articleEditDTO: ArticleEditDTO
  ) {
    return this.articleService.update(articleEditDTO)
  }

  @Post('delete')
  delete(
    @Body() idDto: IdDTO,
  ) {
    return this.articleService.delete(idDto)
  }
}

复制代码

在 article.module 中定义使用那些存储库

import { Module } from '@nestjs/common';
import { ArticleService } from './article.service';
import { ArticleController } from './article.controller';
import { Article } from './entity/article.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
  imports: [
    TypeOrmModule.forFeature([Article]),
  ],
  providers: [ArticleService],
  controllers: [ArticleController]
})
export class ArticleModule {}
复制代码

同时修改下我们的服务,改为使用 TypeORM 存取数据

import { Injectable } from '@nestjs/common';
import { ArticleCreateDTO } from './dto/article-create.dto';
import { ArticleEditDTO } from './dto/article-edit.dto';
import { IdDTO } from './dto/id.dto';
import { ListDTO } from './dto/list.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Article } from './entity/article.entity';

@Injectable()
export class ArticleService {
  list: any[];
  
  constructor(
    @InjectRepository(Article)
    private readonly articleRepository: Repository<Article>,
  ) {
    this.list = []
  }

  async getMore(
    listDTO: ListDTO,
  ) {
    const { page = 1, pageSize = 10 } = listDTO
    const getList = this.articleRepository
      .createQueryBuilder('article')
      .where({ isDelete: false })
      .select([
        'article.id',
        'article.title', 
        'article.description',
        'article.createTime',
        'article.updateTime',
      ])
      .skip((page - 1) * pageSize)
      .take(pageSize)
      .getMany()

    const list = await getList
    return list
  }

  async getOne(
    idDto: IdDTO  
  ) {
    const { id } = idDto
    const articleDetial = await this.articleRepository
      .createQueryBuilder('article')
      .where('article.id = :id', { id })
      .getOne()
    return articleDetial;
  }

  async create(
    articleCreateDTO: ArticleCreateDTO
  ):Promise<Article>{
    const article = new Article();
    article.title = articleCreateDTO.title
    article.description = articleCreateDTO.description
    article.content = articleCreateDTO.content
    const result = await this.articleRepository.save(article);
    return result
  }

  async update(
    articleEditDTO: ArticleEditDTO
  ): Promise<Article>{
    const { id } = articleEditDTO
    let articleToUpdate = await this.articleRepository.findOne({ id })
    articleToUpdate.title = articleEditDTO.title
    articleToUpdate.description = articleEditDTO.description
    articleToUpdate.content = articleEditDTO.content
    const result = await this.articleRepository.save(articleToUpdate)
    return result
  }
  
  async delete (
    idDTO: IdDTO,
  ) {
    const { id } = idDTO
    let articleToUpdate = await this.articleRepository.findOne({ id })
    articleToUpdate.isDelete = true
    const result = await this.articleRepository.save(articleToUpdate)
    return result
  }
}
复制代码

参考

系列

文章分类
前端
文章标签