TypeORM 学习

date
Aug 13, 2019
slug
dzoihzoq
status
Published
tags
Node
summary
type
Post
 

Active Record 与 Data Mapper

Active Record

使用 Active Record 方法,可以在模型 Entity 本身内定义所有查询方法,并使用模型方法保存、删除和加载对象。简单来说,Active Record 模式是一种在模型中访问数据库的方法。

// 实体类
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from "typeorm";

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

  @Column()
  firstName: string;

  @Column()
  lastName: string;

  @Column()
  isActive: boolean;

  // 在User类中创建静态方法等函数
  static findByName(firstName: string, lastName: string) {
    return this.createQueryBuilder("user")
      .where("user.firstName = :firstName", { firstName })
      .andWhere("user.lastName = :lastName", { lastName })
      .getMany();
  }
}


// 示例如何保存AR实体
const user = new User();
user.firstName = "Timber";
user.lastName = "Saw";
user.isActive = true;
await user.save();

// 示例如何删除AR实体
await user.remove();

// 示例如何加载AR实体
const users = await User.find({ skip: 2, take: 5 });
const newUsers = await User.find({ isActive: true });
const timber = await User.findOne({ firstName: "Timber", lastName: "Saw" });

const timber2 = await User.findByName("Timber", "Saw");
所有实体都必须扩展 BaseEntity 类,它提供了与实体一起使用的方法。BaseEntity 具有标准 Repository 的大部分方法。大多数情况下,你不需要将 Repository 或 EntityManager 与 active record 实体一起使用。

Data Mapper

使用 Data Mapper 方法,你可以在名为“repositories”的单独类中定义所有查询方法,并使用存储库保存、删除和加载对象。简单来说,数据映射器是一种在存储库而不是模型中访问数据库的方法。个人感觉,和后面介绍的 EntityManager 比较类似。

// 实体类
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

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

  @Column()
  firstName: string;

  @Column()
  lastName: string;

  @Column()
  isActive: boolean;
}

// 基本使用
const userRepository = connection.getRepository(User);

// 示例如何保存DM实体
const user = new User();
user.firstName = "Timber";
user.lastName = "Saw";
user.isActive = true;
await userRepository.save(user);

// 示例如何删除DM实体
await userRepository.remove(user);

// 示例如何加载DM实体
const users = await userRepository.find({ skip: 2, take: 5 });
const newUsers = await userRepository.find({ isActive: true });
const timber = await userRepository.findOne({ firstName: "Timber", lastName: "Saw" });

// 定义数据映射方法
import { EntityRepository, Repository } from "typeorm";
import { User } from "../entity/User";

@EntityRepository()
export class UserRepository extends Repository<User> {
  findByName(firstName: string, lastName: string) {
    return this.createQueryBuilder("user")
      .where("user.firstName = :firstName", { firstName })
      .andWhere("user.lastName = :lastName", { lastName })
      .getMany();
  }
}

// 使用
const userRepository = connection.getCustomRepository(UserRepository);
const timber = await userRepository.findByName("Timber", "Saw");
在 Nestjs 中,推荐使用 Data Mapper 模式

Entity

columns 类型:
  • PrimaryColumn:默认 int 型。
  • PrimaryGeneratedColumn:默认 int 型。
  • @PrimaryGeneratedColumn(“uuid”):默认 string。

Entity Inheritance 继承式实体

具体表继承,使用实体继承模式减少代码中的重复。 最简单和最有效的是具体的表继承。

// 基类
export abstract class Content {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    title: string;
}

// 子类1
@Entity()
export class Photo extends Content {

    @Column()
    size: string;

}

// 子类2
@Entity()
export class Question extends Content {

    @Column()
    answersCount: number;

}

// 子类3
@Entity()
export class Post extends Content {

    @Column()
    viewCount: number;

}
另外 单表继承(装饰器:TableInheritance,ChildEntity),即多个有公用 Column 的实体数据存在一个 Table 中。与所有 Column 放在一个实体中的区别是:使用 Entity 时,各个子 Entity 管理有差异的 Column,减少代码的耦合。

Entity Embedded 嵌入式实体

嵌入列是一个列,它接受具有自己列的类,并将这些列合并到当前实体的数据库表中。个人觉得和继承差别不大,不过可以控制生成实际 DB Table 中列的顺序。

// 被嵌套类
import { Column} from "typeorm";

export class Name {

    @Column()
    first: string;

    @Column()
    last: string;

}

// 嵌套类
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
import {Name} from "./Name";

@Entity()
export class User {

    @PrimaryGeneratedColumn()
    id: string;

    @Column(type => Name)
    name: Name;

    @Column()
    isActive: boolean;

}

@Entity()
export class Student {

    @PrimaryGeneratedColumn()
    id: string;

    @Column(type => Name)
    name: Name;

    @Column()
    faculty: string;

}

Repository

Repository 用来表示存储库(数据层,DAO),可以看做 Entity 的集合,即 Table 与 DB 的关系。
从 repository 角度看是,Repository 包含 4 种:
  • EntityManager:自带的,最基本的。提供给所有的 Entity 使用。
  • TreeRepository:自带的。提供给几个能组成 Tree 的 Entity 集合使用,意思是父子 Entity 之间必须某一 Column 进行关联。
  • MongoRepository:自带的。
  • custom-repository:自定义存储库。通常为单个实体创建自定义存储库,并包含其特定查询。即在 Repository 的基础上,通过继承或扩展,定制对 DB 的 CRUD。

Entity Manager

EntityManager 是最基本的 Repository 管理器,你可以管理(insert, update, delete, load 等)任何实体,就像放一个实体存储库的集合的地方。你可以通过 getManager()或 Connection 访问实体管理器。
import { getManager } from "typeorm";
import { User } from "./entity/User";

const entityManager = getManager(); // 你也可以通过 getConnection().manager 获取
const user = await entityManager.findOne(User, 1);
user.name = "Umed";
await entityManager.save(user);

API 列表

两个版本,内容大部分一样:

自定义存储库

扩展了标准存储库的定制存储库

扩展了标准 AbstractRepository 的自定义存储库

没有扩展的自定义存储库

在事务中使用自定义存储库或为什么自定义存储库不能是服务

日志

官方文档,在连接配置中增加logging: true,或记录所有日志logging: 'all',或增加日志过滤器logging: ['query', 'error']。支持过滤器类型:
  • query:记录所有查询。
  • error:记录所有失败的查询和错误。
  • schema:记录架构构建过程。
  • warn:记录内部 orm 警告。
  • info:记录内部 orm 信息性消息。
  • log:记录内部 orm 日志消息。
另外设置maxQueryExecutionTime: 1000来记录所有运行超过 1 秒的查询。
自带 4 种不同类型的记录器,通过logger: "file"启用: advanced-console - 默认记录器,它将使用颜色和 sql 语法高亮显示所有记录到控制台中的消息(使用chalk)。 simple-console - 简单的控制台记录器,与高级记录器完全相同,但它不使用任何颜色突出显示。 如果你不喜欢/或者使用彩色日志有问题,可以使用此记录器。 file - 这个记录器将所有日志写入项目根文件夹中的 ormlogs.log(靠近 package.json 和 ormconfig.json)。 debug - 此记录器使用debug package打开日志记录设置你的 env 变量 DEBUG = typeorm:*(注意记录选项对此记录器没有影响)。
也可自定义记录器logger: new MyCustomLogger()
import { Logger } from "typeorm";

export class MyCustomLogger implements Logger {
  // 实现logger类的所有方法
}

© 刘德华 2020 - 2023