ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

MikroORM v7 装饰器完全指南:Legacy 与 ES Spec 装饰器、元数据提供者与迁移实践

MikroORM v7 装饰器完全指南:Legacy 与 ES Spec 装饰器、元数据提供者与迁移实践 后端【免费下载链接】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点击查看免费下载本篇指南以 MikroORM v7 的装饰器实体定义为绝对核心系统讲解 Legacy实验性装饰器与 TC39 Stage 3 ES Spec 装饰器两种写法的配置差异、ReflectMetadataProvider与TsMorphMetadataProvider两条元数据推断路径的取舍以及元数据缓存、ESM 工程下的dynamicImportProvider兼容方案与从 v6 到 v7 的迁移要点。读完本文你将能够在自己的 TypeScript 项目中准确选择装饰器方案、正确配置tsconfig.json与 ORM 配置并解决ERR_UNKNOWN_FILE_EXTENSION、类型推断失效等真实工程问题。为什么还需要装饰器与defineEntity并存MikroORM v7 的官方 Getting Started 指南 默认推荐使用defineEntity辅助函数配合InferEntity获得完整的类型推断但它并不排斥装饰器。装饰器decorators作为定义实体的经典方式依然是社区最熟悉、生态最成熟的路径尤其适合习惯传统 ORM如 TypeORM、TypeGoose声明式语法的团队需要通过Entity()、Property()等注解与 IDE、文档生成工具深度协作的场景已有大量 v6 及更早版本代码、希望平滑升级的存量项目。因此 v7 将装饰器从mikro-orm/core中拆分为独立的mikro-orm/decorators包并同时提供 Legacy 与 ES Spec 两套实现。两类装饰器Legacy实验性与 ES SpecStage 3v7 同时支持两套装饰器标准它们在 TypeScript 编译选项、入口包路径、元数据反射能力和转译器支持上存在明确差异特性Legacy实验性装饰器ES Spec 装饰器TypeScript 配置experimentalDecorators: true无需特殊配置包入口mikro-orm/decorators/legacymikro-orm/decorators/es元数据反射支持配合reflect-metadata不支持ts-morph 支持支持支持转译器支持tsc、swc、babel需插件tsc、esbuild、swc从源码结构看两套实现分别位于 packages/decorators/src/legacy 与 packages/decorators/src/es每个目录下都提供Entity、PrimaryKey、Property、ManyToOne、OneToOne、OneToMany、ManyToMany、Embeddable、Embedded、Enum、Filter、Formula、Indexed、Check、Trigger、hooks、Transactional、CreateRequestContext等装饰器。ES 版本在 index.ts 中额外为不支持Symbol.metadata的运行时如部分 Node.js 版本注入了 polyfill这正是 ES 装饰器能把字段级元数据传播到类装饰器的基础。Legacy 装饰器的配置与示例Legacy 装饰器是 TypeScript 多年来的传统语法需要开启experimentalDecorators编译选项。若希望使用reflect-metadata自动推断类型还需同时开启emitDecoratorMetadata{ compilerOptions: { experimentalDecorators: true, emitDecoratorMetadata: true // only needed with reflect-metadata } }import { Entity, PrimaryKey, Property } from mikro-orm/decorators/legacy; Entity() export class User { PrimaryKey() id!: number; Property() fullName!: string; Property() email!: string; Property({ nullable: true }) age?: number; }注意导入路径是mikro-orm/decorators/legacy而不是mikro-orm/core——这是 v7 相对 v6 最直观的破坏性变化详见下文迁移章节。Legacy 装饰器的实现入口可见 legacy/index.ts例如Entity()在 legacy/Entity.ts 中通过getMetadataFromDecorator拿到实体的元数据对象并合并EntityOptionsPrimaryKey()在 legacy/PrimaryKey.ts 中直接把primary: true写入属性元数据。ES Spec 装饰器TC39 Stage 3ES Spec 装饰器遵循 TC39 Stage 3 提案TypeScript 5.0 原生支持、无需任何编译选项且天然兼容 esbuild 等现代打包器import { Entity, PrimaryKey, Property } from mikro-orm/decorators/es; Entity() export class User { PrimaryKey() id!: number; Property() fullName!: string; Property() email!: string; Property() age?: number; }:::caution ES Spec 装饰器的限制ES Spec 装饰器不支持元数据反射具体影响不能配合reflect-metadata使用关系装饰器中必须显式提供目标实体类型entity或回调TsMorphMetadataProvider仍然可以从源码中推断类型这是 ES 装饰器搭配 ts-morph 可行的关键。:::从实现上看ES 版Property()es/Property.ts根据context.kind区分 field / getter / setter / accessor / method 五种形态并把Symbol.metadata中的元数据通过prepareMetadataContext组装成完整的EntityMetadata。这也解释了为什么 ES 装饰器必须从mikro-orm/decorators/es导入入口处的 polyfill 与元数据传播逻辑是 ES 版本专有的。元数据提供者ORM 如何获知属性类型装饰器只标注了“这是什么”而属性到底是什么类型string、number、RefUser、CollectionBook……需要一个机制去解析。v7 提供两个内置的MetadataProvider元数据提供者ReflectMetadataProvider读取 TypeScript 编译器发射的design:type元数据快而轻TsMorphMetadataProvider用 TypeScript Compiler API通过 ts-morph直接读源码慢但推断能力强。两者的基类与缓存管理统一实现在 packages/core/src/metadata/MetadataProvider.ts其中useCache()L107-L109决定该提供者是否默认启用元数据缓存——TsMorphMetadataProvider覆写为true这也是它性能开销的主要缓解手段。ReflectMetadataProvider基于reflect-metadataReflectMetadataProvider利用reflect-metadata包读取 TypeScript 编译器在emitDecoratorMetadata下发射的类型信息速度快、开销低。安装npm install reflect-metadataORM 配置import { ReflectMetadataProvider } from mikro-orm/decorators/legacy; import { defineConfig } from mikro-orm/sqlite; export default defineConfig({ metadataProvider: ReflectMetadataProvider, entities: [User, Article], // explicit entity references recommended // ... });TypeScript 配置{ compilerOptions: { experimentalDecorators: true, emitDecoratorMetadata: true } }应用引导import reflect-metadata; // Must be imported before any entity import { MikroORM } from mikro-orm/sqlite;reflect-metadata必须放在任何实体文件之前导入通常是入口文件第一行否则装饰器执行时读不到元数据。使用限制需要更显式的装饰器写法由于design:type只能表达Object、Array等粗糙类型关系目标、可空性、Ref包装、数组元素类型、枚举等都无法自动推断必须显式声明import { Entity, ManyToOne, PrimaryKey, Property } from mikro-orm/decorators/legacy; Entity() export class Article { PrimaryKey() id!: number; Property() title!: string; // Must specify entity and nullable explicitly ManyToOne(() User, { nullable: true }) author?: User; // Must specify entity, nullable and ref explicitly ManyToOne(() Publisher, { ref: true, nullable: true }) publisher?: RefPublisher; // Array types need explicit items Property({ type: string[] }) tags: string[] []; }枚举同样需要显式提供官方 metadata-providers.md 给出三种写法参考 L116-L134Enum(() UserRole) // 引用枚举本身需先定义枚举 role: UserRole; Enum({ type: UserRole }) // 枚举名需与实体同文件 role: UserRole; Enum({ items: [a, b, c] }) // 枚举项列表 role: UserRole;此外reflect-metadata在实体间存在循环依赖尤其是多实体同文件或 ESM 工程时可能推断失败此时需要在关系装饰器中显式指定entity: () Author回调并可借助Rel包装器关闭反射ManyToOne({ entity: () Author }) author: RelAuthor;类型推断能力对比ts-morph vs reflect-metadata场景ts-morphreflect-metadata标量类型自动自动可选属性推断为可空需要nullable: true关系目标自动需要entity: () EntityRefT包装自动需要ref: trueLazyRefT标记自动仅类型层面无包装无元数据标志——仅类型层面数组元素类型自动需要显式type枚举自动需要items: Enum联合类型支持不支持:::warning ES Spec 装饰器ReflectMetadataProvider只适配 Legacy 装饰器。ES Spec 装饰器不支持emitDecoratorMetadata因此无法与 reflect-metadata 组合使用。:::从源码看ReflectMetadataProvider.loadEntityMetadatalegacy/ReflectMetadataProvider.ts对每个属性优先解析prop.entity回调否则回退到Reflect.getMetadata(design:type, ...)读取类型当读到的类型是Object且未显式提供columnTypes时会保守地映射为anyL54-L57避免误判为 JSON 列——这也是为什么反射模式下“必须显式写类型”的原因之一。TsMorphMetadataProvider从源码推断类型TsMorphMetadataProvider使用 TypeScript Compiler API经 ts-morph直接解析实体源码文件能在编译期之外获得完整的类型信息从而支持非常DRY的实体定义——装饰器选项可以极度精简。安装npm install mikro-orm/reflectionORM 配置import { TsMorphMetadataProvider } from mikro-orm/reflection; import { defineConfig } from mikro-orm/sqlite; export default defineConfig({ metadataProvider: TsMorphMetadataProvider, entities: [User, Article], // ... });TypeScript 配置{ compilerOptions: { declaration: true, experimentalDecorators: true } }declaration: true是硬性要求当 ORM 运行在编译后的 JavaScript 上时ts-morph 读取的是.d.ts声明文件而不是.ts源码。使用文件夹式发现entities传 glob时还应通过entitiesTs指定 TS 源文件路径并在开发期如tsx运行显式preferTs: true生产环境运行node时则完全依赖.d.ts因此发布产物必须携带.d.ts文件。优势自动推断属性类型包括复杂类型与泛型从可选属性?自动推断可空性同时兼容 Legacy 与 ES Spec 装饰器支持高度 DRY 的实体定义装饰器选项更少。注意事项发现discovery过程较慢可通过元数据缓存缓解需要生成.d.ts文件与部分打包器不兼容如某些配置下的 webpack。DRY 实体示例ts-morphimport { Entity, ManyToOne, PrimaryKey, Property } from mikro-orm/decorators/legacy; Entity() export class Article { PrimaryKey() id!: number; Property() title!: string; // ts-morph infers: type is User, nullable is true ManyToOne() author?: User; // ts-morph infers the Ref wrapper and target entity ManyToOne() publisher?: RefPublisher; }对比上文 reflect-metadata 版本同一实体少写了大量entity/nullable/ref选项。背后的机制在 TsMorphMetadataProvider.ts 中readTypeFromSourceL131-L191读取源码中属性声明节点的 ts 类型解析联合类型与可空性processWrapperL224-L242负责识别Ref、Reference、EntityRef、ScalarRef、ScalarReference等包装器并自动设置ref: true同时对LazyRefT这类仅类型层标记无运行时包装只解包、不设置ref并在用户同时显式写了ref: true时抛出明确的MetadataErrorL115-L122。文件夹式发现 ESM 的兼容问题当使用文件夹式发现entities传 glob并在 Vitest 等 ESM 测试环境中运行时可能出现TypeError: Unknown file extension .ts (ERR_UNKNOWN_FILE_EXTENSION)原因是 MikroORM 内部会动态 import 实体文件而这类动态 import 发生在 ORM 自己的模块上下文中Vitest 等工具无法对其做 TS 转换。解决方法是覆写dynamicImportProvider配置让 ORM 复用你应用上下文中的importexport default defineConfig({ // ... // for vitest to get around TypeError: Unknown file extension .ts (ERR_UNKNOWN_FILE_EXTENSION) dynamicImportProvider: id import(id), });从源码看该配置在 Configuration.ts 中被写入globalThis.dynamicImportProvider随后 fs-utils.ts 在动态加载实体时优先调用它——这正是让 MikroORM 使用应用自身的 import 上下文的底层实现。元数据缓存加速 ts-morph 的启动开销TsMorphMetadataProvider需要解析 TypeScript发现过程较慢。v7 为它默认启用元数据缓存源码见TsMorphMetadataProvider.useCache()返回trueTsMorphMetadataProvider.ts。import { defineConfig } from mikro-orm/sqlite; import { TsMorphMetadataProvider } from mikro-orm/reflection; export default defineConfig({ metadataProvider: TsMorphMetadataProvider, metadataCache: { enabled: true, // enabled by default for TsMorphMetadataProvider // Cache is stored in temp folder by default // Add this folder to .gitignore }, });生产部署时可在构建期生成缓存彻底消除运行期的 ts-morph 依赖npx mikro-orm cache:generate该命令在 packages/cli/src/commands/GenerateCacheCommand.ts 中实现还支持两个实用选项--ts为.ts文件生成开发期缓存--combined path别名-c把所有元数据合并进单个 JSON 文件默认./metadata.json配合GeneratedCacheAdapter使用可让生产环境不再依赖mikro-orm/reflection包。更完整的缓存控制参数Configuration.ts配置项说明默认值enabled是否启用缓存取决于提供者的useCache()ts-morph 默认 truecombined合并为单个缓存文件可为true或自定义路径字符串关闭pretty缓存 JSON 是否美化输出false单行 JSONadapter缓存适配器类未指定时异步MikroORM.init()自动使用FileCacheAdapterFileCacheAdapteroptions传给适配器的参数如{ cacheDir }{ cacheDir: process.cwd() /temp }缓存条目会与源文件修改时间绑定每次读取前自动校验失效当你在 git 分支间切换、实体目录内容发生变化时可能需要手动清空temp目录。缓存机制细节可参考 metadata-cache.md其中还给出了实现SyncCacheAdapter接口get/set/remove/combine?来接入 Redis 等外部存储的完整示例异步缓存需在MikroORM.init()之前预取数据注入metadataCache.options初始化完成后再回写。方案选型四条实体定义路径对比方案优点缺点defineEntity完整类型推断、无需装饰器、处处可用语法与传统 ORM 不同ES 装饰器 ts-morph现代标准、DRY 定义启动较慢、需要.d.ts文件Legacy 装饰器 ts-morphDRY 定义、语法熟悉启动较慢、需要额外配置Legacy 装饰器 reflect-metadata启动快、轻量冗长、类型推断有限选型建议追求最快启动、且愿意多写一点装饰器选项 → Legacy reflect-metadata追求 DRY 与现代标准、能接受.d.ts与缓存 → ES 装饰器 ts-morph或 Legacy ts-morph完全不想碰装饰器、偏好函数式定义 →defineEntity参考 define-entity.md。从 v6 迁移装饰器拆包与导入路径变更v7 最大的破坏性变化之一是装饰器从mikro-orm/core迁出到独立的mikro-orm/decorators包upgrading-v6-to-v7.md。升级步骤显式安装依赖包npm install mikro-orm/decorators把实体文件中的导入全部改到 Legacy 入口- import { Entity, PrimaryKey, Property } from mikro-orm/core; import { Entity, PrimaryKey, Property } from mikro-orm/decorators/legacy;若使用 ES Spec 装饰器import { Entity, PrimaryKey, Property } from mikro-orm/decorators/es;ReflectMetadataProvider同样被迁移且不再是默认提供者需要显式指定- import { ReflectMetadataProvider } from mikro-orm/core; import { ReflectMetadataProvider } from mikro-orm/decorators/legacy;同时记得安装并引入reflect-metadata。完整的 v6 → v7 升级清单含entityDefinition等相关配置项变更见 upgrading-v6-to-v7.md。小结装饰器在 MikroORM v7 中依然是定义实体的一等公民Legacy 装饰器延续 TypeScript 传统、可配合reflect-metadata快速启动ES Spec 装饰器面向未来标准、搭配TsMorphMetadataProvider可获得最 DRY 的实体代码而元数据缓存与cache:generate命令则把 ts-morph 的启动开销压到最低。选择哪条路径取决于你对启动速度、代码简洁度和工具链兼容性的具体权衡——而 v7 已经把这些选择权完整地交给了开发者。赞分享后端【免费下载链接】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点击查看免费下载相关推荐MikroORM v7 装饰器使用全指南Legacy 与 ES Spec 装饰器、元数据提供者与迁移实战MikroORM v7 装饰器使用全指南Legacy 与 ES Spec 装饰器、元数据提供者与迁移实战 本文基于 MikroORM v7 官方文档 usi后端MikroORM v7 装饰器实体定义完整指南Legacy 与 ES Spec 装饰器、元数据提供器与迁移实践MikroORM v7 装饰器实体定义完整指南Legacy 与 ES Spec 装饰器、元数据提供器与迁移实践 本文以 MikroORM v7 的核心文档 u后端Cloud Hypervisor 在线迁移Live Migration完全指南UNIX Socket、TCP、TLS 加密与 VFIO 设备迁移Cloud Hypervisor 在线迁移Live Migration完全指南UNIX Socket、TCP、TLS 加密与 VFIO 设备迁移 Clou后端上一篇MTSplice vs MMSplice新一代组织特异性剪接预测模型的突破与优势下一篇Nix 1.4 发布说明全解析多用户安全修复、builtins.hashString 与构建日志存储重构创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表