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 列表
两个版本,内容大部分一样:
- entity-manager-api,仅包含 EntityManager
自定义存储库
扩展了标准存储库的定制存储库
扩展了标准 AbstractRepository 的自定义存储库
没有扩展的自定义存储库
在事务中使用自定义存储库或为什么自定义存储库不能是服务
日志
- 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类的所有方法
}