找回密码
 立即注册
首页 业界区 业界 如何基于动态关系进行ORM关联查询,并动态推断DTO? ...

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

嗦或 2025-8-8 10:15:17
在上一篇文章(Prisma不能优雅的支持DTO,试试Vona ORM吧)中,我们基于静态关系实现了目录树的关联查询,并且动态推断生成了DTO(用于Swagger元数据)。在这篇文章我们探讨动态关系的用法。
什么是动态关系

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

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

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

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

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

  • v.title:等价于v.openapi({ title: ... })
  • TableIdentity:string | number
2. Customer
  1. @Entity()
  2. export class EntityCustomer extends EntityBase {
  3.   @Api.field(v.min(3))
  4.   name: string;
  5. }
复制代码
Model

1. Order
  1. @Model({ entity: EntityOrder })
  2. export class ModelOrder extends BeanModelBase {}
复制代码
2. Customer
  1. @Model({ entity: EntityCustomer })
  2. export class ModelCustomer extends BeanModelBase {}
复制代码
基于动态关系的查询

现在我们查询订单列表,包含归属的顾客信息:
  1. const orders = await this.scope.model.order.select({
  2.   with: {
  3.     customer: $relationDynamic.belongsTo(
  4.       () => ModelOrder,
  5.       () => ModelCustomer,
  6.       'customerId',
  7.     ),
  8.   },
  9. });
复制代码

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

    • 参数1:Order模型
    • 参数2:Customer模型
    • 参数3:设置关联外键customerId

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

2.png

自动推断DTO

现在我们自动推断DTO,并且设为API的返回数据的类型:
  1. const DtoOrderResult = $Dto.get(
  2.   () => ModelOrder,
  3.   {
  4.     with: {
  5.       customer: $relationDynamic.belongsTo(
  6.         () => ModelOrder,
  7.         () => ModelCustomer,
  8.         'customerId',
  9.       ),
  10.     },
  11.   },
  12. );
  13. @Controller('order')
  14. export class ControllerOrder extends BeanBase {
  15.   @Web.get()
  16.   @Api.body(v.array(v.object(DtoOrderResult)))
  17.   async findAll() {
  18.     return await this.scope.service.order.findAll();
  19.   }
  20. }  
复制代码

  • 行1:动态创建DtoOrderResult
  • 行17:将DtoOrderResult用于Swagger/Openapi的元数据
下面我们看一下API的Swagger效果:
3.png

封装DTO

我们还可以创建一个新的DTO,将前面动态创建的DtoOrderResult封装起来,从而用于其他地方:
在VSCode中,可以通过右键菜单Vona Create/Dto创建DTO的代码骨架:
  1. @Dto()
  2. export class DtoOrderResult {}
复制代码
然后我们通过继承机制来封装DTO:
  1. const DtoOrderResultDynamic = $Dto.get(
  2.   () => ModelOrder,
  3.   {
  4.     with: {
  5.       customer: $relationDynamic.belongsTo(
  6.         () => ModelOrder,
  7.         () => ModelCustomer,
  8.         'customerId',
  9.       ),
  10.     },
  11.   },
  12. );
  13. @Dto()
  14. export class DtoOrderResult extends DtoOrderResultDynamic {}
复制代码
现在,我们再使用新创建的DTO来改造前面的API代码:
  1. + import { DtoOrderResult } from '../dto/orderResult.ts';
  2. @Controller('order')
  3. export class ControllerOrder extends BeanBase {
  4.   @Web.get()
  5. + @Api.body(v.array(v.object(DtoOrderResult)))
  6. + async findAll(): Promise<DtoOrderResult[]> {
  7.     return await this.scope.service.order.findAll();
  8.   }
  9. }
复制代码

  • 行6: 直接传入DtoOrderResult
  • 行7: 返回类型为Promise
结语

Vonajs已开源:https://github.com/vonajs/vona。
Vonajs作者正在B站直播撰写技术文档,工作日每晚8:30,欢迎围观:濮水代码直播间

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册