QueryBuilder
是 TypeORM 最强大的功能之一 ,它允许你使用优雅便捷的语法构建 SQL 查询,执行并获得自动转换的实体。
在实际应用中,是最多的一个
示例:
获取数据库表中的数据
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 {
id: 1,
firstName: "Timber",
lastName: "Saw"
}
创建QueryBuilder
- 使用
connection
import { getConnection } from "typeorm";
// 获取一个数据库连接
const user = await getConnection()
// 创建一个查询构建器
.createQueryBuilder()
// 指定查询的字段为 `user`
.select("user")
// 指定查询的表为 `User` 实体,并使用 `"user"` 别名来引用该实体
.from(User, "user")
// 指定查询的条件为 `id = 1`,其中 `:id` 是一个占位符,表示要查询的 `User` 实体的 `id` 属性为 `1`
.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();
使用QueryBuilder
获取值
const timber = await getRepository(User)
.createQueryBuilder("user")
// 查询 id 为 1 或者 name 为 Timber 的数据
.where("user.id = :id OR user.name = :name", { id: 1, name: "Timber" })
.getOne();
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 }, ...]
getRawOne
和getRawMany
的用法,与getOne
和getMany
方法用法一样,区别在于获取的数据为原始数据
取别名
处理选定的数据,都可以用别名
createQueryBuilder("user")
// 相当于
createQueryBuilder()
.select("user")
.from(User, "user");
这会生成以下 sql 查询语句:
SELECT ... FROM users user
下面的表达式方法,一个查询构造器中不能使用多个,后面的查询条件会覆盖前面的;如果要使用多个,用提供的特殊表达式(特殊表达式一般是该表达式的衍生表达式)
添加where
表达式
有and
和or
两种逻辑,可以添加到表达式里面
where
只有一个,而 andWhere
和 orWhere
可以无限叠加
// 逻辑与
createQueryBuilder("user")
.where("user.firstName = :firstName", { firstName: "Timber" })
.andWhere("user.lastName = :lastName", { lastName: "Saw" });
// 逻辑或
createQueryBuilder("user")
.where("user.firstName = :firstName", { firstName: "Timber" })
.orWhere("user.lastName = :lastName", { lastName: "Saw" });
综合示例:
使用Brackets
将复杂的WHERE
表达式添加到现有的WHERE
中(用Brackets
类来创建一个子查询)
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" })
分析代码:
查询
user
表中registered
字段等于true
,且firstName
字段等于"Timber"
或者lastName
字段等于"Saw"
的数据。
添加having
表达式
having
查询通常用于对查询结果进行分组、聚合统计等操作,而不是用于单独的数据筛选。
有and
和or
两种逻辑,可以添加到表达式里面
having
只有一个,而 andHaving
和 orHaving
可以无限叠加
// 逻辑与
createQueryBuilder("user")
.having("user.firstName = :firstName", { firstName: "Timber" })
.andHaving("user.lastName = :lastName", { lastName: "Saw" });
// 逻辑或
createQueryBuilder("user")
.having("user.firstName = :firstName", { firstName: "Timber" })
.orHaving("user.lastName = :lastName", { lastName: "Saw" });
添加orderBy
表达式
orderBy
表达式用于排序
// 根据`user.id`数值,进行降序排列
createQueryBuilder("user").orderBy("user.id", "DESC");
可以用addOrderBy
添加多个排序条件
createQueryBuilder("user")
.orderBy("user.name")
.addOrderBy("user.id");
添加groupBy
表达式
groupBy
方法查询,又称为分组查询
分组查询通常用于对查询结果进行聚合统计等操作,而不是用于单独的数据筛选。
// 查询结果按照`user.id`字段进行分组
createQueryBuilder("user").groupBy("user.id");
可以使用 addGroupBy
,添加多个分组查询
createQueryBuilder("user")
.groupBy("user.name")
.addGroupBy("user.id");
添加limit
表达式
limit
指定查询结果的最大返回数量
// 查询 user 表中数据,最多返回 10 条数据,如果超过 10 条,就返回最前面的 10 条
createQueryBuilder("user").limit(10);
limit
方法通常需要和orderBy
方法一起使用
// 按照`id`字段进行降序排列
createQueryBuilder("user")
.orderBy("user.id", "DESC")
.limit(10);
添加offset
表达式
offset
表示查询结果的偏移量
// 查询`user`表中的数据,并从查询结果的第`11`条数据开始返回
createQueryBuilder("user").offset(10);
// 指定最多返回`10`条结果,并从查询结果的第`11`条数据开始返回
createQueryBuilder("user")
.limit(10)
.offset(10);
联查
假设有以下实体:
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();
你将会得到以下结果:
// 在 user 中获得一条用户名为 Timber 的所有 photos 数据,
{
id: 1,
name: "Timber",
photos: [{
id: 1,
url: "me-with-chakram.jpg"
}, {
id: 2,
url: "me-with-trees.jpg"
}]
}
可以看到leftJoinAndSelect
自动加载了所有 Timber 的 photos。 第一个参数是你要加载的关系,第二个参数是你为此关系的表分配的别名。 你可以在查询构建器中的任何位置使用此别名。
还可以向连接表达式添加条件,而不是使用"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'
内联和左联
内联用innerJoinAndSelect
外联用leftJoinAndSelect
两者的区别:
内联如果查询的数据没有,不会在执行后面的查询并返回数据;外联会执行后面的查询并返回数据
例如:
const user = await createQueryBuilder("user")
.innerJoinAndSelect("user.photos", "photo", "photo.isRemoved = :isRemoved", { isRemoved: false })
.where("user.name = :name", { name: "Timber" })
.getOne();
内联如果没有任何 photos,INNER JOIN
将不会返回 user。 外联即使没有 photos,LEFT JOIN
也会返回 user
不使用条件的联查
const user = await createQueryBuilder("user")
// `innerJoin`方法用于进行内连接查询,可以将多个数据表进行关联查询
.innerJoin("user.photos", "photo")
.where("user.name = :name", { name: "Timber" })
.getOne();
查询
user
表中name
字段等于"Timber"
的数据,并关联查询名为photos
的另一个数据表。如果查询结果存在,则返回结果集中的第一条数据,否则返回undefined
流数据stream
将获取到的数据,以数据流的方式返回
const stream = await getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id", { id: 1 })
.stream();
使用分页
const users = await getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.skip(5)
.take(10)
.getMany();
跳过前 5 个 users,并获取他们之后的 10 个 user 数据
使用子查询
form
,where
,select
和join
表达式都支持子查询
子查询的结果可以使用别名来引用,并在后续的查询操作中进行使用
// form表达式
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")
.getMany();
使用子查询查询
User
表中registered
字段等于true
的数据,并将name
字段重命名为name
,最终返回所有查询结果集中的数据
// where 表达式
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();
查询
Post
实体中title
字段的值在User
表中registered
字段等于true
的数据的name
字段中出现过的所有数据,并返回所有查询结果集中的数据
// select 表达式
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();
使用子查询查询
User
表中第一条数据的name
字段,并将查询结果与Post
实体的id
字段进行组合,最终返回所有查询结果集中的数据
使用 Query Builder 插入
import { getConnection } from "typeorm";
await getConnection()
.createQueryBuilder()
// 擦插入
.insert()
// 插入表
.into(User)
// 插入数据
.values([{ firstName: "Timber", lastName: "Saw" }, { firstName: "Phantom", lastName: "Lancer" }])
.execute();
使用 Query Builder 更新
import { getConnection } from "typeorm";
await getConnection()
.createQueryBuilder()
.update(User)
.set({ firstName: "Timber", lastName: "Saw" })
.where("id = :id", { id: 1 })
.execute();
使用 Query Builder 删除
import { getConnection } from "typeorm";
await getConnection()
.createQueryBuilder()
.delete()
.from(User)
.where("id = :id", { id: 1 })
.execute();
缓存查询
cache 表达式
用来做查询结果缓存
,可以对其进行属性配置
const users = await connection
.createQueryBuilder(User, "user")
.where("user.isAdmin = :isAdmin", { isAdmin: true })
.cache(true)
.getMany();
// or
const users = await connection.getRepository(User).find({
where: { isAdmin: true },
cache: true
});
可以设置时间,来做到控制缓存时间的控制,例如:cache(1000),表示1 秒内缓存无效,实际效果是用户在 1 秒内打开用户页面 150 次,则在此期间只会执行一次查询
// 两种查询方式
const users = await connection
.createQueryBuilder(User, "user")
.where("user.isAdmin = :isAdmin", { isAdmin: true })
.cache(60000) // 1 分钟
.getMany();
const users = await connection.getRepository(User).find({
where: { isAdmin: true },
cache: 60000
});
可以给缓存设置id,就是标识,方便精准查询和删除
const users = await connection
.createQueryBuilder(User, "user")
.where("user.isAdmin = :isAdmin", { isAdmin: true })
.cache("users_admins", 25000)
.getMany();
const users = await connection.getRepository(User).find({
where: { isAdmin: true },
cache: {
id: "users_admins",
milisseconds: 25000
}
});
// 删除可以根据 id ,来精准删除缓存
await connection.queryResultCache.remove(["users_admins"]);
学习完查询,可以看看如何连接数据并缓存数据的
Connection数据库连接
点击Connection数据库连接,查看数据缓存