乐闻世界logo
搜索文章和话题

TypeORM 的 QueryBuilder 如何使用?包括复杂查询、关联查询、分页排序等高级功能

2月18日 22:19

QueryBuilder 是 TypeORM 中最强大、最灵活的查询工具,它允许开发者构建复杂的 SQL 查询,同时保持类型安全和可读性。

QueryBuilder 基础用法

创建 QueryBuilder

typescript
import { DataSource } from 'typeorm'; const dataSource = new DataSource(/* 配置 */); // 方式1: 通过 Repository 创建 const userRepository = dataSource.getRepository(User); const queryBuilder = userRepository.createQueryBuilder('user'); // 方式2: 通过 DataSource 创建 const queryBuilder = dataSource.createQueryBuilder(User, 'user');

基本查询操作

typescript
// 查询所有用户 const users = await dataSource .createQueryBuilder('user') .getMany(); // 查询单个用户 const user = await dataSource .createQueryBuilder('user') .where('user.id = :id', { id: 1 }) .getOne(); // 查询并计数 const count = await dataSource .createQueryBuilder('user') .getCount();

条件查询

Where 子句

typescript
// 简单条件 const users = await dataSource .createQueryBuilder('user') .where('user.age > :age', { age: 18 }) .getMany(); // 多个条件 (AND) const users = await dataSource .createQueryBuilder('user') .where('user.age > :age', { age: 18 }) .andWhere('user.isActive = :isActive', { isActive: true }) .getMany(); // OR 条件 const users = await dataSource .createQueryBuilder('user') .where('user.age > :age', { age: 18 }) .orWhere('user.role = :role', { role: 'admin' }) .getMany(); // 复杂条件组合 const users = await dataSource .createQueryBuilder('user') .where( new Brackets(qb => { qb.where('user.age > :age', { age: 18 }) .orWhere('user.role = :role', { role: 'admin' }); }) ) .andWhere('user.isActive = :isActive', { isActive: true }) .getMany();

操作符

typescript
import { Like, Between, In, MoreThan, LessThan } from 'typeorm'; // LIKE 查询 const users = await dataSource .createQueryBuilder('user') .where('user.name LIKE :name', { name: '%John%' }) .getMany(); // 或者使用 Like 操作符 const users = await dataSource .createQueryBuilder('user') .where('user.name = :name', { name: Like('%John%') }) .getMany(); // BETWEEN 查询 const users = await dataSource .createQueryBuilder('user') .where('user.age = :age', { age: Between(18, 30) }) .getMany(); // IN 查询 const users = await dataSource .createQueryBuilder('user') .where('user.id IN :ids', { ids: [1, 2, 3] }) .getMany(); // 或者使用 In 操作符 const users = await dataSource .createQueryBuilder('user') .where('user.id = :ids', { ids: In([1, 2, 3]) }) .getMany(); // 比较操作符 const users = await dataSource .createQueryBuilder('user') .where('user.age = :age', { age: MoreThan(18) }) .andWhere('user.score = :score', { score: LessThan(100) }) .getMany();

关联查询

Left Join 和 Inner Join

typescript
// Left Join (包含没有关联数据的记录) const users = await dataSource .createQueryBuilder('user') .leftJoinAndSelect('user.posts', 'post') .where('user.id = :id', { id: 1 }) .getMany(); // Inner Join (只包含有关联数据的记录) const users = await dataSource .createQueryBuilder('user') .innerJoinAndSelect('user.posts', 'post') .where('user.id = :id', { id: 1 }) .getMany(); // 多层关联 const users = await dataSource .createQueryBuilder('user') .leftJoinAndSelect('user.posts', 'post') .leftJoinAndSelect('post.comments', 'comment') .leftJoinAndSelect('comment.author', 'commentAuthor') .getMany();

Join 条件

typescript
const users = await dataSource .createQueryBuilder('user') .leftJoin('user.posts', 'post', 'post.status = :status', { status: 'published' }) .addSelect(['post.title', 'post.createdAt']) .getMany();

排序和分页

排序

typescript
// 单字段排序 const users = await dataSource .createQueryBuilder('user') .orderBy('user.createdAt', 'DESC') .getMany(); // 多字段排序 const users = await dataSource .createQueryBuilder('user') .orderBy('user.createdAt', 'DESC') .addOrderBy('user.name', 'ASC') .getMany(); // 随机排序 (MySQL) const users = await dataSource .createQueryBuilder('user') .orderBy('RAND()') .getMany();

分页

typescript
// 基本分页 const page = 1; const pageSize = 10; const users = await dataSource .createQueryBuilder('user') .skip((page - 1) * pageSize) .take(pageSize) .getMany(); // 获取总数和分页数据 const [users, total] = await dataSource .createQueryBuilder('user') .skip((page - 1) * pageSize) .take(pageSize) .getManyAndCount(); console.log(`Total: ${total}, Page: ${page}, PageSize: ${pageSize}`);

聚合查询

Group By 和 Having

typescript
// 按角色分组统计用户数 const result = await dataSource .createQueryBuilder('user') .select('user.role', 'role') .addSelect('COUNT(*)', 'count') .groupBy('user.role') .getRawMany(); // 使用 Having 过滤分组 const result = await dataSource .createQueryBuilder('user') .select('user.role', 'role') .addSelect('COUNT(*)', 'count') .groupBy('user.role') .having('COUNT(*) > :minCount', { minCount: 5 }) .getRawMany();

聚合函数

typescript
// 统计总数 const count = await dataSource .createQueryBuilder('user') .select('COUNT(*)', 'count') .getRawOne(); // 计算平均值 const avgAge = await dataSource .createQueryBuilder('user') .select('AVG(user.age)', 'avgAge') .getRawOne(); // 求和 const totalScore = await dataSource .createQueryBuilder('user') .select('SUM(user.score)', 'totalScore') .getRawOne(); // 最大值和最小值 const result = await dataSource .createQueryBuilder('user') .select('MAX(user.age)', 'maxAge') .addSelect('MIN(user.age)', 'minAge') .getRawOne();

子查询

使用 SubQueryFactory

typescript
import { SubQueryFactory } from 'typeorm'; const users = await dataSource .createQueryBuilder('user') .where((qb: SelectQueryBuilder<User>) => { const subQuery = qb .subQuery() .select('post.userId') .from(Post, 'post') .where('post.title LIKE :title', { title: '%TypeORM%' }) .getQuery(); return 'user.id IN ' + subQuery; }) .setParameter('title', '%TypeORM%') .getMany();

使用 EXISTS

typescript
const users = await dataSource .createQueryBuilder('user') .where((qb: SelectQueryBuilder<User>) => { const subQuery = qb .subQuery() .select('1') .from(Post, 'post') .where('post.userId = user.id') .getQuery(); return 'EXISTS ' + subQuery; }) .getMany();

更新和删除

更新操作

typescript
// 简单更新 await dataSource .createQueryBuilder(User, 'user') .update() .set({ name: 'Updated Name' }) .where('id = :id', { id: 1 }) .execute(); // 条件更新 await dataSource .createQueryBuilder(User, 'user') .update() .set({ isActive: false }) .where('user.lastLoginAt < :date', { date: new Date('2024-01-01') }) .execute(); // 基于子查询的更新 await dataSource .createQueryBuilder(User, 'user') .update() .set({ score: () => 'score + 10' }) .where('user.id IN :ids', { ids: [1, 2, 3] }) .execute();

删除操作

typescript
// 简单删除 await dataSource .createQueryBuilder(User, 'user') .delete() .where('id = :id', { id: 1 }) .execute(); // 条件删除 await dataSource .createQueryBuilder(User, 'user') .delete() .where('user.createdAt < :date', { date: new Date('2023-01-01') }) .andWhere('user.isActive = :isActive', { isActive: false }) .execute();

高级特性

原生 SQL

typescript
const users = await dataSource .createQueryBuilder(User, 'user') .where('user.id = :id', { id: 1 }) .andWhere('JSON_CONTAINS(user.preferences, :preferences)', { preferences: JSON.stringify({ theme: 'dark' }) }) .getMany();

缓存

typescript
const users = await dataSource .createQueryBuilder('user') .where('user.isActive = :isActive', { isActive: true }) .cache(60000) // 缓存 60 秒 .getMany();

事务

typescript
await dataSource.transaction(async transactionalEntityManager => { const queryRunner = transactionalEntityManager.queryRunner; await queryRunner.manager .createQueryBuilder(User, 'user') .insert() .values({ name: 'John', email: 'john@example.com' }) .execute(); await queryRunner.manager .createQueryBuilder(Post, 'post') .insert() .values({ title: 'New Post', authorId: 1 }) .execute(); });

性能优化建议

  1. 避免 N+1 查询: 使用 leftJoinAndSelect 一次性加载关联数据
  2. 只选择需要的字段: 使用 select() 明确指定需要的列
  3. 合理使用索引: 为常用查询条件添加数据库索引
  4. 使用缓存: 对不常变化的数据启用查询缓存
  5. 限制返回结果: 使用 take()skip() 实现分页
  6. 监控查询性能: 使用 getQuery()getSql() 查看生成的 SQL

QueryBuilder 是 TypeORM 中最强大的查询工具,掌握它的使用可以让你构建出高效、灵活的数据库查询。

标签:TypeORM