查询构建器(QueryBuilder)
原创2026/3/18大约 5 分钟
QueryBuilder 是 TypeORM 中最灵活的查询工具,支持链式调用构建复杂 SQL,兼顾可读性和灵活性,是原生 SQL 的“类型安全替代方案”,尤其适合多表联查、复杂条件筛选、聚合统计等场景。
一、QueryBuilder 基础用法
1. 初始化 QueryBuilder
QueryBuilder 可从 Repository/EntityManager 初始化,核心方法为 createQueryBuilder:
import { User } from "./entity/User";
import { AppDataSource } from "./data-source";
const userRepository = AppDataSource.getRepository(User);
// 方式1:从 Repository 初始化(推荐,自动关联实体)
const queryBuilder = userRepository.createQueryBuilder("u");
// "u" 是实体别名,后续查询中通过别名引用字段
// 方式2:从 EntityManager 初始化(跨实体场景)
const queryBuilder = AppDataSource.manager.createQueryBuilder(User, "u");
// 方式3:直接构建原生 SQL 查询(无实体关联)
const rawQueryBuilder = AppDataSource.createQueryBuilder().select("*").from("sys_user", "u");2. 核心基础方法(select/from/where/orderBy/limit/offset)
// 基础查询:查询用户表指定字段,带条件、排序、分页
const users = await userRepository
.createQueryBuilder("u") // 别名 u 代表 User 实体
.select(["u.id", "u.username", "u.nickname"]) // 仅查询指定字段
.addSelect("u.age") // 追加字段
.from(User, "u") // 显式指定实体(可省略,Repository 已关联)
.where("u.age > :age", { age: 18 }) // 条件(参数绑定防注入)
.andWhere("u.deletedAt IS NULL") // 追加 AND 条件
.orWhere("u.nickname LIKE :nickname", { nickname: "%张三%" }) // 追加 OR 条件
.orderBy("u.createTime", "DESC") // 排序:字段 + 方向(ASC/DESC)
.addOrderBy("u.age", "ASC") // 追加排序
.skip(0) // 跳过条数(分页:(页码-1)*每页条数)
.take(10) // 取条数(每页条数)
.getMany(); // 执行查询,返回多条结果
// 单条结果查询
const user = await userRepository
.createQueryBuilder("u")
.where("u.id = :id", { id: 1 })
.getOne(); // 无结果返回 null
// 统计条数
const count = await userRepository
.createQueryBuilder("u")
.where("u.age > :age", { age: 18 })
.getCount();二、复杂条件查询
1. 运算符与逻辑组合
QueryBuilder 支持所有 SQL 运算符,通过字符串模板 + 参数绑定实现复杂条件:
import { MoreThan, LessThan, Like, In, Not } from "typeorm";
// 方式1:字符串模板(推荐,更灵活)
const users = await userRepository
.createQueryBuilder("u")
.where("u.age BETWEEN :minAge AND :maxAge", { minAge: 18, maxAge: 30 })
.andWhere("u.username IN (:...usernames)", { usernames: ["zhangsan", "lisi"] }) // 数组参数(IN 条件)
.andWhere("u.nickname IS NOT NULL")
.getMany();
// 方式2:结合 TypeORM 运算符(类型安全)
const users = await userRepository
.createQueryBuilder("u")
.where({
age: MoreThan(18),
username: Not(In(["admin", "root"])),
nickname: Like("%王%"),
})
.getMany();
// 分组条件(AND/OR 嵌套)
const users = await userRepository
.createQueryBuilder("u")
.where("u.age > 18")
.andWhere(new Brackets((qb) => {
qb.where("u.nickname LIKE :nick1", { nick1: "%张%" })
.orWhere("u.nickname LIKE :nick2", { nick2: "%李%" });
}))
.getMany();
// 生成 SQL:WHERE age > 18 AND (nickname LIKE '%张%' OR nickname LIKE '%李%')2. IN/NOT IN/ LIKE/BETWEEN 专项示例
// IN 条件(数组参数)
const users = await userRepository
.createQueryBuilder("u")
.where("u.id IN (:...ids)", { ids: [1, 2, 3, 4] })
.getMany();
// LIKE 模糊查询
const users = await userRepository
.createQueryBuilder("u")
.where("u.username LIKE :username", { username: "zhang%" }) // 前缀匹配
.orWhere("u.nickname LIKE :nickname", { nickname: "%三%" }) // 包含匹配
.getMany();
// BETWEEN 范围查询
const users = await userRepository
.createQueryBuilder("u")
.where("u.createTime BETWEEN :start AND :end", {
start: "2024-01-01",
end: "2024-12-31",
})
.getMany();三、联表查询(leftJoin/innerJoin)
联表是 QueryBuilder 的核心优势,支持一对一、一对多、多对多关联查询:
1. 基础联表示例(一对一/一对多)
假设有 User(用户)和 Profile(用户资料)一对一关联,User 和 Order(订单)一对多关联:
// 左连接(LEFT JOIN):查询用户 + 关联资料(无资料也返回用户)
const usersWithProfile = await userRepository
.createQueryBuilder("u")
.leftJoin("u.profile", "p") // "u.profile" 是 User 实体中关联 Profile 的属性
.select(["u.id", "u.username", "p.gender", "p.address"])
.getMany();
// 内连接(INNER JOIN):仅返回有资料的用户
const usersWithProfile = await userRepository
.createQueryBuilder("u")
.innerJoin("u.profile", "p")
.select(["u", "p"])
.getMany();
// 联表 + 条件:查询用户 + 关联的未取消订单
const usersWithOrders = await userRepository
.createQueryBuilder("u")
.leftJoinAndSelect("u.orders", "o", "o.status != :status", { status: "canceled" })
.where("u.id = :id", { id: 1 })
.getOne();2. 多对多联表
假设有 User 和 Role 多对多关联(中间表 user_role):
const usersWithRoles = await userRepository
.createQueryBuilder("u")
.leftJoinAndSelect("u.roles", "r") // 自动关联多对多中间表
.where("r.code = :code", { code: "admin" }) // 筛选有管理员角色的用户
.getMany();四、聚合函数与分组(count/sum/avg/max/min + groupBy/having)
1. 聚合函数示例
// 统计:用户总数、平均年龄、最大年龄、最小年龄
const stats = await userRepository
.createQueryBuilder("u")
.select("COUNT(u.id)", "total")
.addSelect("AVG(u.age)", "avgAge")
.addSelect("MAX(u.age)", "maxAge")
.addSelect("MIN(u.age)", "minAge")
.where("u.deletedAt IS NULL")
.getRawOne(); // getRawOne 返回原生对象({ total: 100, avgAge: 25, ... })
// 按字段分组统计:按年龄分组,统计每个年龄的用户数
const ageGroup = await userRepository
.createQueryBuilder("u")
.select("u.age", "age")
.addSelect("COUNT(u.id)", "userCount")
.groupBy("u.age")
.having("COUNT(u.id) > :count", { count: 5 }) // 筛选分组后结果(仅年龄组用户数>5)
.orderBy("userCount", "DESC")
.getRawMany();五、子查询
QueryBuilder 支持嵌套子查询,实现复杂的多层筛选:
// 示例1:WHERE 中的子查询(查询有订单的用户)
const usersWithOrders = await userRepository
.createQueryBuilder("u")
.where("EXISTS (SELECT 1 FROM `order` o WHERE o.userId = u.id)")
.getMany();
// 示例2:FROM 中的子查询(先筛选订单,再关联用户)
const subQuery = AppDataSource.createQueryBuilder()
.select("o.userId")
.from("order", "o")
.where("o.amount > :amount", { amount: 100 })
.distinct(true); // 去重
const users = await userRepository
.createQueryBuilder("u")
.where(`u.id IN (${subQuery.getQuery()})`)
.setParameters(subQuery.getParameters()) // 继承子查询参数
.getMany();六、参数绑定(防 SQL 注入)
参数绑定是 QueryBuilder 安全的核心,禁止手动拼接 SQL 字符串:
// 正确方式1:命名参数(推荐)
const username = "zhangsan' OR 1=1 -- "; // 恶意输入
const user = await userRepository
.createQueryBuilder("u")
.where("u.username = :username", { username }) // 自动转义,防注入
.getOne();
// 正确方式2:位置参数
const user = await userRepository
.createQueryBuilder("u")
.where("u.username = ?", [username])
.getOne();
// 数组参数(IN 条件)
const usernames = ["zhangsan", "lisi"];
const users = await userRepository
.createQueryBuilder("u")
.where("u.username IN (:...usernames)", { usernames }) // 数组展开
.getMany();至此,本章节的学习就到此结束了,如有疑惑,可对接技术客服进行相关咨询。