SQL 方案:Repository
每个 Repository 对应一个数据库实体(Entity),封装该实体的所有数据库操作。
NestJS 通过依赖注入(DI)将 Repository 注入到 Service 中,避免硬编码数据库逻辑。
适合简单场景
参考:https://typeorm.nodejs.cn/entity-manager-api
创建 Repository
注入 Repository 到 Service
ts
// user.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
@Injectable()
export class UserService {
// 注入User实体的Repository
constructor(
@InjectRepository(User)
private readonly userRepo: Repository<User>,
) {}
}插入(Insert)
TypeORM 提供 4 种核心新增方式,适用于不同场景:
shell
方法 作用 触发钩子 适用场景
create 仅创建内存中的实体实例(不入库) ❌ 先验证 / 补全数据,再入库
save 新增或更新(根据主键判断) ✅(beforeInsert) 通用新增 / 更新
insert 纯新增(不支持更新) ❌ 批量新增、无需钩子
upsert 插入或更新(冲突时更新) ❌ 避免重复插入- 示例 1:create + save(推荐,支持钩子)
ts
// 创建实例 → 补全数据 → 入库
async createUser() {
// 1. 创建内存实例(可验证数据)
const user = this.userRepo.create({
username: 'zhangsan',
password: '123456',
age: 20,
});
// 2. 补全自定义默认值(可选)
user.age = user.age || 18;
// 3. 入库(触发beforeInsert钩子)
return await this.userRepo.save(user);
}- 示例 2:直接 save(简化版)
ts
async createUserSimple() {
return await this.userRepo.save({
username: 'lisi',
password: '654321',
});
}- 示例 3:批量新增(insert)
ts
async batchCreate() {
// insert返回InsertResult,包含生成的主键等
const result = await this.userRepo.insert([
{ username: 'wangwu', password: '111111' },
{ username: 'zhaoliu', password: '222222' },
]);
return result.generatedMaps; // 获取新增数据的主键
}
// insert(增加):单条插入 & 批量插入
return await this.userRepository.insert(User, { username: 'Timber', password: 'Timber' });
return await this.userRepository.insert(User, [
{
username: 'Timber',
password: '123',
},
{
username: 'alias',
password: '456',
},
]);- 示例 4:upsert(插入或更新)
ts
async upsertUser() {
// 冲突字段:username(唯一索引),冲突时更新age
return await this.userRepo.upsert(
{ username: 'zhangsan', age: 25 },
{ conflictPaths: ['username'] }, // 冲突判断字段
);
}删除(Delete)
删除分「硬删除」和「软删除」,软删除需实体配置 @DeleteDateColumn。
shell
方法 类型 触发钩子 适用场景
remove 硬删 ✅(beforeRemove) 删除实体实例
delete 硬删 ❌ 条件 / 主键删除
softDelete 软删 ❌ 标记删除时间,不物理删除
restore 恢复软删 ❌ 还原软删数据- 示例 1:硬删除(remove)
ts
async removeUser(id: number) {
const user = await this.userRepo.findOneBy({ id });
if (!user) throw new Error('用户不存在');
// 触发beforeRemove钩子
return await this.userRepo.remove(user);
}- 示例 2:硬删除(delete)
ts
async deleteUser(id: number) {
const result = await this.userRepo.delete(id);
return result.affected; // 受影响行数
}
// delete(删除):单条删除 & 批量删除 & 按条件删除 & 截断
return await this.userRepository.delete( 1);
return await this.userRepository.delete( [1, 2, 3]);
return await this.userRepository.delete({ username: 'alias' });
return await this.userRepository.clear(); // 清空表中所有数据- 示例 3:软删除与恢复
ts
// 软删除
async softDeleteUser(id: number) {
return await this.userRepo.softDelete(id);
}
// 恢复软删数据
async restoreUser(id: number) {
return await this.userRepo.restore(id);
}
// 查询包含软删的数据
async queryWithDeleted() {
return await this.userRepo.find({
withDeleted: true, // 包含软删数据
where: { id: 1 },
});
}
// 仅查询软删的数据
async queryOnlyDeleted() {
return await this.userRepo.find({
withDeleted: true,
where: { deleteTime: Not(null) },
});
}更新(Update)
更新操作分「触发钩子」和「不触发钩子」两类,需根据场景选择:
shell
方法 触发钩子 适用场景
save ✅(beforeUpdate) 基于实体实例更新
update ❌ 批量更新、简单条件更新
merge ❌(仅内存合并) 合并实体数据后更新
updateBy ❌(TypeORM 0.3+) 简化条件更新- 示例 1:save 更新(推荐,支持钩子)
ts
async updateUser(id: number) {
// 1. 查询原实体
const user = await this.userRepo.findOneBy({ id });
if (!user) throw new Error('用户不存在');
// 2. 修改属性
user.age = 30;
user.username = 'zhangsan_new';
// 3. 入库(触发beforeUpdate钩子)
return await this.userRepo.save(user);
}
// save(插入&修改&单条&批量,特点是只针对于完整实体,不可以是部分字段,但update可修改部分也可修改整个实体):存在即更新,不存在则插入
return await this.userRepository.save(user);
return await this.userRepository.save([user1, user2, user3]);- 示例 2:update 批量更新
ts
async batchUpdate() {
// 条件:age < 20 → 更新age为20
const result = await this.userRepo.update(
{ age: LessThan(20) }, // 更新条件
{ age: 20 }, // 更新数据
);
return result.affected; // 受影响的行数
}
// update(修改):单条修改 & 按条件修改(批量)
return await this.userRepository.update(User, { age: 18 }, { username: 'ADULT' }); // 更新年龄 18 的列
return await this.userRepository.update(User, 1, { username: 'ADULT' }); // 更新 id 为 1的列- 示例 3:merge 合并更新
js
async mergeUser(id: number) {
const user = await this.userRepo.findOneBy({ id });
// 合并数据(仅内存,需save入库)
const mergedUser = this.userRepo.merge(user, { age: 35 });
return await this.userRepo.save(mergedUser);
}查询操作
查询是最常用且最灵活的操作,TypeORM 提供多套 API,覆盖简单查询到复杂联表。
shell
方法 作用 参数示例 返回值
find 查询多条 { where: { age: 18 }, take: 10 } 实体数组
findOne 查询单条(兼容旧版) { where: { id: 1 } } 实体 /undefined
findOneBy 简化查询(TypeORM 0.3+Ω { id: 1 } 实体 /undefined
findByIds 根据主键数组查询 [1,2,3] 实体数组
findAndCount 查询并返回总数 { skip: 0, take: 10 } [实体数组, 总数]- 示例 1:基础查询(带筛选、排序、分页)
ts
async queryUsers() {
// 筛选:age > 18 且 username 包含 'zhang'
// 排序:按创建时间降序
// 分页:跳过前0条,取10条
const [users, total] = await this.userRepo.findAndCount({
select: ['id', 'username', 'age'], // 仅查询指定字段
where: {
age: MoreThan(18), // 操作符:大于18(需导入MoreThan)
username: Like('%zhang%'), // 模糊查询
},
order: { createTime: 'DESC' },
skip: 0, // 偏移量(分页页码-1 * 每页数量)
take: 10, // 每页数量
relations: ['articles'], // 关联查询用户的文章(需实体定义关联)
});
return { users, total };
}- 示例 2:按主键查询
ts
async getUserById(id: number) {
// TypeORM 0.3+ 推荐 findOneBy
return await this.userRepo.findOneBy({ id });
// 旧版写法(兼容)
// return await this.userRepo.findOne({ where: { id } });
}- 示例 3:高级查询:QueryBuilder(复杂查询)
ts
async complexQuery() {
// 创建查询构建器,别名u对应users表
const users = await this.userRepo.createQueryBuilder('u')
.select(['u.id', 'u.username']) // 选择字段
.addSelect('AVG(u.age)', 'avgAge') // 聚合函数
.leftJoinAndSelect('u.articles', 'a') // 左联文章表(别名a)
.where('u.age > :age', { age: 18 }) // 条件
.andWhere('u.username LIKE :name', { name: '%zhang%' })
.orderBy('u.createTime', 'DESC')
.skip(0)
.take(10)
.getMany(); // 获取结果(getRawMany()获取原生数据,getOne()单条)
return users;
}- 示例 4:原生 SQL 查询
ts
async rawQuery() {
// 执行原生SQL,返回原始数据(无实体映射)
const result = await this.userRepo.query(
'SELECT id, username FROM users WHERE age > ?',
[18], // 参数化查询,防止SQL注入
);
return result;
}- 查询方法示例
ts
// findOne(查单条):查询匹配的第一条数据。
return await this.userRepository.findOne(User, { where: { username: 'alias' } });
// findOneBy(查单条):查询匹配的第一条数据
return await this.userRepository.findOneBy(User, { username: 'alias' });
// findOneOrFail(查单条):查询匹配的第一条数据。如果没有匹配项,则拒绝返回的 Promise。
return await this.userRepository.findOneOrFail(User, { where: { username: 'alias' } });
// findOneByOrFail(查单条):查询匹配的第一条数据。如果没有匹配项,则拒绝返回的 Promise。
return await this.userRepository.findOneByOrFail(User, { username: 'alias' });
// find(查多条):查询匹配的所有数据,返回 []
return await this.userRepository.find(User, { where: { username: 'alias' } });
// findBy(查多条):查询匹配的所有数据,返回 []
return await this.userRepository.findBy(User, { username: 'alias' });
// findAndCount(查多条&统计):查询匹配的所有数据,外加统计数
return await this.userRepository.findAndCount(User, { where: { username: 'alias' } });
// findAndCountBy(查多条&统计):查询匹配的所有数据,外加统计数
return await this.userRepository.findAndCountBy(User, { username: 'alias' });存在判断&统计数&增减列
ts
// exists:检查数据是否存在。
return await this.userRepository.exists(User, { where: { username: 'alias' } });
// existsBy:检查数据是否存在。
return await this.userRepository.existsBy(User, { username: 'alias' });
// count:统计数
return await this.userRepository.count(User, { where: { username: 'alias' } });
// countBy:统计数
return await this.userRepository.countBy(User, { username: 'alias' });
// increment:按条件增加列
return await this.userRepository.increment(User, { firstName: 'Timber' }, 'age', 3);
// decrement:按条件减少列
return await this.userRepository.decrement(User, { firstName: 'Timber' }, 'age', 3);事务
- 方式 1:QueryRunner(手动控制,推荐)
ts
async transactionDemo() {
// 1. 创建QueryRunner
const queryRunner = this.userRepo.manager.connection.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction(); // 开启事务
try {
// 2. 执行事务内操作(需用queryRunner的Repository)
const userRepo = queryRunner.manager.getRepository(User);
await userRepo.save({ username: 'transaction', password: '123' });
await userRepo.update({ username: 'zhangsan' }, { age: 40 });
// 3. 提交事务
await queryRunner.commitTransaction();
} catch (error) {
// 4. 回滚事务
await queryRunner.rollbackTransaction();
throw error;
} finally {
// 5. 释放连接
await queryRunner.release();
}
}- 方式 2:EntityManager.transaction(简化版)
ts
async simpleTransaction() {
return await this.userRepo.manager.transaction(async (manager) => {
// 事务内操作(使用manager的Repository)
await manager.getRepository(User).save({ username: 'trans2', password: '456' });
await manager.getRepository(User).delete({ username: 'lisi' });
});
}Repository 方法参数
- 查找选项 options
ts
// select:限定查询返回的字段
return await this.userRepository.find({
select: {
id: true,
username: true,
lastName: true,
},
})
// relations:加载的其他建立关系的实体表
return await this.userRepository.find({
relations: {
profile: true,
photos: true,
},
})
// where:条件查询(与 AND)
return await this.userRepository.find({
where: {
id: "1",
username: "Saw",
},
})
// where:条件查询(或 OR)
return await this.userRepository.find({
where: [
{ id: "1", username: "Saw" },
{ id: "2", username: "Lee" },
],
})
// where:条件查询(嵌套内部查询)
return await this.userRepository.find({
relations: {
project: true,
},
where: {
project: {
name: "TypeORM",
initials: "TORM",
},
},
})
// order:排序查询
return await this.userRepository.find({
order: {
updateTime: "DESC",
createTime: "ASC",
},
})
// skip --> OFFSET:分页查询(currentPage)
return await this.userRepository.find({
skip: 5,
})
// take --> LIMIT:分页查询(pages)
return await this.userRepository.find({
take: 10,
})
// cache 启用或禁用查询结果缓存
return await this.userRepository.find({
cache: true,
})
// lock 启用查询锁定机制。只能在 findOne 和 findOneBy 方法中使用
return await this.userRepository.findOne({
lock: { mode: "optimistic", version: 1 },
})- where查询
ts
// 非运算 !=:SELECT * FROM "user" WHERE "title" != 'About #1'
import { Not } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
title: Not("About #1"),
})
// 比较运算 =:SELECT * FROM "user" WHERE "title" = 'About #1'
import { Equal } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
title: Equal("About #1"),
})
// 比较运算 <:SELECT * FROM "user" WHERE "age" < 10
import { LessThan } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
age: LessThan(10),
})
// 比较运算 <=:SELECT * FROM "user" WHERE "age" < 10
import { LessThanOrEqual } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
age: LessThanOrEqual(10),
})
// 比较运算 >:SELECT * FROM "user" WHERE "age" > 10
import { MoreThan } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
age: MoreThan(10),
})
// 比较运算 >=:SELECT * FROM "user" WHERE "age" >= 10
import { MoreThanOrEqual } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
age: MoreThanOrEqual(10),
})
// 比较运算 like:SELECT * FROM "user" WHERE "username" LIKE '%alias%'
import { Like } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
username: Like("%alias%"),
})
// 比较运算 ILike:SELECT * FROM "user" WHERE "username" ILike '%alias%'
import { ILike } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
username: ILike("%alias%"),
})
// 比较运算 BETWEEN:SELECT * FROM "user" WHERE "age" BETWEEN 1 AND 10
import { Between } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
age: Between(1, 10),
})
// 比较运算 In:SELECT * FROM "user" WHERE "username" IN ('About #2','About #3')
import { In } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
username: In(["About #2", "About #3"]),
})
// 比较运算 Any:SELECT * FROM "user" WHERE "username" = ANY(['About #2','About #3'])
import { Any } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
username: Any(['About #2','About #3'])
})
// 比较运算 IsNull:SELECT * FROM "user" WHERE "username" IS NULL
import { IsNull } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
username: IsNull()
})
// 组合(多字段 AND):SELECT * FROM "post" WHERE NOT("age" > 10) AND NOT("username" = 'About #2')
return await this.userRepository.getRepository(User).findBy({
age: Not(MoreThan(10)),
username: Not(Equal("About #2")),
})
// 组合 AND: SELECT * FROM "post" WHERE NOT("username" = 'About #2') AND "username" ILIKE '%About%'
import { And } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
username: And(Not(Equal("About #2")), ILike("%About%")),
})
// 组合 OR: SELECT * FROM "post" WHERE "username" = 'About #2' OR "username" ILIKE 'About%'
import { Or } from "typeorm"
return await this.userRepository.getRepository(User).findBy({
username: Or(Equal("About #2"), ILike("About%")),
})