NestJs的基础使用

初始化项目

创建项目

// 安装脚手架(只需要安装一次,因为这个是全局的)
npm i -g @nestjs/cli
// 创建项目
nest new project-name
// (该过程有个选择包管理工具的,我选的yarn)

启动项目

yarn run start:dev
// 可以在浏览器访问localhost:3000  输出helloWorld

控制器和路由

查看命令

nest - g;
// 列表描述(name:生成的文件名,alias:简写文件名,description:文件的描述)

生成controller层命令为

// 要使用CLI创建控制器,只需执行$ nest g controller [生成的文件夹名]命令。
// 也可以使用简写
nest g co [生成的文件夹名]
// 然后查看src下面的+创建的文件夹
// 项目创建完成查看app.module.ts里面会自动将创建的controller注入进来

在新创建的controller内添加以下内容,确保能被访问到

import { Controller, Get } from '@nestjs/common';
// 这里有个小坑(也不算坑,这个Get在我电脑不会自动导入,一度导致我以为报错了,长个记性)
// 配置了下面的之后启动项目访问:localhost:3000/cat 会返回一个JSON对象(数组和对象自动转JSON)
@Controller('cat')
export class CatController {@Get() //不写参数默认为空,直接方位/cat就能访问到这个接口say() {return {name: 'cat',};}@Get('/say2') //访问localhost:3000/cat/say2 会返回一个数组(被json转化过的)say2() {return [1, 2, 3, 4, 5];}
}

生成service层命令为

// 要使用CLI创建控制器,只需执行$ nest g service [生成的文件夹名]命令。
// 也可以使用简写
nest g s [生成的文件夹名] // 建议和controller放在同一文件夹
// 然后查看src下面的+创建的文件夹
// 项目创建完成查看app.module.ts里面会自动将创建的service注入进来

controller和service结合使用

// controller改造如下
import { Controller, Get } from '@nestjs/common';
import { CatService } from './cat.service';@Controller('cat')
export class CatController {// 私有的,只读的serviceconstructor(private readonly catService:CatService){}@Get()say() {return this.catService.say();}@Get('/say1')say1() {return this.catService.say1();}
}// service改造如下
import { Injectable } from '@nestjs/common';@Injectable()
export class CatService {say() {return {name: 'catservice',};}say1() {return {name: [1, 2, 3, 4, 5, 6, 7],};}
}
// http://localhost:3000/cat/say1 返回{name:[1,2,3,4,5,6,7]}

路由介绍

官方路由示例

import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common';
import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto';@Controller('cats')
export class CatsController {@Post()create(@Body() createCatDto: CreateCatDto) {return 'This action adds a new cat';}@Get()findAll(@Query() query: ListAllEntities) {return `This action returns all cats (limit: ${query.limit} items)`;}@Get(':id')findOne(@Param('id') id: string) {return `This action returns a #${id} cat`;}@Put(':id')update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {return `This action updates a #${id} cat`;}@Delete(':id')remove(@Param('id') id: string) {return `This action removes a #${id} cat`;}
}

本人这里还是以上面个人创建的controller和service为例

以下全是catcontroller.ts内容(都是针对不同接口类型接收参数的介绍)
import {Body,Controller,Delete,Get,Param,Post,Put,Query,
} from '@nestjs/common';
import { CatService } from './cat.service';@Controller('cat')
export class CatController {// 私有的,只读的serviceconstructor(private readonly catService: CatService) {}// 1.@Query// 浏览器访问: http://localhost:3000/cat/say?name=%E5%B0%8F%E7%BA%A2&age=18// 打印如下:query { name: '小红', age: '18' }// 无参数时打印为: query {}@Get('/say')say(@Query() query: any) {// 限定参数类型为anyconsole.log('query', query);return this.catService.say();}// 2.动态路由 :id// 浏览器访问:http://localhost:3000/cat/99// 打印如下: id1 99@Get(':id')say1(@Param('id') id: number) {console.log('id1', id);return this.catService.say1();}// 3.动态路由 /say3/:id// 浏览器访问:http://localhost:3000/cat/say3/99// 打印如下: id3 99@Get('/say3/:id')say3(@Param('id') id: string) {console.log('id3', id);return this.catService.say1();}// 4.动态路由 无参形式// 浏览器访问:http://localhost:3000/cat/say4/99// 打印如下: param {id:'99'}@Get('/say4/:id')// Param参数为空say4(@Param() param: { id: number }) {console.log('param', param);return this.catService.say1();}// 5.Delete 删除接口// APIFOX访问:http://localhost:3000/cat/say5/1 接口类型换成:Delete// 打印如下: param { id: '1' }@Delete('/say5/:id')// Param参数为空say5(@Param() param: { id: number }) {console.log('param', param);return this.catService.say1();}// 6.Put 新增接口// APIFOX访问:http://localhost:3000/cat/say5/1 接口类型换成:Put// 打印如下: param { id: '1' }@Put('/say6/:id')// Param参数为空say6(@Param() param: { id: number }) {console.log('param', param);return this.catService.say1();}// 7.Post 新增接口// APIFOX访问:http://localhost:3000/cat 接口类型换成:Post// 参数在接口测试工具的body的json类型加入以下参数: { id: 1, name: '萧寂' }// 打印如下: cat { id: 1, name: '萧寂' }@Post()say7(@Body() cat: any) {console.log('cat', cat);return this.catService.say1();}
}

提供者(也就是Java中的实体类)

在上面创建的cat文件夹内创建interface文件夹,在文件夹内创建cat.interface.ts文件
在上面创建的cat文件夹内创建dto文件夹,在文件夹内创建create-cat.dto.ts文件

// cat.interface.ts
export interface Cat {name: string;age: number;breed: string;
}// create-cat.dto.ts
import { Cat } from "../interface/cat.interface";
export class CreateCatDto implements Cat{name: string;age: number;breed: string;
}// cat.controller.ts
import {Body,Controller,Post,
} from '@nestjs/common';
import { CatService } from './cat.service';
import { CreateCatDto } from './dto/create-cat.dto';@Controller('cat')
export class CatController {// 私有的,只读的serviceconstructor(private readonly catService: CatService) {}// APIFOX访问:http://localhost:3000/cat 接口类型换成:Post// 参数在接口测试工具的body的json类型加入以下参数: { id: 1, name: '萧寂' }// 打印如下: cat { id: 1, name: '萧寂' }@Post()say(@Body() cat: CreateCatDto) {console.log('cat', cat); // 在apiFox打印结果和上面一样,只是加了类型校验,但是由于ts只进行类型约束,并不要求参数限定必须一致,因此这里请求的参数与dao和接口类不一致也可以拿到参数,并不会报错,只是不存在的参数不会对其类型进行校验而已了return this.catService.say1();}
}

中间件

创建中间件

nest g mi logger
// 安装完成后在目录中会多出一个logger文件夹//在app.mudule.ts放入以下内容进行注册绑定中间件
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatController } from './cat/cat.controller';
import { CatService } from './cat/cat.service';
import { LoggerMiddleware } from './logger/logger.middleware';@Module({imports: [],controllers: [AppController, CatController],providers: [AppService, CatService],
})
export class AppModule {configure(consumer: MiddlewareConsumer) {// 下面这段代码代表先绑定中间件再应用到路由上,当访问cat或者带cat子接口时会被拦截到consumer.apply(LoggerMiddleware).forRoutes('cat');// 由于同一个接口名可能会有get,put,delete,post等方法,因此可以使用下面方式对指定类型的接口进行拦截consumer.apply(LoggerMiddleware).forRoutes({path:'cat',method:RequestMethod.GET}); // 只拦截get请求的cat接口// 也可以直接将controller作为参数(这样的话,在当前控制器下面所有的请求都会被拦截到的)consumer.apply(LoggerMiddleware).forRoutes(CatController);// 如果当前需要拦截的请求较多,可以将controller作为参数,但又不希望其中个别接口被拦截到,可以使用下面方式忽略需要拦截的接口consumer.apply(LoggerMiddleware).exclude({path:'cat/:id',method:RequestMethod.GET},{path:'cat',method:RequestMethod.POST}).forRoutes(CatController);}
}// ------------------------------------------------------------------------------------------------
// 在logger/lagger.middleware.tss中添加如下内容
import { Injectable, NestMiddleware } from '@nestjs/common';@Injectable()
export class LoggerMiddleware implements NestMiddleware {use(req: any, res: any, next: () => void) {// 每次访问cat接口都会被拦截到,并打印出一下日志console.log('正在访问cat接口');next();}
}

以上就是中间件基础使用方法了

函数式中间件

// 在logger/lagger.middleware.tss中添加如下内容
import { Injectable, NestMiddleware } from '@nestjs/common';@Injectable()
export class LoggerMiddleware implements NestMiddleware {use(req: any, res: any, next: () => void) {console.log('111222', 111222);next();}
}// 下面这个是函数式中间件(只有函数式中间件可以注册成为全局中间件)
export function logger(req, res, next) {console.log('Request_logger');next();
}

注册成为全局中间件,只有函数式中间件可以注册成为全局中间件

// 在main.ts进行注册(代码如下)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { logger } from './logger/logger.middleware';async function bootstrap() {const app = await NestFactory.create(AppModule);// 将中间件注册成为全局中间件app.use(logger);await app.listen(3000);
}
bootstrap();

异常处理

异常抛出

例如我们输入了一个不存在的地址,就会响应一个404,这个是框架给我们默认返回的一个异常,有时候我们需要手动去抛出一个异常
Nest提供了一个内置的HttpException类,为基础异常类,可以很好的帮助我们进行异常的处理

// 在controller内其中一个接口如下:import {Get,HttpException,HttpStatus} from '@nestjs/common';@Get('/xiaoji')xiaoji(): string {throw new HttpException('服务器异常', HttpStatus.FORBIDDEN);}
// 浏览器访问:localhost:3000/cat/xiaoji
// 返回值为: {"statusCode":403,"message":"服务器异常"}

自定义异常及异常的处理

创建并配置异常处理器

// 创建异常过滤器
nest g f http-exception
// 创建完成后目录会多出一个http-exception文件夹// 在新创建的http-exception.filter.ts文件放入以下内容(已经处理好的)
import {ArgumentsHost,Catch,ExceptionFilter,HttpException,
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter<T> implements ExceptionFilter {catch(exception: HttpException, host: ArgumentsHost) {const ctx = host.switchToHttp();const response = ctx.getResponse<Response>();const request = ctx.getRequest<Request>();const status = exception.getStatus();response.status(status).json({statusCode: status,timestamp: new Date().toISOString(),path: request.url,});}
}

使用异常处理器

在cat.controller.ts内使用

import {Body,Controller,Get,HttpException,HttpStatus,UseFilters,
} from '@nestjs/common';
import { HttpExceptionFilter } from 'src/http-exception/http-exception.filter';// 在接口使用@Get('/xiaoji')// 访问到这个接口直接抛出异常(异常的格式为我们上面定制的格式(状态码,时间和接口路径))@UseFilters(new HttpExceptionFilter())xiaoji(): string {// 如果这里不抛出异常,上面这个异常过滤器不会执行,只有手动抛出异常则异常过滤器才会执行throw new HttpException('服务器异常', HttpStatus.FORBIDDEN);}

全局使用异常过滤器

// 例如有的情况有异常,但是我们不能一个个来抛出,例如全局的404异常,因此需要使用全局异常过滤器,方式如下// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { logger } from './logger/logger.middleware';
import { HttpExceptionFilter } from './http-exception/http-exception.filter';async function bootstrap() {const app = await NestFactory.create(AppModule);// 将中间件注册成为全局中间件app.use(logger);// 全局注册异常过滤器app.useGlobalFilters(new HttpExceptionFilter());//await app.listen(3000);
}
bootstrap();// 当访问一个不存在的接口会返回404状态码和时间以及异常的接口路径(改写了默认的样式)

管道

管道有两个典型的应用场景:
转换:管道将输入数据转换为所需的数据输出(例如,将字符串转换为整数)
验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常

Nest 自带九个开箱即用的管道,即

  • ValidationPipe
  • ParseIntPipe
  • ParseFloatPipe
  • ParseBoolPipe
  • ParseArrayPipe
  • ParseUUIDPipe
  • ParseEnumPipe
  • DefaultValuePipe
  • ParseFilePipe

转换

这里以ParseIntPipe为例,进行数据的转化

// 在cat.controller.ts其中一个接口如下内容
import {Get,Param,ParseIntPipe} from '@nestjs/common';
//这个管道可以将接收的参数转为number类型,否则返回的是{id:"123"},转换之后变成{id:123}
//如果参数给个abc导致不能转化成number,则会抛出400错误的异常,代表转化失败@Get(':id')say1(@Param('id', new ParseIntPipe()) id: number) {return {id: id,};}

验证(守卫)

基础使用

守卫在所有中间件之后执行,但在拦截器或管道之前执行。

// 创建守卫
nest g gu role
// 在新建的role.guard.ts内放入以下内容
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';@Injectable()
export class RoleGuard implements CanActivate {canActivate(context: ExecutionContext,): boolean | Promise<boolean> | Observable<boolean> {console.log('RoleGuard');return true;}
}// 再新建一个controller
nest g co user
// controller内放入以下内容
import {Controller,Get,Param,ParseIntPipe,UseGuards,
} from '@nestjs/common';
import { RoleGuard } from 'src/role/role.guard';@Controller('user')
@UseGuards(RoleGuard) // 使用守卫,当访问下面任意接口时就会触发守卫,并打印('RoleGuard')
export class UserController {@Get(':id')// @UseGuards(RoleGuard) // 使用守卫,当访问当前接口时就会触发守卫,并打印('RoleGuard')getUserDetial(@Param('id', ParseIntPipe) id: number) {return {id,};}
}

定义权限,进行路由比对

// controller.ts内加入下面代码
import {Controller,Get,Param,ParseIntPipe,UseGuards,SetMetadata,
} from '@nestjs/common';
import { RoleGuard } from 'src/role/role.guard';@Controller('user')
export class UserController {@Get(':id')@SetMetadata('role', ['access1', 'access2'])@UseGuards(RoleGuard) // 使用守卫getUserDetial(@Param('id', ParseIntPipe) id: number) {return {id,};}
}// role.guard.ts加入下面代码
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';@Injectable()
export class RoleGuard implements CanActivate {// 加上反射(可以拿到接口对应相关的一些信息)constructor(private reflector: Reflector) {}canActivate(context: ExecutionContext,): boolean | Promise<boolean> | Observable<boolean> {console.log('RoleGuard');// 下面的role与controller接口的@SetMetadata('role', ['access1','access2'])第一个参数保持一致const roles = this.reflector.get<string[]>('role', context.getHandler());console.log('roles', roles); // 打印的roles就是['access1','access2'],刚刚的第二个参数if (!roles) {return true;}const request = context.switchToHttp().getRequest();const user = request.user;// 上面那个可以在接口上定义权限,然后拿到定义的权限,这里的user代表用户发请求携带的权限,这里可以进行路由比对,有权限放行,没权限返回false// 这里是拿到了用户的标识,我们的系统可以查询到用户的权限console.log('user',user);return true;}
}

第二种方式(装饰器模式)

// 创建装饰器
nest g d role
// 创建完成后会发现多了个role.decorator.ts文件// controller.ts
import {Controller,Get,UseGuards,
} from '@nestjs/common';
import { Role } from 'src/role/role.decorator'; // 引入装饰器
import { RoleGuard } from 'src/role/role.guard'; // 引入守卫@Controller('user')
@UseGuards(RoleGuard) // 使用守卫,下面的所有接口都会触发守卫
export class UserController {@Get('user/list')@Role('access1', 'access2','access3', 'access4') // 装饰器内的参数就是权限(个人感觉较麻烦不如上面那个)getUserList() {return [1, 2, 3, 4];}
}// 访问接口得到-----roles [ 'access1', 'access2', 'access3', 'access4' ]

数据库

连接数据库

// 安装依赖
yarn add --save @nestjs/typeorm typeorm mysql2
// @nestjs/typeorm这个本人运行上面命令一直安装不上,只好单独安装一下就成功了(yarn add @nestjs/typeorm)// 安装完成后在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: 'admin',database: 'shop',// entities: [], // 实体类文件,下面创建完curd会讲怎么写的synchronize: true, //是否将实体同步到数据库retryDelay:500, // 重试连接数据库的间隔retryAttempts: 10, // 重试连接数据库的次数autoLoadEntities: true, // 自动加载实体(加上这个就不用上面的entities了)logging: true, // 是否开启日志logger: 'advanced-console', // 日志}),],
})
export class AppModule {}

创建CURD

nest g res test
// 选择第一个REST API(遵循rest api的规范)
// 选择y 确定创建curd(增删改查)// 找到test/entities/test.entity.ts(只要创建了CURD这种文件,一般实体类都是这个位置,那么就可以动态加载实体类了,找到刚刚创建数据库的地方,将entities: [],填充以下内容,用于动态获取实体类),如果autoLoadEntities设置为true了,这个属性可以去掉,autoLoadEntities用于自动加载实体
entities: [__dirname + '/**/*.entity{.ts,.js}'],

在创建完的CURD文件内的实体类编写如下代码

test/entities/test.entity.ts

// 定义实体,定义列,定义自增主键
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';@Entity() // 定义实体类
export class Test {@PrimaryGeneratedColumn()id: number; // 自增id列@Column()name: string;  // 列名@Column()password: string;@Column()age: number;
}

在test/test.model.ts里面编写如下代码

import { Module } from '@nestjs/common';
import { TestService } from './test.service';
import { TestController } from './test.controller';
// 引入实体
import { Test } from './entities/test.entity';
// 引入orm框架
import {TypeOrmModule} from '@nestjs/typeorm'
@Module({// 引入注册imports:[TypeOrmModule.forFeature([Test])],controllers: [TestController],providers: [TestService],
})
export class TestModule {}

保存重启项目会发现MySQL里面的shop这个数据库多了一个test这张表,表内的字段和实体类一一对应

实体类介绍

// 定义实体,定义列,定义自增主键
import { Entity, Column, PrimaryGeneratedColumn,CreateDateColumn,Generated } from 'typeorm';@Entity() // 定义实体类
export class Test {@PrimaryGeneratedColumn("uuid") // 自增的uuidid: number; // 自增id列@Column({type:"varchar",length:255}) // 列类型name: string;@Column()password: string;@Column()age: number;// @CreateDateColumn()  // 自增日期@CreateDateColumn({ type: 'timestamp' }) // 时间戳类型createTime: Date;@Generated('uuid') // 自动生成列uuid: string;@Column({   // 定义枚举类型type: 'enum',enum: [1, 2, 3, 4],default: 1,})xiaoji: number;
}
mysql 所有类型int, tinyint, smallint, mediumint, bigint, float, double, dec, decimal, numeric, date, datetime, timestamp, time, year, char, varchar, nvarchar, text, tinytext, mediumtext, blob, longtext, tinyblob, mediumblob, longblob, enum, json, binary, geometry, point, linestring, polygon, multipoint, multilinestring, multipolygon, geometrycollection

列选项

    @Column({type:"varchar",name:"ipaaa", //数据库表中的列名nullable:true, //在数据库中使列NULL或NOT NULL。 默认情况下,列是nullable:falsecomment:"注释",select:true,  //定义在进行查询时是否默认隐藏此列。 设置为false时,列数据不会显示标准查询,适合用于密码。 默认情况下,列是select:truedefault:"xxxx", //加数据库级列的DEFAULT值primary:false, //将列标记为主要列。 使用方式和@ PrimaryColumn相同。update:true, //指示"save"操作是否更新列值。如果为false,则只能在第一次插入对象时编写该值。 默认值为"true"collation:"", //定义列排序规则。})ip:string

simple-array 列类型
有一种称为simple-array的特殊列类型,它可以将原始数组值存储在单个字符串列中。 所有值都以逗号分隔

@Entity()
export class User {@PrimaryGeneratedColumn()id: number;@Column("simple-array")names: string[];
}

**simple-json列类型
还有一个名为simple-json的特殊列类型,它可以存储任何可以通过 JSON.stringify 存储在数据库中的值。 当你的数据库中没有 json 类型而你又想存储和加载对象,该类型就很有用了。 例如:
**

@Entity()
export class User {@PrimaryGeneratedColumn()id: number;@Column("simple-json")profile: { name: string; nickname: string };
}

数据库的增删改查的使用

axios 接口定义

import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:3000'// 增
export const addUser = (data) => axios.post('/user',data).then(res => res.data)// 查
export const getList = (data) => axios.get('/user',{params:data}).then(res => res.data)// 删
export const delUser = (data) => axios.delete(`/user/${data.id}`).then(res => res.data)// 改
export const updateUser = (data) => axios.patch(`/user/${data.id}`,data).then(res => res.data)

nest后端代码为

实体类为

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class CreateUserDto {@Column()name:string@Column()desc:string
}

controller代码如下

import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';@Controller('user')
export class UserController {constructor(private readonly userService: UserService) {}// 增@Post()create(@Body() createUserDto: CreateUserDto) {return this.userService.create(createUserDto);}// 查@Get()findAll(@Query() query:{keyWord:string,page:number,pageSize:number}) {return this.userService.findAll(query);}// 改@Patch(':id')update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {return this.userService.update(+id, updateUserDto);}// 删@Delete(':id')remove(@Param('id') id: string) {return this.userService.remove(+id);}
}

service代码如下

1.引入 InjectRepository typeOrm 依赖注入 接受一个实体
2.引入类型 Repository 接受实体泛型
3.Like 用于模糊查询
4.save 保存 find 查询 update 更新 delete 删除

import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { Repository, Like } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
@Injectable()
export class UserService {constructor(@InjectRepository(User) private readonly user: Repository<User>) { }// 增create(createUserDto: CreateUserDto) {const data = new User() // 创建实体类对象data.name = createUserDto.name // 实体类的name赋值为传来的namedata.desc = createUserDto.desc // 实体类的desc赋值为传来的descreturn this.user.save(data) // 保存(每次保存都相当于添加一次)}// 查async findAll(query: { keyWord: string, page: number, pageSize: number }) {const data = await this.user.find({ // 查询where: {name: Like(`%${query.keyWord}%`) //模糊查询},order: {id: "DESC" // 倒序},skip: (query.page - 1)* query.pageSize, // skip跳过,从0开始take:query.pageSize, // 需要展示的每页的数量})const total = await this.user.count({  // 总数量where: {name: Like(`%${query.keyWord}%`)},})return {data,total}}// 改update(id: number, updateUserDto: UpdateUserDto) {return this.user.update(id, updateUserDto)}// 删remove(id: number) {return this.user.delete(id)}
}

Module代码如下

import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import {TypeOrmModule} from '@nestjs/typeorm'
import { User } from './entities/user.entity';
@Module({imports:[TypeOrmModule.forFeature([User])],controllers: [UserController],providers: [UserService]
})
export class UserModule {}

[多表联查的使用]

新增了一个接口
export const addTags = (data) => axios.post(‘/user/add/tags’,data).then(res => res.data)

后端Nestjs
1.新建一个 tags.entity.ts
定义Tags的数据表

import { Column, Entity, PrimaryGeneratedColumn, BeforeInsert, CreateDateColumn, Generated, OneToOne, JoinColumn, ManyToOne } from 'typeorm'
import { User } from './user.entity'
@Entity()
export class Tags {@PrimaryGeneratedColumn()id: number@Column()tags:string@ManyToOne(()=>User,(user)=>user.tags)@JoinColumn()user:User
} 

Modal 需要关联tag表

import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import {TypeOrmModule} from '@nestjs/typeorm'
import { User } from './entities/user.entity';
import { Tags } from './entities/tags.entity';
// import { example } from './entities/tags.entity';
@Module({imports:[TypeOrmModule.forFeature([User,Tags])],controllers: [UserController],providers: [UserService]
})
export class UserModule {}

然后user表跟tags表进行关联

import { Column, Entity, PrimaryGeneratedColumn, BeforeInsert, CreateDateColumn, Generated, OneToOne, JoinColumn, OneToMany } from 'typeorm'
import { Tags } from './tags.entity'
@Entity()
export class User {@PrimaryGeneratedColumn()id: number@Column({ type: "varchar", length: 255 })name: string@Column({ type: "text" })desc: string@Generated('uuid')uuid: string@CreateDateColumn({ type: "timestamp" })createTime: Date@OneToMany(() => Tags, (tags) => tags.user)tags:Tags[]// example: example
}

这儿我们解释一下 OneToMany 和 ManyToOne的用法
对于用户来说一个用户可以拥有多个tag 他们的关系是一对多 OneToMany
对于tag来说他们是多个tag指定单个用户 所以是 ManyToOne
在这里插入图片描述
OneToMany 接受两个参数
第一个参数是个函数返回关联的类 所以在user表关联tag
第二个参数 创建双向关系
ManyToOne 用法一样

@OneToMany(() => Tags, (tags) => tags.user)

保存该关系

沿用上一章的代码增加Controller 增加 addTags

import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';@Controller('user')
export class UserController {constructor(private readonly userService: UserService) {}@Post('/add/tags')addTags (@Body() params:{tags:string[],userId:number}) {return this.userService.addTags(params)}@Post()create(@Body() createUserDto: CreateUserDto) {return this.userService.create(createUserDto);}@Get()findAll(@Query() query:{keyWord:string,page:number,pageSize:number}) {return this.userService.findAll(query);}@Patch(':id')update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {return this.userService.update(+id, updateUserDto);}@Delete(':id')remove(@Param('id') id: string) {return this.userService.remove(+id);}
}

service 增加 addTags 方法

import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { Repository, Like } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { Tags } from './entities/tags.entity';
// import { example } from './entities/tags.entity';
@Injectable()
export class UserService {constructor(@InjectRepository(User) private readonly user: Repository<User>,@InjectRepository(Tags) private readonly tag: Repository<Tags>) { }//通过前端传入的userId 查到当前id 的用户信息,然后拿到前端传入的tags [tag1,tag2,tag3]
// 进行遍历 给tag实例进行赋值 然后调用保存方法添加tag 添加完之后 通过 tagList 保存该tag类
// 最后把tagList 赋给 user类的tags属性 然后重新调用save 进行更新async addTags (params:{tags:string[],userId:number}) {const userInfo = await this.user.findOne({where:{id:params.userId}})const tagList:Tags[] = []for (let i = 0;i<params.tags.length;i++) {let T =  new Tags()T.tags = params.tags[i];await this.tag.save(T)tagList.push(T)}userInfo.tags = tagList;console.log(userInfo,1)return this.user.save(userInfo)}async create(createUserDto: CreateUserDto) {const data = new User()// const ex = new example()data.name = createUserDto.namedata.desc = createUserDto.desc// await this.example.save(ex)return this.user.save(data)}async findAll(query: { keyWord: string, page: number, pageSize: number }) {const data = await this.user.find({//查询的时候如果需要联合查询需要增加 relationsrelations:['tags'],where: {name: Like(`%${query.keyWord}%`)},order:{id:"DESC",},skip: (query.page - 1) * query.pageSize,take: query.pageSize})const total = await this.user.count({where: {name: Like(`%${query.keyWord}%`)},})return {data,total}}update(id: number, updateUserDto: UpdateUserDto) {return this.user.update(id, updateUserDto)}remove(id: number) {return this.user.delete(id)}
}

事务

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/258399.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MDK官网如何下载stm32支持包

网站&#xff1a;https://www.keil.com/demo/eval/arm.htm 1 2 3点这个下载

4.Java程序设计-基于springboot得在线考试系统

编程技术交流、源码分享、模板分享、网课分享 企鹅&#x1f427;裙&#xff1a;772162324 摘要&#xff1a; 本文设计并实现了一款基于Spring Boot框架的在线考试系统小程序。随着远程学习和在线教育的普及&#xff0c;对于灵活、便捷的在线考试系统的需求逐渐增加。该小程序…

视频推拉流直播点播EasyDSS平台点播文件加密存储的实现方法

视频推拉流直播点播系统EasyDSS平台&#xff0c;可提供流畅的视频直播、点播、视频推拉流、转码、管理、分发、录像、检索、时移回看等功能&#xff0c;可兼容多操作系统&#xff0c;还能支持CDN转推&#xff0c;具备较强的可拓展性与灵活性&#xff0c;在直播点播领域具有广泛…

低代码与MES:智能制造的新篇章

一、引言 随着工业4.0和智能制造的兴起&#xff0c;企业对于生产过程的数字化、智能化需求日益迫切。制造执行系统&#xff08;MES&#xff09;作为连接计划层与控制层的关键信息系统&#xff0c;在提升生产效率、优化资源配置、保障产品质量等方面发挥着重要作用。然而&#…

微信小程序 - 创建 ZIP 压缩包

微信小程序 - 创建 ZIP 压缩包 场景分享代码片段导入 JSZip创建ZIP文件追加写入文件测试方法参考资料 场景 微信小程序只提供了解压ZIP的API&#xff0c;并没有提供创建ZIP的方法。 当我们想把自己处理好的保存&#xff0c;打包ZIP保存下来时就需要自己实现了。 分享代码片段…

2024 IEEE Fellow名单出炉(附部分华人获奖理由)

11月22日&#xff0c;美国电子电气工程师学会&#xff08;IEEE&#xff09;公布了 2024 年新晋 Fellow 名单。因为这些学术大咖也是访问学者、博士后研究人员及联合培养博士心仪的目标导师&#xff0c;故知识人网小编特编辑此文以飨读者。 IEEE&#xff0c;全称Institute of El…

Mybatis XML 多表查询

这篇需结合 <<Mybatis XML 配置文件>>那一篇博客一起看 工作中尽量避免使用多表查询,尤其是对性能要求非常高的项目 我们之前建了个用户表(代码在Mybatis XML配置文件那篇博客里),这次再建一个文章表,代码如下 : -- 创建⽂章表 DROP TABLE IF EXISTS articleinf…

【EI会议征稿】2024年粤港澳大湾区数字经济与人工智能国际学术会议(DEAI2024)

2024年粤港澳大湾区数字经济与人工智能国际学术会议(DEAI2024) 2024 Guangdong-Hong Kong-Macao Greater Bay Area International Conference on Digital Economy and Artificial Intelligence(DEAI2024) 2024年粤港澳大湾区数字经济与人工智能国际学术会议(DEAI2024)由广东科…

Spring Cloud + Vue前后端分离-第4章 使用Vue cli 4搭建管理控台

Spring Cloud Vue前后端分离-第4章 使用Vue cli 4搭建管理控台 4-1 使用vue cli创建admin项目 Vue 简介 Vue作者尤雨溪在google工作时&#xff0c;最早只想研究angular的数据绑定功能&#xff0c;后面觉得这个小功能很好用&#xff0c;有前景&#xff0c;就再扩展&#xff…

高效率完成工作任务的工具推荐,待办清单类工具用哪个

日常办公中&#xff0c;领导常常会以高效率完成工作任务来评判一个员工是否敬业&#xff0c;是否在工作岗位上兢兢业业。而想要高效率完成工作也是有技巧的&#xff0c;如提前对各项工作做好规划&#xff0c;制定工作条目清单&#xff0c;跟进好工作任务的进展等等。 职场办公…

Python文件操作(txt + xls + json)

文章目录 简介1、使用with_open读取和保存&#xff1a;.txt .bin&#xff08;二进制文本&#xff09;1.1、with open语句详解1.1、项目实战 2、使用pandas读取和保存&#xff1a;.xls .xlsx2.1、pandas简介2.2、环境配置2.3、项目实战 3、 使用json.dump读取和保存&#xff1…

SpringBoot项目访问resources下的静态资源

1.新建一个配置文件夹&#xff0c;放配置类 2.编辑 WebMvcConfig.java package com.southwind.configuration;import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import or…