TypeORM 入门教程

TypeORM 入门教程

目录


什么是 TypeORM

TypeORM 是一个基于 TypeScript 的 ORM(Object-Relational Mapping,对象关系映射) 框架,它可以帮助你:

  • 类和装饰器 来定义数据库表结构
  • 面向对象的方式 操作数据库,而不是写原生 SQL
  • 自动处理 数据库表之间的关联关系
  • 支持 多种数据库(MySQL、PostgreSQL、SQLite、MongoDB 等)

简单来说,TypeORM 让你可以像操作 JavaScript 对象一样操作数据库。


安装与配置

1. 安装依赖

bash 复制代码
# npm
npm install typeorm reflect-metadata mysql2

# yarn
yarn add typeorm reflect-metadata mysql2

💡 reflect-metadata 是必须的,TypeORM 依赖它实现装饰器功能。

2. 创建数据库连接

在项目入口文件(如 index.tsapp.ts)中创建连接:

typescript 复制代码
import 'reflect-metadata'
import { createConnection } from 'typeorm'

async function main() {
  const connection = await createConnection({
    type: 'mysql',
    host: 'localhost',
    port: 3306,
    username: 'root',
    password: '123456',
    database: 'my_database',
    entities: [__dirname + '/entity/*.ts'], // 实体文件路径
    synchronize: true, // 开发环境自动同步表结构(生产环境禁用!)
    logging: true, // 打印 SQL 日志
  })

  console.log('数据库连接成功!')
}

main().catch(console.error)

3. 配置 ormconfig.json(推荐)

创建项目根目录的 ormconfig.json 文件,方便管理:

json 复制代码
{
  "type": "mysql",
  "host": "localhost",
  "port": 3306,
  "username": "root",
  "password": "123456",
  "database": "my_database",
  "entities": ["src/entity/**/*.ts"],
  "synchronize": true,
  "logging": true
}

这样创建连接时可以简化:

typescript 复制代码
import { createConnection } from 'typeorm'

// 自动读取 ormconfig.json 配置
const connection = await createConnection()

核心概念

1. Entity(实体)

实体 = 数据库表,用 TypeScript 类 + 装饰器定义。

2. Repository(仓库)

仓库 = 操作数据库的 API,提供 CRUD 方法。

3. Connection(连接)

连接 = 数据库会话,管理所有数据库操作。

4. Decorator(装饰器)

TypeORM 使用装饰器来定义表结构和关联关系。


实体定义

基础实体

typescript 复制代码
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'

@Entity('users') // 映射到 users 表
export class User {
  @PrimaryGeneratedColumn() // 主键,自增
  id: number

  @Column() // 普通列
  name: string

  @Column({ unique: true }) // 唯一约束
  email: string

  @Column({ nullable: true }) // 允许为空
  age: number

  @Column({ default: 'active' }) // 默认值
  status: string

  @Column({
    type: 'timestamp',
    default: () => 'CURRENT_TIMESTAMP', // 默认当前时间
  })
  createdAt: Date
}

常用列类型

typescript 复制代码
@Column({ type: 'varchar', length: 100 })  // 字符串,最大100
@Column({ type: 'text' })                  // 长文本
@Column({ type: 'int' })                   // 整数
@Column({ type: 'decimal', precision: 10, scale: 2 }) // 精确数字
@Column({ type: 'boolean' })               // 布尔
@Column({ type: 'json' })                  // JSON 对象

CRUD 操作

获取 Repository

typescript 复制代码
import { getRepository } from 'typeorm'
import { User } from './entity/User'

const userRepository = getRepository(User)

创建(Create)

typescript 复制代码
// 方式 1:直接创建
const user = new User()
user.name = '张三'
user.email = 'zhangsan@example.com'
await userRepository.save(user)

// 方式 2:使用 create() 方法
const user = userRepository.create({
  name: '李四',
  email: 'lisi@example.com',
})
await userRepository.save(user)

// 批量创建
const users = userRepository.create([
  { name: '王五', email: 'wangwu@example.com' },
  { name: '赵六', email: 'zhaoliu@example.com' },
])
await userRepository.save(users)

读取(Read)

typescript 复制代码
// 根据 ID 查找
const user = await userRepository.findOne(1)

// 查找多个
const users = await userRepository.find()

// 条件查询
const users = await userRepository.find({
  where: { status: 'active' },
  order: { id: 'DESC' }, // 降序
  take: 10, // 限制返回 10 条
})

// 查找或创建(如果不存在则创建)
const user = await userRepository.findOneOrCreate({
  where: { email: 'test@example.com' },
  defaults: { name: '新用户' },
})

// 统计数量
const count = await userRepository.count()

更新(Update)

typescript 复制代码
// 根据 ID 更新
await userRepository.update(1, { name: '新名字' })

// 批量更新
await userRepository.update(
  { status: 'inactive' },
  { status: 'deleted' }
)

// 先查找再更新
const user = await userRepository.findOne(1)
user.name = '更新后的名字'
await userRepository.save(user) // save 会自动识别是新增还是更新

删除(Delete)

typescript 复制代码
// 根据 ID 删除
await userRepository.delete(1)

// 根据条件删除
await userRepository.delete({ status: 'deleted' })

// 先查找再删除
const user = await userRepository.findOne(1)
await userRepository.remove(user)

关联关系

TypeORM 支持三种关联关系:

关系类型 SQL 术语 说明
OneToOne 1:1 一对一
OneToMany 1:N 一对多
ManyToOne N:1 多对一
ManyToMany N:N 多对多

一对一(OneToOne)

typescript 复制代码
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm'

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number

  @Column()
  name: string

  @OneToOne(() => UserProfile, profile => profile.user)
  @JoinColumn() // 创建外键列
  profile: UserProfile
}

@Entity()
export class UserProfile {
  @PrimaryGeneratedColumn()
  id: number

  @Column()
  bio: string

  @OneToOne(() => User, user => user.profile)
  user: User
}

一对多(OneToMany)/ 多对一(ManyToOne)

typescript 复制代码
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number

  @OneToMany(() => Photo, photo => photo.user)
  photos: Photo[]
}

@Entity()
export class Photo {
  @PrimaryGeneratedColumn()
  id: number

  @Column()
  url: string

  @ManyToOne(() => User, user => user.photos)
  user: User
}

查询关联数据

typescript 复制代码
// 使用 relations 选项
const user = await userRepository.findOne(1, {
  relations: ['photos'], // 加载关联的 photos
})

console.log(user.photos) // [Photo, Photo, ...]

查询构建器

TypeORM 提供 QueryBuilder 用于构建复杂查询:

typescript 复制代码
import { getRepository } from 'typeorm'

// 简单查询
const users = await getRepository(User)
  .createQueryBuilder('user')
  .where('user.name = :name', { name: '张三' })
  .getMany()

// 联表查询
const photos = await getRepository(Photo)
  .createQueryBuilder('photo')
  .leftJoinAndSelect('photo.user', 'user') // 左连接
  .where('user.name = :name', { name: '张三' })
  .getMany()

// 分页查询
const users = await getRepository(User)
  .createQueryBuilder('user')
  .skip(0) // 跳过前 10 条
  .take(10) // 每页 10 条
  .orderBy('user.id', 'DESC')
  .getMany()

// 聚合查询
const result = await getRepository(User)
  .createQueryBuilder('user')
  .select('COUNT(user.id)', 'count')
  .where('user.status = :status', { status: 'active' })
  .getRawOne() // 获取原始结果

数据库迁移

1. 创建迁移文件

bash 复制代码
npx typeorm migration:create src/migration/CreateUserTable

2. 编写迁移代码

typescript 复制代码
import { MigrationInterface, QueryRunner, Table } from 'typeorm'

export class CreateUserTable1234567890 implements MigrationInterface {
  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.createTable(
      new Table({
        name: 'users',
        columns: [
          {
            name: 'id',
            type: 'int',
            isPrimary: true,
            isGenerated: true,
            generationStrategy: 'increment',
          },
          {
            name: 'name',
            type: 'varchar',
            length: '100',
            isNullable: false,
          },
          {
            name: 'email',
            type: 'varchar',
            length: '100',
            isUnique: true,
          },
          {
            name: 'created_at',
            type: 'timestamp',
            default: 'now()',
          },
        ],
      }),
      true
    )
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.dropTable('users')
  }
}

3. 运行迁移

bash 复制代码
# 执行所有待执行的迁移
npx typeorm migration:run

# 回滚最近一次迁移
npx typeorm migration:revert

最佳实践

1. 环境配置

typescript 复制代码
// 开发环境:synchronize: true(自动同步表结构)
// 生产环境:synchronize: false,使用 migration 管理表结构

2. 使用 Repository 模式

typescript 复制代码
// ✅ 推荐:使用 Repository
const user = await userRepository.findOne(1)

// ❌ 避免:直接使用 QueryRunner
const user = await connection.query('SELECT * FROM users WHERE id = 1')

3. 处理关联关系

typescript 复制代码
// 避免 N+1 查询问题,使用 addSelectAndMap 或 QueryBuilder
const users = await getRepository(User)
  .createQueryBuilder('user')
  .leftJoinAndSelect('user.photos', 'photo')
  .getMany()

4. 错误处理

typescript 复制代码
import { QueryFailedError } from 'typeorm'

try {
  await userRepository.save(user)
} catch (error) {
  if (error instanceof QueryFailedError) {
    // 处理数据库错误(如唯一约束冲突)
    console.error('数据库错误:', error.message)
  }
  throw error
}

5. 事务处理

typescript 复制代码
import { getManager } from 'typeorm'

await getManager().transaction(async transactionManager => {
  // 在事务中执行多个操作
  await transactionManager.save(user)
  await transactionManager.save(order)
  // 任意一个操作失败,整个事务会回滚
})

6. 使用 DTO 和 Validation

typescript 复制代码
import { IsString, IsEmail, MinLength } from 'class-validator'

export class CreateUserDto {
  @IsString()
  @MinLength(2)
  name: string

  @IsEmail()
  email: string
}

常见问题

Q: synchronize: true 在生产环境有什么风险?

A: 它会自动修改数据库结构,可能导致数据丢失。生产环境应该使用 migration 管理表结构。

Q: 如何处理软删除?

A: 使用 @DeleteDateColumn() 装饰器:

typescript 复制代码
@Column({ name: 'deleted_at', nullable: true, select: false })
deletedAt: Date

// 查询时自动过滤已删除的数据
const users = await userRepository.find({ withDeleted: false })

Q: 如何优化性能?

A:

  1. 只查询需要的字段(select 选项)
  2. 使用 eager: false(默认),按需加载关联数据
  3. 合理使用索引
  4. 避免 N+1 查询问题

总结

TypeORM 通过 装饰器 + 面向对象 的方式,让数据库操作变得简单直观:

功能 方法
定义表结构 @Entity + @Column
主键 @PrimaryGeneratedColumn
CRUD repository.find/save/update/delete
关联关系 @OneToOne/OneToMany/ManyToOne/ManyToMany
复杂查询 QueryBuilder
表结构迁移 migration:create + migration:run

掌握这些核心概念,你就可以开始用 TypeORM 开发了!🚀


📅 创建时间:2026-08-13

相关推荐
IT_陈寒2 小时前
Vite静态资源路径这个大坑害我调了一下午
前端·人工智能·后端
oliver_sys_log2 小时前
绕过 Yearning 查询体验限制:我做了一个 DataGrip 只读 SQL 代理
后端·mysql
水深火乐2 小时前
golang-jwt v5 入门
后端
carson9552 小时前
基于springboot和vue的文本文件上传下载在线编辑功能
后端
RunProof可证工程2 小时前
AI 写的登录接口能跑,上线前我却查出 SQL 注入写法
sql·代码规范
n8n2 小时前
Spring AI 提示词工程进阶:System / User / Assistant 角色、Prompt Template 动态拼装与多角色人设切换
后端
步行cgn2 小时前
Spring Boot 主入口类上的 @Enable 和 @Scan 注解详解
java·spring boot·后端
Lyra_Infra3 小时前
云效主机部署场景下 Python 服务生命周期问题复盘
后端·python
程序员cxuan3 小时前
GPT images 2.5 一手实测,这也太颠了。。。
后端·程序员