后端【免费下载链接】mikro-ormTypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, MS SQL Server, PostgreSQL and SQLite/libSQL databases.项目地址https://gitcode.com/gh_mirrors/mi/mikro-orm点击查看免费下载Entity Repository 是 MikroORM 中位于EntityManager之上的薄封装层既是查询特定实体类型的便捷入口也是注入领域自定义逻辑的官方扩展点。本文基于 MikroORM v6.6 文档 repositories结合仓库源码 EntityRepository.ts、EntityManager.ts 和 Configuration.ts 的实现讲解默认仓储的用法、自定义仓储的注册方式、EntityRepositoryType类型推断机制、全局默认仓储配置以及 v6 中被移除的持久化方法与其替代方案。什么是 EntityRepositoryEntity Repositories 是构建在EntityManager之上的薄层。它们的作用是作为一个扩展点extension point你可以添加自定义方法甚至改写已有方法的行为。默认的EntityRepository实现只是把调用转发给底层的EntityManager实例。EntityRepository类携带了实体类型entity type因此你不需要在每次find或findOne调用中都传入实体类。以查询Book为例const booksRepository em.getRepository(Book); // same as em.find(Book, { author: ... }, { ... }) const books await booksRepository.find({ author: ... }, { populate: [author], limit: 1, offset: 2, orderBy: { title: QueryOrder.DESC }, }); console.log(books); // Book[]默认实现携带哪些方法从源码 EntityRepository.ts 可以看到EntityRepositoryEntity extends object的构造函数只接收两个参数底层EntityManager实例和实体名。constructor( protected readonly em: EntityManager, protected readonly entityName: EntityNameEntity, ) {}默认实现中可用即转发给EntityManager的方法包括查询类findOne、findOneOrFail、find、findAndCount、findAll、findByCursor游标分页、stream流式读取、count、countBy写入类insert、insertMany、upsert、upsertMany、nativeUpdate、nativeDelete实体构造与操作类create、assign、merge、map、getReference、populate、canPopulate辅助方法getEntityName()、getEntityManager()返回底层EntityManager实例。以upsert为例源码中的 JSDoc 直接给出了它生成的 SQL 语义EntityRepository.ts#L109-L114// insert into author (age, email) values (33, foobar.com) on conflict (email) do update set age 41 const author await em.getRepository(Author).upsert({ email: foobar.com, age: 33 });另外源码中还有一个值得注意的防护逻辑populate和assign方法在调用前会通过validateRepositoryType()校验传入实体的类型是否与仓储绑定的实体一致不匹配时抛出ValidationError.fromWrongRepositoryTypeEntityRepository.ts#L468-L486。也就是说AuthorRepository上populate一个Book实例会在运行时直接报错而不是静默执行。提示从源码结构看em.repo(entityName)是em.getRepository()的等价快捷方法EntityManager.ts#L208-L212两者行为一致。自定义仓储Custom Repository要使用自定义仓储只需继承EntityRepositoryT类import { EntityRepository } from mikro-orm/mysql; // or any other driver package export class CustomAuthorRepository extends EntityRepositoryAuthor { // custom methods... public findAndUpdate(...) { // ... } }注意自 v4 起你需要确保使用的是类型正确的EntityRepository才能访问到驱动专属方法如createQueryBuilder()。因此应从你实际使用的驱动包如mikro-orm/mysql、mikro-orm/postgresql、mikro-orm/sqlite导入EntityRepository而不是从mikro-orm/core导入。通过Entity装饰器注册定义好仓储类后通过Entity装饰器注册Entity({ repository: () CustomAuthorRepository }) export class Author { // ... }这里有两个关键点Repository()装饰器已在 v5 中移除必须使用Entity({ repository: () MyRepository })的形式。仓储引用必须放在回调函数中传递() CustomAuthorRepository而非直接传类引用。这是为了避免当仓储内部又要引用实体类时产生循环依赖circular dependency问题——回调将类的解析推迟到运行时实际需要实例化仓储的时刻。这一设计在源码中得到印证实体元数据中repository的类型就是() Constructor形式的回调metadata/types.ts#L150-L151EntityManager.getRepository()在首次访问某个实体时才惰性实例化并缓存仓储实例// packages/core/src/EntityManager.ts#L192-L203 getRepository(entityName: EntityNameEntity): GetRepositoryEntity, Repository { const meta this.metadata.get(entityName); if (!this.#repositoryMap.has(meta)) { const RepositoryClass this.config.getRepositoryClass(meta.repository) as ConstructorEntityRepositoryany; this.#repositoryMap.set(meta, new RepositoryClass(this, entityName)); } return this.#repositoryMap.get(meta) as GetRepositoryEntity, Repository; }可以看到仓储实例以实体元数据为键缓存在#repositoryMap中——同一实体每次getRepository()拿到的是同一个实例。注册完成后通过em.getRepository()即可访问你的自定义仓储。仓库中的真实用例 tests/entities/Author.ts 正是这样写的Entity({ repository: () AuthorRepository }) export class Author extends BaseEntity { // ... [EntityRepositoryType]?: AuthorRepository; }不使用装饰器时如何注册如果你使用EntitySchema或defineEntity()定义实体见 define-entity同样可以指定仓储EntitySchema提供setCustomRepository()方法EntitySchema.ts#L341-L344而defineEntity()接受同样的repository选项并在类型层面做同样的推断defineEntity.ts。推断自定义仓储类型EntityRepositoryType默认情况下em.getRepository()返回的是泛型的EntityRepositoryT而不是你注册的具体子类。要让 TypeScript 把返回类型推断为具体的自定义仓储类可以使用EntityRepositoryType符号Entity({ repository: () AuthorRepository }) export class Author { [EntityRepositoryType]?: AuthorRepository; } const repo em.getRepository(Author); // repo has type AuthorRepository这个机制的原理在类型定义 typings.ts#L648-L654 中/** Resolves the custom repository type for an entity (from [EntityRepositoryType]), or falls back to Fallback. */ export type GetRepository Entity extends { [k: PropertyKey]: any }, Fallback, Entity[typeof EntityRepositoryType] extends EntityRepositoryany | undefined ? NonNullableEntity[typeof EntityRepositoryType] : Fallback;EntityRepositoryType是一个普通的Symboltypings.ts#L185-L186在实体类上以可选属性[EntityRepositoryType]?: AuthorRepository的形式声明后GetRepository工具类型就会读取该属性并把它作为em.getRepository(Author)的返回类型若实体没有声明则回退到泛型EntityRepositoryT。defineEntity()的用户也可以借助这个符号做类型断言参考 tests/defineEntity.test.ts#L223type QuxRepoType IQux[typeof EntityRepositoryType];同样地你也可以通过MikroORM.init({ entityRepository: CustomBaseRepository })全局注册一个自定义基础仓储作为所有未显式指定repository的实体的默认仓储下一节详述。全局默认基础仓储entityRepository 配置项如果你希望整个项目里所有实体都使用某个带公共逻辑的仓储基类而不必在每个Entity上重复声明可以在 ORM 配置中指定全局基础仓储MikroORM.init({ entityRepository: ExtendedEntityRepository, })选择哪个仓储类的解析优先级在 Configuration.ts#L438-L448 的getRepositoryClass()中实现顺序为实体级实体元数据上通过repository: () ...注册的回调Entity选项或EntitySchema.setCustomRepository()全局级MikroORM.init()配置中的entityRepository选项平台默认数据库平台自带的默认仓储类this.#platform.getRepositoryClass()例如 SQL 平台会返回带有驱动扩展方法的子类。// packages/core/src/utils/Configuration.ts getRepositoryClass(repository: () EntityClassEntityRepositoryAnyEntity): OptionsD, EM[entityRepository] { if (repository) { return repository(); } if (this.#options.entityRepository) { return this.#options.entityRepository; } return this.#platform.getRepositoryClass(); }这一优先级链解释了为什么实体级注册总是覆盖全局配置也解释了为什么平台默认仓储能携带createQueryBuilder()等驱动专属方法。v6 中被移除的 EntityRepository 持久化方法以下方法自 v6 起不再出现在EntityRepository实例上persistpersistAndFlushremoveremoveAndFlushflush移除的原因是这些方法具有误导性它们给人一种在某个作用域上下文例如只操作User类型中工作的错觉而实际上它们只是底层EntityManager同名方法的快捷方式。官方的建议是涉及实体持久化persist/remove/flush时直接操作EntityManager不要经由仓储仓储应当被当作自定义逻辑的扩展点来使用例如封装 query builder 的用法如果确实需要访问这些方法可以使用repository.getEntityManager()拿到底层EntityManager直接调用EntityRepository.ts#L464-L466。如果你希望把这些方法保留在仓储层面可以定义一个自定义基础仓储并全局启用它import { EntityManager, EntityRepository, AnyEntity } from mikro-orm/mysql; export class ExtendedEntityRepositoryT extends object extends EntityRepositoryT { persist(entity: AnyEntity | AnyEntity[]): EntityManager { return this.em.persist(entity); } async persistAndFlush(entity: AnyEntity | AnyEntity[]): Promisevoid { await this.em.persistAndFlush(entity); } remove(entity: AnyEntity): EntityManager { return this.em.remove(entity); } async removeAndFlush(entity: AnyEntity): Promisevoid { await this.em.removeAndFlush(entity); } async flush(): Promisevoid { return this.em.flush(); } }然后在 ORM 配置中指定MikroORM.init({ entityRepository: ExtendedEntityRepository, })结合上一节提到的EntityRepositoryType符号你还可以把[EntityRepositoryType]?: ExtendedEntityRepository声明在一个自定义基础实体中让所有继承该基础实体的实体在em.getRepository()时自动获得正确的基础仓储类型。小结与延伸阅读EntityRepository是EntityManager的薄封装默认实现只是把方法转发给EntityManager同时把实体类型固化到仓储上免去每次传实体类的开销自定义仓储 继承EntityRepositoryT 在Entity({ repository: () ... })中用回调注册v5 起Repository()装饰器已移除用[EntityRepositoryType]?: MyRepository让em.getRepository()返回精确的子类类型底层由GetRepository工具类型实现未显式注册仓储的实体按实体级回调 → 全局entityRepository配置 → 平台默认的优先级选择仓储类v6 移除了persist/persistAndFlush/remove/removeAndFlush/flush持久化请直接用EntityManager或按上文方案在全局基础仓储中补回这些方法。延伸阅读仓库内相关文档定义实体含仓储注册的完整示例、Entity Manager、定义实体无装饰器方案。相关源码入口EntityRepository.ts、EntityManager.ts#L192-L212、Configuration.ts#L438-L448可运行参考tests/entities/Author.ts、tests/entities/Book.ts、tests/features/decorators/es/decorators.sqlite.test.ts。赞分享后端【免费下载链接】mikro-ormTypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, MS SQL Server, PostgreSQL and SQLite/libSQL databases.项目地址https://gitcode.com/gh_mirrors/mi/mikro-orm点击查看免费下载相关推荐如何用Compose Specification实现配置与密钥管理安全最佳实践如何用Compose Specification实现配置与密钥管理安全最佳实践 Compose Specification是Docker生态中用于定义多容器应Linq.J类型安全泛型查询保障Linq.J类型安全泛型查询保障 你是否曾经在Java集合操作中遇到过这样的困境明明代码编译通过了运行时却抛出 ClassCastException 或后端Baserow核心算法解析排序、过滤、搜索的数学原理Baserow核心算法解析排序、过滤、搜索的数学原理 Baserow作为一款开源无代码数据库工具其高效的数据处理能力依赖于底层精心设计的排序、过滤和搜索算法后端前端数据库低代码工作流自动化上一篇KMS智能激活方案彻底告别Windows和Office激活困扰下一篇终极科学文库PDF解密指南永久解除7天限制的完整解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
IEC61850 协议解读:从 MMS 报文到 GOOSE 订阅的配置骨架与验证动作 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 13:03:27
2026年AI Agent工程化落地:用TaoToken统一Key打通MCP协议到生产级智能体 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 13:03:20
DeskcommCRM实战:如何构建销售团队愿意用的客户管理系统 DeskcommCRM:从零搭建一套销售团队真正愿意用的客户管理系统拿到 DeskcommCRM 这个项目代号的时候,我第一反应是:这又是一套"领导想管人、销售嫌麻烦"的客户关系管理系统。客户团队提需求时七嘴八舌,总结下来就一句话&a… · 2026/9/25 13:24:28
萤火商城v2.0.8多端版:一套代码编译五端的工程实践与避坑指南 简介:萤火商城 v2.0.8 多端版是一套轻量级、高性能、前后端分离的开源电商系统,面向希望快速搭建独立商城的开发者、创业者及二次开发学习者。系统支持微信小程序、H5、公众号与 APP 多端覆盖,前后端源码 100% 开源,所见即所得&am… · 2026/9/25 13:24:28
DeskcommCRM实战拆解:从沟通资产到销售团队落地的轻量CRM设计 第一次看到 DeskcommCRM 这个词的时候,我第一反应是:这不像一个传统 CRM 的产品名。“Desk”加“comm”加“CRM”,组合方式透着一种很明确的取向——它不打算做那种销售经理喜欢、一线销售却极其抗拒的“录入系统”。我后来跟几个做客户管理的… · 2026/9/25 13:24:28
抖音无水印批量下载:3步把博主主页上百条作品存进电脑 抖音无水印批量下载:3步把博主主页上百条作品存进电脑 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback suppo… · 2026/9/25 13:24:28
桌面端CRM如何整合客户沟通与预约管理?DeskcommCRM实战解析 不知道你有没有遇到过这种场景:客户微信上问你"这周六下午还能约吗",你翻聊天记录翻到半小时前,又去查一个Excel排班表,再跑到另一个系统里找客户上次消费的记录,来回切换几个窗口总算凑齐了信息,… · 2026/9/25 13:24:22
UE5 GAS技能系统核心模块与网络同步实战解析 1. 为什么要用GAS:技能系统从来不只是"放个技能"先说个真实场景。我遇到过很多团队,做技能系统的第一步是在角色身上加一个状态变量,比如bIsCastingFireball,再用Switch节点判断当前状态,技能冷却就开个Time… · 2026/9/25 13:24:22
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37