ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MikroORM Seeding 完全指南:Seeder 类、实体工厂与数据库种子数据实践

MikroORM Seeding 完全指南:Seeder 类、实体工厂与数据库种子数据实践 后端【免费下载链接】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 的 seeding 机制提供了解决方案通过 Seeder 类组织种子脚本、配合实体工厂Entity Factory批量生成数据即可用几条命令快速填充数据库。本篇指南基于 docs/versioned_docs/version-7.2/seeding.md 与mikro-orm/seeder包源码完整讲解配置项、CLI 命令、Seeder 编写、工厂定义与关系生成并给出测试与生产环境中的落地方式读完即可在项目中直接上手。安装与启用 SeedManagerSeeder 功能由独立的mikro-orm/seeder包提供。首先安装该依赖npm install mikro-orm/seeder然后在 ORM 配置中注册SeedManager扩展。以mikro-orm.config.ts为例import { SeedManager } from mikro-orm/seeder; export default defineConfig({ // ... extensions: [SeedManager], });从源码实现看SeedManager.register()会通过orm.config.registerExtension(mikro-orm/seeder, ...)把种子管理器挂到 ORM 实例上见 SeedManager.ts之后即可通过orm.seeder访问。核心包在 MikroORM.ts 中暴露了get seeder()访问器用于运行种子脚本在MikroORM.init()时MikroORM.ts 也会尝试加载该扩展。注意mikro-orm/seeder与faker-js/faker相互独立自 v6 起Faker 不再从 seeder 包中重新导出需要单独安装。配置项与默认值Seeder 的默认设置可通过 MikroORM 配置轻松覆盖。seeder.path与seeder.pathTs的作用方式与实体发现中的entities/entitiesTs一致前者指向编译后的 JS 文件目录后者指向 TS 源码目录。以下是完整配置项及其默认值MikroORM.init({ seeder: { path: ./seeders, // seeder 文件目录编译后的 JS pathTs: undefined, // TS seeder 文件目录使用时应将编译产物路径放到 path defaultSeeder: DatabaseSeeder,// 默认 seeder 类名 glob: !(*.d).{js,ts}, // seeder 文件匹配规则所有 .js/.ts排除 .d.ts emit: ts, // seeder 生成模式js | ts fileName: (className: string) className, // seeder 文件命名约定 }, });这些选项同样定义在核心包的 Configuration.ts 的SeederOptions接口中并额外支持seedersList字段——直接以类引用或{ name, class }对象形式注册 seeder替代基于文件系统的发现机制此时SeedManager.seedString()会优先查表而不是扫描目录见 SeedManager.ts。配置还支持通过环境变量覆盖变量前缀为MIKRO_ORM_SEEDER_参见 env-vars.tsMIKRO_ORM_SEEDER_PATHMIKRO_ORM_SEEDER_PATH_TSMIKRO_ORM_SEEDER_EMITMIKRO_ORM_SEEDER_GLOBMIKRO_ORM_SEEDER_DEFAULT_SEEDER一个值得注意的自动探测行为SeedManager.init()内部会在未显式配置路径时检查项目是否存在src、dist、build目录若./seeders默认目录不存在会自动把path指向./dist/seeders或./build/seeders、pathTs指向./src/seeders帮助新项目免配置启动见 SeedManager.ts。编写 Seeder 类Seeder 类继承自mikro-orm/seeder的Seeder基类只需实现一个run(em)方法。执行npx mikro-orm seeder:run时该方法即被调用在其中定义要向数据库写入什么数据、如何写入。可以用 EntityManager 直接创建实体也可以使用实体工厂见下文。用 CLI 生成 Seeder 骨架CLI 提供seeder:create命令参数可以是名字、类名或连字符形式的名字npx mikro-orm seeder:create DatabaseSeeder # 生成类 DatabaseSeeder npx mikro-orm seeder:create test # 生成类 TestSeeder npx mikro-orm seeder:create project-names # 生成类 ProjectNamesSeeder命令默认在./seeders/目录生成文件目录可通过seeder.path或环境变量MIKRO_ORM_SEEDER_PATH修改。类名格式化逻辑在 CreateSeederCommand.ts剥离名称中已有的Seeder后缀、按-分段并首字母大写最后统一拼接Seeder后缀。生成的文件内容由emit决定——TS 模式生成export class XxxSeeder extends SeederJS 模式生成 CommonJS 风格模块见 SeedManager.ts。一个最基本的 Seederimport { EntityManager } from mikro-orm/core; import { Seeder } from mikro-orm/seeder; import { Author } from ./author export class DatabaseSeeder extends Seeder { async run(em: EntityManager): Promisevoid { // 会自动持久化 const author em.create(Author, { name: John Snow, email: snowwall.st }); // 如果改用 const author new Author() 创建 // 则需要显式调用 em.persist(author)。 } }需要特别说明的是seeder 场景下的 EntityManager 强制启用了persistOnCreate即使 ORM 全局配置中显式关闭也是如此——SeedManager构造时会对 fork 出的 EM 执行this.#config.set(persistOnCreate, true)见 SeedManager.ts。因此em.create()会自动附带em.persist()而通过实体构造函数new Author()创建的对象不会被自动跟踪必须手动em.persist()。此外无论通过命令行还是编程方式运行 seederrun方法执行完毕后都会自动执行一次flush和clearSeedManager.seed()在每次运行 seeder 后调用this.#em.flush()与this.#em.clear()见 SeedManager.ts。在 Seeder 中使用实体工厂与其为每个实体手写全部属性不如使用实体工厂批量生成大量记录。在run中实例化工厂并调用make即可import { EntityManager } from mikro-orm/core; import { Seeder } from mikro-orm/seeder; import { AuthorFactory } from ../factories/author.factory export class DatabaseSeeder extends Seeder { async run(em: EntityManager): Promisevoid { new AuthorFactory(em).make(10); // 生成 10 个 author 实体 } }注意make只负责生成并持久化persist实体不会立即 flush最终 flush 由 SeedManager 在 seeder 结束后统一完成。组合多个 Seedercall 方法与共享上下文当单个 seeder 文件过大时可以用call方法把数据库种子脚本拆分成多个文件。call接收一个em和一个 seeder 类数组import { EntityManager } from mikro-orm/core; import { Seeder } from mikro-orm/seeder; import { AuthorSeeder, BookSeeder } from ../seeders export class DatabaseSeeder extends Seeder { run(em: EntityManager): Promisevoid { return this.call(em, [ AuthorSeeder, BookSeeder, ]); } }从 Seeder.ts 的源码可以看到call的实现细节它会为每个子 seederem.fork()出一个独立上下文实例化后执行其run方法并在每个子 seeder 结束后单独flush——这意味着子 seeder 之间天然隔离但也意味着跨 seeder 的实体引用不能依赖同一 EM 的隐式追踪而要通过共享上下文传递。call会自动创建共享上下文对象作为run方法的第二个参数传给每个子 seeder。这非常适合生成相互引用的实体AuthorSeeder先把作者写入上下文BookSeeder再从中取出并关联export class AuthorSeeder extends Seeder { async run(em: EntityManager, context: Dictionary): Promisevoid { // 把实体保存到上下文 context.author em.create(Author, { name: ..., email: ..., }); } }export class BookSeeder extends Seeder { async run(em: EntityManager, context: Dictionary): Promisevoid { em.create(Book, { title: ..., author: context.author, // 使用上下文中的实体 }); } }实体工厂Entity Factory测试场景下与其在测试开始前手工编写每条记录的每个属性不如用Factory为实体定义一组默认属性。下面是一个针对 Author 实体的工厂示例import { Factory } from mikro-orm/seeder; import { faker } from faker-js/faker; import { Author } from ./entities/author.entity; export class AuthorFactory extends FactoryAuthor { model Author; definition(): PartialAuthor { return { name: faker.person.findName(), email: faker.internet.email(), age: faker.random.number(18, 99), }; } }工厂需要满足两点model属性指明工厂为哪个实体生成实例definition方法返回创建实体时应用的默认属性集。示例中借助 Faker 库方便地生成各类随机测试数据。从基类实现看FactoryTEntity, TInput的makeEntity会调用definition得到默认数据再通过this.em.create(model, data, { persist: false })创建实体并应用each回调见 Factory.ts。用工厂创建实体工厂定义好后即可用于生成实体导入工厂、实例化并调用makeOneconst author new AuthorFactory(orm.em).makeOne();生成多个实体调用make方法参数为生成数量// 生成 5 个 authors const authors new AuthorFactory(orm.em).make(5);覆盖默认属性可以向make/makeOne传入对象覆盖部分默认值只有指定属性被替换其余保持工厂默认值// 生成单个 author const author new AuthorFactory(orm.em).makeOne({ name: John Snow, }); // 生成 5 个 authors const authors new AuthorFactory(orm.em).make(5, { name: John Snow, });持久化实体create/createOne在实例化实体的同时通过 EntityManager 的persistflush将其写入数据库// 生成并持久化单个 author const author await new AuthorFactory(orm.em).createOne(); // 生成并持久化 5 个 authors const authors await new AuthorFactory(orm.em).create(5);同样支持传入覆盖参数// 生成并持久化单个 author const author await new AuthorFactory(orm.em).createOne({ name: John Snow, }); // 生成并持久化 5 个 authors const authors await new AuthorFactory(orm.em).create(5, { name: John Snow, });各方法的差异在 Factory.ts 中一目了然makeOne/make只调用em.persist()不 flushcreateOne/create则在此基础上额外执行await this.em.flush()见 Factory.ts。在 seeder 场景下由于 SeedManager 结束时会统一 flushmake系列通常就已足够在测试中若需立即落库则应使用create系列。工厂关系生成单实体的海量数据生成固然方便但多数情况下你需要同时为多个实体造数并建立它们之间的关系。通过.each()定义关系each方法可以链式调用接收一个对工厂输出实体进行转换的函数在实体返回前应用// ManyToOne / OneToOne 关系 const books: Book[] new BookFactory(orm.em).each(book { book.author new AuthorFactory(orm.em).makeOne(); }).make(5);// OneToMany / ManyToMany 关系 const books: Book[] new BookFactory(orm.em).each(book { book.owners.set(new OwnerFactory(orm.em).make(5)); }).make(5);从 Factory.ts 可见each只是保存一个回调接收实体与序号 index由makeEntity在实体创建后调用因此也适用于create系列在持久化前应用。通过.definition()定义关系另一种方式是在definition方法内部构建嵌套实体。该方法还可以接收不属于实体 schema 的额外参数export class AuthorFactory extends Factory AuthorEntity, EntityDataAuthorEntity { booksCount?: number } { model AuthorEntity; async definition( params?: EntityDataAuthorEntity { booksCount?: number } ): EntityDataAuthorEntity { const name params.name ?? faker.person.findName(); const books params.books ?? ( [...Array(params?.booksCount ?? 0)].map((v, i) new BookFactory(this.em).makeEntity({ title: ${name} Trilogy - Part ${i 1} }) ) ); return { ...params, name, books }; } } // 最终调用 new AuthorFactory(em).createOne({ booksCount: 4 })这里通过第二个泛型参数扩展了TInput类型使工厂支持自定义输入参数如booksCount并在definition中利用this.em基类构造时保存的 EntityManager嵌套调用其他工厂生成关联实体。注意makeEntity在定义关系时是理想选择——它创建实体但不持久化避免生成中间实体产生多余写入。在命令行中使用执行seeder:run即可为数据库填充种子数据。默认运行DatabaseSeeder类其内部可继续调用其他 seeder默认类可通过seeder.defaultSeeder或环境变量MIKRO_ORM_SEEDER_DEFAULT_SEEDER修改也可用--class显式指定npx mikro-orm seeder:run npx mikro-orm seeder:run --classBookSeederCLI 命令的实现见 DatabaseSeedCommand.tsseeder:run支持-c, --class选项最终调用orm.seeder.seedString(className)未指定时回退到配置的defaultSeeder见 DatabaseSeedCommand.ts。migrate:fresh与schema:fresh命令配合--seed选项可以做到全量重建先删除所有表再重跑全部迁移或基于当前实体重新生成 schema最后执行DatabaseSeedernpx mikro-orm migration:fresh --seed # 删库、运行全部迁移并执行 DatabaseSeeder npx mikro-orm schema:fresh --seed # 重建数据库并执行 DatabaseSeeder若不想运行DatabaseSeeder可修改默认 seeder 配置或直接显式指定类名npx mikro-orm migration:fresh --seed TestSeeder # 删库、运行全部迁移并执行 TestSeeder npx mikro-orm schema:fresh --seed ProjectsSeeder # 重建数据库并执行 ProjectsSeeder在测试中使用有了 seeder 和工厂测试前初始化数据变得非常简洁。典型模式如下let orm: MikroORM; beforeAll(async () { // 初始化 ORM orm await MikroORM.init({ ... }); // 刷新数据库从干净状态开始 await orm.schema.refresh(); // 然后运行 seeder await orm.seeder.seed(DatabaseSeeder); }); test(() { // 执行测试 }); afterAll(async () { // 关闭连接 await orm.close(); });其中orm.seeder.seed(DatabaseSeeder)直接以类引用运行 seeder对应SeedManager.seed()见 SeedManager.ts与 CLI 按类名字符串查找的seedString()不同适合在测试等已加载类的场景使用。仓库测试目录中也有现成范例例如 tests/database/seeder/database.seeder.ts 演示了call(em, [UserSeeder, ProjectSeeder])的组合写法。生产环境运行编译后的 Seeder在生产环境你可能希望使用编译后的 seeder 文件。只需相应配置 ORM 中的 seeder 路径import { MikroORM, Utils } from mikro-orm/core; await MikroORM.init({ seeder: { path: dist/seeders, pathTs: src/seeders, }, // 或者用自动检测 // seeder: { // path: Utils.detectTypeScriptSupport() ? src/seeders : dist/seeders, // }, // ... });这样可以在 CLI 中通常已启用 TS 支持生成 TS 格式的 seeder 文件同时在生产环境使用编译后的 JS 文件。运行时是否启用 TS 模式由preferTs配置与Utils.detectTypeScriptSupport()共同决定——SeedManager 初始化时会据此在pathTs与path之间选择实际扫描目录见 SeedManager.ts。小结MikroORM 的 seeding 体系由三层构成Seeder基类Seeder.ts负责组织种子脚本与组合执行Factory基类Factory.ts负责按默认定义批量生成实体SeedManagerSeedManager.ts负责目录发现、类加载与执行编排。三者配合seeder:create、seeder:run、schema:fresh --seed、migration:fresh --seed等 CLI 命令即可在开发、测试与生产环境中统一、可重复地管理数据库初始数据——这也是初始化演示数据、搭建测试基座、快速重建环境时最省力的实践路径。赞分享后端【免费下载链接】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 Seeding 完整指南用 Seeder 与 Entity Factory 高效填充数据库MikroORM Seeding 完整指南用 Seeder 与 Entity Factory 高效填充数据库 在初始化应用、跑测试或搭建演示环境时手动逐条插后端Phinx数据库种子(Seeding)功能详解Phinx数据库种子 Seeding 功能详解 什么是数据库种子 Seeding 数据库种子 Seeding 是Phinx从0.5.0版本开始引入的一项重要功能数据库后端开发工具MikroORM Seeding 实战指南用 Seeders 与 Entity Factories 高效填充数据库MikroORM Seeding 实战指南用 Seeders 与 Entity Factories 高效填充数据库 初始化应用、编写集成测试时手动为数据库准后端上一篇NeoEloquent 迁移完整教程neo4j:migrate 系列命令从入门到精通下一篇Social-Auto-Upload跨平台Cookie持久化架构与异步验证机制设计创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表