使用 Query Builder 进行查询
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
什么是 QueryBuilder?
QueryBuilder 是 TypeORM 最强大的功能之一 -
它允许您使用优雅便捷的语法构建 SQL 查询,
执行这些查询并自动获取转换后的实体对象。
QueryBuilder 的简单示例:
const firstUser = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id", { id: 1 })
.getOne()
它会构建如下 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 的重要注意事项
使用 QueryBuilder 时,必须在 WHERE 表达式中提供唯一的参数名。以下写法是错误的:
const result = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.linkedSheep", "linkedSheep")
.leftJoinAndSelect("user.linkedCow", "linkedCow")
.where("user.linkedSheep = :id", { id: sheepId })
.andWhere("user.linkedCow = :id", { id: cowId })
...但以下写法是正确的:
const result = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.linkedSheep", "linkedSheep")
.leftJoinAndSelect("user.linkedCow", "linkedCow")
.where("user.linkedSheep = :sheepId", { sheepId })
.andWhere("user.linkedCow = :cowId", { cowId })
注意我们分别使用了 :sheepId 和 :cowId 作为唯一参数名,而不是对两个不同参数重复使用 :id。
如何创建和使用 QueryBuilder?
有几种方式可以创建 Query Builder:
-
使用 DataSource:
const user = await dataSource
.createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.id = :id", { id: 1 })
.getOne() -
使用实体管理器:
const user = await dataSource.manager
.createQueryBuilder(User, "user")
.where("user.id = :id", { id: 1 })
.getOne() -
使用仓库:
const user = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id", { id: 1 })
.getOne()
共有 5 种不同类型的 QueryBuilder:
-
SelectQueryBuilder- 用于构建和执行SELECT查询。示例:const user = await dataSource
.createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.id = :id", { id: 1 })
.getOne() -
InsertQueryBuilder- 用于构建和执行INSERT查询。示例:await dataSource
.createQueryBuilder()
.insert()
.into(User)
.values([
{ firstName: "Timber", lastName: "Saw" },
{ firstName: "Phantom", lastName: "Lancer" },
])
.execute() -
UpdateQueryBuilder- 用于构建和执行UPDATE查询。示例:await dataSource
.createQueryBuilder()
.update(User)
.set({ firstName: "Timber", lastName: "Saw" })
.where("id = :id", { id: 1 })
.execute() -
DeleteQueryBuilder- 用于构建和执行DELETE查询。示例:await dataSource
.createQueryBuilder()
.delete()
.from(User)
.where("id = :id", { id: 1 })
.execute() -
RelationQueryBuilder- 用于构建和执行关系特定操作 [待定]。示例:await dataSource
.createQueryBuilder()
.relation(User, "photos")
.of(id)
.loadMany()
您 可以在任何类型的查询构建器之间切换, 切换后将会获得新的查询构建器实例(这与其他方法不同)。
使用 QueryBuilder 获取数据
要从数据库中获取单条结果,
例如通过 ID 或名称获取用户,必须使用 getOne:
const timber = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id OR user.name = :name", { id: 1, name: "Timber" })
.getOne()
getOneOrFail 会从数据库获取单条结果,但如果
结果不存在,则会抛出 EntityNotFoundError 错误:
const timber = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id OR user.name = :name", { id: 1, name: "Timber" })
.getOneOrFail()
要从数据库中获取多个结果,
例如获取数据库中的所有用户,请使用 getMany:
const users = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.getMany()
使用查询构建器可以获取两种类型的结果:实体或原始数据。
大多数情况下,您需要从数据库中选择真实实体,例如用户。
为此,您可以使用 getOne 和 getMany。
但有时您需要选择特定数据,比如_所有用户照片的总数_。
这类数据不是实体,称为原始数据。
要获取原始数据,请使用 getRawOne 和 getRawMany。
示例如下:
const { sum } = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.select("SUM(user.photosCount)", "sum")
.where("user.id = :id", { id: 1 })
.getRawOne()
const photosSums = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.select("user.id")
.addSelect("SUM(user.photosCount)", "sum")
.groupBy("user.id")
.getRawMany()
// result will be like this: [{ id: 1, sum: 25 }, { id: 2, sum: 13 }, ...]
获取计数结果
您可以通过 getCount() 获取查询将返回的行数计数。该方法返回数字形式的计数结果,而非实体结果。
const count = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.where("user.name = :name", { name: "Timber" })
.getCount()
这将生成以下 SQL 查询:
SELECT count(*) FROM users user WHERE user.name = 'Timber'
别名的作用是什么?
我们使用了 createQueryBuilder("user")。但 "user" 是什么?
它只是一个常规的 SQL 别名。
除了处理选择数据时,我们在各处都使用别名。
createQueryBuilder("user") 等效于:
createQueryBuilder().select("user").from(User, "user")
这将生成以下 SQL 查询:
SELECT ... FROM users user
在此 SQL 查询中,users 是表名,user 是我们为该表分配的别名。
稍后我们使用该别名访问表:
createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.name = :name", { name: "Timber" })
这将生成以下 SQL 查询:
SELECT ... FROM users user WHERE user.name = 'Timber'
注意,我们通过创建查询构建器时分配的 user 别名来使用 users 表。
一个查询构建器不限于单个别名,它们可以有多个别名。 每个 SELECT 语句可以有独立别名, 您可以从多个表中选择,每个表都有独立别名, 可以连接多个表,每个表都有独立别名。 您可以使用这些别名访问正在选择的表(或正在选择的数据)。
使用参数转义数据
我们使用了 where("user.name = :name", { name: "Timber" })。
{ name: "Timber" } 代表什么?这是用于防止 SQL 注入的参数。
我们本可以写成:where("user.name = '" + name + "'),
但这不安全,会导致代码面临 SQL 注入风险。
安全的方式是使用特殊语法:where("user.name = :name", { name: "Timber" }),
其中 :name 是参数名称,值在对象中指定:{ name: "Timber" }。
.where("user.name = :name", { name: "Timber" })
是以下写法的简写形式:
.where("user.name = :name")
.setParameter("name", "Timber")
注意:不要在查询构建器中为不同值重复使用相同参数名。多次设置时值会被覆盖。
您也可以使用特殊扩展语法提供值数组,将其转换为 SQL 语句中的值列表:
.where("user.name IN (:...names)", { names: [ "Timber", "Crystal", "Lina" ] })
将生成:
WHERE user.name IN ('Timber', 'Crystal', 'Lina')
添加 WHERE 表达式
添加 WHERE 表达式非常简单:
createQueryBuilder("user").where("user.name = :name", { name: "Timber" })
这将生成:
SELECT ... FROM users user WHERE user.name = 'Timber'
可以在现有 WHERE 表达式中添加 AND 条件:
createQueryBuilder("user")
.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'
可以在现有 WHERE 表达式中添加 OR 条件:
createQueryBuilder("user")
.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'
可以在 WHERE 表达式中使用 IN 查询:
createQueryBuilder("user").where("user.id IN (:...ids)", { ids: [1, 2, 3, 4] })
这将生成以下 SQL 查询:
SELECT ... FROM users user WHERE user.id IN (1, 2, 3, 4)
可以使用 Brackets 向现有 WHERE 添加复杂的 WHERE 表达式
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 查询:
SELECT ... FROM users user WHERE user.registered = true AND (user.firstName = 'Timber' OR user.lastName = 'Saw')
可以使用 NotBrackets 向现有 WHERE 添加取反的复杂 WHERE 表达式
createQueryBuilder("user")
.where("user.registered = :registered", { registered: true })
.andWhere(
new NotBrackets((qb) => {
qb.where("user.firstName = :firstName", {
firstName: "Timber",
}).orWhere("user.lastName = :lastName", { lastName: "Saw" })
}),
)
这将生成以下 SQL 查询:
SELECT ... FROM users user WHERE user.registered = true AND NOT((user.firstName = 'Timber' OR user.lastName = 'Saw'))
你可以根据需要组合任意多个 AND 和 OR 表达式。如果多次使用 .where 方法,将会覆盖之前的所有 WHERE 表达式。
注意:使用 orWhere 时要特别小心——当处理同时包含 AND 和 OR 的复杂表达式时,请记住这些表达式会直接堆叠而没有优先级处理。有时你可能需要创建完整的 where 字符串来替代,避免使用 orWhere。
添加 HAVING 表达式
添加 HAVING 表达式非常简单:
createQueryBuilder("user").having("user.name = :name", { name: "Timber" })
这将生成以下 SQL 查询:
SELECT ... FROM users user HAVING user.name = 'Timber'
可以在现有 HAVING 表达式中添加 AND 条件:
createQueryBuilder("user")
.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'
可以在现有 HAVING 表达式中添加 OR 条件:
createQueryBuilder("user")
.having("user.firstName = :firstName", { firstName: "Timber" })
.orHaving("user.lastName = :lastName", { lastName: "Saw" })
这将生成以下 SQL 查询:
SELECT ... FROM users user HAVING user.firstName = 'Timber' OR user.lastName = 'Saw'
你可以根据需要组合任意多个 AND 和 OR 表达式。如果多次使用 .having 方法,将会覆盖之前的所有 HAVING 表达式。
添加 ORDER BY 表达式
添加 ORDER BY 表达式非常简单:
createQueryBuilder("user").orderBy("user.id")
这将生成:
SELECT ... FROM users user ORDER BY user.id
你可以将排序方向从升序改为降序(或反之):
createQueryBuilder("user").orderBy("user.id", "DESC")
createQueryBuilder("user").orderBy("user.id", "ASC")
可以添加多个排序条件:
createQueryBuilder("user").orderBy("user.name").addOrderBy("user.id")
也可以使用字段映射进行排序:
createQueryBuilder("user").orderBy({
"user.name": "ASC",
"user.id": "DESC",
})
如果多次使用 .orderBy 方法,将会覆盖之前的所有 ORDER BY 表达式。
添加 DISTINCT ON 表达式(仅限 Postgres)
当同时使用 distinct-on 和 order-by 表达式时,distinct-on 表达式必须与最左侧的 order-by 字段匹配。distinct-on 表达式的解释规则与 order-by 相同。请注意:在没有 order-by 表达式的情况下使用 distinct-on 意味着每个集合的第一行结果是不可预测的。
添加 DISTINCT ON 表达式非常简单:
createQueryBuilder("user").distinctOn(["user.id"]).orderBy("user.id")
这将生成:
SELECT DISTINCT ON (user.id) ... FROM users user ORDER BY user.id
添加 GROUP BY 表达式
添加 GROUP BY 表达式非常简单:
createQueryBuilder("user").groupBy("user.id")
这将生成以下 SQL 查询:
SELECT ... FROM users user GROUP BY user.id
使用 addGroupBy 添加更多分组条件:
createQueryBuilder("user").groupBy("user.name").addGroupBy("user.id")
如果多次使用 .groupBy 方法,将会覆盖之前的所有 GROUP BY 表达式。
添加 LIMIT 表达式
添加 LIMIT 表达式非常简单:
createQueryBuilder("user").limit(10)
这将生成以下 SQL 查询:
SELECT ... FROM users user LIMIT 10
最终生成的 SQL 查询取决于数据库类型(SQL、MySQL、Postgres 等)。注意:在包含连接或子查询 的复杂查询中,LIMIT 可能不会按预期工作。如果实现分页功能,建议使用 take 方法替代。
添加 OFFSET 表达式
添加 SQL OFFSET 表达式非常简单:
createQueryBuilder("user").offset(10)
这将生成以下 SQL 查询:
SELECT ... FROM users user OFFSET 10
最终生成的 SQL 查询取决于数据库类型(SQL、MySQL、Postgres 等)。注意:在包含连接或子查询的复杂查询中,OFFSET 可能不会按预期工作。如果实现分页功能,建议使用 skip 方法替代。
关联查询(Joining relations)
假设你有以下实体:
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[]
}
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" 及其所有照片:
const user = await createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.where("user.name = :name", { name: "Timber" })
.getOne()
你将获得以下结果:
{
id: 1,
name: "Timber",
photos: [{
id: 1,
url: "me-with-chakram.jpg"
}, {
id: 2,
url: "me-with-trees.jpg"
}]
}
如你所见,leftJoinAndSelect 自动加载了 Timber 的所有照片。第一个参数是要加载的关系,第二个参数是为该关系表分配的别名。你可以在查询构建器的任何位置使用这个别名。例如,让我们获取 Timber 所有未被删除的照片:
const user = await createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.where("user.name = :name", { name: "Timber" })
.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 = 'Timber' AND photo.isRemoved = FALSE
你也可以在连接表达式中直接添加条件,而不是使用 "where":
const user = await createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo", "photo.isRemoved = :isRemoved", {
isRemoved: false,
})
.where("user.name = :name", { name: "Timber" })
.getOne()
这将生成如下 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:
const user = await createQueryBuilder("user")
.innerJoinAndSelect(
"user.photos",
"photo",
"photo.isRemoved = :isRemoved",
{ isRemoved: false },
)
.where("user.name = :name", { name: "Timber" })
.getOne()
这将生成:
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 的区别在于:当用户没有照片时,INNER JOIN 不会返回该用户,而 LEFT JOIN 仍会返回用户数据。要了解更多连接类型,请参阅 SQL 文档。
不选择数据的连接
你可以连接数据但不选择它们,使用 leftJoin 或 innerJoin:
const user = await createQueryBuilder("user")
.innerJoin("user.photos", "photo")
.where("user.name = :name", { name: "Timber" })
.getOne()
这将生成:
SELECT user.* FROM users user
INNER JOIN photos photo ON photo.user = user.id
WHERE user.name = 'Timber'
如果 Timber 有照片,此查询会返回用户数据但不会返回照片。