使用 Query Builder 查询
- 什么是
QueryBuilder
- 如何创建和使用
QueryBuilder
- 使用
QueryBuilder
获取值 - 什么是别名?
- 使用参数来转义数据
- 添加
WHERE
表达式 - 添加
HAVING
表达式 - 添加
ORDER BY
表达式 - 添加
GROUP BY
表达式 - 添加
LIMIT
表达式 - 添加
OFFSET
表达式 - 联查
- 内联和左联
- 不使用条件的联查
- 联查任何实体或表
- 联查和映射功能
- 获取生成的sql查询语句
- 获得原始结果
- 流数据
- 分页
- 加锁
- 查询部分字段
- 使用子查询
- 隐藏列
什么是QueryBuilder
QueryBuilder
是 TypeORM 最强大的功能之一 ,它允许你使用优雅便捷的语法构建 SQL 查询,执行并获得自动转换的实体。
QueryBuilder
的简单示例:
const firstUser = await connection
.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
有几种方法可以创建Query Builder
:
-
使用 connection:
import { getConnection } from "typeorm";
const user = await getConnection()
.createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.id = :id", { id: 1 })
.getOne(); -
使用 entity manager:
import { getManager } from "typeorm";
const user = await getManager()
.createQueryBuilder(User, "user")
.where("user.id = :id", { id: 1 })
.getOne(); -
使用 repository:
import { getRepository } from "typeorm";
const user = await getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id", { id: 1 })
.getOne();
有 5 种不同的QueryBuilder
类型可用:
-
SelectQueryBuilder
- 用于构建和执行SELECT
查询。 例如:import { getConnection } from "typeorm";
const user = await getConnection()
.createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.id = :id", { id: 1 })
.getOne(); -
InsertQueryBuilder
- 用于构建和执行INSERT
查询。 例如:import { getConnection } from "typeorm";
await getConnection()
.createQueryBuilder()
.insert()
.into(User)
.values([{ firstName: "Timber", lastName: "Saw" }, { firstName: "Phantom", lastName: "Lancer" }])
.execute(); -
UpdateQueryBuilder
- 用于构建和执行UPDATE
查询。 例如:import { getConnection } from "typeorm";
await getConnection()
.createQueryBuilder()
.update(User)
.set({ firstName: "Timber", lastName: "Saw" })
.where("id = :id", { id: 1 })
.execute(); -
DeleteQueryBuilder
- 用于构建和执行DELETE
查询。 例如:import { getConnection } from "typeorm";
await getConnection()
.createQueryBuilder()
.delete()
.from(User)
.where("id = :id", { id: 1 })
.execute(); -
RelationQueryBuilder
- 用于构建和执行特定于关系的操作[TBD]。
你可以在其中切换任何不同类型的查询构建器,一旦执行,则将获得一个新的查询构建器实例(与所有其他方法不同)。
使用QueryBuilder
获取值
要从数据库中获取单个结果,例如通过 id 或 name 获取用户,必须使用getOne
:
const timber = await getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id OR user.name = :name", { id: 1, name: "Timber" })
.getOne();
要从数据库中获取多个结果,例如,要从数据库中获取所有用户,请使用getMany
:
const users = await getRepository(User)
.createQueryBuilder("user")
.getMany();
使用查询构建器查询可以获得两种类型的结果:entities 或 raw results。
大多数情况下,你只需要从数据库中选择真实实体,例如 users。
为此,你可以使用getOne
和getMany
。
但有时你需要选择一些特定的数据,比方说所有sum of all user photos。
此数据不是实体,它称为原始数据。
要获取原始数据,请使用getRawOne
和getRawMany
。
例如:
const { sum } = await getRepository(User)
.createQueryBuilder("user")
.select("SUM(user.photosCount)", "sum")
.where("user.id = :id", { id: 1 })
.getRawOne();
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")
相当于:
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 表。
一个查询构建器不限于一个别名,它们可以有多个别名。 每个选择都可以有自己的别名,你可以选择多个有自己别名的表,你可以使用自己的别名连接多个表。 你也可以使 用这些别名来访问选择的表(或正在选择的数据)。
使用参数来转义数据
我们使用了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", "Cristal", "Lina" ] })
该语句将生成:
WHERE user.name IN ('Timber', 'Cristal', 'Lina')
添加WHERE
表达式
添加 WHERE
表达式就像:
createQueryBuilder("user").where("user.name = :name", { name: "Timber" });
将会生成以下 SQL 语句:
SELECT ... FROM users user WHERE user.name = 'Timber'
你可以将 AND
添加到现有的 WHERE
表达式中:
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'
你也可以添加 OR
添加到现有的 WHERE
表达式中:
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'
你可以使用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')
你可以根据需要组合尽可能多的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'
你可以添加 AND
到已经存在的 HAVING
表达式中:
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'
你可以添加 OR
到已经存在的 HAVING
表达式中:
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");
将会生成一下 SQL 语句:
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");
还可以使用排序字段作为一个 map:
createQueryBuilder("user").orderBy({
"user.name": "ASC",
"user.id": "DESC"
});
如果你使用了多个.orderBy
,后面的将覆盖所有之前的ORDER BY
表达式。
添加GROUP BY
表达式
添加 GROUP BY
表达式很简单:
createQueryBuilder("user").groupBy("user.id");
将会生成以下 SQL 语句:
SELECT ... FROM users user GROUP BY user.id
如果要使用更多 group-by, 则可以使用 addGroupBy
:
createQueryBuilder("user")
.groupBy("user.name")
.addGroupBy("user.id");
如果使用了多个.groupBy
,则后面的将会覆盖之前所有的 ORDER BY
表达式。
添加LIMIT
表达式
添加 LIMIT
表达式很简单:
createQueryBuilder("user").limit(10);
将会生成以下 SQL 语句:
SELECT ... FROM users user LIMIT 10
生成的 SQL 查询取决于数据库的类型(SQL,mySQL,Postgres 等)。
注意:如果你使用带有连接或子查询的复杂查询,LIMIT 可能无法正 常工作。
如果使用分页,建议使用take
代替。
添加OFFSET
表达式
添加 SQLOFFSET
表达式很简单:
createQueryBuilder("user").offset(10);
将会生成以下 SQL 语句:
SELECT ... FROM users user OFFSET 10
生成的 SQL 查询取决于数据库的类型(SQL,mySQL,Postgres 等)。
注意:如果你使用带有连接或子查询的复杂查询,OFFSET 可能无法正常工作。
如果使用分页,建议使用skip
代替。
联查
假设有以下实体:
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"加载他所有的 photos:
const user = await createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.where("user.name = :name", { name: "Timber" })
.getOne();