ARTICLE DETAIL

资讯详情

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

MikroORM 多 Schema 使用指南:实体定义、运行时切换与 SQLite ATTACH DATABASE 实战

MikroORM 多 Schema 使用指南:实体定义、运行时切换与 SQLite ATTACH DATABASE 实战 后端【免费下载链接】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 7.0 官方文档docs/versioned_docs/version-7.0/multiple-schemas.md编写系统讲解如何在 MySQL、PostgreSQL、SQLite/libSQL 中将实体定义到多个 SchemaMySQL 语境中即 database涵盖实体级 Schema 声明、EntityManager/EntityRepository/QueryBuilder运行时切换、通配符 Schema、默认 Schema 与 SQLiteATTACH DATABASE多库挂载等完整实战方案。读完本文你将掌握多租户数据隔离、读写分离数据库分库、日志/用户数据独立存储等场景下的 MikroORM 配置与编码方式并能结合源码与测试理解其底层实现原理。一、多 Schema 支持概览与使用前提在 MySQL、PostgreSQL 以及通过ATTACH DATABASE的 SQLite 中都可以把实体定义到多个 Schema 上。MySQL 术语中称为 database但从实现角度看它就是 schema。MikroORM 官方文档强调了一个重要前提要使用多 Schema你的连接需要能访问所有这些 Schema单个 MikroORM 实例中不支持多连接。也就是说多 Schema 方案依赖一个连接 多个命名空间而不是为每个库建立独立连接池。这一前提与后文 SQLiteattachDatabases的实现完全吻合——从 BaseSqliteConnection.ts 的源码可以看到所有附加数据库都是通过同一条连接的attach databaseSQL 语句挂载的。二、在实体上声明 Schema最简单的方式是直接在实体上通过schema选项声明或者在自定义表名tableName中带上 Schema 前缀Entity({ schema: first_schema }) export class Foo { ... } // 或者使用带 schema 前缀的自定义表名 Entity({ tableName: second_schema.bar }) export class Bar { ... }此后照常使用这些实体即可。生成的 SQL 会把该tableName值作为表名使用因此只要连接能访问对应 Schema一切都会按预期工作。例如 PostgreSQL 驱动最终生成的查询形如select ... from first_schema.foo这一点可以在 multiple-schemas.postgres.test.ts 的 SQL 断言中看到如select a0.* from n1.author as a0 ...、from n5.book ...。tableName与schema两种方式的效果等价推荐优先使用schema选项因为它语义更清晰且在通配符 Schema、SchemaGenerator等场景下行为更可预期。三、运行时指定 SchemaEntityManager / EntityRepository / QueryBuilder除了实体级声明还可以在查询时动态指定 Schema适合多租户一张表、按租户切换库的场景// 通过 FindOptions 指定 schema const user await em.findOne(User, { ... }, { schema: client-123 });EntityRepository的find/findOne等方法同样接受该选项QueryBuilder则通过.withSchema()方法在生成 SQL 时限定 Schema。在测试 attach-database.sqlite.test.ts 中should use dynamic schema from FindOptions 用例验证了em.findOne(UserProfile, { id }, { schema: users_db })可以跨附加数据库正确读取实体。向指定 Schema 写入数据要创建实体到特定 Schema需要使用QueryBuilder的withSchema()const qb em.createQueryBuilder(User); await qb.insert({ email: foobar.com }).withSchema(client-123);此外em.insert()/em.insertMany()也支持通过选项指定 Schema这在 multiple-schemas.postgres.test.ts 的 use different schema via options in em.insert/Many 用例中有完整验证em.insert(Book, book31, { schema: n3 })与em.insertMany(Book, [book51, book52], { schema: n5 })会分别生成insert into n3.book ...与insert into n5.book ... returning id。需要注意的是同一个实体集合中的不同实例可以分属不同 Schema 用于持久化但加载时一次只能从一个 Schema 读取因为单个查询无法跨多个 Schema 联表注释见 multiple-schemas.postgres.test.ts。四、在 EntityManager 上设置默认 Schema如果不想在每个实体或每次操作上都声明 Schema可以.fork()一个 EntityManager 并设置默认 Schemaconst fork em.fork({ schema: client-123 }); await fork.findOne(User, { ... }); // 等价于 const user await em.findOne(User, { ... }, { schema: client-123 });创建实体时fork 出的 EM 同样会套用默认 Schemaconst fork em.fork({ schema: client-123 }); const user new User(); user.email foobar.com; await fork.persist(user).flush(); // 等价于 const qb em.createQueryBuilder(User); await qb.insert({ email: foobar.com }).withSchema(client-123);从源码看这一行为由 EntityManager.fork() 实现fork.#schema options.schema ?? em.#schema见 EntityManager.ts即 fork 出来的新实例会继承父 EM 的 Schema并允许通过options.schema覆盖。运行时设置或清除 Schemaem.schema client-123; // 直接赋值 const fork em.fork({ schema: client-1234 }); fork.schema null; // 清除默认 schemaEntityManager.schema是上下文感知的如果在 RequestContext 处理器 内执行全局 EM 会返回当前请求上下文对应的 Schema。这使得同一份代码在多租户请求中无需手动传参即可命中正确的 Schema。五、通配符 Schema一个实体对应多个 Schema某些场景下同一实体需要在多个 Schema 中同时存在例如每个租户一个库。MikroORM 支持用*声明通配符 SchemaEntity({ schema: * }) export class Book { PrimaryKey() id!: number; Property({ nullable: true }) name?: string; ManyToOne(() Author, { nullable: true, deleteRule: cascade }) author?: Author; ManyToOne(() Book, { nullable: true }) basedOn?: Book; }这类实体在默认情况下会被SchemaGenerator忽略因为生成器无法凭空猜测要建在哪个 Schema你必须通过create/update/drop方法的schema选项或 CLI 的--schema参数明确指定目标 Schema。在运行时通配符 Schema 会被按以下优先级替换为实际 SchemaFindOptions.schemaEntityManager.schemaORM 配置中的schema选项这一点在 multiple-schemas.postgres.test.ts 中得到充分验证测试先对n2~n5分别执行orm.schema.update({ schema: nX })见该文件第 79-82 行再通过orm.config.set(schema, n2)第 83 行让通配符实体默认落到n2。flush 后实体的 Schema 会被保存wrap(book).getSchema()返回n2身份标识映射键Identity Map key也会带上 Schema 前缀如Book-n2:1、BookTag-n5:4见该文件第 119-133 行、第 221-239 行证明不同 Schema 中的同名实体在 Unit of Work 中互不混淆。关于迁移的注意事项目前多 Schema 动态实体不支持通过 ORM 迁移处理迁移总是忽略通配符 Schema 实体必须显式使用SchemaGenerator。考虑到这类实体的动态属性合理的做法是仅在需要时动态同步 Schema例如放到某个 API 端点中按需执行。如果仍然希望使用 ORM 迁移则需要手动把动态 Schema 的 SQL 语句追加到迁移文件里并建议对这些查询使用safe模式{ safe: true }。六、SQLite 多库ATTACH DATABASESQLite 通过ATTACH DATABASE命令支持多 Schema可以将额外数据库文件挂载到同一条连接上每个附加库充当一个独立 Schema表通过schema.table_name语法访问。配置 attachDatabases使用attachDatabases选项指定连接时要挂载的数据库import { MikroORM } from mikro-orm/sqlite; // 或 mikro-orm/libsql const orm await MikroORM.init({ dbName: ./main.db, entities: [Author, Book, UserProfile, LogEntry], attachDatabases: [ { name: users_db, path: ./users.db }, { name: logs_db, path: /var/data/logs.db }, ], });attachDatabases类型为{ name: string; path: string }[]其语义在 Configuration.ts 中有完整注释SQLite/libSQL: databases to attach on connection. Each attached database acts as a schema, accessible viaschema.tablesyntax. Entities can reference attached databases viaEntity({ schema: db_name }).。相对路径会根据baseDir选项解析未设置时基于当前工作目录。这一行为在 BaseSqliteConnection.ts 中实现fs.absolutePath(db.path, baseDir)之后生成attach database path as name语句attach-database.sqlite.test.ts 的 should resolve relative paths from baseDir 用例验证了相对路径确实落在baseDir下。定义挂载库中的实体附加数据库中的实体通过schema选项引用数据库名主库的 schema 可写为main也可省略。以下四种实体定义方式等价defineEntity class推荐类型安全import { defineEntity, p } from mikro-orm/core; // 主库实体主库 schema 可省略 const AuthorSchema defineEntity({ name: Author, schema: main, properties: { id: p.number().primary(), name: p.string(), }, }); export class Author extends AuthorSchema.class {} AuthorSchema.setClass(Author); // 附加库实体 const UserProfileSchema defineEntity({ name: UserProfile, schema: users_db, properties: { id: p.number().primary(), username: p.string(), }, }); export class UserProfile extends UserProfileSchema.class {} UserProfileSchema.setClass(UserProfile);defineEntityimport { defineEntity, p } from mikro-orm/core; export const Author defineEntity({ name: Author, schema: main, properties: { id: p.number().primary(), name: p.string(), }, }); export const UserProfile defineEntity({ name: UserProfile, schema: users_db, properties: { id: p.number().primary(), username: p.string(), }, });reflect-metadata装饰器Entity({ schema: main }) class Author { PrimaryKey() id!: number; Property() name!: string; } Entity({ schema: users_db }) class UserProfile { PrimaryKey() id!: number; Property() username!: string; }ts-morph// 与 reflect-metadata 相同仅元数据来源不同ts-morph 通过静态分析生成元数据 Entity({ schema: main }) class Author { ... } Entity({ schema: users_db }) class UserProfile { ... }实体间可以像普通实体一样建立关系附加库中的实体通过schema指向所属库挂载后所有库的表都可通过schema.table访问。Schema Generator 对附加库的完整支持schema generator对附加数据库提供完整支持会根据实体的schema选项在正确的附加数据库中建表检测并对所有附加数据库中的表做 diff为每个数据库生成正确的迁移 SQL// 在全部数据库主库 附加库中建表 await orm.schema.create(); // 跨所有数据库更新 schema await orm.schema.update();对应测试 attach-database.sqlite.test.ts 覆盖了这些行为pragma database_list能列出main、users_db、logs_db第 113-122 行schemaHelper.getAllTables()返回main.main_author、users_db.user_profile、logs_db.log_entry等第 203-215 行schema.update()能检测到users_db.user_profile上手工添加的bio列并生成drop column bio第 253-270 行getCreateSchemaSQL()不会输出create schema语句SQLite 使用 ATTACH 而非 CREATE SCHEMA但仍包含附加库建表语句第 272-279 行。附加库连接的生命周期细节对于 libSQL 驱动附加数据库是按连接保存的状态一旦底层连接被回收重建挂载信息会丢失。因此 BaseSqliteConnection.ts 把pragma foreign_keys on与附加语句一起定义为getConnectionSetupSql()连接设置 SQL在连接重建时重放。测试 attached databases survive a long-lived connectionattach-database.sqlite.test.ts通过vi.useFakeTimers模拟时间前进 60 秒后仍能查询附加库实体验证了这一机制replaying the setup restores the state a recycled connection lost第 488-501 行则手动 detach 后调用replayConnectionSetup()恢复挂载状态。SQLite 附加库的限制libSQL 远程连接使用远程 libSQL URLlibsql://、https://时不支持ATTACH DATABASE只能挂载本地文件数据库。该校验在 LibSqlConnection.ts 中实现抛错信息为 ATTACH DATABASE is not supported for remote libSQL connectionsattach-database.sqlite.test.ts 对libsql://与https://两种远程 URL 均有断言。跨库外键SQLite 允许同一连接内附加库之间的外键但 SQL 语法中被引用的表名不能包含 Schema 前缀——MikroORM 会自动处理这一差异。事务所有附加数据库共享同一条连接内的事务作用域。七、深入源码Schema 的底层流转结合上述特性可以从源码梳理 Schema 的完整流转链路配置层attachDatabases定义于 Configuration.ts仅对 SQLite/libSQL 驱动生效SqlitePlatform.supportsSchemas()会根据attachDatabases是否存在返回true见 SqlitePlatform.ts。连接层SQLite 连接建立时依次执行pragma foreign_keys on与附加库语句BaseSqliteConnection.tslibSQL 驱动在连接前校验远程 URL 并拒绝附加LibSqlConnection.ts。EM 层fork({ schema })把 Schema 存为 fork 实例的私有字段EntityManager.ts查询、插入时作为默认值参与 SQL 拼接。执行层ChangeSetPersister根据withSchema参数决定持久化 SQL 是否携带 Schema 限定ChangeSetPersister.ts通配符 Schema 在运行时按FindOptions.schema→EntityManager.schema→ ORM 配置schema的优先级解析。Identity Map 层Schema 参与身份标识键的构成Entity-schema:id保证同一主键在不同 Schema 中是不同的实体实例避免 Unit of Work 串扰见 multiple-schemas.postgres.test.ts。八、典型应用场景小结多租户隔离每个租户一个 Schema配合通配符 Schema em.fork({ schema })或FindOptions.schema一套实体服务所有租户按请求上下文动态切换。分库分表的数据分离将日志、用户资料、业务主数据拆到不同数据库文件SQLiteattachDatabases或不同 SchemaPostgreSQL/MySQL实体间仍可保持关系与级联操作。动态 Schema 同步对通配符实体使用SchemaGenerator的create/update/drop({ schema })或 CLI--schema参数按需建表/改表迁移文件以safe模式手动补充动态 SQL。需要提醒的是多 Schema 依赖单连接访问全部命名空间若你的数据库部署要求独立连接例如不同实例、不同账号则应考虑多个 MikroORM 实例而非本文所述的单实例多 Schema 方案。参考资料官方文档docs/versioned_docs/version-7.0/multiple-schemas.md配置定义packages/core/src/utils/Configuration.tsSQLite 附加库实现packages/sql/src/dialects/sqlite/BaseSqliteConnection.tslibSQL 远程校验packages/libsql/src/LibSqlConnection.ts持久化层 Schema 处理packages/core/src/unit-of-work/ChangeSetPersister.ts测试验证tests/features/attach-database/attach-database.sqlite.test.ts、tests/features/multiple-schemas/multiple-schemas.postgres.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点击查看免费下载相关推荐MikroORM 多 Schema 使用指南实体定义、运行时切换与通配符 Schema 原理MikroORM 多 Schema 使用指南实体定义、运行时切换与通配符 Schema 原理 导读 本文围绕 MikroORM 的多 Schema 支持展开后端MikroORM 多 Schema 使用完全指南多数据库架构、通配符 Schema 与 SQLite ATTACH DATABASE 实战MikroORM 多 Schema 使用完全指南多数据库架构、通配符 Schema 与 SQLite ATTACH DATABASE 实战 在 MySQL、P后端MikroORM 多 Schema 实战指南跨数据库 Schema 映射、Wildcard 租户与 SQLite ATTACH DATABASEMikroORM 多 Schema 实战指南跨数据库 Schema 映射、Wildcard 租户与 SQLite ATTACH DATABASE MikroO后端上一篇5分钟快速上手ncmdumpGUI免费解锁网易云音乐NCM文件终极指南下一篇CANN ops-math 静默数据损坏检测算子 aclnnSilentCheckV2 接口使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表