数据查询
数据查询
要从数据库中获取单个结果,例如通过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();
使用查询构建器查询可以获得两种类型的结果:getOne
和getMany
但有时你需要选择一些特定的数据,比方说所有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 }, ...]
返回部分字段
如果只想选择实体的某些属性,可以使用以下语法:
const users = await getRepository(User)
.createQueryBuilder("user")
.select(["user.id", "user.name"])
.getMany();
这只会选择
别名
我们使用
createQueryBuilder()
.select("user")
.from(User, "user");
// SELECT ... FROM users user
在这个
createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.name = :name", { name: "Timber" });
// SELECT ... FROM users user WHERE user.name = 'Timber'
一个查询构建器不限于一个别名,它们可以有多个别名每个选择都可以有自己的别名,你可以选择多个有自己别名的表,你可以使用自己的别名连接多个表你也可以使用这些别名来访问选择的表(或正在选择的数据
隐藏列
如果要查询的模型具有addSelect
函数来从列中检索信息。
假设你有以下实体:
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column({ select: false })
password: string;
}
使用标准的find
或查询,你将不会接收到模型的password
属性但是,如果执行以下操作:
const users = await connection
.getRepository(User)
.createQueryBuilder()
.select("user.id", "id")
.addSelect("user.password")
.getMany();
你将在查询中获得属性password
。
条件查询
我们使用了where("user.name = :name", { name: "Timber" })
{name:“Timber”}
where("user.name ='"+ name +"')
,但是这不安全,因为有可能被where("user.name =name",{name:"Timber"})
,其中 name
是参数名,值在对象中指定:{name:"Timber"}
。
.where("user.name = :name", { name: "Timber" })
// 是下面的简写
.where("user.name = :name")
.setParameter("name", "Timber")
注意:不要在查询构建器中为不同的值使用相同的参数名称。如果多次设置则后值将会把前面的覆盖。还可以提供一组值,并使用特殊的扩展语法将它们转换为
.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" });
将会生成以下
SELECT ... FROM users user WHERE user.name = 'Timber'
你可以将 AND
添加到现有的 WHERE
表达式中:
createQueryBuilder("user")
.where("user.firstName = :firstName", { firstName: "Timber" })
.andWhere("user.lastName = :lastName", { lastName: "Saw" });
将会生成以下
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" });
将会生成以下
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" })
将会生成以下
SELECT ... FROM users user WHERE user.registered = true AND (user.firstName = 'Timber' OR user.lastName = 'Saw')
你可以根据需要组合尽可能多的AND
和OR
表达式如果你多次使用.where
,你将覆盖所有以前的WHERE
表达式注意:小心orWhere
AND
和OR
表达式的复杂表达式,请记住他们将无限制的叠加有时你只需要创建一个orWhere
。
添加HAVING 表达式
添加HAVING
表达式很简单:
createQueryBuilder("user").having("user.name = :name", { name: "Timber" });
将会生成以下
SELECT ... FROM users user HAVING user.name = 'Timber'
你可以添加 AND
到已经存在的 HAVING
表达式中:
createQueryBuilder("user")
.having("user.firstName = :firstName", { firstName: "Timber" })
.andHaving("user.lastName = :lastName", { lastName: "Saw" });
将会生成以下
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" });
将会生成以下
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
表达式。
添加GROUP BY 表达式
添加 GROUP BY
表达式很简单:
createQueryBuilder("user").groupBy("user.id");
将会生成以下
SELECT ... FROM users user GROUP BY user.id
如果要使用更多addGroupBy
createQueryBuilder("user")
.groupBy("user.name")
.addGroupBy("user.id");
如果使用了多个.groupBy
,则后面的将会覆盖之前所有的 ORDER BY
表达式。
添加LIMIT 表达式
添加 LIMIT
表达式很简单:
createQueryBuilder("user").limit(10);
将会生成以下
SELECT ... FROM users user LIMIT 10
生成的take
代替。
添加OFFSET 表达式
添加OFFSET
表达式很简单:
createQueryBuilder("user").offset(10);
将会生成以下
SELECT ... FROM users user OFFSET 10
生成的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;
}
现在让我们假设你要用用户
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
自动加载了所有
const user = await createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.where("user.name = :name", { name: "Timber" })
.andWhere("photo.isRemoved = :isRemoved", { isRemoved: false })
.getOne();
将会生成以下
SELECT user.*, photo.* FROM users user
LEFT JOIN photos photo ON photo.user = user.id
WHERE user.name = 'Timber' AND photo.isRemoved = FALSE
你还可以向连接表达式添加条件,而不是使用
const user = await createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo", "photo.isRemoved = :isRemoved", {
isRemoved: false
})
.where("user.name = :name", { name: "Timber" })
.getOne();
这将生成以下
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();
This will generate:
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
也会返回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'
这将会返回
子查询
你可以轻松创建子查询FROM
,WHERE
和JOIN
表达式都支持子查询例如:
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();
使用更优雅的方式来做同样的事情:
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();
或者,你可以创建单独的查询构建器并使用其生成的
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
中创建子查询,如下所示:
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();
或使用更优雅的语法:
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();
如果想添加一个子查询做为addFrom
。你也可以在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();
分页
大多数情况下,在开发应用程序时,你可能需要分页功能如果你的应用程序中有分页,
const users = await getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.take(10)
.getMany();
将会返回前
const users = await getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.skip(10)
.getMany();
将返回除了前
你可以组合这些方法:
const users = await getRepository(User)
.createQueryBuilder("user")
.leftJoinAndSelect("user.photos", "photo")
.skip(5)
.take(10)
.getMany();
这将跳过前
take
和skip
可能看起来像我们正在使用limit
和offset
,但它们不是一旦你有更复杂的连接或子查询查询,limit
和offset
可能无法正常工作使用take
和skip
可以防止这些问题。
加锁
const users = await getRepository(User)
.createQueryBuilder("user")
.setLock("pessimistic_read")
.getMany();
要使用
const users = await getRepository(User)
.createQueryBuilder("user")
.setLock("pessimistic_write")
.getMany();
要使用
const users = await getRepository(User)
.createQueryBuilder("user")
.setLock("optimistic", existUser.version)
.getMany();
要使用
const users = await getRepository(User)
.createQueryBuilder("user")
.setLock("dirty_read")
.getMany();
@Version
和@UpdatedDate
装饰器一起使用。
与Relations 结合
import { getConnection } from "typeorm";
await getConnection()
.createQueryBuilder()
.relation(Post, "categories")
.of(post)
.add(category);
这段代码相当于:
import { getManager } from "typeorm";
const postRepository = getRepository(Post);
const post = await postRepository.findOne(1, { relations: ["categories"] });
post.categories.push(category);
await postRepository.save(post);
但是这样使用第一种方式效率更高,因为它执行的操作数量最少,并且绑定数据库中的实体,这比每次都调用
此外,当进行绑定时,可以不需要使用实体,只需要使用实体
import { getConnection } from "typeorm";
await getConnection()
.createQueryBuilder()
.relation(Post, "categories")
.of(1)
.add(3);
如果你使用了复合主键,则必须将它们作为
import { getConnection } from "typeorm";
await getConnection()
.createQueryBuilder()
.relation(Post, "categories")
.of({ firstPostId: 1, secondPostId: 3 })
.add({ firstCategoryId: 2, secondCategoryId: 4 });
也可以按照添加实体的方式删除实体:
import { getConnection } from "typeorm";
// 此代码从给定的post中删除一个category
await getConnection()
.createQueryBuilder()
.relation(Post, "categories")
.of(post) // 也可以使用post id
.remove(category); // 也可以只使用category ID
添加和删除相关实体针对多对多
和一对多
关系对于一对一
和多对一
关系,请使用set
代替:
import { getConnection } from "typeorm";
// 此代码set给定post的category
await getConnection()
.createQueryBuilder()
.relation(Post, "categories")
.of(post) // 也可以使用post id
.set(category); // 也可以只使用category ID
如果要取消设置关系(将其设置为null
传递给set
方法:
import { getConnection } from "typeorm";
// 此代码取消设置给定post的category
await getConnection()
.createQueryBuilder()
.relation(Post, "categories")
.of(post) // 也可以使用post id
.set(null);
除了更新关系外,关系查询构建器还允许你加载关系实体例如,假设在Post
实体内部,我们有多对多的categories
关系和多对一的user
关系,为加载这些关系,你可以使用以下代码:
import { getConnection } from "typeorm";
const post = await getConnection().manager.findOne(Post, 1);
post.categories = await getConnection()
.createQueryBuilder()
.relation(Post, "categories")
.of(post) // 也可以使用post id
.loadMany();
post.author = await getConnection()
.createQueryBuilder()
.relation(User, "user")
.of(post) // 也可以使用post id
.loadOne();