数据操作(CRUD)详解
原创2026/3/18大约 4 分钟
一、Repository 模式(推荐)
Repository(仓库)是 TypeORM 针对单个实体的操作封装,每个实体对应一个 Repository,提供标准化的 CRUD 方法,是最常用的数据操作方式。
1. 获取 Repository
import { AppDataSource } from "./data-source";
import { User } from "./entity/User";
// 方式1:从数据源直接获取
const userRepository = AppDataSource.getRepository(User);
// 方式2:NestJS 中通过依赖注入(推荐)
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
}2. Repository 核心 CRUD 方法
(1)新增/保存数据(save)
save 方法兼具“新增”和“更新”能力:传入无主键数据则新增,有主键则更新。
// 新增单条数据
const newUser = new User();
newUser.username = "zhangsan";
newUser.nickname = "张三";
const savedUser = await userRepository.save(newUser);
// 批量新增
const userList = [
{ username: "lisi", nickname: "李四" },
{ username: "wangwu", nickname: "王五" },
];
await userRepository.save(userList);
// 更新数据(根据主键)
const existUser = await userRepository.findOne({ where: { id: 1 } });
existUser.nickname = "张三_更新";
await userRepository.save(existUser);(2)查询数据
| 方法 | 核心作用 | 示例 |
|---|---|---|
| find | 查询多条数据(支持条件/分页) | userRepository.find({ where: { age: 18 } }) |
| findOne | 查询单条数据(无结果返回 null) | userRepository.findOne({ where: { id: 1 } }) |
| findByIds | 按主键列表查询 | userRepository.findByIds([1, 2, 3]) |
| findOneBy | 简化版 findOne(仅条件) | userRepository.findOneBy({ username: "zhangsan" }) |
| count | 统计符合条件的条数 | userRepository.count({ where: { age: >18 } }) |
查询示例(含条件、排序、分页):
// 基础条件查询
const adultUsers = await userRepository.find({
where: { age: 18 }, // 等于条件
// 复杂条件(需导入 Operators)
// where: { age: MoreThan(18), username: Like("%zhang%") }
});
// 排序 + 分页
const paginatedUsers = await userRepository.find({
where: { deletedAt: null }, // 排除软删除
order: { createTime: "DESC" }, // 按创建时间降序
skip: 0, // 跳过条数(页码-1)* 每页条数
take: 10, // 取10条(每页条数)
select: ["id", "username", "nickname"], // 仅返回指定字段
});
// 统计条数
const adultCount = await userRepository.count({
where: { age: MoreThan(18) },
});(3)删除数据
import { MoreThan } from "typeorm";
// 物理删除(单条)
await userRepository.delete(1); // 按主键删除
await userRepository.delete({ age: MoreThan(100) }); // 按条件删除
// 物理删除(批量)
await userRepository.delete([1, 2, 3]);
// 软删除(需实体配置 @DeleteDateColumn)
await userRepository.softDelete(1); // 按主键软删
await userRepository.softRemove(existUser); // 按实体对象软删
// 恢复软删除
await userRepository.restore(1);(4)更新数据(update)
update 是更高效的更新方式(无需先查询实体):
// 按主键更新
await userRepository.update(1, { nickname: "张三_更新2" });
// 按条件更新
await userRepository.update(
{ age: MoreThan(18) }, // 条件
{ nickname: "成年用户" } // 要更新的字段
);
// 增量更新(如数值+1)
await userRepository.increment(
{ id: 1 }, // 条件
"age", // 字段
1 // 增量值
);
await userRepository.decrement({ id: 1 }, "age", 1); // 减量二、EntityManager 模式
EntityManager 是全局数据操作管理器,可操作所有实体,适合跨实体操作场景:
// 获取 EntityManager
const entityManager = AppDataSource.manager;
// 新增数据
const newUser = new User();
newUser.username = "zhaoliu";
await entityManager.save(newUser);
// 查询数据
const user = await entityManager.findOne(User, { where: { id: 1 } });
// 跨实体操作(示例:同时保存用户和订单)
await entityManager.transaction(async (txManager) => {
await txManager.save(user);
await txManager.save(order);
});三、批量操作
批量操作可大幅提升性能,避免循环单条操作:
// 批量新增(推荐)
const userBatch = Array.from({ length: 100 }, (_, i) => ({
username: `user_${i}`,
nickname: `用户_${i}`,
}));
await userRepository.save(userBatch);
// 批量更新(按条件)
await userRepository.update(
{ age: LessThan(18) },
{ nickname: "未成年用户" }
);
// 批量删除
await userRepository.delete({ age: MoreThan(100) });四、软删除与恢复(完整示例)
需先在实体中配置 @DeleteDateColumn,再使用以下方法:
// 软删除
await userRepository.softDelete(1); // 按ID
await userRepository.softRemove(user); // 按实体
// 恢复软删除
await userRepository.restore(1);
// 查询控制
const onlyActive = await userRepository.find(); // 仅未删除
const includeDeleted = await userRepository.find({ withDeleted: true }); // 包含删除
const onlyDeleted = await userRepository.find({
where: { deletedAt: Not(IsNull()) },
withDeleted: true,
});五、原生 SQL 执行
复杂查询场景可直接执行原生 SQL,兼顾灵活性:
// 执行查询
const rawUsers = await userRepository.query(
"SELECT id, username FROM sys_user WHERE age > ?",
[18] // 参数绑定(防SQL注入)
);
// 执行增删改
await userRepository.query(
"UPDATE sys_user SET nickname = ? WHERE id = ?",
["张三_原生更新", 1]
);
// EntityManager 执行原生 SQL
const [users, count] = await entityManager.query(
"SELECT * FROM sys_user LIMIT 10"
);六、关键注意事项
save方法会触发实体监听者(如@BeforeInsert),而update``delete不会;- 软删除仅对
Repository的内置查询方法生效,原生 SQL 需手动过滤deletedAt IS NULL; - 参数绑定(如
query方法的第二个参数)可有效防止 SQL 注入,禁止手动拼接 SQL; - 批量操作时,
save会逐一验证实体,若数据量极大,可考虑原生 SQL 或createQueryBuilder。
至此,本章节的学习就到此结束了,如有疑惑,可对接技术客服进行相关咨询。