背景
最近维护的许多 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) export class DocumentRepository extends Repository<Prository> { async findDocuments() {} }
|
引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Module({ imports: [ TypeOrmModule.forRootAsync({ useFactory: getTypeORMConfig, }), TypeOrmModule.forFeature([ 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
| @Module({ imports: [ TypeOrmModule.forRootAsync({ useFactory: getTypeORMConfig, }), TypeOrmModule.forFeature([ DocumentRepository, HistoryRepository, SystemRepository, RetryRepository, ]), ], providers: [DocumentSubscriber], })
|
在 service 中使用 repository
- 直接在构造函数中初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| export class ProductService { constructor( @InjectRepository(Template) private readonly templateRepository: Repository<Template> ) {} }
export class ProductService { constructor(ctx: Context) { super(ctx); this.repo = getRepository(Template); } }
|
- 对方法使用
@Transaction
, @TransactionRepository
1 2 3 4 5 6
| @Transaction public async create( @TransactionRepository(Template) templateRepository: Repository<Template> ) { actRepository.save(...) }
|
reference
- Subscribers a.k.a Entity Listeners of TypeORM on NestJS