# 使用 Query Builder 查询 - [使用 Query Builder 查询](#%E4%BD%BF%E7%94%A8-query-builder-%E6%9F%A5%E8%AF%A2) - [什么是 `QueryBuilder`](#%E4%BB%80%E4%B9%88%E6%98%AF-querybuilder) - [如何创建和使用 `QueryBuilder`](#%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E5%92%8C%E4%BD%BF%E7%94%A8-querybuilder) - [使用 `QueryBuilder` 获取值](#%E4%BD%BF%E7%94%A8-querybuilder-%E8%8E%B7%E5%8F%96%E5%80%BC) - [什么是别名?](#%E4%BB%80%E4%B9%88%E6%98%AF%E5%88%AB%E5%90%8D) - [使用参数来转义数据](#%E4%BD%BF%E7%94%A8%E5%8F%82%E6%95%B0%E6%9D%A5%E8%BD%AC%E4%B9%89%E6%95%B0%E6%8D%AE) - [添加 `WHERE` 表达式](#%E6%B7%BB%E5%8A%A0-where-%E8%A1%A8%E8%BE%BE%E5%BC%8F) - [添加 `HAVING` 表达式](#%E6%B7%BB%E5%8A%A0-having-%E8%A1%A8%E8%BE%BE%E5%BC%8F) - [添加 `ORDER BY` 表达式](#%E6%B7%BB%E5%8A%A0-order-by-%E8%A1%A8%E8%BE%BE%E5%BC%8F) - [添加 `GROUP BY` 表达式](#%E6%B7%BB%E5%8A%A0-group-by-%E8%A1%A8%E8%BE%BE%E5%BC%8F) - [添加 `LIMIT` 表达式](#%E6%B7%BB%E5%8A%A0-limit-%E8%A1%A8%E8%BE%BE%E5%BC%8F) - [添加 `OFFSET` 表达式](#%E6%B7%BB%E5%8A%A0-offset-%E8%A1%A8%E8%BE%BE%E5%BC%8F) - [联查](#%E8%81%94%E6%9F%A5) - [内联 和 左联](#%E5%86%85%E8%81%94-%E5%92%8C-%E5%B7%A6%E8%81%94) - [不使用条件的联查](#%E4%B8%8D%E4%BD%BF%E7%94%A8%E6%9D%A1%E4%BB%B6%E7%9A%84%E8%81%94%E6%9F%A5) - [联查任何实体或表](#%E8%81%94%E6%9F%A5%E4%BB%BB%E4%BD%95%E5%AE%9E%E4%BD%93%E6%88%96%E8%A1%A8) - [联查和映射功能](#%E8%81%94%E6%9F%A5%E5%92%8C%E6%98%A0%E5%B0%84%E5%8A%9F%E8%83%BD) - [获取生成的 sql 查询语句](#%E8%8E%B7%E5%8F%96%E7%94%9F%E6%88%90%E7%9A%84-sql-%E6%9F%A5%E8%AF%A2%E8%AF%AD%E5%8F%A5) - [获得原始结果](#%E8%8E%B7%E5%BE%97%E5%8E%9F%E5%A7%8B%E7%BB%93%E6%9E%9C) - [流数据](#%E6%B5%81%E6%95%B0%E6%8D%AE) - [使用分页](#%E4%BD%BF%E7%94%A8%E5%88%86%E9%A1%B5) - [加锁](#%E5%8A%A0%E9%94%81) - [查询部分字段](#%E6%9F%A5%E8%AF%A2%E9%83%A8%E5%88%86%E5%AD%97%E6%AE%B5) - [使用子查询](#%E4%BD%BF%E7%94%A8%E5%AD%90%E6%9F%A5%E8%AF%A2) - [隐藏列](#%E9%9A%90%E8%97%8F%E5%88%97) ## 什么是 `QueryBuilder` `QueryBuilder`是 TypeORM 最强大的功能之一 ,它允许你使用优雅便捷的语法构建 SQL 查询,执行并获得自动转换的实体。 `QueryBuilder`的简单示例: ```typescript const firstUser = await connection .getRepository(User) .createQueryBuilder("user") .where("user.id = :id", { id: 1 }) .getOne(); ``` 它将生成以下 SQL 查询: ```sql SELECT user.id as userId, user.firstName as userFirstName, user.lastName as userLastName FROM users user WHERE user.id = 1 ``` 然后返回一个 `User` 实例: ``` User { id: 1, firstName: "Timber", lastName: "Saw" } ``` ## 如何创建和使用 `QueryBuilder` 有几种方法可以创建`Query Builder`: - 使用 connection: ```typescript import { getConnection } from "typeorm"; const user = await getConnection() .createQueryBuilder() .select("user") .from(User, "user") .where("user.id = :id", { id: 1 }) .getOne(); ``` - 使用 entity manager: ```typescript import { getManager } from "typeorm"; const user = await getManager() .createQueryBuilder(User, "user") .where("user.id = :id", { id: 1 }) .getOne(); ``` - 使用 repository: ```typescript import { getRepository } from "typeorm"; const user = await getRepository(User) .createQueryBuilder("user") .where("user.id = :id", { id: 1 }) .getOne(); ``` 有 5 种不同的`QueryBuilder`类型可用: - `SelectQueryBuilder` - 用于构建和执行`SELECT`查询。 例如: ```typescript import { getConnection } from "typeorm"; const user = await getConnection() .createQueryBuilder() .select("user") .from(User, "user") .where("user.id = :id", { id: 1 }) .getOne(); ``` - `InsertQueryBuilder` - 用于构建和执行`INSERT`查询。 例如: ```typescript import { getConnection } from "typeorm"; await getConnection() .createQueryBuilder() .insert() .into(User) .values([{ firstName: "Timber", lastName: "Saw" }, { firstName: "Phantom", lastName: "Lancer" }]) .execute(); ``` - `UpdateQueryBuilder` - 用于构建和执行`UPDATE`查询。 例如: ```typescript import { getConnection } from "typeorm"; await getConnection() .createQueryBuilder() .update(User) .set({ firstName: "Timber", lastName: "Saw" }) .where("id = :id", { id: 1 }) .execute(); ``` - `DeleteQueryBuilder` - 用于构建和执行`DELETE`查询。 例如: ```typescript import { getConnection } from "typeorm"; await getConnection() .createQueryBuilder() .delete() .from(User) .where("id = :id", { id: 1 }) .execute(); ``` - `RelationQueryBuilder` - 用于构建和执行特定于关系的操作[TBD]。 你可以在其中切换任何不同类型的查询构建器,一旦执行,则将获得一个新的查询构建器实例(与所有其他方法不同)。 ## 使用 `QueryBuilder` 获取值 要从数据库中获取单个结果,例如通过 id 或 name 获取用户,必须使用`getOne`: ```typescript const timber = await getRepository(User) .createQueryBuilder("user") .where("user.id = :id OR user.name = :name", { id: 1, name: "Timber" }) .getOne(); ``` 要从数据库中获取多个结果,例如,要从数据库中获取所有用户,请使用`getMany`: ```typescript const users = await getRepository(User) .createQueryBuilder("user") .getMany(); ``` 使用查询构建器查询可以获得两种类型的结果:**entities** 或 **raw results**。 大多数情况下,你只需要从数据库中选择真实实体,例如 users。 为此,你可以使用`getOne`和`getMany`。 但有时你需要选择一些特定的数据,比方说所有*sum of all user photos*。 此数据不是实体,它称为原始数据。 要获取原始数据,请使用`getRawOne`和`getRawMany`。 例如: ```typescript const { sum } = await getRepository(User) .createQueryBuilder("user") .select("SUM(user.photosCount)", "sum") .where("user.id = :id", { id: 1 }) .getRawOne(); ``` ```typescript const photosSums = await getRepository(User) .createQueryBuilder("user") .select("user.id") .addSelect("SUM(user.photosCount)", "sum") .where("user.id = :id", { id: 1 }) .getRawMany(); // 结果会像这样: [{ id: 1, sum: 25 }, { id: 2, sum: 13 }, ...] ``` ## 什么是别名? 我们使用`createQueryBuilder("user")`。 但什么是"user"? 它只是一个常规的 SQL 别名。 我们在任何地方都使用别名,除非我们处理选定的数据。 `createQueryBuilder("user")` 相当于: ```typescript createQueryBuilder() .select("user") .from(User, "user"); ``` 这会生成以下 sql 查询: ```sql SELECT ... FROM users user ``` 在这个 SQL 查询中,`users`是表名,`user`是我们分配给该表的别名。 稍后我们使用此别名来访问表: ```typescript createQueryBuilder() .select("user") .from(User, "user") .where("user.name = :name", { name: "Timber" }); ``` 以上代码会生成如下 SQL 语句: ```sql SELECT ... FROM users user WHERE user.name = 'Timber' ``` 看到了吧,我们使用了在创建查询构建器时分配的`user`别名来使用 users 表。 一个查询构建器不限于一个别名,它们可以有多个别名。 每个选择都可以有自己的别名,你可以选择多个有自己别名的表,你可以使用自己的别名连接多个表。 你也可以使用这些别名来访问选择的表(或正在选择的数据)。 ## 使用参数来转义数据 我们使用了`where("user.name = :name", { name: "Timber" })`. `{name:“Timber”}`代表什么? 这是我们用来阻止 SQL 注入的参数。 我们可以写:`where("user.name ='"+ name +"')`,但是这不安全,因为有可能被 SQL 注入。 安全的方法是使用这种特殊的语法:`where("user.name =name",{name:"Timber"})`,其中`name`是参数名,值在对象中指定: `{name:"Timber"}`。 ```typescript .where("user.name = :name", { name: "Timber" }) ``` 是下面的简写: ```typescript .where("user.name = :name") .setParameter("name", "Timber") ``` ## 添加 `WHERE` 表达式 添加 `WHERE` 表达式就像: ```typescript createQueryBuilder("user").where("user.name = :name", { name: "Timber" }); ``` 将会生成以下 SQL 语句: ```sql SELECT ... FROM users user WHERE user.name = 'Timber' ``` 你可以将 `AND` 添加到现有的 `WHERE` 表达式中: ```typescript createQueryBuilder("user") .where("user.firstName = :firstName", { firstName: "Timber" }) .andWhere("user.lastName = :lastName", { lastName: "Saw" }); ``` 将会生成以下 SQL 语句: ```sql SELECT ... FROM users user WHERE user.firstName = 'Timber' AND user.lastName = 'Saw' ``` 你也可以添加 `OR` 添加到现有的 `WHERE` 表达式中: ```typescript createQueryBuilder("user") .where("user.firstName = :firstName", { firstName: "Timber" }) .orWhere("user.lastName = :lastName", { lastName: "Saw" }); ``` 将会生成以下 SQL 语句: ```sql SELECT ... FROM users user WHERE user.firstName = 'Timber' OR user.lastName = 'Saw' ``` 你可以使用`Brackets`将复杂的`WHERE`表达式添加到现有的`WHERE`中: ```typescript createQueryBuilder("user") .where("user.registered = :registered", { registered: true }) .andWhere(new Brackets(qb => { qb.where("user.firstName = :firstName", { firstName: "Timber" }) .orWhere("user.lastName = :lastName", { lastName: "Saw" }) ``` 将会生成以下 SQL 语句: ```sql SELECT ... FROM users user WHERE user.registered = true AND (user.firstName = 'Timber' OR user.lastName = 'Saw') ``` 你可以根据需要组合尽可能多的`AND`和`OR`表达式。 如果你多次使用`.where`,你将覆盖所有以前的`WHERE`表达式。 注意:小心`orWhere` - 如果你使用带有`AND`和`OR`表达式的复杂表达式,请记住他们将无限制的叠加。 有时你只需要创建一个 where 字符串,避免使用`orWhere`。 ## 添加 `HAVING` 表达式 添加`HAVING`表达式很简单: ```typescript createQueryBuilder("user").having("user.name = :name", { name: "Timber" }); ``` 将会生成以下 SQL 语句: ```sql SELECT ... FROM users user HAVING user.name = 'Timber' ``` 你可以添加 `AND` 到已经存在的 `HAVING` 表达式中: ```typescript createQueryBuilder("user") .having("user.firstName = :firstName", { firstName: "Timber" }) .andHaving("user.lastName = :lastName", { lastName: "Saw" }); ``` 将会生成以下 SQL 语句: ```sql SELECT ... FROM users user HAVING user.firstName = 'Timber' AND user.lastName = 'Saw' ``` 你可以添加 `OR` 到已经存在的 `HAVING` 表达式中: ```typescript createQueryBuilder("user") .having("user.firstName = :firstName", { firstName: "Timber" }) .orHaving("user.lastName = :lastName", { lastName: "Saw" }); ``` 将会生成以下 SQL 语句: ```sql SELECT ... FROM users user HAVING user.firstName = 'Timber' OR user.lastName = 'Saw' ``` 你可以根据需要组合尽可能多的`AND`和`OR`表达式。 如果使用多个`.having`,后面的将覆盖所有之前的`HAVING`表达式。 ## 添加 `ORDER BY` 表达式 添加 `ORDER BY` 很简单: ```typescript createQueryBuilder("user").orderBy("user.id"); ``` 将会生成一下 SQL 语句: ```sql SELECT ... FROM users user ORDER BY user.id ``` 你可以将排序方向从升序更改为降序(或反之亦然): ```typescript createQueryBuilder("user").orderBy("user.id", "DESC"); createQueryBuilder("user").orderBy("user.id", "ASC"); ``` 也可以添加多个排序条件: ```typescript createQueryBuilder("user") .orderBy("user.name") .addOrderBy("user.id"); ``` 还可以使用排序字段作为一个 map: ```typescript createQueryBuilder("user").orderBy({ "user.name": "ASC", "user.id": "DESC" }); ``` 如果你使用了多个`.orderBy`,后面的将覆盖所有之前的`ORDER BY`表达式。 ## 添加 `GROUP BY` 表达式 添加 `GROUP BY` 表达式很简单: ```typescript createQueryBuilder("user").groupBy("user.id"); ``` 将会生成以下 SQL 语句: ```sql SELECT ... FROM users user GROUP BY user.id ``` 如果要使用更多 group-by, 则可以使用 c`addGroupBy`: ```typescript createQueryBuilder("user") .groupBy("user.name") .addGroupBy("user.id"); ``` 如果使用了多个`.groupBy` ,则后面的将会覆盖之前所有的 `ORDER BY` 表达式。 ## 添加 `LIMIT` 表达式 添加 `LIMIT` 表达式很简单: ```typescript createQueryBuilder("user").limit(10); ``` 将会生成以下 SQL 语句: ```sql SELECT ... FROM users user LIMIT 10 ``` 生成的 SQL 查询取决于数据库的类型(SQL,mySQL,Postgres 等)。 注意:如果你使用带有连接或子查询的复杂查询,LIMIT 可能无法正常工作。 如果使用分页,建议使用`take`代替。 ## 添加 `OFFSET` 表达式 添加 SQL`OFFSET`表达式很简单: ```typescript createQueryBuilder("user").offset(10); ``` 将会生成以下 SQL 语句: ```sql SELECT ... FROM users user OFFSET 10 ``` 生成的 SQL 查询取决于数据库的类型(SQL,mySQL,Postgres 等)。 注意:如果你使用带有连接或子查询的复杂查询,OFFSET 可能无法正常工作。 如果使用分页,建议使用`skip`代替。 ## 联查 假设有以下实体: ```typescript import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; import { Photo } from "./Photo"; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @OneToMany(type => Photo, photo => photo.user) photos: Photo[]; } ``` ```typescript import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; import { User } from "./User"; @Entity() export class Photo { @PrimaryGeneratedColumn() id: number; @Column() url: string; @ManyToOne(type => User, user => user.photos) user: User; } ``` 现在让我们假设你要用用户"Timber"加载他所有的 photos: ```typescript const user = await createQueryBuilder("user") .leftJoinAndSelect("user.photos", "photo") .where("user.name = :name", { name: "Timber" }) .getOne(); ``` 你将会得到以下结果: ```typescript { id: 1, name: "Timber", photos: [{ id: 1, url: "me-with-chakram.jpg" }, { id: 2, url: "me-with-trees.jpg" }] } ``` 你可以看到`leftJoinAndSelect`自动加载了所有 Timber 的 photos。 第一个参数是你要加载的关系,第二个参数是你为此关系的表分配的别名。 你可以在查询构建器中的任何位置使用此别名。 例如,让我们获得所有未删除的 Timber 的 photos。 ```typescript const user = await createQueryBuilder("user") .leftJoinAndSelect("user.photos", "photo") .where("user.name = :name", { name: "Timber" }) .andWhere("photo.isRemoved = :isRemoved", { isRemoved: false }) .getOne(); ``` 将会生成以下 SQL 查询: ```sql SELECT user.*, photo.* FROM users user LEFT JOIN photos photo ON photo.user = user.id WHERE user.name = 'Timber' AND photo.isRemoved = FALSE ``` 你还可以向连接表达式添加条件,而不是使用"where": ```typescript const user = await createQueryBuilder("user") .leftJoinAndSelect("user.photos", "photo", "photo.isRemoved = :isRemoved", { isRemoved: false }) .where("user.name = :name", { name: "Timber" }) .getOne(); ``` 这将生成以下 sql 查询: ```sql SELECT user.*, photo.* FROM users user LEFT JOIN photos photo ON photo.user = user.id AND photo.isRemoved = FALSE WHERE user.name = 'Timber' ``` ## 内联 和 左联 如果你想使用`INNER JOIN`而不是`LEFT JOIN`,只需使用`innerJoinAndSelect`: ```typescript const user = await createQueryBuilder("user") .innerJoinAndSelect("user.photos", "photo", "photo.isRemoved = :isRemoved", { isRemoved: false }) .where("user.name = :name", { name: "Timber" }) .getOne(); ``` This will generate: ```sql SELECT user.*, photo.* FROM users user INNER JOIN photos photo ON photo.user = user.id AND photo.isRemoved = FALSE WHERE user.name = 'Timber' ``` `LEFT JOIN`和`INNER JOIN`之间的区别在于,如果没有任何 photos,`INNER JOIN`将不会返回 user。 即使没有 photos,`LEFT JOIN`也会返回 user。 要了解有关不同连接类型的更多信息,请参阅 [SQL 文档](https://msdn.microsoft.com/en-us/library/zt8wzxy4.aspx). ## 不使用条件的联查 你可以在不使用条件的情况下联查数据。 要做到这一点,使用`leftJoin`或`innerJoin`: ```typescript const user = await createQueryBuilder("user") .innerJoin("user.photos", "photo") .where("user.name = :name", { name: "Timber" }) .getOne(); ``` 将会生成如下 SQL 语句: ```sql SELECT user.* FROM users user INNER JOIN photos photo ON photo.user = user.id WHERE user.name = 'Timber' ``` 这将会返回 Timber 如果他有 photos,但是并不会返回他的 photos。 ## 联查任何实体或表 你不仅能联查关系,还能联查任何其他实体或表。 例如: ```typescript const user = await createQueryBuilder("user") .leftJoinAndSelect(Photo, "photo", "photo.userId = user.id") .getMany(); ``` ```typescript const user = await createQueryBuilder("user") .leftJoinAndSelect("photos", "photo", "photo.userId = user.id") .getMany(); ``` ## 联查和映射功能 将`profilePhoto`添加到`User`实体,你可以使用`QueryBuilder`将任何数据映射到该属性: ```typescript export class User { /// ... profilePhoto: Photo; } ``` ```typescript const user = await createQueryBuilder("user") .leftJoinAndMapOne("user.profilePhoto", "user.photos", "photo", "photo.isForProfile = TRUE") .where("user.name = :name", { name: "Timber" }) .getOne(); ``` 这将加载 Timber 的个人资料照片并将其设置为`user.profilePhoto`。 如果要加载并映射单个实体,请使用`leftJoinAndMapOne`。 如果要加载和映射多个实体,请使用`leftJoinAndMapMany`。 ## 获取生成的 sql 查询语句 有时你可能想要获取`QueryBuilder`生成的 SQL 查询。 为此,请使用`getSql`: ```typescript const sql = createQueryBuilder("user") .where("user.firstName = :firstName", { firstName: "Timber" }) .orWhere("user.lastName = :lastName", { lastName: "Saw" }) .getSql(); ``` 出于调试目的,你也可以使用`printSql`: ```typescript const users = await createQueryBuilder("user") .where("user.firstName = :firstName", { firstName: "Timber" }) .orWhere("user.lastName = :lastName", { lastName: "Saw" }) .printSql() .getMany(); ``` 此查询将返回 users 并将使用的 sql 语句打印到控制台。 ## 获得原始结果 使用选择查询构建器可以获得两种类型的结果:**entities** 和 **raw results**。 大多数情况下,你只需要从数据库中选择真实实体,例如 users。 为此,你可以使用`getOne`和`getMany`。 但是,有时需要选择特定数据,例如 _sum of all user photos_。 这些数据不是实体,它被称为原始数据。 要获取原始数据,请使用`getRawOne`和`getRawMany`。 例如: ```typescript const { sum } = await getRepository(User) .createQueryBuilder("user") .select("SUM(user.photosCount)", "sum") .where("user.id = :id", { id: 1 }) .getRawOne(); ``` ```typescript const photosSums = await getRepository(User) .createQueryBuilder("user") .select("user.id") .addSelect("SUM(user.photosCount)", "sum") .where("user.id = :id", { id: 1 }) .getRawMany(); // 结果将会像这样: [{ id: 1, sum: 25 }, { id: 2, sum: 13 }, ...] ``` ## 流数据 你可以使用`stream`来返回流。 Streaming 返回原始数据,必须手动处理实体转换: ```typescript const stream = await getRepository(User) .createQueryBuilder("user") .where("user.id = :id", { id: 1 }) .stream(); ``` ## 使用分页 大多数情况下,在开发应用程序时,你可能需要分页功能。 如果你的应用程序中有分页,page slider 或无限滚动组件,则使用此选项。 ```typescript const users = await getRepository(User) .createQueryBuilder("user") .leftJoinAndSelect("user.photos", "photo") .take(10) .getMany(); ``` 将会返回前 10 个 user 的 photos。 ```typescript const users = await getRepository(User) .createQueryBuilder("user") .leftJoinAndSelect("user.photos", "photo") .skip(10) .getMany(); ``` 将返回除了前 10 个 user 以外的所有 user 的 photos。 你可以组合这些方法: ```typescript const users = await getRepository(User) .createQueryBuilder("user") .leftJoinAndSelect("user.photos", "photo") .skip(5) .take(10) .getMany(); ``` 这将跳过前 5 个 users,并获取他们之后的 10 个 user。 `take`和`skip`可能看起来像我们正在使用`limit`和`offset`,但它们不是。 一旦你有更复杂的连接或子查询查询,`limit`和`offset`可能无法正常工作。 使用`take`和`skip`可以防止这些问题。 ## 加锁 QueryBuilder 支持 optimistic 和 pessimistic 锁定。 要使用 pessimistic 读锁定,请使用以下方法: ```typescript const users = await getRepository(User) .createQueryBuilder("user") .setLock("pessimistic_read") .getMany(); ``` 要使用 pessimistic 写锁定,请使用以下方法: ```typescript const users = await getRepository(User) .createQueryBuilder("user") .setLock("pessimistic_write") .getMany(); ``` 要使用 optimistic 锁定,请使用以下方法: ```typescript const users = await getRepository(User) .createQueryBuilder("user") .setLock("optimistic", existUser.version) .getMany(); ``` Optimistic 锁定与`@ Version`和`@ UpdatedDate`装饰器一起使用。 ## 查询部分字段 如果只想选择实体的某些属性,可以使用以下语法: ```typescript const users = await getRepository(User) .createQueryBuilder("user") .select(["user.id", "user.name"]) .getMany(); ``` 这只会选择`User`的`id`和`name`。 ## 使用子查询 你可以轻松创建子查询。 `FROM`,`WHERE`和`JOIN`表达式都支持子查询。 例如: ```typescript const qb = await getRepository(Post).createQueryBuilder("post"); const posts = qb .where( "post.title IN " + qb .subQuery() .select("user.name") .from(User, "user") .where("user.registered = :registered") .getQuery() ) .setParameter("registered", true) .getMany(); ``` 使用更优雅的方式来做同样的事情: ```typescript 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: ```typescript const userQb = await connection .getRepository(User) .createQueryBuilder("user") .select("user.name") .where("user.registered = :registered", { registered: true }); const posts = await connection .getRepository(Post) .createQueryBuilder("post") .where("post.title IN (" + userQb.getQuery() + ")") .setParameters(userQb.getParameters()) .getMany(); ``` 你可以在`FROM`中创建子查询,如下所示: ```typescript const userQb = await connection .getRepository(User) .createQueryBuilder("user") .select("user.name", "name") .where("user.registered = :registered", { registered: true }); const posts = await connection .createQueryBuilder() .select("user.name", "name") .from("(" + userQb.getQuery() + ")", "user") .setParameters(userQb.getParameters()) .getRawMany(); ``` 或使用更优雅的语法: ```typescript const posts = await connection .createQueryBuilder() .select("user.name", "name") .from(subQuery => { return subQuery .select("user.name", "name") .from(User, "user") .where("user.registered = :registered", { registered: true }); }, "user") .getRawMany(); ``` 如果想添加一个子查询做为"second from",请使用`addFrom`。 你也可以在`SELECT`语句中使用子查询: ```typescript const posts = await connection .createQueryBuilder() .select("post.id", "id") .addSelect(subQuery => { return subQuery .select("user.name", "name") .from(User, "user") .limit(1); }, "name") .from(Post, "post") .getRawMany(); ``` ## 隐藏列 如果要查询的模型具有"select:false"的列,则必须使用`addSelect`函数来从列中检索信息。 假设你有以下实体: ```typescript import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column({ select: false }) password: string; } ``` 使用标准的`find`或查询,你将不会接收到模型的`password`属性。 但是,如果执行以下操作: ```typescript const users = await connection .getRepository(User) .createQueryBuilder() .select("user.id", "id") .addSelect("user.password") .getMany(); ``` 你将在查询中获得属性`password`。