阅读 575
【前端】嘿,Nest.js实战开发系列 02 ── 使用 Typeorm操作数据库

【前端】嘿,Nest.js实战开发系列 02 ── 使用 Typeorm操作数据库

写在前面

在上一篇文章《【前端】嘿,Nest.js实战开发系列 01 ── Nest.js初体验》中介绍了如何上手nest.js,同时详细介绍了如何进行项目创建、路由访问和创建模块,这些都是项目实践的基础。随着项目的推进,我们就要考虑如何实现数据库的连接和使用,这样可以用来实现前后端数据交互的数据存储。

环境准备:

  • Mysql 5.7
  • TypeORM 0.2.34

TypeORM集成

在Nest.js中可以使用任意数据库,且内部集成提供了TypeORM 和 Sequelize ,开箱即用@nestjs/typeorm和@nestjs/sequelize包。Nest 使用TypeORM,因为它是可用于 TypeScript 的最成熟的对象关系映射器 (ORM)。由于它是用 TypeScript 编写的,因此它可以很好地与 Nest 框架集成。

要开始使用它,我们首先安装所需的依赖项。在命令行中输入:

$ 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',
      entities: [],
      synchronize: true,
    }),
  ],
})
export class AppModule {}
复制代码

切记:synchronize: true不应在生产中使用设置- 否则您可能会丢失生产数据。

该forRoot()方法支持TypeORM包中的createConnection()函数公开的所有配置属性。此外,还有几个额外的配置属性如下所述。

属性作用
retryAttempts尝试连接到数据库的次数(默认值:10)
retryDelay连接重试之间的延迟(ms)(默认值:3000)
autoLoadEntities如果true,实体将被自动加载(默认值:false)
keepConnectionAlive如果true,连接不会在应用程序关闭关闭(默认值:false)

当然,也可以在根目录下创建ormconfig.json文件,在文件中进行数据库信息的设置。

ormconfig.json

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

然后,我们可以在forRoot()没有任何选项的情况下调用:

app.module.ts

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

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

注意:静态 glob 路径(例如,dist/**/*.entity{ .ts,.js})将无法与webpack正常工作。

其实,ormconfig.json文件是由typeorm库加载的,因此不会应用额外的属性设置。TypeORM 提供了getConnectionOptions从ormconfig文件或环境变量中读取连接选项的函数。

完成以上操作后,TypeORMConnection和EntityManager对象将可用于在整个项目中注入(无需导入任何模块)。

app.module.ts

import { TypeOrmModule } from '@nestjs/typeorm';
import { Connection } from "typeorm";
import { UsersModule } from './users/users.module';


@Module({
  imports: [
    TypeOrmModule.forRoot(),
    UsersModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {
  constructor(private connection: Connection){}
}

复制代码

存储库模式

TypeORM支持存储库设计模式,因此每个实体都有自己的存储库。这些存储库可以从数据库连接中获得。

下面创建一个用户实体,users.entity.ts在users目录下,

users.entity.ts

import {Entity, PrimaryGeneratedColumn, Column, BeforeInsert, JoinTable, ManyToMany, OneToMany} from 'typeorm';
import { IsEmail } from 'class-validator';
import * as argon2 from 'argon2';
import { ArticleEntity } from '../article/article.entity';

@Entity('user')
export class UserEntity {

  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  @IsEmail()
  email: string;

  @Column({default: ''})
  bio: string;

  @Column({default: ''})
  image: string;

  @Column()
  password: string;

  @BeforeInsert()
  async hashPassword() {
    this.password = await argon2.hash(this.password);
  }

  @ManyToMany(type => ArticleEntity)
  @JoinTable()
  favorites: ArticleEntity[];

  @OneToMany(type => ArticleEntity, article => article.author)
  articles: ArticleEntity[];
}

复制代码

现在开始使用Users实体,只需要在users.module.ts文件中通过entities模块forFeature()方法选项中的数组来进行导入。

users.module.ts

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import {UsersEntity} from "./users.entity";
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
  imports: [TypeOrmModule.forFeature([UsersEntity])],
  providers: [UsersService],
  controllers: [
    UsersController
  ],
  exports: [UsersService]
})
export class UsersModule {}

复制代码

此模块使用forFeature()来定义在当前范围内注册了那些存储库,此时将可以使用装饰器将UsersRepository 注入到`UsersService @InjectRepository().

users.service.ts

import {Get, Post, Body, Put, Delete, Query, Param, Controller} from '@nestjs/common';
import { UsersService } from './users.service';

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

  // 查找指定用户
  @Get("find/:id")
  async findById(@Query("id") id: number){
    return this.usersService.findById(id);
    
  }
}

复制代码

数据表间的关系

关系是在两个或多个表之间建立的关联,是基于每张表的公共字段,通常是主键和外键。

数据表之间有三种关系:

关系作用
One-to-one主表中的每一行在外部表中只有一个关联行。使用@OneToOne()装饰器来定义这种类型的关系。
One-to-many / Many-to-one主表中的每一行在外部表中都有一个或多个相关行。使用@OneToMany()和@ManyToOne()装饰器来定义这种类型的关系。
Many-to-many主表中的每一行在外表中都有很多相关的行,而外表中的每条记录在主表中都有很多相关的行。使用@ManyToMany()装饰器来定义这种类型的关系。
因此,在实体中定义关系可以使用相应的装饰器。

测试代码

users.controller.ts

import {Get, Post, Body, Put, Delete, Query, Param, Controller} from '@nestjs/common';
import { UsersService } from './users.service';

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

  // 查找指定用户
  @Get("find/:id")
  async findById(@Query("id") id: number){
    return this.usersService.findById(id);
    
  }
复制代码

当我们运行代码时,数据库自动生成了users表。

而当我们在postman向服务器请求指定id的用户信息时,请求结果如下所示:

后台显示结果如下:

我们看到以上代码测试是正确的。

小结

本篇文章介绍了mysql和typeorm之间的关系,typeorm的配置,nest是如何通过typeorm连接数据库,以及简单的用户表数据查询。

其实笔者之前也用过 Sequelize ,现在想要尝试typeorm和nest的搭配,所以文章写的有些乱,建议诸位多加查看官方文档:《Nest官方文档》《Typeorm官方文档》

参考文章

《Nest官方文档》

《Typeorm官方文档》

《Nest.js 从零到壹系列(二):使用 Sequelize 操作数据库》

《全栈搭建个人博客(2)--nest+typeorm搭建博客后端》

写在最后

我是前端小菜鸡,感谢大家的阅读,我将继续和大家分享更多优秀的文章,此文参考了大量书籍和文章,如果有错误和纰漏,希望能给予指正。

更多最新文章敬请关注笔者掘金账号一川萤火和公众号前端万有引力

文章分类
前端
文章标签