后端【免费下载链接】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点击查看免费下载物化视图Materialized View将查询结果物理存储在数据库中以牺牲数据新鲜度为代价换取更快的读取性能。本篇指南基于当前仓库mikro-orm的官方文档与源码实现完整讲解如何在 MikroORM 中以四种方式定义物化视图实体、控制创建时的数据填充WITH DATA/WITH NO DATA、通过refreshMaterializedView()手动与并发刷新、像普通实体一样查询以及 Schema 生成器对物化视图的自动创建、删除与变更检测。读完本文你将掌握一套可直接落地的物化视图集成方案并理解其底层 SQL 生成与只读约束的实现原理。物化视图与普通视图的本质区别普通视图View在查询时才动态执行其定义中的 SQL数据始终与底层表保持一致但每次读取都有计算开销。物化视图则把查询结果预先计算并落盘存储读取时直接扫描已物化的数据因此读性能显著更好代价是数据可能过期——底层表发生变化后物化视图不会自动同步必须显式执行刷新REFRESH MATERIALIZED VIEW才能反映最新数据。在 MikroORM 中视图实体包括普通视图与物化视图都通过实体选项中的view字段声明两者仅差一个开关普通视图view: true或view: {}物化视图view: { materialized: true }。定义物化视图实体创建物化视图实体只需在实体选项中设置view: { materialized: true }并同时提供expression物化视图的定义 SQL即AS之后的查询语句与tableName。MikroORM 支持四种等价的定义方式你可以根据自己的元数据提供方式装饰器反射、ts-morph 静态分析或纯代码定义任选其一。方式一defineEntity class推荐类型安全用defineEntity描述 Schema再导出一个继承该 Schema 的 class兼顾代码定义的类型安全与实体类的可实例化import { defineEntity, p } from mikro-orm/postgresql; const AuthorStatsSchema defineEntity({ name: AuthorStats, tableName: author_stats_matview, view: { materialized: true }, expression: select a.id, a.name, count(b.id)::int as book_count from author a left join book b on b.author_id a.id group by a.id , properties: { id: p.integer().primary(), name: p.string(), bookCount: p.integer(), }, }); export class AuthorStats extends AuthorStatsSchema.class {} AuthorStatsSchema.setClass(AuthorStats);这里name是实体名称用于 MikroORM 元数据与关系解析tableName是物化视图在数据库中的真实名称expression是视图定义体。p.integer().primary()声明主键——虽然物化视图实体在 ORM 层是只读的但主键仍可用于标识与查询条件这也是后续「并发刷新」所需的唯一索引能够被 Schema 生成器感知的基础。方式二defineEntity纯代码定义如果不需要实体类实例直接导出defineEntity的返回值即可import { defineEntity, p } from mikro-orm/postgresql; export const AuthorStats defineEntity({ name: AuthorStats, tableName: author_stats_matview, view: { materialized: true }, expression: select a.id, a.name, count(b.id)::int as book_count from author a left join book b on b.author_id a.id group by a.id , properties: { id: p.integer().primary(), name: p.string(), bookCount: p.integer(), }, });方式三reflect-metadata 装饰器使用Entity装饰器时把view: { materialized: true }与expression放进实体选项属性则照常用PrimaryKey()与Property()声明import { Entity, Property, PrimaryKey } from mikro-orm/postgresql; Entity({ tableName: author_stats_matview, view: { materialized: true }, expression: select a.id, a.name, count(b.id)::int as book_count from author a left join book b on b.author_id a.id group by a.id , }) export class AuthorStats { PrimaryKey() id!: number; Property() name!: string; Property() bookCount!: number; }方式四ts-morph 静态分析ts-morph方式在代码写法上与装饰器方式完全一致区别在于元数据是在编译期由 ts-morph 从实体类源码中静态提取的运行时不再依赖reflect-metadataimport { Entity, Property, PrimaryKey } from mikro-orm/postgresql; Entity({ tableName: author_stats_matview, view: { materialized: true }, expression: select a.id, a.name, count(b.id)::int as book_count from author a left join book b on b.author_id a.id group by a.id , }) export class AuthorStats { PrimaryKey() id!: number; Property() name!: string; Property() bookCount!: number; }仓库中的集成测试也印证了这一配置形式在 view-entities.postgres.test.ts 中测试实体AuthorStatsMaterialized使用view: { materialized: true }AuthorStatsNoData使用view: { materialized: true, withData: false }且二者都声明了主键属性。测试还验证了实体元数据会自动带上三个标志L976-L981const meta orm.getMetadata().get(AuthorStatsMaterialized); expect(meta.view).toBe(true); expect(meta.materialized).toBe(true); expect(meta.readonly).toBe(true);也就是说只要声明了view: { materialized: true }ORM 会自动把该实体标记为只读readonly: true详见下文「只读行为」一节。创建时不填充数据withData: false默认情况下物化视图创建时会立即执行定义查询并填充数据对应 PostgreSQL 的CREATE MATERIALIZED VIEW ... WITH DATA。如果你希望先创建一个空的物化视图、稍后再填充数据可以设置withData: falseconst AuthorStats defineEntity({ name: AuthorStats, tableName: author_stats_matview, view: { materialized: true, withData: false }, // Creates with WITH NO DATA expression: select ..., properties: { ... }, });这在两种场景下尤其有用一是Schema 创建时底层表还是空的——此时物化视图定义中的关联查询无数据可物化先用WITH NO DATA建好结构待数据就绪后再刷新二是希望精确控制首次数据填充的时机把昂贵的聚合计算推迟到业务低峰期统一执行。从源码看withData直接决定了生成的 SQL 后缀。PostgreSQL 方言的 SchemaHelper 在 PostgreSqlSchemaHelper.ts 中实现override createMaterializedView( name: string, schema: string | undefined, definition: string, withData true, ): string { const viewName this.quote(this.getTableName(name, schema)); const dataClause withData ? with data : with no data; return create materialized view ${viewName} as ${definition}${dataClause}; }注意withData的默认值是true且 Schema 生成器在调用时使用了view.withData ?? true兜底SqlSchemaGenerator.ts因此不显式声明时始终按「带数据创建」处理。一个值得注意的细节对于WITH NO DATA的物化视图Schema 生成器会跳过其索引的创建——因为视图还没有数据可索引索引会在后续REFRESH填充数据后、由下一次schema:update补建。该行为在 SqlSchemaGenerator.ts 的注释与分支中有明确体现。刷新物化视图物化视图缓存了查询结果因此要让数据反映底层表的最新状态必须刷新它。MikroORM 在PostgreSqlEntityManager以及pglite共用的基类BasePostgreSqlEntityManager上提供了refreshMaterializedView方法import { MikroORM, EntityManager } from mikro-orm/postgresql; const orm await MikroORM.init({ ... }); const em orm.em; // Refresh the materialized view await em.refreshMaterializedView(AuthorStats); // Now queries will return the updated data const stats await em.find(AuthorStats, {});并发刷新PostgreSQL 支持并发刷新物化视图REFRESH MATERIALIZED VIEW CONCURRENTLY刷新过程中视图仍可被读取不会阻塞查询。使用并发刷新的前提是物化视图上至少存在一个唯一索引否则 PostgreSQL 会直接报错// Refresh concurrently (requires unique index on the view) await em.refreshMaterializedView(AuthorStats, { concurrently: true });底层实现refreshMaterializedView的完整实现位于 BasePostgreSqlEntityManager.ts其调用链可以总结为三步校验实体类型通过this.getMetadata(entityName)获取元数据若meta.view或meta.materialized不为真立即抛出Entity ${meta.className} is not a materialized view。仓库测试 view-entities.postgres.test.ts 专门验证了向普通实体Author2调用该方法会抛出此错误确定目标 Schemaschema meta.schema ?? this.config.get(schema)——优先使用实体上显式声明的schema否则回退到全局配置的默认 schema生成并执行 SQL交给当前平台的 SchemaHelper 生成REFRESH MATERIALIZED VIEW语句并通过this.execute(sql)执行。对应 SQL 模板在 PostgreSqlSchemaHelper.tsoverride refreshMaterializedView(name: string, schema?: string, concurrently false): string { const concurrent concurrently ? concurrently : ; return refresh materialized view${concurrent} ${this.quote(this.getTableName(name, schema))}; }即concurrently: true时生成refresh materialized view concurrently author_stats_matview否则生成不带concurrently的普通刷新语句。测试 view-entities.postgres.test.ts 演示了完整的「写入→刷新→读到新数据」工作流先插入新作者与书籍并 flush此时物化视图仍只显示旧数据调用refreshMaterializedView后重新查询新作者才出现在结果中——这正是物化视图「手动刷新才更新」语义的直接验证。查询物化视图物化视图实体与普通实体无异可以完全使用 MikroORM 的查询 API// Find all const allStats await em.find(AuthorStats, {}); // Find with conditions const prolificAuthors await em.find(AuthorStats, { bookCount: { $gte: 5 }, }); // Find one const authorStats await em.findOne(AuthorStats, { name: Jon Snow });查询条件支持 MikroORM 的全部操作符体系如$gte、$in、$like等物化视图实体也可以出现在关系、QueryBuilder、原生 SQL 等场景中。仓库测试 view-entities.postgres.test.ts 中em.find(AuthorStatsMaterialized, {})返回的实体数据与物化视图内容完全一致确认了查询路径与普通实体相同。只读行为物化视图实体会被自动标记为只读readonly: true。尝试对其实例做修改并 flushMikroORM不会生成任何 UPDATE 语句——变更既不会写入物化视图物化视图本就无法直接写入也不会被静默应用到其他地方const stats await em.findOne(AuthorStats, { id: 1 }); stats.bookCount 100; // This change wont be persisted await em.flush(); // No UPDATE will be generated for this entity这一自动行为同样被源码与测试双重确认元数据测试断言了meta.readonly trueview-entities.postgres.test.ts。若业务上确实需要「写入」物化视图数据正确的做法是刷新其依赖的底层表数据后再调用refreshMaterializedView()让视图重新物化。Schema 生成Schema 生成器orm.schema对物化视图提供了一等支持创建、删除、更新三条命令都会自动包含物化视图的处理// Create schema (includes CREATE MATERIALIZED VIEW statements) await orm.schema.create(); // Drop schema (includes DROP MATERIALIZED VIEW statements) await orm.schema.drop(); // Update schema (detects changes to materialized views) await orm.schema.update();生成的 SQL创建物化视图时默认生成的 SQL 如下对应withData默认truecreate materialized view author_stats_matview as select a.id, a.name, count(b.id)::int as book_count from author a left join book b on b.author_id a.id group by a.id with data;当设置withData: false时尾部变为with no datacreate materialized view author_stats_matview as select ... with no data;删除时生成的是带cascade的语句PostgreSqlSchemaHelper.tsdrop materialized view if exists author_stats_matview cascade测试 view-entities.postgres.test.ts 验证了getDropSchemaSQL()输出中包含drop materialized view与视图名。变更检测与依赖排序Schema 更新schema:update不仅会创建与删除物化视图还会检测物化视图之间的依赖关系。在 SqlSchemaGenerator.ts 中生成器会对每个视图定义的FROM/JOIN子句做正则匹配如果视图 A 的定义引用了视图 B则 B 必须先于 A 创建从而保证依赖顺序正确。仓库还提供了专门的 diffing 测试 materialized-view-diffing.postgres.test.ts含对应快照用于验证 Schema 生成器对物化视图变更如定义变化的检测与增量 SQL 生成。元数据加载物化视图也会被 Schema 内省introspection识别。PostgreSQL 方言通过查询pg_matviews系统视图加载物化视图及其定义、填充状态ispopulated见 PostgreSqlSchemaHelper.tsloadMaterializedViews会把is_populated映射为withData字段存入DatabaseSchema的DatabaseView对象DatabaseSchema.ts同时加载其上的索引——这意味着从已有数据库反向生成实体entity generator时也能识别物化视图。限制使用物化视图实体时需明确以下边界仅支持 PostgreSQL物化视图只在 PostgreSQL 方言含pglite上受支持。其他数据库在使用view: { materialized: true }时会直接报错。这一点在源码中有明确证据——基类 SchemaHelper.ts 中createMaterializedView、dropMaterializedViewIfExists、refreshMaterializedView、getListMaterializedViewsSQL的默认实现全部抛出Not supported by given driver仅 PostgreSQL 方言覆写了这些方法没有自动刷新MikroORM 不会在每次查询或每次 flush 后自动刷新物化视图数据的新鲜度完全由你控制。必须手动调用refreshMaterializedView()或在数据库层面配置刷新机制如触发器、pg_cron定时任务、应用层调度器等。最佳实践按数据变化频率选择视图类型对频繁变化的数据普通视图view: true反而是更合适的选择——它始终实时无需维护刷新周期物化视图适合变化不频繁、或能接受一定数据滞后的聚合统计场景报表、排行榜、计数等。为常用查询列建立索引物化视图支持索引应针对高频查询条件建索引。例如为下面的列建立唯一索引与非唯一索引CREATE UNIQUE INDEX author_stats_id_idx ON author_stats_matview (id); CREATE INDEX author_stats_book_count_idx ON author_stats_matview (book_count);其中唯一索引是启用并发刷新的硬性前提。由于物化视图实体的主键声明会被 Schema 生成器感知你也可以直接让实体主键对应的唯一索引随视图一起创建。在低峰期调度刷新刷新物化视图会重新执行聚合查询并重写整块数据开销较大。建议使用数据库调度器如pg_cron或应用层定时任务把刷新安排在业务低流量时段对于WITH NO DATA创建的空视图首次填充也应选在数据就绪后的低峰期。生产环境优先使用并发刷新如果应用在刷新期间仍需要读取该视图务必使用{ concurrently: true }前提是视图上有唯一索引避免刷新过程中的锁阻塞读请求刷新频率低、允许短暂不可读的场景普通刷新开销更小。监控视图体积物化视图会持续占用磁盘空间且每次刷新都会产生写放大。应监控其大小增长对超大数据集考虑分区partitioning等策略防止物化视图成为新的存储瓶颈。赞分享后端【免费下载链接】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点击查看免费下载相关推荐LogicFlow 文档站加载失败三步让本地示例与文档离线跑起来LogicFlow 文档站加载失败三步让本地示例与文档离线跑起来 LogicFlow 是专注于业务自定义的流程图编辑框架。克隆仓库后示例跑不起来或官网一直后端跨平台文本编辑器 Notepad-- 5 步跑通目录搜索与文件对比跨平台文本编辑器 Notepad 5 步跑通目录搜索与文件对比 你手头有一整目录散落的代码和配置改一处要翻遍几十上百个文件Notepad 帮你在一个窗口里搜桌面应用Presto 物化视图刷新完全指南REFRESH MATERIALIZED VIEW 语法、增量刷新与谓词缝合原理Presto 物化视图刷新完全指南REFRESH MATERIALIZED VIEW 语法、增量刷新与谓词缝合原理 物化视图Materialized Vie大数据数据库后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考