TypeORM Repository 入门教程

TypeORM 是一个强大的 Node.js ORM 框架,支持多种数据库(如 MySQL、PostgreSQL、SQLite 等),并提供了 Repository 模式来简化数据操作。以下是 TypeORM Repository 的入门指南,涵盖核心概念、基本操作及高级用法。

一、核心概念
  1. Repository 的作用

    Repository 是 TypeORM 中用于操作特定实体的核心接口,封装了数据库的增删改查(CRUD)操作。每个实体(如 UserPhoto)对应一个专属的 Repository,提供类型安全的方法调用。

  2. Repository 与 EntityManager 的区别

    • Repository :专注于单一实体,提供类型化的方法(如 find()save())。
    • EntityManager :管理多个实体,适用于事务操作或跨实体操作。
      推荐:优先使用 Repository,复杂事务时使用 EntityManager。
二、快速入门
1. 环境准备
  • 安装依赖

    bash 复制代码
    npm install typeorm reflect-metadata pg  # 以 PostgreSQL 为例
  • 配置数据源
    data-source.ts 中定义数据库连接:

    typescript 复制代码
    import "reflect-metadata";
    import { DataSource } from "typeorm";
    import { User } from "./entity/User";
    
    export const AppDataSource = new DataSource({
      type: "postgres",
      host: "localhost",
      port: 5432,
      username: "root",
      password: "admin",
      database: "test",
      entities: [User],
      synchronize: true, // 自动同步实体到数据库表
    });
2. 定义实体

创建 User 实体类,映射到数据库表:

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

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

  @Column()
  firstName: string;

  @Column()
  lastName: string;

  @Column({ default: 0 })
  age: number;
}
3. 获取 Repository

通过 AppDataSource 获取 User 的 Repository:

typescript 复制代码
import { AppDataSource } from "./data-source";
import { User } from "./entity/User";

async function main() {
  await AppDataSource.initialize();
  const userRepository = AppDataSource.getRepository(User);
  // 后续操作...
}
main();
三、基本操作
1. 插入数据
  • 单条插入

    typescript 复制代码
    const user = new User();
    user.firstName = "Timber";
    user.lastName = "Saw";
    user.age = 25;
    await userRepository.save(user); // 插入新记录
  • 批量插入

    typescript 复制代码
    const users = [
      { firstName: "Alice", lastName: "Smith", age: 30 },
      { firstName: "Bob", lastName: "Johnson", age: 28 },
    ];
    await userRepository.insert(users); // 直接插入对象数组
2. 查询数据
  • 查询所有记录

    typescript 复制代码
    const allUsers = await userRepository.find();
    console.log(allUsers);
  • 条件查询

    typescript 复制代码
    // 按 ID 查询
    const firstUser = await userRepository.findOneBy({ id: 1 });
    
    // 多条件查询
    const timber = await userRepository.findOneBy({
      firstName: "Timber",
      lastName: "Saw",
    });
  • 高级查询(FindOptions)

    typescript 复制代码
    const users = await userRepository.find({
      select: ["firstName", "lastName"], // 只查询指定字段
      where: { age: { gt: 20 } }, // 年龄大于 20
      order: { age: "DESC" }, // 按年龄降序
      skip: 0, // 跳过 0 条
      take: 10, // 取 10 条
    });
3. 更新数据
  • 通过实体更新

    typescript 复制代码
    const user = await userRepository.findOneBy({ id: 1 });
    if (user) {
      user.age = 26;
      await userRepository.save(user); // 更新记录
    }
  • 直接更新

    typescript 复制代码
    await userRepository.update(
      { id: 1 }, // 条件
      { age: 26 } // 更新字段
    );
4. 删除数据
  • 通过实体删除

    typescript 复制代码
    const user = await userRepository.findOneBy({ id: 1 });
    if (user) {
      await userRepository.remove(user); // 删除记录
    }
  • 直接删除

    typescript 复制代码
    await userRepository.delete({ id: 1 }); // 根据条件删除
四、高级用法
1. 关联查询

假设 UserPhoto 是一对多关系:

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

  @Column()
  url: string;

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

@Entity()
export class User {
  // ...其他字段
  @OneToMany(() => Photo, (photo) => photo.user)
  photos: Photo[];
}
  • 查询关联数据

    typescript 复制代码
    const userWithPhotos = await userRepository.find({
      relations: ["photos"], // 加载关联的 photos
      where: { id: 1 },
    });
2. 使用 QueryBuilder

QueryBuilder 提供更灵活的查询方式:

typescript 复制代码
const users = await userRepository
  .createQueryBuilder("user")
  .select(["user.firstName", "user.lastName"])
  .where("user.age > :age", { age: 20 })
  .orderBy("user.age", "DESC")
  .getMany();
3. 事务管理

使用 EntityManager 处理事务:

typescript 复制代码
await AppDataSource.manager.transaction(async (transactionalEntityManager) => {
  const user = new User();
  user.firstName = "Transaction";
  user.lastName = "User";
  await transactionalEntityManager.save(user);

  const photo = new Photo();
  photo.url = "https://example.com/photo.jpg";
  photo.user = user;
  await transactionalEntityManager.save(photo);
});
五、最佳实践
  1. 使用 Repository 替代直接 SQL

    Repository 提供了类型安全的方法,减少 SQL 注入风险。

  2. 合理使用 FindOptions

    通过 find()selectwhere 等选项优化查询性能。

  3. 事务处理

    涉及多个实体的操作时,使用事务确保数据一致性。

  4. 分页查询

    使用 skiptake 实现分页,避免一次性加载大量数据。

  5. 缓存查询结果

    对不常变的数据,启用查询缓存(cache: true)提升性能。