写点什么

如何基于动态关系进行 ORM 关联查询,并动态推断 DTO?

作者:
  • 2025-08-08
    河南
  • 本文字数:1812 字

    阅读完需:约 6 分钟

如何基于动态关系进行ORM关联查询,并动态推断DTO?

在上一篇文章(https://xie.infoq.cn/article/d1c1e074e9d953393e3961306)中,我们基于静态关系实现了目录树的关联查询,并且动态推断生成了 DTO(用于 Swagger 元数据)。在这篇文章我们探讨动态关系的用法。

什么是动态关系

那么,什么是动态关系呢?在大型业务系统中,我们会创建大量数据模型,这些数据模型之间的关联众多,我们不可能将所有关联通过静态关系的机制事先声明出来。特别是当存在大量业务模块,这些数据模型散落在不同的业务模块中,那么通过静态关系事先声明所有的关联关系也变得不太现实。比如,Prisma 就只支持静态关系。如果事先没有定义静态关系,在实际代码中,我们就需要提供一种使用动态关系的机制,让我们的查询、类型推断、DTO 推断等能力得以正常使用。

准备数据模型

在上一篇文章中,我们已经介绍了如何创建 Entity 和 Model,这里不再赘述。而是直接把 Order 和 Customer 的 Entity 和 Model 罗列出来,然后演示动态关系的用法

Entity

1. Order

@Entity()export class EntityOrder extends EntityBase {  @Api.field(v.openapi({ title: $locale('OrderNo') }), v.default(''), v.min(3))  orderNo: string;
@Api.field(v.title($locale('Remark')), v.optional()) remark?: string;
@Api.field(v.tableIdentity()) customerId: TableIdentity;}
复制代码


  • v.openapi:声明字段的元数据,用于 Swagger

  • title:采用 $locale 定义,从而让 Swagger 元数据支持多语言能力

  • v.title:等价于 v.openapi({ title: ... })

  • TableIdentity:string | number

2. Customer

@Entity()export class EntityCustomer extends EntityBase {  @Api.field(v.min(3))  name: string;}
复制代码

Model

1. Order

@Model({ entity: EntityOrder })export class ModelOrder extends BeanModelBase {}
复制代码

2. Customer

@Model({ entity: EntityCustomer })export class ModelCustomer extends BeanModelBase {}
复制代码

基于动态关系的查询

现在我们查询订单列表,包含归属的顾客信息:


const orders = await this.scope.model.order.select({  with: {    customer: $relationDynamic.belongsTo(      () => ModelOrder,      () => ModelCustomer,      'customerId',    ),  },});
复制代码


  • $relationDynamic:提供一组工具,用于定义动态关系

  • belongsTo:定义多对一的关系

  • 参数 1:Order 模型

  • 参数 2:Customer 模型

  • 参数 3:设置关联外键 customerId


下面我们看一下orders的类型推断效果:




自动推断 DTO

现在我们自动推断 DTO,并且设为 API 的返回数据的类型:


const DtoOrderResult = $Dto.get(  () => ModelOrder,  {    with: {      customer: $relationDynamic.belongsTo(        () => ModelOrder,        () => ModelCustomer,        'customerId',      ),    },  },);
@Controller('order')export class ControllerOrder extends BeanBase { @Web.get() @Api.body(v.array(v.object(DtoOrderResult))) async findAll() { return await this.scope.service.order.findAll(); }}
复制代码


  • 行 1:动态创建DtoOrderResult

  • 行 17:将DtoOrderResult用于 Swagger/Openapi 的元数据


下面我们看一下 API 的 Swagger 效果:



封装 DTO

我们还可以创建一个新的 DTO,将前面动态创建的DtoOrderResult封装起来,从而用于其他地方:


在 VSCode 中,可以通过右键菜单Vona Create/Dto创建 DTO 的代码骨架:


@Dto()export class DtoOrderResult {}
复制代码


然后我们通过继承机制来封装 DTO:


const DtoOrderResultDynamic = $Dto.get(  () => ModelOrder,  {    with: {      customer: $relationDynamic.belongsTo(        () => ModelOrder,        () => ModelCustomer,        'customerId',      ),    },  },);
@Dto()export class DtoOrderResult extends DtoOrderResultDynamic {}
复制代码


现在,我们再使用新创建的 DTO 来改造前面的 API 代码:


+ import { DtoOrderResult } from '../dto/orderResult.ts';
@Controller('order')export class ControllerOrder extends BeanBase { @Web.get()+ @Api.body(v.array(v.object(DtoOrderResult)))+ async findAll(): Promise<DtoOrderResult[]> { return await this.scope.service.order.findAll(); }}
复制代码


  • 行 6: 直接传入DtoOrderResult

  • 行 7: 返回类型为Promise<DtoOrderResult[]>

结语

Vonajs 已开源:https://github.com/vonajs/vona


Vonajs 作者正在 B 站直播撰写技术文档,工作日每晚 8:30,欢迎围观:濮水代码直播间

用户头像

关注

还未添加个人签名 2019-02-03 加入

还未添加个人简介

评论

发布
暂无评论
如何基于动态关系进行ORM关联查询,并动态推断DTO?_node.js_风_InfoQ写作社区