nest-typeorm学习:增删改查操作

文章问题导向

访问数据库的方式有哪些?
typeorm增删改查操作的方式有哪些?

如果你都有了答案,可以忽略本文章,或去nest学习导图寻找更多答案


注意

学习该文章,需要有一定的mysql知识基础
你已经会使用nest连接mysql,如果不会:去学习
此文章需要多次阅读,才能容易理解


多种访问数据库的方式

第一种:Connection

import { Injectable } from '@nestjs/common';
import { Connection } from 'typeorm';
import { UsersEntity } from './entities/user.entity';

@Injectable()
export class UserService {
  constructor(
    private readonly connection: Connection,
  ) { }

  async test() {
    使用封装好方法:
    return await this.connection
      .getRepository(UsersEntity)
      .findOne({ where: { id: 1 } });

	使用createQueryBuilder:
    return await this.connection
      .createQueryBuilder()
      .select('user')
      .from(UsersEntity, 'user')
      .where('user.id = :id', { id: 1 })
      .getOne();
  }
}

第二种:Repository,需要@nestjs/typeorm的InjectRepository来注入实体

import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { UsersEntity } from './entities/user.entity';
import { InjectRepository } from '@nestjs/typeorm';

@Injectable()
export class UserService {
  constructor(
  	@InjectRepository(UsersEntity)  注入实体
	private readonly usersRepository: Repository<UsersEntity>,
  ) { }

  async test() {
  	使用封装好方法:
    return await this.usersRepository.find({ where: { id: 1 } });

	使用createQueryBuilder:
    return await this.usersRepository
      .createQueryBuilder('user')
      .where('id = :id', { id: 1 })
      .getOne();
  }
}

第三种:getConnection():语法糖,是Connection类型

import { Injectable } from '@nestjs/common';
import { getConnection } from 'typeorm';
import { UsersEntity } from './entities/user.entity';

@Injectable()
export class UserService {
  async test() {
  	使用封装好方法:
    return await getConnection()
      .getRepository(UsersEntity)
      .find({ where: { id: 1 } });

	使用createQueryBuilder:
    return await getConnection()
      .createQueryBuilder()
      .select('user')
      .from(UsersEntity, 'user')
      .where('user.id = :id', { id: 1 })
      .getOne();
  }
}

第四种:getRepository:语法糖

import { Injectable } from '@nestjs/common';
import { getRepository } from 'typeorm';
import { UsersEntity } from './entities/user.entity';

@Injectable()
export class UserService {
  async test() {
  	使用封装好方法:
    return await getRepository(UsersEntity).find({ where: { id: 1 } });

	使用createQueryBuilder:
    return await getRepository(UsersEntity)
      .createQueryBuilder('user')
      .where('user.id = :id', { id: 1 })
      .getOne();
  }
}

第五种:getManager

import { Injectable } from '@nestjs/common';
import { getManager } from 'typeorm';
import { UsersEntity } from './entities/user.entity';

@Injectable()
export class UserService {
  async test() {
  	使用封装好方法:
    return await getManager().find(UsersEntity, { where: { id: 1 } });

	使用createQueryBuilder:
	return await getManager()
      .createQueryBuilder(UsersEntity, 'user')
      .where('user.id = :id', { id: 1 })
      .getOne();
  }
}

简单总结

使用的方式太多,建议使用:2,4,比较方便

Connection核心类:
connection                           等于getConnection
connection.manager                   等于getManager, 等于getConnection.manager
connection.getRepository             等于getRepository, 等于getManager.getRepository
connection.createQueryBuilder        使用QueryBuilder
connection.createQueryRunner         开启事务


1. EntityManager 和 Repository都封装了操作数据的方法,注意:两者的使用方式是不一样的,(实在不明白搞这么多方法做什么,学得头大)
getManager是EntityManager的类型,getRepository是Repository的类型

2. 都可以使用createQueryBuilder,但使用的方式略有不同

增删改查的三种方式

第一种:使用sql语句,适用于sql语句熟练的同学
第二种:typeorm封装好的方法,增删改 + 简单查询
第三种:QueryBuilder查询生成器,适用于关系查询,多表查询,复杂查询
其实底层最终都会生成sql语句,只是封装了几种方式而已,方便人们使用。

第一种:sql语句

export class UserService {
  constructor(
    @InjectRepository(UsersEntity)
    private readonly usersRepository: Repository<UsersEntity>,
  ) { }

  async findAll() {
    return await this.usersRepository.query('select * from users');  在query中填写sql语句
  }
}

第二种:typeorm封装好的api方法

这里使用第二种访问数据库的方式

export class UserService {
  constructor(
    @InjectRepository(UsersEntity)
    private readonly usersRepository: Repository<UsersEntity>,
  ) { }

  async findAll() {
    return await this.usersRepository.findAndCount();  封装好的方法
  }
}

api方法

更多方法,在代码中,按住ctrl + 左键 点击方法名, 即可进入ts文件,获取更多的方法和参数说明

save(user)            创建:返回该数据的所有字段
insert(user)          快速插入一条数据,插入成功:返回插入实体,与save方法不同的是,它不执行级联、关系和其他操作。
删
remove(user)          删除:返回该数据的可见字段
softRemove(user);     拉黑:返回该数据的可见字段,该删除实体必须拥有@DeleteDateColumn()字段,被拉黑的用户还存在数据库中,但无法被find查找到,会在@DeleteDateColumn()字段中添加删除时间,可使用recover恢复
改
update(id, user)      更新:返回更新实体,不是该数据的字段
恢复
recover({ id })       恢复:返回id,将被softRemove删除(拉黑)的用户恢复,恢复成功后可以被find查找到


查找全部
find()
find({id:9})                   条件查找,写法一,找不到返回空对象
find({where:{id:10}})          条件查找,写法二,找不到返回空对象
findAndCount()                 返回数据和总的条数

查找一个
findOne(id);                       根据ID查找,找不到返回undefined
findOne({ where: { username } });  条件查找,找不到返回undefined

根据ID查找一个或多个
findByIds([1,2,3]);            查找n个,全部查找不到返回空数组,找到就返回找到的

其他
hasId(new UsersEntity())       检测实体是否有合成ID,返回布尔值
getId(new UsersEntity())       获取实体的合成ID,获取不到返回undefined
create({username: 'admin12345', password: '123456',})  创建一个实体,需要调用save保存
count({ status: 1 })           计数,返回数量,无返回0
increment({ id }, 'age', 2);   增加,给条件为id的数据的age字段增加2,成功返回改变实体
decrement({ id }, 'age', 2)    减少,给条件为id的数据的age字段增加2,成功返回改变实体

谨用
findOneOrFail(id)              找不到直接报500错误,无法使用过滤器拦截错误,不要使用
clear()                        清空该数据表,谨用!!!

find更多参数

this.userRepository.find({
    select: ["firstName", "lastName"],             要的字段
    relations: ["photos", "videos"],               关系查询
    where: {                                       条件查询
        firstName: "Timber",
        lastName: "Saw"
    },
    where: [{ username: "li" }, { username: "joy" }],   多个条件or, 等于:where username = 'li' or username = 'joy'
    order: {                                       排序
        name: "ASC",
        id: "DESC"
    },
    skip: 5,                                       偏移量
    take: 10,                                      每页条数
    cache: 60000                                   启用缓存:1分钟
});

find进阶选项

TypeORM 提供了许多内置运算符,可用于创建更复杂的查询

import { Not, Between, In } from "typeorm";
return await this.usersRepository.find({
    username: Not('admin'),
});
将执行以下查询:
SELECT * FROM "users" WHERE "username" != 'admin'


return await this.usersRepository.find({
    likes: Between(1, 10)
});
SELECT * FROM "users" WHERE "likes" BETWEEN 1 AND 10


return await this.usersRepository.find({
    username: In(['admin', 'admin2']),
});
SELECT * FROM "users" WHERE "title" IN ('admin', 'admin2')

更多查看官网


第三种:QueryBuilder查询生成器

使用链式操作

QueryBuilder增,删,改

增加
return await this.usersRepository
  .createQueryBuilder()
  .insert()                       声明插入操作
  .into(UsersEntity)              插入的实体
  .values([                       插入的值,可插入多个
    { username: 'Timber', password: '123456' },
    { username: 'Timber2', password: '123456' },
  ])
  .execute();                     执行


修改
return this.usersRepository
  .createQueryBuilder()
  .update(UsersEntity)
  .set({ username: 'admin22' })
  .where('id = :id', { id: 2 })
  .execute();


删除
return this.usersRepository
  .createQueryBuilder()
  .delete()
  .from(UsersEntity)
  .where('id = :id', { id: 8 })
  .execute();


处理异常:请求成功会返回一个对象, 如果raw.affectedRows != 0 就是成功
"raw": {
      "fieldCount": 0,
      "affectedRows": 2,
      "insertId": 13,
      "serverStatus": 2,
      "warningCount": 0,
      "message": "&Records: 2  Duplicates: 0  Warnings: 0",
      "protocol41": true,
      "changedRows": 0
}

查询

简单例子

export class UserService {
  constructor(
    @InjectRepository(UsersEntity)
    private readonly usersRepository: Repository<UsersEntity>,
  ) { }

  async findAll() {
    return await this.usersRepository
    .createQueryBuilder('user')                      创建生成器,参数:别名
    .where('user.id = :id', { id: id })              条件
    .innerJoinAndSelect('user.avatar', 'avatar')     关系查询
    .addSelect('user.password')                      添加显示字段
    .getOne();                                       获取一条数据
  }
}

QueryBuilder查询生成器说明

查询单表

访问数据库的方式不同:

方式一:没有指定实体,需要使用from指定实体
return await getConnection()
      .createQueryBuilder()
      .select('user.username')             ‘user’:全部字段,‘user.username’:只获取username
      .from(UsersEntity, 'user')1:连接的实体, 参2:别名
      .where('user.id = :id', { id: 1 })
      .getOne();

方式二:指定实体:默认获取全部字段
return await getConnection()
      .createQueryBuilder(UsersEntity, 'user')   指定实体
      .where('user.id = :id', { id: 1 })
      .getOne();

方式三: 已经在访问时指定了实体:默认获取全部字段
return await this.usersRepository
      .createQueryBuilder('user')          别名
      .where('user.id = :id', { id: 1 })
      .getOne();

获取结果

.getSql();          获取实际执行的sql语句,用于开发时检查问题
.getOne();          获取一条数据(经过typeorm的字段处理)
.getMany();         获取多条数据
.getRawOne();       获取一条原数据(没有经过typeorm的字段处理)
.getRawMany();      获取多条原数据
.stream();          返回流数据

如:经过typeorm的字段处理,获取到的就是实体设计时的字段
{
    "status": 200,
    "message": "请求成功",
    "data": {
        "id": 1,
        "username": "admin",
        "gender": "male",
        "age": 18,
        "status": 1,
        "createdAt": "2021-04-26T09:58:54.469Z",
        "updatedAt": "2021-04-28T14:47:36.000Z",
        "deletedAt": null
    }
}

如:没有经过typeorm的字段处理,将数据库的字段原生不动的显示出来
{
    "status": 200,
    "message": "请求成功",
    "data": {
        "user_id": 1,
        "user_username": "admin",
        "user_gender": "male",
        "user_age": 18,
        "user_status": 1,
        "user_created_at": "2021-04-26T09:58:54.469Z",
        "user_updated_at": "2021-04-28T14:47:36.000Z",
        "user_deleted_at": null
    }
}

查询部分字段

.select(["user.id", "user.name"])
实际执行的sql语句:SELECT user.id, user.name FROM users user;

添加隐藏字段:实体中设置select为false时,是不显示字段,使用addSelect会将字段显示出来
.addSelect('user.password')

where条件

.where("user.name = :name", { name: "joy" })
等于
.where("user.name = :name")
.setParameter("name", "Timber")
实际执行的sql语句:SELECT * FROM users user WHERE user.name = 'joy'

多个条件
.where("user.firstName = :firstName", { firstName: "Timber" })
.andWhere("user.lastName = :lastName", { lastName: "Saw" });
实际执行的sql语句:SELECT * FROM users user WHERE user.firstName = 'Timber' AND user.lastName = 'Saw'

in
.where("user.name IN (:...names)", { names: [ "Timber", "Cristal", "Lina" ] })
实际执行的sql语句:SELECT * FROM users user WHERE user.name IN ('Timber', 'Cristal', 'Lina')

or
.where("user.firstName = :firstName", { firstName: "Timber" })
.orWhere("user.lastName = :lastName", { lastName: "Saw" });
实际执行的sql语句:SELECT * FROM users user WHERE user.firstName = 'Timber' OR user.lastName = 'Saw'

子句
const posts = await connection
  .getRepository(Post)
  .createQueryBuilder("post")
  .where(qb => {
    const subQuery = qb
      .subQuery()
      .select("user.name")
      .from(User, "user")
      .where("user.registered = :registered")
      .getQuery();
    return "post.title IN " + subQuery;
  })
  .setParameter("registered", true)
  .getMany();
实际执行的sql语句:select * from post where post.title in (select name from user where registered = true)

having筛选

.having("user.firstName = :firstName", { firstName: "Timber" })
.andHaving("user.lastName = :lastName", { lastName: "Saw" });
实际执行的sql语句:SELECT ... FROM users user HAVING user.firstName = 'Timber' AND user.lastName = 'Saw'

orderBy排序

.orderBy("user.name", "DESC")
.addOrderBy("user.id", "asc");
等于
.orderBy({
  "user.name": "ASC",
  "user.id": "DESC"
});

实际执行的sql语句:SELECT * FROM users user order by user.name asc, user.id desc;

group分组

.groupBy("user.name")
.addGroupBy("user.id");

关系查询(多表)

1参:你要加载的关系,2参:可选,你为此表分配的别名,3参:可选,查询条件

左关联查询
.leftJoinAndSelect("user.profile", "profile")     

右关联查询
.rightJoinAndSelect("user.profile", "profile")    

内联查询
.innerJoinAndSelect("user.photos", "photo", "photo.isRemoved = :isRemoved", { isRemoved: false })         


例子:
const result = await this.usersRepository
	.createQueryBuilder('user')
    .leftJoinAndSelect("user.photos", "photo")
    .where("user.name = :name", { name: "joy" })
  	.andWhere("photo.isRemoved = :isRemoved", { isRemoved: false })
  	.getOne();

实际执行的sql语句:
SELECT user.*, photo.* 
FROM users user
LEFT JOIN photos photo ON photo.user = user.id
WHERE user.name = 'joy' AND photo.isRemoved = FALSE;


const result = await this.usersRepository
	.innerJoinAndSelect("user.photos", "photo", "photo.isRemoved = :isRemoved", { isRemoved: false })
    .where("user.name = :name", { name: "Timber" })
    .getOne();

实际执行的sql语句:
SELECT user.*, photo.* FROM users user
INNER JOIN photos photo ON photo.user = user.id AND photo.isRemoved = FALSE
WHERE user.name = 'Timber';


多个关联
const result = await this.usersRepository
  .createQueryBuilder("user")
  .leftJoinAndSelect("user.profile", "profile")
  .leftJoinAndSelect("user.photos", "photo")
  .leftJoinAndSelect("user.videos", "video")
  .getOne();

学习更多

nest学习导图

NestJS中使用TypeORM进行分页查询的步骤如下: 1. 首先,在你的DAO或Repository文件中使用`InjectRepository`注入Repository: ```typescript @Injectable() export class TicketDao { constructor( @InjectRepository(TicketEntity) private readonly ticketRepo: Repository<TicketEntity> ) {} } ``` 2. 在`async filterAndPageQuery()`方法中创建一个queryBuilder: ```typescript async filterAndPageQuery() { let qb = this.ticketRepo.createQueryBuilder('ticket'); // 在qb中可以写下面的条件筛选和分页查询代码 // 接下来的 qb.xxx 都是使用 QueryBuilder 的增删查改操作 } ``` 3. 使用`skip`和`take`方法实现分页查询: ```typescript qb = qb .skip(pageParam.pageSize * (pageParam.current - 1)) .take(pageParam.pageSize); ``` 其中,`skip`方法用于设置跳过的条数,`take`方法用于限制查询的条数。 4. 获取查询结果及查询结果总数: ```typescript return await qb.getManyAndCount(); ``` 或者,如果你只需要查询结果而不需要总数,可以使用`.getMany()`方法。 以上是使用NestJS和TypeORM进行分页查询的基本步骤。你可以根据实际需求进行相应的修改和扩展。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [nestjs typeorm 条件筛选、排序、分页 常见查询功能的实现](https://blog.csdn.net/landiyaaa/article/details/104730677)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值