Skip to content

参考:TypeORM 装饰器、Repository、QueryBuilder 速查

基于 TypeORM 1.0(2026 复兴版) · 核于 2026-08

速查

  • TypeORM 是什么:OOP 风格的装饰器 ORM;@Entity class = 表,两种模式 Active Record / DataMapper。
  • TypeORM 1.0(2026.6):社区接管复兴——ESM、mysql2/pg-native、new DataSource() 替代 createConnection、NestJS 11 对齐。
  • 两种模式:Active Record(继承 BaseEntity,难测试)/ DataMapper(Repository,推荐)。
  • 关系@OneToMany(反向,不建列)/@ManyToOne(持有外键 + @JoinColumn)/@OneToOne/@ManyToMany@JoinTable 建中间表)。
  • 迁移migration:generate(生成)/run/revert;synchronize 生产必须 false。
  • NestJSforRoot(连接)+ forFeature([Entity])(注册)+ @InjectRepository(注入)。
  • QueryBuilder:复杂查询首选;参数绑定(:name + params)防注入。

一、装饰器速查

类/表

装饰器作用
@Entity({ name })标记 class 为表,name 指定表名
@Entity({ schema, database })跨 schema/database

主键

装饰器作用
@PrimaryGeneratedColumn()自增整数主键
@PrimaryGeneratedColumn('uuid')UUID 主键
@PrimaryGeneratedColumn('identity', { generatedIdentity })Postgres generated identity
@PrimaryColumn()手动主键

装饰器作用
@Column({ type, length, nullable, default, unique, name })普通列
@CreateDateColumn()插入时自动当前时间
@UpdateDateColumn()更新时自动当前时间
@DeleteDateColumn()软删除(deletedAt)
@VersionColumn()乐观锁版本号

关系

装饰器作用
@OneToOne(() => X, x => x.y)一对一
@OneToMany(() => X, x => x.y)一对多(反向,不建列)
@ManyToOne(() => X, x => y)多对一(持有外键)
@ManyToMany(() => X, x => x.y)多对多
@JoinColumn({ name })声明外键列(在持有外键一侧)
@JoinTable()声明中间表(多对多其中一侧)
{ eager: true }每次 find 自动带关联
{ cascade: true }save 时级联保存关联
{ lazy: true }访问时才查(易 N+1,慎用)
{ onDelete: 'CASCADE' }外键级联删除

二、Repository API(DataMapper)

ts
const repo = dataSource.getRepository(User)
// 或 NestJS: @InjectRepository(User) private repo: Repository<User>

// 创建(实例化不入库)
const u = repo.create({ name: 'Alice', email: 'a@b.com' })

// 保存(插入或更新)
await repo.save(u)
await repo.save([u1, u2])                    // 批量

// 查询
const all = await repo.find()
const one = await repo.findOne({ where: { id: 1 } })
const list = await repo.find({
  where: { role: 'ADMIN', age: Raw((a) => `${a} > 18`) },
  relations: { posts: true },
  order: { createdAt: 'DESC' },
  take: 20, skip: 0,
  select: { id: true, email: true },
  withDeleted: false,                        // 含软删除的?
})

// 简化查
await repo.findBy({ name: 'Alice' })         // 仅 where
await repo.findOneBy({ email: 'a@b.com' })
await repo.findOneOrFail({ where: { id: 1 } })   // 找不到抛错

// 更新/删除(不加载实体,直接 SQL)
await repo.update({ role: 'USER' }, { active: false })
await repo.delete({ id: 1 })
await repo.softDelete({ id: 1 })             // 软删除
await repo.restore({ id: 1 })                // 恢复软删除

// 计数
await repo.count({ where: { active: true } })

// 批量插入(高性能)
await repo.insert([{ name: 'a' }, { name: 'b' }])

// QueryBuilder
await repo.createQueryBuilder('u').where('u.id = :id', { id: 1 }).getOne()

三、Active Record API

ts
class User extends BaseEntity {
  @PrimaryGeneratedColumn() id: number
  @Column() name: string
}

// 静态方法
await User.find()
await User.findOne({ where: { id: 1 } })
await User.findBy({ name: 'Alice' })
await User.count({ where: { active: true } })
await User.delete({ id: 1 })
await User.update({ role: 'USER' }, { active: false })
await User.insert([{ name: 'a' }])

// 实例方法
const u = new User()
u.name = 'Bob'
await u.save()
await u.remove()
await u.reload()

四、QueryBuilder 模式

ts
// 链式
await repo
  .createQueryBuilder('u')                  // 别名 u
  .select(['u.id', 'u.email'])
  .addSelect('COUNT(p.id)', 'postCount')
  .leftJoin('u.posts', 'p')
  .where('u.role = :role', { role: 'ADMIN' })
  .andWhere('u.age > :age', { age: 18 })
  .groupBy('u.id')
  .having('COUNT(p.id) > :n', { n: 5 })
  .orderBy('u.name', 'ASC')
  .take(20).skip(0)
  .getRawMany()                              // 或 getMany() / getOne() / getCount()

// 子查询
.where((qb) => {
  const sub = qb.subQuery().select('p.authorId').from(Post, 'p').getQuery()
  return 'u.id IN ' + sub
})

// 参数绑定(防注入,必须用)
.where('u.email = :email', { email: userInput })

五、CLI 命令速查

命令用途
typeorm init初始化项目
typeorm migration:generate -d <ds> <Name>对比实体生成迁移
typeorm migration:create <Name>建空迁移(手写)
typeorm migration:run -d <ds>应用迁移
typeorm migration:revert -d <ds>回滚最近迁移
typeorm migration:show -d <ds>查看迁移状态
typeorm entity:create -d <dir> <Name>生成实体骨架
typeorm schema:drop -d <ds>删所有表
typeorm schema:sync -d <ds>同步实体到库(生产慎用)

<ds> 是 DataSource 实例文件路径(如 dist/data-source.js)。

六、三大 ORM 对比

维度TypeORMPrismaDrizzle ORM
范式OOP(Active Record/DataMapper)Schema-first + 代码生成SQL-faithful(代码即 schema)
schema 语言TS 装饰器 @Entity.prisma DSLTS 代码 pgTable
类型来源手写实体类+装饰器推断生成式推断原生 TS
复杂查询QueryBuilder(强)受限(回落 raw)原生 SQL(最强)
NestJS 集成默认第三方第三方
边缘运行时Prisma 7 起支持最佳
托管服务Accelerate/Postgres
典型场景NestJS、OOP 习惯类型安全、schema 即文档控 SQL、轻量、边缘

七、易错点清单

  • "生产开 synchronize: true":错。生产必须 false,synchronize 改 schema 可能丢数据,用迁移。
  • "Active Record 用于中大型项目":不推荐。Active Record 难测试,中大型项目用 DataMapper(Repository)。
  • "QueryBuilder 字符串拼接 SQL":危险,SQL 注入。用 :param 占位符 + params 对象参数绑定。
  • "lazy 关系随便用":慎用。lazy 在循环访问时触发 N+1。生产用 relationsleftJoinAndSelect 显式 join。
  • "eager 关系没代价":eager 每次 find 带关联,可能查大量数据。按需用 relations
  • "1.0 还用 createConnection":TypeORM 1.0 推荐用 new DataSource() + .initialize()
  • "TypeORM 类型推断像 Prisma 一样自动":不。TypeORM 类型靠手写实体类维护,装饰器元数据偶有不精确。
  • "NestJS 用 forRoot 就够了":还要 forFeature([Entity]) 注册实体的 Repository,才能 @InjectRepository
  • "find 的 relations 不会 N+1":正确用法不会(一次 join);但循环访问 lazy 才 N+1。误用 find+循环则 N+1。
  • "cascade: true 总是好":慎用。级联 save 误传关联可能误删/误更新,确认业务语义再开。

八、进阶方向(链接其他叶)

  • Prisma —— Schema-first + 代码生成的类型安全 ORM
  • Drizzle ORM —— TypeScript-first、SQL-faithful、边缘运行时友好
  • NestJS Best Practices —— NestJS 框架最佳实践(含 TypeORM 集成)

权威链接