nestjs入门学习规划:https://blog.csdn.net/lxy869718069/article/details/114028195
说明
最近在学习nestjs推荐使用的typeorm工具来进行数据库相关的操作,然而在刚开始接触typeorm的时候我心里十分迷惘。
比如:为什么一个find()就能够查询一个表的所有数据?sql在哪?delete()、findOne()…这些方法到底是个什么东西,有什么参考没?QueryBuilder又是什么?又如getConnection().createQueryBuilder().delete().from(User).where().execute()之类的链式操作又是个什么东西?为什么能够查询出来数据?明明没有sql来着,这些方法我不知道怎么办?总而言之就是一片迷惘。
因此去网上找了typeorm的中文文档看了一下:https://typeorm.biunav.com/zh/
看完之后倒是有不少的了解,但是心中也有了不少问题,比如:为什么一种查询方式(如:获取数据列表)可以有多种的操作方案(1、query(sql语句),2、find(),3、QueryBuilder方式,4…)?平常我该用哪种操作呢?
因此我也在网上找了不少的示例,然而几乎没有什么解答的,都是直接一个常规的通用代码丢上来就没了。
因此本文将以一个常规的增删查改示例来对typeorm常用的数据操作方式进行整理。
前提
首先得连接好数据库。
数据库连接文章:https://blog.csdn.net/lxy869718069/article/details/103408695
项目目录结构:
开始实现
1、准备user.entity.ts的内容
代码:
import {
Column,
Entity,
PrimaryGeneratedColumn,
BaseEntity,
} from 'typeorm';
/* 单表 */
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'varchar', name: 'name' })
name: string;
@Column({ type: 'varchar', name: 'age' })
age: string;
@Column({ type: 'varchar', name: 'account' })
account: string;
@Column({ type: 'varchar', name: 'password' })
password: string;
}
一个简单的单表而已,没有任何其他麻烦的内容
2、准备user.controller.ts的内容
由于此内容没有其他的特别操作,因此不做说明,直接代码送上:
import {
Controller,
Get,
Post,
Delete,
Body,
Query,
Put,
} from '@nestjs/common';
import { UserService } from './user.service';
import { User } from '../entities/user.entity';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
/* 查询所有列表 */
@Get('list')
findAll(): Promise<User[]> {
return this.userService.findAll();
}
/* 查询单个详情 */
@Get('detail')
findOne(@Query() query): Promise<User> {
return this.userService.findOne(query);
}
/* 新增数据 */
@Post('add')
addOne(@Body() rUser): Promise<String> {
return this.userService.addOne(rUser);
}
/* 修改数据 */
@Put('update')
updateOne(@Body() uUser): Promise<String> {
return this.userService.updateOne(uUser);
}
/* 删除数据 */
@Delete('del')
delOne(@Query() query): Promise<String> {
return this.userService.delOne(query);
}
}
3、准备user.module.ts的内容
代码如下:
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from '../entities/user.entity';
@Module({
// 将entity中的User进行导入,这样UserService才能够正常使用
imports: [TypeOrmModule.forFeature([User])],
controllers: [UserController], // 注册控制器模块
providers: [UserService], // 注册服务模块
})
export class UserModule {}
4、准备app.module.ts的内容
代码如下:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
// 子模块加载
import { UserModule } from './user/user.module';
@Module({
imports: [
// 加载连接数据库
TypeOrmModule.forRoot({
type: 'mysql', // 数据库类型
host: 'localhost', // 数据库ip地址
port: 3306, // 端口
username: 'root', // 登录名
password: '123456', // 密码
database: 'test', // 数据库名称
entities: [__dirname + '/**/*.entity{.ts,.js}'], // 扫描本项目中.entity.ts或者.entity.js的文件
synchronize: true, // 定义数据库表结构与实体类字段同步(这里一旦数据库少了字段就会自动加入,根据需要来使用)
timezone: '+08:00', // 东八区
}),
// 加载UserModule子模块
UserModule,
],
})
export class AppModule {}
这个文件主要是配置连接数据库相关的数据以及加载子模块而已,至于main.ts没什么需要配置的东西,因此无需变动。
5、准备user.service.ts的内容(核心)
准备那么久我们最重要的就是要知道怎么对数据库进行操作以及使用哪些方法或者方式进行操作,同时了解多种操作方式的优劣以及该使用哪种操作方式。
先上代码:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, getConnection } from 'typeorm';
import { User } from '../entities/user.entity';
/*
使用Repository<>对象执行增删查改的操作
*/
@Injectable()
export class UserService {
constructor(
/*
使用InjectRepository装饰器并引入Repository<>对象
这样就可以使用typeorm的Repository Api相关的操作
Repository Api:https://typeorm.biunav.com/zh/repository-api.html#repositoryapi
*/
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
/*
获取所有用户数据列表
*/
async findAll(): Promise<User[]> {
/*
第一种方式 拼接sql并执行sql进行查询
这种方式自由,想要什么直接拼接然后执行即可获得数据,对sql比较熟悉的有优势
虽然用的较少,但在一些复杂的查询或者自己对orm不熟悉的情况下使用也是不错的。
*/
// let getsql = 'select * from user';
// let list = await this.userRepository.query(getsql);
/*
第二种方式直接调用Repository Api的相关方法
这种方式简单快捷,通常用于各种常规增删查改操作,可以提高效率。
问题则是需要掌握Repository Api各种内置的方法以及其参数的常规使用,用多了就熟悉了
*/
let list = await this.userRepository.find();
/*
第三种方式直接获取连接属性,并且构建QueryBuilder查询,这种方式无需使用@InjectRepository装饰器实例化相关对象,随时随地用
这种方式可扩展可兼容,简单便捷,也是TypeORM 最强大的功能之一,可以使用优雅便捷的语法构建 SQL 查询
但是需要更多的了解QueryBuilder查询构建的方式
*/
// let list = await getConnection()
// .createQueryBuilder()
// .select('user')
// .from(User, 'user')
// .getMany();
return list;
}
/*
获取单个用户详情
*/
async findOne(query): Promise<User> {
/*
第一种方式 拼接sql并执行sql进行查询
这种方式自由,想要什么直接拼接然后执行即可获得数据,对sql比较熟悉的有优势
虽然用的较少,但在一些复杂的查询或者自己对orm不熟悉的情况下使用也是不错的。
*/
// let getsql = 'select * from user where id =' + query.id;
// let list = await this.userRepository.query(getsql);
/*
第二种方式直接调用Repository Api的相关方法
这种方式简单快捷,通常用于各种常规增删查改操作,可以提高效率。
问题则是需要掌握各种内置的方法以及其参数的常规使用,用多了就熟悉了
*/
let list = await this.userRepository.findOne({ id: query.id });
/*
第三种方式直接获取连接属性,并且构建QueryBuilder查询,这种方式无需使用@InjectRepository装饰器实例化相关对象,随时随地用
这种方式可扩展可兼容,简单便捷,也是TypeORM 最强大的功能之一,可以使用优雅便捷的语法构建 SQL 查询
但是需要更多的了解QueryBuilder查询构建的方式
*/
// let list = await getConnection()
// .createQueryBuilder()
// .select('user')
// .from(User, 'user')
// .where('id = :id', { id: query.id })
// .getOne();
return list;
}
/*
新增用户
*/
async addOne(rUser): Promise<String> {
/*
第一种方式 拼接sql并执行sql进行查询
这种方式自由,想要什么直接拼接然后执行即可获得数据,对sql比较熟悉的有优势
虽然用的较少,但在一些复杂的查询或者自己对orm不熟悉的情况下使用也是不错的。
*/
// let addsql = `INSERT INTO user(name,age,account,password)
// values("${rUser.name}",${rUser.age},"${rUser.account}",${rUser.password})`;
// let list = await this.userRepository.query(addsql);
/*
第二种方式直接调用Repository Api的相关方法
这种方式简单快捷,通常用于各种常规增删查改操作,可以提高效率。
问题则是需要掌握各种内置的方法以及其参数的常规使用,用多了就熟悉了
注:save()方法亦可
*/
let list = await this.userRepository.insert(rUser);
/*
第三种方式直接获取连接属性,并且构建QueryBuilder查询,这种方式无需使用@InjectRepository装饰器实例化相关对象,随时随地用
这种方式可扩展可兼容,简单便捷,也是TypeORM 最强大的功能之一,可以使用优雅便捷的语法构建 SQL 查询
但是需要更多的了解QueryBuilder查询构建的方式
*/
// let list = await getConnection()
// .createQueryBuilder()
// .insert()
// .into(User)
// .values(rUser)
// .execute();
if (list) {
return '新增成功!';
} else {
return '新增失败!';
}
}
/*
修改用户
*/
async updateOne(uUser): Promise<String> {
/*
第一种方式 拼接sql并执行sql进行查询
这种方式自由,想要什么直接拼接然后执行即可获得数据,对sql比较熟悉的有优势
虽然用的较少,但在一些复杂的查询或者自己对orm不熟悉的情况下使用也是不错的。
*/
// let upsql = `UPDATE user SET
// name = "${uUser.name}",age = "${uUser.age}",account = "${uUser.account}", password = "${uUser.password}"
// WHERE id = "${uUser.id}"`;
// let list = await this.userRepository.query(upsql);
/*
第二种方式直接调用Repository Api的相关方法
这种方式简单快捷,通常用于各种常规增删查改操作,可以提高效率。
问题则是需要掌握各种内置的方法以及其参数的常规使用,用多了就熟悉了
注:save()方法亦可
*/
let list = await this.userRepository.update({ id: uUser.id }, uUser);
/*
第三种方式直接获取连接属性,并且构建QueryBuilder查询,这种方式无需使用@InjectRepository装饰器实例化相关对象,随时随地用
这种方式可扩展可兼容,简单便捷,也是TypeORM 最强大的功能之一,可以使用优雅便捷的语法构建 SQL 查询
但是需要更多的了解QueryBuilder查询构建的方式
*/
// let list = await getConnection()
// .createQueryBuilder()
// .update(User)
// .set(uUser)
// .where('id = :id', { id: uUser.id })
// .execute();
if (list) {
return '修改成功!';
} else {
return '修改失败!';
}
}
/*
删除用户
*/
async delOne(query): Promise<String> {
/*
第一种方式 拼接sql并执行sql进行查询
这种方式自由,想要什么直接拼接然后执行即可获得数据,对sql比较熟悉的有优势
虽然用的较少,但在一些复杂的查询或者自己对orm不熟悉的情况下使用也是不错的。
*/
// let dsql = `DELETE FROM user WHERE id = ${query.id}`;
// let list = await this.userRepository.query(dsql);
/*
第二种方式直接调用Repository Api的相关方法
这种方式简单快捷,通常用于各种常规增删查改操作,可以提高效率。
问题则是需要掌握Repository Api各种内置的方法以及其参数的常规使用,用多了就熟悉了
*/
let list = await this.userRepository.delete({ id: query.id });
/*
第三种方式直接获取连接属性,并且构建QueryBuilder查询,这种方式无需使用@InjectRepository装饰器实例化相关对象,随时随地用
这种方式可扩展可兼容,简单便捷,也是TypeORM 最强大的功能之一,可以使用优雅便捷的语法构建 SQL 查询
但是需要更多的了解QueryBuilder查询构建的方式
*/
// let list = await getConnection()
// .createQueryBuilder()
// .delete()
// .from(User)
// .where('id = :id', { id: query.id })
// .execute();
if (list) {
return '删除成功!';
} else {
return '删除失败!';
}
}
}
解读
内容看起来非常多,但实际上并不多,仅仅只是注释多点,下面以获取列表来进行解读
通常情况下,对一个表进行操作,尤其是子模块使用TypeOrmModule.forFeature()进行了注册的操作如下,我们都需要使用@InjectRepository()装饰器在constructor中进行实例化。如:
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
......
这样实例化之后便能够获得User的Repository(存储库)对象,这可能会有点难以理解,但事实就是如此,拿到对象才能调用其方法嘛!
Repository API的方法路径:https://typeorm.biunav.com/zh/repository-api.html
这样我们就可以使用this.userRepository.find()之类的查询方式了。这种方法封装好了sql语句。
注意:nestjs不只是提供了@InjectRepository()注解,还提供了@EntityRepository()等注解,其差异性可以自行了解,同时EntityManager(实体管理器)和Repository(存储库)的差异性也有必要了解一下。
本例提供了三种常用的操作数据库的方式,了解并熟悉了这三种方式就足够开发用了。
第一种方式简单粗暴,直接使用EntityManager(实体管理器)或Repository(存储库)的query()方法执行拼接出来的sql语句即可,就如同在数据库执行一样,这种方式非常自由,但是比较麻烦而已,但有必要知道与使用,因为保不准什么时候就会需要。
第二种方式,使用EntityManager(实体管理器)或Repository(存储库)的内置方法,这些方法是写好了的,直接去查询api即可使用,好处在于简单方便,毕竟一个方法丢几个参数就搞定了呢!
第三种方式非常特别。这种方式可以不用使用@InjectRepository()实例化某些entity。而是直接使用即可,这也是typeorm的一个比较有意义的功能,问题在于构建的方法上手比较的麻烦,需要自己多去了解,但好处就是可以随意构建任何自己想要构建的sql语句而且还十分方便。因此本方式常用于一些复杂语句的构建。