找回密码
 立即注册
首页 业界区 业界 使用这个model操作数据库,一爽到底

使用这个model操作数据库,一爽到底

羊夏菡 2025-9-25 21:05:56
在前一篇文章中,我们简要介绍了Vonajs的核心功能。在这里,我们再来看看Vonajs提供的Model能力,可以让我们用简洁、优雅的代码全方位操作数据库,比如,动态分表、软删除、多租户、动态数据源、二级缓存,等等
创建Model

比如,我们在模块 demo-student 中创建一个 Model: student
1. Cli命令
  1. $ vona :create:bean model student --module=demo-student
复制代码
2. 菜单命令

使用菜单命令可以显著提升开发体验,减少心智负担
  1. 右键菜单 - [模块路径]: `Vona Create/Model`
复制代码
1.png

Model定义
  1. import { BeanModelBase, Model } from 'vona-module-a-database';
  2. import { EntityStudent } from '../entity/student.ts';
  3. @Model({ entity: EntityStudent })
  4. export class ModelStudent extends BeanModelBase<EntityStudent> {}
复制代码

  • 继承自 BeanModelBase 基类
  • 使用 Model 装饰器
  • 设置对应的 entity
使用Model

在 Vona 中使用 Model,支持依赖注入和依赖查找。推荐使用依赖查找,因为依赖查找可以让代码更加简洁、直观
1. 本模块查找
  1. class ServiceStudent {
  2.   async findAll(): Promise<EntityStudent[]> {
  3.     return await this.scope.model.student.select();
  4.   }
  5. }
复制代码

  • this.scope:当前模块的scope对象
  • this.scope.model.student:从scope中直接获取student model实例
2. 跨模块查找
  1. class ServiceStudent {
  2.   async findAll(): Promise<EntityStudent[]> {
  3.     return await this.$scope.demoStudent.model.student.select();
  4.   }
  5. }
复制代码

  • this.$scope.demoStudent:获取指定模块的scope对象
  • this.$scope.demoStudent.model.student:从scope中直接获取student model实例
CRUD

在这里,我们仅介绍基本的 CRUD 操作
1. Create
  1. class ServiceStudent {
  2.   async create(student: DtoStudentCreate): Promise<EntityStudent> {
  3.     return await this.scope.model.student.insert(student);
  4.   }
  5. }
复制代码
2. Read
  1. class ServiceStudent {
  2.   async findAll(): Promise<EntityStudent[]> {
  3.     return await this.scope.model.student.select();
  4.   }
  5.   
  6.   async findOne(id: TableIdentity): Promise<EntityStudent | undefined> {
  7.     return await this.scope.model.student.get({ id });
  8.   }
  9. }
复制代码

  • id: 类型为TableIdentity,支持number和bignumber两种字段类型
3. Update
  1. class ServiceStudent {
  2.   async update(id: TableIdentity, student: DtoStudentUpdate) {
  3.     return await this.scope.model.student.update({
  4.       ...student,
  5.       id,
  6.     });
  7.   }
  8. }
复制代码
4. Delete
  1. class ServiceStudent {
  2.   async remove(id: TableIdentity) {
  3.     return await this.scope.model.student.delete({ id });
  4.   }
  5. }
复制代码
Knex Query Builder

Vona Model 底层使用的是knex。因此,也支持 knex 提供的Query Builder
1. builder

调用 model 的 builder 方法获取 knex query builder 的实例
  1. class ServiceStudent {
  2.   async findAll(): Promise<EntityStudent[]> {
  3.     return await this.scope.model.student.builder().where('name', 'tom').orderBy('name');
  4.   }
  5. }
复制代码
2. builderSelect

builderSelect 方法也是获取 knex query builder 的实例,与 builder 不同的是,builderSelect 支持软删除和多实例/多租户
  1. class ServiceStudent {
  2.   async findAll(): Promise<EntityStudent[]> {
  3.     return await this.scope.model.student.builderSelect().where('name', 'tom').orderBy('name');
  4.   }
  5. }
复制代码
Model Options

名称说明entitymodel对应的entitytablemodel对应的表名disableDeleted是否禁用软删除,默认为falsedisableInstance是否禁用多实例/多租户,默认为falseclientName指定数据源名称cacheOptions配置缓存参数,默认启用基于redis的缓存

  • table:

    • 如果为空,则从 entity 获取表名
    • 可以指定函数,实现动态分表的能力

App config配置

如果通过@Model装饰器设置的Model options不符合业务需要,我们还可以在 App config 中修改 Model options的配置。比如,设置缓存参数
src/backend/config/config/config.dev.ts
  1. // onions
  2. config.onions = {
  3.   model: {
  4.     'demo-student:student': {
  5.       disableDeleted: true,   // 禁用软删除
  6.       disableInstance: true,  // 禁用多实例/多租户
  7.       cacheOptions: false,    // 禁用缓存
  8.       clientName: 'mysql',    // 使用数据源:mysql
  9.     },
  10.   },
  11. };
复制代码

  • demo-student:student:模块名称+model名称
动态分表

Model 支持动态分表的能力。比如,我们对 Order 进行分表处理,将每天的订单存入order_YYYYMMDD格式的数据表中
  1. @Model({ table: (ctx: VonaContext, defaultTable: keyof ITableRecord) => {
  2.   return `${defaultTable}_${moment().format('YYYYMMDD')}`;
  3. } })
  4. class ModelOrder {}
复制代码

  • ctx: 可以基于当前请求的上下文来动态生成表名
  • defaultTable: 默认的表名,从entity中获取
软删除, 多实例/多租户

Model 默认自动启用软删除,当删除一条数据时,并不进行物理删除,而是设置字段 deleted 的值为 true
Model 默认支持多实例/多租户。对数据进行 CRUD 操作时,自动从 Request 上下文获取实例Id,并传入 sql 语句中
比如,执行model.student.select(),那么生成的 sql 如下:
  1. select "demoStudent"."id" from "demoStudent"
  2.   where "demoStudent"."iid" = 1 and "demoStudent"."deleted" = false
复制代码

  • iid:实例 Id
  • deleted:软删除
临时禁用软删除

我们也可以在执行 model 方法时临时指定软删除参数
  1. class ServiceStudent {
  2.   async findAll(): Promise<EntityStudent[]> {
  3.     return await this.scope.model.student.select({}, { disableDeleted: true });
  4.   }
  5. }
复制代码
数据源

Vona 支持多数据库、多数据源。可以针对任何一个数据源调用 Model 的方法
1. 默认数据源

在默认情况下,Model 使用的是系统设置的缺省数据源
env/.env
  1. DATABASE_DEFAULT_CLIENT = 'pg' # pg/mysql
复制代码

  • 系统当前默认使用的是pg数据源
2. 静态数据源


  • 在 Model options 中指定数据源
  1. @Model({ clientName: 'mysql' })
  2. class ModelBook {}
复制代码

  • clientName:需要指定的数据源名称
  • 在 App config 中指定数据源
src/backend/config/config/config.dev.ts
  1. // onions
  2. config.onions = {
  3.   model: {
  4.     'demo-student:student': {
  5.       clientName: 'mysql',    // 使用数据源:mysql
  6.     },
  7.   },
  8. };
复制代码
3. 动态数据源

我们还可以在代码中动态指定数据源
  1. class ServiceStudent {
  2.   async findAll(): Promise<EntityStudent[]> {
  3.     const modelMysql = this.scope.model.student.newInstance('mysql');
  4.     return await modelMysql.select();
  5.   }
  6. }
复制代码

  • newInstance: 传入数据源名称,既可创建新的model实例
缓存

Vona Model 默认启用了缓存,从而使系统在默认情况下就具备非常高的性能
Model 支持二级缓存/mem缓存/redis缓存。默认使用的是redis缓存。因为redis缓存可以在分布式场景下保持数据的一致性,而mem缓存在分布式场景下同步数据有延时。如果业务数据变更不频繁,可以使用mem缓存或者二级缓存,从而获得更高性能
如何设置缓存配置:
1. 禁用缓存
  1. @Model({ cacheOptions: false })
  2. class ModelStudent {}
复制代码
2. 在 Model options 中配置缓存
  1. @Model({ cacheOptions: {
  2.   mode: 'all', // all/mem/redis
  3.   mem: {
  4.     max: 500,
  5.     ttl: 5 * 1000, // 5s
  6.   },
  7.   redis: {
  8.     ttl: 5 * 1000, // 5s
  9.   },
  10. } })
  11. class ModelStudent {}
复制代码

  • mode:缓存模式:支持二级缓存/mem缓存/redis缓存
  • mem:mem缓存配置
  • redis:redis缓存配置
3. 在 App config 中配置缓存

我们也可以在App config中根据业务需要修改Model的缓存配置
src/backend/config/config/config.dev.ts
  1. // onions
  2. config.onions = {
  3.   model: {
  4.     'demo-student:student': {
  5.       cacheOptions: {
  6.         mode: 'all', // all/mem/redis
  7.         mem: {
  8.           max: 500,
  9.           ttl: 5 * 1000, // 5s
  10.         },
  11.         redis: {
  12.           ttl: 5 * 1000, // 5s
  13.         },
  14.       }
  15.     },
  16.   },
  17. };
复制代码
结语

Vonajs已开源:https://github.com/vonajs/vona
Vonajs作者正在B站直播撰写技术文档,工作日每晚8:30,欢迎围观:濮水代码
开发一个功能完备的Nodejs框架固然费时费力,但是撰写技术文档更加费尽心思。写文档的过程同时也是再次梳理思路的过程,即便是围观,也有利于加深对框架设计的理解,探索不一样的架构设计路径。目前已完成部分文档,更多精彩正在逐步展开,欢迎参与

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
3 天前

举报

喜欢鼓捣这些软件,现在用得少,谢谢分享!
您需要登录后才可以回帖 登录 | 立即注册