TypeORM中如何基于type字段实现动态多表关联(避免多可空关系)
解决方案:根据枚举字段动态关联不同实体
针对你不想创建多个可空关联字段的需求,这里提供几种TypeORM下的可行方案:
方案一:单表继承(Table Inheritance)
把AccountingDocument作为父实体,为每种发票类型创建对应的子实体,子实体中定义专属的关联关系。这种方式能严格保证每种类型的文档只关联对应发票,且数据库层面有外键约束。
代码示例:
首先定义父实体和枚举:
import { Entity, Column, PrimaryGeneratedColumn, ParentEntity } from "typeorm"; export enum DocumentType { BUY = "buy", SELL = "sell" } @Entity() @ParentEntity() export class AccountingDocument { @PrimaryGeneratedColumn() id: number; @Column('enum', { enum: DocumentType }) type: DocumentType; // 其他公共字段... }
然后创建对应子实体,各自关联专属发票:
import { ChildEntity, ManyToOne } from "typeorm"; import { AccountingDocument, DocumentType } from "./AccountingDocument"; import { BuyInvoice } from "./BuyInvoice"; @ChildEntity(DocumentType.BUY) export class BuyAccountingDocument extends AccountingDocument { @ManyToOne(() => BuyInvoice) invoice: BuyInvoice; }
import { ChildEntity, ManyToOne } from "typeorm"; import { AccountingDocument, DocumentType } from "./AccountingDocument"; import { SellInvoice } from "./SellInvoice"; @ChildEntity(DocumentType.SELL) export class SellAccountingDocument extends AccountingDocument { @ManyToOne(() => SellInvoice) invoice: SellInvoice; }
优缺点:
- 优点:ORM自动处理关联逻辑,数据库外键约束生效,类型安全,查询时可直接获取对应实体的关联数据。
- 缺点:实体数量随关联类型增加而增多,查询所有文档时需要处理多态查询。
方案二:手动管理关联ID+自定义查询逻辑
在AccountingDocument中只存储发票ID和类型,通过自定义方法或查询构建器手动关联对应的发票表。这种方式不需要新增实体,适合快速实现的场景。
代码示例:
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm"; import { DocumentType } from "./DocumentType"; import { getRepository } from "typeorm"; import { BuyInvoice } from "./BuyInvoice"; import { SellInvoice } from "./SellInvoice"; @Entity() export class AccountingDocument { @PrimaryGeneratedColumn() id: number; @Column('enum', { enum: DocumentType }) type: DocumentType; @Column() invoiceId: number; // 自定义方法获取关联发票 async getInvoice() { switch(this.type) { case DocumentType.BUY: return await getRepository(BuyInvoice).findOne(this.invoiceId); case DocumentType.SELL: return await getRepository(SellInvoice).findOne(this.invoiceId); // 其他类型... default: return null; } } }
查询时也可以用查询构建器关联对应表:
// 查询所有采购类文档及其发票 const buyDocs = await getRepository(AccountingDocument) .createQueryBuilder("doc") .leftJoinAndSelect("BuyInvoice", "invoice", "invoice.id = doc.invoiceId") .where("doc.type = :type", { type: DocumentType.BUY }) .getMany();
优缺点:
- 优点:实体结构简单,无需新增实体,灵活度高。
- 缺点:失去ORM自动关联加载能力,需要手动处理查询逻辑,数据库层面无默认外键约束(可手动添加)。
方案三:使用JSON字段存储关联信息(不推荐)
把关联的实体类型和ID存在JSON字段中,这种方式完全脱离ORM的关联机制,仅作为极端场景的备选——会丢失类型检查和外键约束,查询性能也较差。
代码示例:
@Column('json') invoiceRef: { type: DocumentType, id: number };
内容的提问来源于stack exchange,提问作者soroush madani
相关产品推荐
相关产品推荐

