nestjs使用记录(一) - typeorm使用

背景

最近维护的许多 toB 系统都使用 typeorm 并大量使用其中的装饰器decorator 进行 db 的 CRUD, 忙里偷闲稍微记录一下

引言

文章会使用 的例子并结合自身项目开发中的内容讲解

typeorm in nestjs

引入 db 配置

同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
username: 'postgres',
password: 'pass',
port: 5432,
database: 'shop',
entities: [Product],
subscribers: [ProductSubscriber],
synchronize: true,
autoLoadEntities: true,
logger: 'advanced-console',
logging: 'all'
}),
ProductModule
],
controllers: [AppController],
providers: [AppService]
})
export class AppModule {}

异步

异步需要传入一个函数, 例子中是getTypeORMConfig, 函数返回值和同步引入一直

1
2
3
TypeOrmModule.forRootAsync({
useFactory: getTypeORMConfig
});

引入 Entity 或 Repository

Entity是描述表结构的类, Repository是可以理解成自定义Entity, 可以在原有Entity基础上自定义方法

Entity

@CreateDateColumn, UpdateDateColumn 特殊列,自动设置为实体的插入/更新时间。 不需要在此列中手动写入值,该值会自动设置
@DeleteDateColumn 会在soft-delete时自动设置, 暂时没用到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import {
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
PrimaryGeneratedColumn,
UpdateDateColumn
} from 'typeorm';

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

@Column({ type: 'varchar' })
name: string;

@Column({ type: 'numeric' })
quantity: number;

@Column({ type: 'decimal', precision: 12, scale: 2 })
price: number;

@Column({ type: 'boolean', default: false })
isDeleted: boolean;

@CreateDateColumn()
createDate: Date;

@UpdateDateColumn()
updateDate: Date;

@DeleteDateColumn()
deleteDate: Date;
}

Repository

1
2
3
4
5
@EntityRepository(Product) // 传入entity
export class DocumentRepository extends Repository<Prository> {
async findDocuments() {}
// ...custom function
}

引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// app.module.ts
@Module({
imports: [
TypeOrmModule.forRootAsync({
useFactory: getTypeORMConfig,
}),
TypeOrmModule.forFeature([ // 可以传入entity或repository
DocumentRepository,
HistoryRepository,
SystemRepository,
RetryRepository,
]),
],
})

subscribe listener

typeorm 提供了一系列钩子监听各种操作并在操作之前/之后执行钩子中定义的方法, 看字面意思都知道这些钩子的触发时机, 就不一一介绍

  • AfterLoad
  • AfterInsert
  • BeforeInsert
  • AfterUpdate
  • BeforeUpdate
  • AfterRemove
  • BeforeRemove

定义 subcriber

listenTo设置表示监听的 entity/repository, 不设置则表示监听所有 entity/repository

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { EntitySubscriberInterface, EventSubscriber, UpdateEvent } from 'typeorm';
import { Product } from './product.entity';
import { Logger } from '@nestjs/common';

@EventSubscriber()
export class ProductSubscriber implements EntitySubscriberInterface<Product> {
listenTo(): any {
return Product;
}

afterUpdate(event: UpdateEvent<Product>): Promise<any> | void {
const priceGotUpdated = event.updatedColumns.find(
(value) => value.propertyName,
Product.prototype.price
);
if (priceGotUpdated) {
if (Number(event.databaseEntity.price) !== event.entity.price) {
Logger.log(
`Price changed from
${event.databaseEntity.price} to
${event.entity.price}`,
'Product Price Updated'
);
}
}
}
}

引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// app.module.ts
@Module({
imports: [
TypeOrmModule.forRootAsync({
useFactory: getTypeORMConfig,
}),
TypeOrmModule.forFeature([
DocumentRepository,
HistoryRepository,
SystemRepository,
RetryRepository,
]),
],
providers: [DocumentSubscriber],
})

在 service 中使用 repository

  1. 直接在构造函数中初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// nestjs
export class ProductService {
constructor(
@InjectRepository(Template)
private readonly templateRepository: Repository<Template>
) {}
}

// eggjs
export class ProductService {
constructor(ctx: Context) {
super(ctx);
this.repo = getRepository(Template);
}
}
  1. 对方法使用@Transaction, @TransactionRepository
1
2
3
4
5
6
@Transaction
public async create(
@TransactionRepository(Template) templateRepository: Repository<Template>
) {
actRepository.save(...)
}

reference

  1. Subscribers a.k.a Entity Listeners of TypeORM on NestJS

nestjs使用记录(一) - typeorm使用
https://mariana-yui.github.io/2022/07/01/2022-07-01-typeorm-apply-to-nestjs/
作者
Mariana
发布于
2022年7月1日
许可协议