前言

  • 前面分别学习了nest与typeorm的基本使用,下面需要把2者结合起来。
  • 本篇任务:
    1、创建users、posts、role表,每个表字段不少于4个
    2、users和posts是一对多的关系(不要求一定创建外键)
    3、users和role是多对多的关系(不要求一定创建外键)
    4、users、posts、role的增删改操作
    5、查询用户列表,要同时查询出关联的posts和role的数据
    6、给用户分配角色的时候时候要加上事务
    7、上面的全部提供restfull api接口的方式
  • 官网资料:https://docs.nestjs.com/techniques/database

新建项目

  • 我们使用官网脚手架进行安装。
nest new yourproject
  • 同时需要安装typeorm与mysql,数据库搭建不在本篇内容。
npm install --save @nestjs/typeorm typeorm mysql

链接数据库

  • 对于数据库敏感信息,需要放置env里,nest提供了个包内部使用dotenv来解决:
$ npm i --save @nestjs/config
  • 关于配置env,中文文档和英文文档说法不太一样,中文文档那个应该过时了。
  • 如果没啥特别的配置,使用时在appmodule中import:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';@Module({imports: [ConfigModule.forRoot()],
})
export class AppModule {}
  • 使用自定义配置,需要建立配置文件导出 config/database.config.ts
export default () => ({type: process.env.DB_TYPE,host: process.env.DB_HOST,port: Number(process.env.DB_PORT),database: process.env.DB_DATABASE,username: process.env.DB_USERNAME,password: process.env.DB_PASSWORD,logging: true,
});
  • 然后appmodule下导入,同时配置typeorm:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import configuration from '../config/database.config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';@Module({imports: [ConfigModule.forRoot({load: [configuration],}),TypeOrmModule.forRoot(),],controllers: [AppController],providers: [AppService],
})
export class AppModule {}
  • 配置typeorm的ormconfig文件,用来连接数据库:
module.exports = [{name: 'default',type: process.env.DB_TYPE,host: process.env.DB_HOST,port: Number(process.env.DB_PORT),database: process.env.DB_DATABASE,username: process.env.DB_USERNAME,password: process.env.DB_PASSWORD,logging: false,synchronize: true,entities: ['dist/src/**/*.entity.{ts,js}'],migrations: ['src/migration/*.ts'],subscribers: ['src/subscriber/**/*.ts'],cli: {entitiesDir: 'src/',migrationsDir: 'src/migration',subscribersDir: 'src/subscriber',},},
];
  • 此时start,发现已经成功链接数据库了。
  • nest上要的env实际不是链接数据库,而是去提供了一个config的service,typeorm才是真正利用env链接得数据库。
  • 可以新建个模块来验证下:
  • 使用命令新建个模块三件套:
nest g mo user
nest g co user
nest g s user
  • module下导入它
@Module({imports: [ConfigModule],controllers: [UserController],providers: [UserService],
})
export class UserModule {}
  • 在控制器中,可以试着打印注入的config服务,如果能打印出来则成功:
@Controller('user')
export class UserController {constructor(private configService: ConfigService) {// get an environment variableconst dbUser = this.configService.get<string>('DB_TYPE');// get a custom configuration valueconst dbHost = this.configService.get<string>('DB_PORT');console.log(dbUser, dbHost);}
}

编写实体

  • 首先创建user的实体,在其文件夹下建立user.entity.ts。
  • 因为ormconfig里配置了实体后缀,所以实体必须以此结尾,代码直接拷贝上次的
import {Entity,PrimaryGeneratedColumn,Column,CreateDateColumn,UpdateDateColumn,DeepPartial
} from 'typeorm';@Entity({ name: 'user' })
export class UserEntity {@PrimaryGeneratedColumn({type: 'int',name: 'id',comment: '主键id',})id: number;@Column({type: 'varchar',nullable: false,length: 50,unique: true,name: 'username',comment: '用户名',})username: string;@Column({type: 'varchar',nullable: false,length: 100,comment: '密码',})password: string;@Column('tinyint', {nullable: false,default: () => 0,name: 'is_del',comment: '是否删除,1表示删除,0表示正常',})isDel: number;@CreateDateColumn({type: 'timestamp',nullable: false,name: 'created_at', comment: '创建时间',})createdAt: Date;@UpdateDateColumn({type: 'timestamp',nullable: false,name: 'updated_at',comment: '更新时间',})updateAt: Date;
}
export type UserEntityDataType = UserEntityDataType = DeepPartial<UserEntity>;
  • module中需要导入实体:
@Module({imports: [ConfigModule, TypeOrmModule.forFeature([UserEntity ])],controllers: [UserController],providers: [UserService],
})
  • 先在服务中写个新增和查询:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';import { UserEntity ,UserEntityDataType} from './user.entity';@Injectable()
export class UserService {constructor(@InjectRepository(UserEntity)private readonly userRepository: Repository<UserEntity>,) {}async createUser(data:UserEntityDataType): Promise<UserEntity> {return await this.userRepository.save(data);}async userList(): Promise<UserEntity[]> {return await this.userRepository.find();}
}
  • 在控制器中写入路由和服务:
import { Controller, Post, Body, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { UserService } from './user.service';
import { UserEntity,UserEntityDataType } from './user.entity';
@Controller('user')
export class UserController {constructor(private configService: ConfigService,private readonly userService: UserService,) {// get an environment variableconst dbUser = this.configService.get<string>('DB_TYPE');// get a custom configuration valueconst dbHost = this.configService.get<string>('DB_PORT');console.log(dbUser, dbHost);}@Post()async createUser(@Body() data:UserEntityDataType,): Promise<UserEntity> {return await this.userService.createUser(data);}@Get()async userList(): Promise<UserEntity[]> {return await this.userService.userList();}
}
  • 启动服务,拿postman之类玩意测试下。
  • 使用get访问http://localhost:3000/user 应该拿到空数组。
  • 然后使用Post新增个user:
{"username":"yehuozhili","password":"12345"
}
  • 得到回复:
{"username": "yehuozhili","password": "12345","id": 1,"isDel": 0,"createdAt": "2020-09-08T22:24:09.569Z","updateAt": "2020-09-08T22:24:09.569Z"
}
  • 查看数据库有写入就ok。
  • 下面照葫芦画瓢,把posts、role表创建好,users和posts是一对多,users和role是多对多的关系
  • 2个模块使用命令创建三件套,新建其实体。
  • posts实体抄上次的,role抄上次tag的。
  @ManyToOne(() => UserEntity,user => user.posts,)user: UserEntity;
  @ManyToMany(() => UserEntity,user => user.roles,)@JoinTable({ name: 'role_user' })users: UserEntity[];
  @OneToMany(() => Posts,post => post.user,)posts: Posts[];@ManyToMany(() => Roles,role => role.users,)roles: Roles[];
  • 实体制作完成,下面制作服务与控制器,别忘了引入实体到Module上。
  • 服务与控制器和user的写法基本一致,值得注意的就是外键保存的话需要注入额外的服务拿到Repository,我终于知道为啥service需要分离了,真是不写不知道。
  • 还需要写修改和删除,我就简单写写,无非就是传参问题。
  @Put()async changePassw(@Body() data: UserEntityDataType) {if (data.id !== undefined && data.password !== undefined) {return await this.userService.changePassword(data.id, data.password);} else {return 'you need to pass id and password';}}@Delete()async delUser(@Body() data: UserEntityDataType) {return this.userService.delUser(data.id);}
  • 服务就这样:
  async changePassword(id: number, newPassword: string): Promise<UserEntity> {const user = await this.userRepository.findOne(id);user.password = newPassword;return await this.userRepository.save(user);}async delUser(id: number): Promise<UserEntity> {const user = await this.userRepository.findOne(id);return await this.userRepository.remove(user);}
}
  • 这样就完成了
  • 开启服务试试效果。这里我已经测试过ok。

事务

  • 这里我们对创建role使用事物,创建一个role同时,分配给指定user 。
  • 关于多对多以及事务,这里有几个坑。
  • 首先多对多是有个中间表,也就是user或者role其中一个没加上对方,都会完全添加不上。
  • 第二个坑就是查询user时,user的表中role字段是不显示的,只有加入relation才显示,自己指定是无效的。
  • 第三个坑就是事务操作必须manager从头干到尾,不能使用repo进行保存,否则repo一旦save,必然存进数据库,而manager如果发现之中有错误,就算是save完再error,也能回滚。
  • 这里使用role的服务进行创建的transition操作:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Roles } from './roles.entity';
import { Repository, EntityManager } from 'typeorm';
import { UserEntity } from 'src/user/user.entity';
import { RoleCreateDataType } from './roles.controller';
import { isArray } from 'util';@Injectable()
export class RolesService {constructor(@InjectRepository(Roles)private readonly roleRepository: Repository<Roles>,) {}async create(data: RoleCreateDataType,manager: EntityManager,): Promise<string> {const roles = new Roles();roles.name = data.role.name;const user = await manager.getRepository(UserEntity).findOne(data.user.id, { relations: ['roles'] });console.log(user.roles);//找到的user里加入roleif (isArray(user.roles)) {user.roles.push(roles);} else {user.roles = [roles];}roles.users = [user];console.log(user);await manager.save(user);await manager.save(roles);return 'ok';}async getList(): Promise<Roles[]> {return await this.roleRepository.find();}
}
  • 控制器使用transaction:
export type RoleCreateDataType = {user: UserEntityDataType;role: RolesTypes;
};@Controller('roles')
export class RolesController {constructor(private readonly roleService: RolesService) {}@Post()@Transaction()async createUser(@Body()data: RoleCreateDataType,@TransactionManager() manager: EntityManager,): Promise<string> {return await this.roleService.create(data, manager);}@Get()async userList(): Promise<Roles[]> {return await this.roleService.getList();}
}

【nest】nest结合typeorm基本使用相关推荐

  1. nestjs配置MySQL数据库,Nest.js 中的数据库操作

    安装 Typeorm 为了与 SQL 和 NoSQL 数据库集成,Nest.js 提供了@nestjs/typeorm 软件包.Nest.js 使用 TypeORM,因为它是 TypeScript 最 ...

  2. Nest+Vue实战:工作计划管理系统

    博客原文和更新地址:Nest+Vue实战:工作计划管理系统 前言 该项目是学习Nest.js框架所得,前端基于Vue.js + Vuex + VueRouter + ElementUI + SCSS, ...

  3. 学完这篇 Nest.js 实战,还没入门的来锤我!(长文预警)

    大厂技术  高级前端  Node进阶 点击上方 程序员成长指北,关注公众号 回复1,加入高级Node交流群 前言 最近一直比较忙, 而且自己工作中做的事也不适合写文章,所以一直没有更文.. 最近接到一 ...

  4. javafx 自定义控件_JavaFX自定义控件– Nest Thermostat第2部分

    javafx 自定义控件 自从我开始创建Nest恒温器FX自定义控件以来,已经有一段时间了! 因此,上次,正如Gerrit Grunwald所建议的那样,我花了一些时间使用inkscape复制Nest ...

  5. JavaFX自定义控件– Nest Thermostat第2部分

    自从我开始创建Nest恒温器FX自定义控件以来,已经有一段时间了! 因此,上次,如Gerrit Grunwald所建议,我花了一些时间用inkscape复制Nest恒温器设计,这是构建JavaFX版本 ...

  6. PCLINT(2):MVG NEST LOC (圈复杂度 嵌套深度 代码行数)

    PCLINT(2):MVG NEST LOC 1. PCLINT MVG NEST LOC 2. 圈复杂度的表现: 1. PCLINT MVG NEST LOC PCLINT MVG圈复杂度 NEST ...

  7. 【NEST】脉冲神经网络仿真平台入门手册整理翻译记录

    这是国庆前导师让了解的脉冲神经仿真平台NEST的部分介绍手册的翻译和整理,记录一下留个备份,主要内容可以通过查看文档中的链接索引到官网. 如需要手册代码合辑及例程ipynb文件,请查看NEST脉冲神经 ...

  8. nest空调控制器_如何使用Alexa控制Nest Learning Thermostat

    nest空调控制器 You can do a lot of things with Amazon's Alexa voice assistant, and now, thanks to new sma ...

  9. JAVA套料程序_Nest4J是一款基于Java作为开发语言的Nest算法包

    Nest4J Nest4J是一款基于Java作为开发语言的Nest算法包.可以看做一款能在服务端进行运行计算的Nest算法库. 基于SVGNest进行了Java化的改造. 同样这也作为了我本科的毕业设 ...

  10. 怎么购买 nest_购买Nest恒温器时如何省钱

    怎么购买 nest The Nest Thermostat isn't exactly cheap, but if you've been in the market for a smart ther ...

最新文章

  1. matplotlib可视化必知必会富文本绘制方法
  2. drop table中cascade的含义及用法
  3. .NET MAUI 已在塔架就位 ,4月份发布RC
  4. (pytorch-深度学习系列)ResNet残差网络的理解-学习笔记
  5. 从头学习MVC4基础之视图
  6. LocalDB 和Compact
  7. 注册了Stack Overflow
  8. ssh问题:ssh_exchange_identification: Connection closed by remote host
  9. JavaScript学习(六十二)—解析选项和序列化选项
  10. Swift入坑系列—集合类型
  11. 【Java】java插件化开发
  12. linux中at重定位命令,readelf命令_Linux readelf 命令用法详解:用于显示elf格式文件的信息...
  13. 程序员如何财务自由【原创】
  14. ch340g电路图5v和3.3v供电电路
  15. NYoj 239 :月老的难题(二分图最大匹配)
  16. 常见的ICE工具和集成开发环境
  17. 技术一旦被用来作恶,究竟会有多可怕?
  18. 网络工程师和java工程师,请问做网络工程师与程序员哪个更愉快呢
  19. 红米note4x线刷miui9国际版开发版
  20. cpp实现直线的DDA算法

热门文章

  1. C++调试时出现<optimized out>问题的解决办法
  2. Vue中加载mapv和mapvgl
  3. 一图读懂XTransfer从注册到提现全流程,隐藏秘笈、建议收藏!
  4. (可下载)《2020年网络安全威胁信息研究报告(2021年)》上新
  5. 登陆操作:用户名和密码及进行录入判断 当用户名是li 密码是123456的时候显示登陆成功 。如果密码和用户名录入错需要循环录入。 并且录入错误次数超过三次提示:账号已锁。并且停掉循环
  6. 打工人看过来,2022 个税申报开始了!
  7. leetCode:Letter Combinations of a Phone Number
  8. C语言数字图像处理进阶---13 Ins1977滤镜
  9. js数字位数不够前面补零
  10. linux基础(二)