TypeOrm+Nest.js分页处理(含GROUP BY分组查询)

本文介绍了如何在Nest.js应用中结合TypeOrm进行分页处理,特别是针对包含GROUP BY的复杂查询。提供的代码经过实际测试,适用于生产环境,帮助开发者高效地实现数据分页和分组查询功能。

说明:该代码为已经过测试的生产环境代码
若有所帮助,望君轻轻一点,手有余香

import { RpcException } from '@nestjs/microservices';
import { EntityManager, getManager, SelectQueryBuilder } from 'typeorm';

/*
 * @Author: Horace
 * @Date: 2019-08-30 14:21:12
 * @Last Modified by: Horace
 * @Last Modified time: 2019-08-30 14:21:12
 */
export class Pagination {
  // @IsNotEmpty()
  // @ApiModelProperty({ description: "页码", type: "number", example: 1 })
  private pageNum: number;
  // @IsNotEmpty()
  // @ApiModelProperty({ description: "一页显示条数", type: "number", example: 10 })
  private pageSize: number;
  // @ApiModelProperty({ description: "总页数", type: "number", example: 10 })
  private totalNum: number;
  // @ApiModelProperty({ description: "总条数", type: "number", example: 10 })
  private totalRows: number;
  // @ApiModelProperty({ description: "数据" })
  public data?: any[];

  public get _pageNum(): number {
    return this.pageNum;
  }

  public set _pageNum(param: number) {
    if (param <= 0) {
      param = 1;
    }
    // if (param > this._totalNum) {
    //     param = this._totalNum;
    // }
    this.pageNum = param;
  }

  public get _pageSize() {
    return this.pageSize;
  }

  public set _pageSize(param: number) {
    this.pageSize = param;
  }

  public get _totalRows() {
    return this.totalRows;
  }

  public set _totalRows(param: number) {
    this.totalRows = param;
  }

  // @ApiModelProperty({ description: "总页数", type: "number", example: 10 })
  public get _totalNum() {
    return this.totalNum;
  }

  public set _totalNum(param: number) {
    this.totalNum = param;
  }
  
  /**
   * queryBuilder分页查询
   * @param pageHelper
   * @param pageHelper.getRayMany 是否需要实体之外的参数
   * @param queryBuilder
   */
  public static async findByPage(queryBuilder: SelectQueryBuilder<any>, pageHelper?: { getRayMany?: boolean; pageNum?: number; pageSize?: number }): Promise<Pagination> {
    if (!pageHelper) {
      pageHelper = {};
    }
    const pagination = new Pagination();
    if (queryBuilder.getSql().includes('GROUP BY')) {
      const sql = queryBuilder.getSql();
      const selectFields = sql.slice(sql.indexOf('SELECT ') + 7, sql.indexOf(' FROM'));
      const countSql = sql.replace(selectFields, '1');
      const execSql = sql.includes('ORDER BY') ? countSql.substring(0, countSql.indexOf('ORDER BY')) : countSql;
      pagination._totalRows = +(await getManager().query(`select count(1) as 'cnt' from (${execSql}) a`, Object.values(queryBuilder.getParameters())))[0].cnt;
    } else {
      pagination._totalRows = await queryBuilder.getCount();
    }
    pagination._pageSize = pageHelper.pageSize && pageHelper.pageSize > 0 ? +pageHelper.pageSize : 10;
    pagination._totalNum = Math.ceil(pagination._totalRows / pagination._pageSize);
    const pageNum = pageHelper.pageNum && pageHelper.pageNum > 0 ? +pageHelper.pageNum : 1;
    pagination._pageNum = pageNum < pagination._totalNum ? +pageNum : +pagination._totalNum;
    queryBuilder.limit(pagination._pageSize);
    queryBuilder.offset(Number((pagination._pageNum - 1) * pagination._pageSize));
    pagination.data = pageHelper.getRayMany ? await queryBuilder.getRawMany() : await queryBuilder.getMany();
    return pagination;
  }

    /**
     * 原生SQL分页
     * @param sql    sql语句
     * @param entityManager
     * @param parameters    查询参数
     * @param pageHelper    分页参数
     */
    public static async findByPageNativeSql(sql: string, entityManager: EntityManager, parameters?: any[], pageHelper?: { getRayMany?: boolean; pageNum?: number; pageSize?: number }): Promise<Pagination> {
        pageHelper = !pageHelper ? {} : pageHelper;
        parameters = !parameters ? [] : parameters;
        const pagination = new Pagination();

        /** SELECT 在SQL中的位置 */
        let selectIndex = sql.indexOf('SELECT');
        selectIndex = selectIndex === -1 ? sql.indexOf('select') : selectIndex;
        /** FROM 在SQL中的位置 */
        let fromIndex = sql.indexOf('FROM');
        fromIndex = fromIndex === -1 ? sql.indexOf('from') : fromIndex;
        // 判断SQL中是否包含 SELECT/FROM
        if (selectIndex === -1 || fromIndex === -1) {
            throw new RpcException('sql is invalid!');
        }

        if (sql.includes('GROUP BY') || sql.includes('group by') ) { // 分组查询计数
            /** 去掉子查询中多余的排序 */
            const subSelect = sql.includes('ORDER BY') ? sql.substring(0, sql.indexOf('ORDER BY')) : sql;
            pagination._totalRows = +(await getManager().query(`select count(1) as 'cnt' from (${subSelect}) a`, parameters.splice(sql.split('?').length - 1)))[0].cnt;
        } else {
            /** 查询的字段 */
            const selectFields = sql.slice(selectIndex + 6, fromIndex);
            /** 替换查询字段:前后必须要加空格 */
            const countSql = sql.replace(selectFields, ' count(1) as cnt ');
            pagination._totalRows = +(await getManager().query(countSql, parameters.splice(sql.split('?').length - 1)))[0].cnt;
        }
        pagination._pageSize = pageHelper.pageSize && pageHelper.pageSize > 0 ? +pageHelper.pageSize : 10;
        pagination._totalNum = Math.ceil(pagination._totalRows / pagination._pageSize);
        const pageNum = pageHelper.pageNum && pageHelper.pageNum > 0 ? +pageHelper.pageNum : 1;
        pagination._pageNum = pageNum < pagination._totalNum ? +pageNum : +pagination._totalNum;
        // SQL添加分页参数
        sql = sql + ' limit ' + (pagination._pageNum - 1) * pagination._pageSize + ',' + pagination._pageSize;
        pagination.data = await entityManager.query(sql, parameters);
        return pagination;
    }
}
一套目前来说最好的nestjs实战教程,提供QQ长期问答服务. 本人从 08 年到 18 年一直从事于 PHP 的开发。从 18 年开始转向 Typescript+React+Nestjs 的技术栈。目前来说 React 应该是一个非常好用的前端框架,生态非常完善,并且十分灵活简单。Nestjs 则是 Node.js 中唯一且无敌存在的后端 web 框架。因为我个人从事这套技术栈开发已经 4 年多,所以颇有心得,做了这套 React18 视频教程和 Nestjs 实战视频教程。现在视频教程也是刚刚开始做了一部分,还在持续更新。使用 TS 全栈开发可以基本涵盖各种平台的方方面面,比如开发桌面应用的 Electron, 开发小程序的 Taro, 开发 Spa 中后台的 React,开发 SSR 网站的 next.js,开发移动应用的 React Native, 开发 CLI 的 Yargs, 以及开发后端Nestjs。基本学会一套,全面够用,再加上 Monorepo 组织结构,一个仓库所有平台都可以搞定。 包以下知识点 - 掌握Nestjs框架的依赖注入,模块,提供者,生命周期等概念- 掌握DTO数据验证,响应序列化,异常过滤器等常用功能- 学会编写一些常用的class-validator验证约束- 熟练掌握Typeorm以及NestjsTypeorm结合开发- 学会整合Swagger输出Open API文档- 掌握TS装饰器以及反射元数据的定义和使用- 编写一些数据库相关的数据验证约束(比如树形表的同级别某字段唯一验证等)- 学会通过继承并魔改Nestjs源码编写自定义的全局验证器- 可以编写自定义的配置系统以及核心功能包- 学会自定义的代码组织方式(比如教程中我把默认的Nestjs应用改成Util+PluginModule模式)- 掌握编写一些常用的Util仓库(比如数据库,Redis,Restful)- 利用Yargs结合魔改后的框架可以编写一些自定义CLI命令(比如数据迁移,数据填充等)- 掌握如何利用阿里云/腾讯云推送邮件和短信- 掌握使用消息列队(MQ)的方式异步推送邮件和短信- 掌握守卫原理以及编写一些用户验证的守卫- 编写一个完善的用户系统(JWT认证,短信/邮件登录,短信/邮件注册,找回密码,绑定手机和邮箱等)- 熟练地通过编写装饰器去实现一些常用的功能- 通过SSE,WebSockets实现用户的上线,下线以及消息实时推送,消息广播等- 学会使用云存储来上传文件- 学会大文件断点雪川- 实现RBAC的权限系统- 理解请求范围概念以及性能方便的考量- 自己构建配置系统,实现配置验证以及通过YAML或数据库来进行动态配置- 通过适用Vscode进行Debug以及编写Jest测试来提升开发效率与程序的可用性- 学会使用Node来编写自定义的CLI命令- 利用NestCURD进行快速开发- 学会Graphql替代Restful写API- 使用Mongodb替代关系型数据库- 掌握一些常用的打包工具,比如通过ncc打包成单文件,通过pack打包成二进制等- 学会一些常用的部署方式,比如通过nginx+pm2反向代理部署,devops自动化CI,CD等- 学会使用pnpm workspaces来使用monreopo组织代码
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值