恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
MikroORM 中的 JSON 属性:定义、按对象属性查询与索引实战指南
首页
资讯中心
/
MikroORM 中的 JSON 属性:定义、按对象属性查询与索引实战指南
MikroORM 中的 JSON 属性:定义、按对象属性查询与索引实战指南
发布时间:2026/9/25 16:20:37
后端【免费下载链接】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 6.6 版本文档docs/versioned_docs/version-6.6/json-properties.md撰写并结合packages/core、packages/sql及各大驱动包的源码实现进行纵深讲解。你将掌握如何在实体中声明 JSON 属性、如何按 JSON 对象内部属性含任意嵌套层级进行查询、如何在 JSON 路径上创建普通索引与唯一索引以及不同数据库驱动PostgreSQL、MySQL、MariaDB、SQLite、MSSQL、MongoDB、Oracle在底层是如何各显神通实现统一语义的。MikroORM 的核心设计目标之一是让同一套实体定义与查询代码在不同数据库驱动之间获得一致的体验。JSON 属性正是这一设计理念的集中体现数据库驱动对 JSON 的处理方式差异巨大——有的自动解析为对象有的返回 JSON 字符串但 MikroORM 通过内置的JsonType统一了开发者的使用体验。定义 JSON 属性在实体上声明一个 JSON 属性非常简单只需要在Property()装饰器中指定type: jsonimport { Entity, Property } from mikro-orm/core; Entity() export class Book { Property({ type: json, nullable: true }) meta?: { foo: string; bar: number }; }当type被指定为json时MikroORM 会自动使用内置的JsonType来处理该属性的读写见 packages/core/src/types/JsonType.ts。这也是自定义类型体系中预置的一员——在 docs/versioned_docs/version-6.6/custom-types.md 中可以看到JsonType的作用是以驱动无关的方式处理序列化仅在需要时调用parse和stringify。此外在 packages/core/src/platforms/Platform.ts 的getMappedType()中object与json都会被映射到JsonType——这意味着即使不显式写type: json只要类型提示是一个对象例如通过 reflect-metadata 推断出的Object也会自动采用JsonType处理。JsonType 的底层行为从源码看JsonType的核心逻辑集中在以下几个方法convertToDatabaseValue(value, platform, context)当值为null/undefined时原样返回否则委托给platform.convertJsonToDatabaseValue(value, context)完成序列化convertToJSValue(value, platform, context)先判断当前列是否为 JSON 列json/jsonb或platform.getJsonDeclarationSQL()如果驱动本身能自动转换 JSONplatform.convertsJsonAutomatically()或属性是 object 类型的嵌入对象则直接返回原始值避免重复解析否则调用platform.convertJsonToJSValue反序列化getColumnType(prop, platform)返回platform.getJsonDeclarationSQL()即各驱动声明 JSON 列的标准 SQL 类型PostgreSQL 为jsonbMSSQL 为nvarchar(max)其余多数为jsoncompareAsType()返回any表示在变更集比较时按任意值处理。这套平台委托机制正是跨驱动一致体验的根源所有序列化/反序列化、列类型声明、查询路径生成都下沉到具体的Platform实现中去而实体代码始终保持驱动无关。按 JSON 对象属性查询从 v4.4.2 开始MikroORM 支持直接按 JSON 对象的内部属性进行查询。所谓按 JSON 对象属性查询是指查询条件可以写成与被查询 JSON 结构完全一致的对象字面量任意层级嵌套均可const b await em.findOne(Book, { meta: { valid: true, nested: { foo: 123, bar: 321, deep: { baz: 59, qux: false, }, }, }, });这段查询条件会逐层展开为对 JSON 内部路径的访问。在 PostgreSQL 上会生成如下 SQLselect e0.* from book as e0 where (meta-valid)::bool true and meta-nested-foo 123 and (meta-nested-bar)::float8 321 and (meta-nested-deep-baz)::float8 59 and (meta-nested-deep-qux)::bool false limit 1可以看到valid、bar、baz、qux这些布尔或数值类型的右侧值在 PostgreSQL 上会被自动加上::bool、::float8之类的显式类型转换cast从而保证与 JSON 内部值的比较是类型安全的。这正是 packages/sql/src/dialects/postgresql/BasePostgreSqlPlatform.ts 中getSearchJsonPropertyKey()的行为它根据右侧值的运行时类型从#jsonTypeCasts表中选取对应的 PostgreSQL 类型如boolean→bool、number→float8来包裹表达式。查询条件的底层展开过程当你在查询条件中写下形如{ meta: { nested: { foo: 123 } } }的对象时MikroORM 会经由 packages/core/src/platforms/Platform.ts 的processJsonCondition()递归处理如果当前值仍是一个普通对象且键不包含查询操作符如$in就继续向下展开路径把每一层键累积进path数组到达叶子节点时根据getJsonValueType()推断值的运行时类型字符串、数字、布尔若为数组则取首元素类型调用getSearchJsonPropertyKey(path, type, alias, value)生成最终的 SQL 表达式。而getSearchJsonPropertyKey()在各平台都有覆盖实现正是这些实现决定了同一查询在不同数据库上生成不同但语义等价的 SQL通用 SQL 平台packages/sql/src/AbstractSqlPlatform.ts使用json_extract(column, $.path)表达式并对路径键通过quoteJsonKey()做转义简单的字母数字键保持原样包含特殊字符的键用双引号包裹并按 JSON path 字符串语法转义\与PostgreSQL使用-与-运算符并配合类型转换MSSQLpackages/mssql/src/MsSqlPlatform.ts使用json_value(column, $.path)并将布尔值 cast 为bitOraclepackages/oracledb/src/OraclePlatform.ts同样使用json_value()表达式MongoDB由于 MongoDB 原生以 BSON 存储文档条件对象直接原样下推给驱动即可无需生成 SQL 路径表达式。操作符与排序JSON 属性查询不仅支持等值条件还完整支持 MikroORM 的查询操作符。仓库测试 tests/EntityManager.oracledb.test.ts 中有直接证据const b2 await orm.em.findOneOrFail(Book2, { meta: { category: { $in: [god like] }, items: 3 } }); // 支持操作符GH #1487同一测试还验证了嵌套多层 JSON 条件valid: truenested.foonested.deep.baz/qux都能命中同一条记录。此外JSON 属性同样可以用于排序见同文件 tests/EntityManager.oracledb.test.ts 的order by json properties测试const res await orm.em.fork().findAll(Book2, { orderBy: { meta: { nested: { deep: { baz: QueryOrder.DESC } } } }, });也就是说orderBy同样支持对 JSON 内部任意路径进行升降序排序。查询时的 JSON 序列化处理值得一提的是 packages/sql/src/AbstractSqlPlatform.ts 中的quoteValue()当绑定参数值是普通对象或带有JsonProperty标记时会自动JSON.stringify后再转义确保 JSON 值以正确的字符串形式进入 SQL。而在 MySQL 连接层packages/mysql/src/MySqlConnection.tsret.jsonStrings !this.platform.convertsJsonAutomatically()决定了驱动是否需要以 JSON 字符串形式返回结果——只有驱动本身能自动转换 JSON 的平台如 MongoDB、MySQL 的某些版本才会关闭该开关。驱动对 JSON 处理的差异一览驱动列声明类型是否自动转换 JSONJSON 路径表达式备注PostgreSQLjsonb否由 ORM 处理-/- 类型 cast检测到数字/布尔右侧值时自动 cast见 BasePostgreSqlPlatform.tsMySQLjson是convertsJsonAutomatically()为真json_extract索引使用json_value(...)MariaDBjson否MariaDbPlatform.tsjson_extract不支持 JSON 路径索引SQLitejson否SqlitePlatform.tsjson_extract依赖 SQLite 的 JSON1 扩展MSSQLnvarchar(max)否MsSqlPlatform.tsjson_value布尔值 cast 为bitMongoDBBSON原生文档是MongoPlatform.ts条件对象直接下推还保留 JSON 内的日期preservesDatesInsideJsonOraclejson是OraclePlatform.tsjson_value路径键同样经quoteJsonKey转义需要注意的是在 packages/core/src/platforms/Platform.ts 中convertsJsonAutomatically()的默认实现返回false只有 MongoDB、MySQL 等少数平台覆写为true。这一标志同时影响JsonType.convertToJSValue()是否跳过反序列化以及 MySQL 连接层jsonStrings的取值是整个 JSON 统一体验的关键开关。在 JSON 属性上创建索引普通索引Index 与点路径要在 JSON 属性上创建索引使用实体级的Index()装饰器并传入点路径dot pathEntity() Index({ properties: metaData.foo }) Index({ properties: [metaData.foo, metaData.bar] }) // 复合索引 export class Book { Property({ type: json, nullable: true }) metaData?: { foo: string; bar: number }; }在 PostgreSQL 上这会生成基于表达式expression index的索引语句create index book_meta_data_foo_index on book ((meta_data-foo));其实现位于 packages/sql/src/dialects/postgresql/BasePostgreSqlPlatform.ts 的getJsonIndexDefinition()它把点路径拆成首尾两段用-取出末级属性中间路径用-逐层深入。唯一索引Unique与普通索引对称使用Unique()装饰器即可创建唯一索引Entity() Unique({ properties: metaData.foo }) Unique({ properties: [metaData.foo, metaData.bar] }) // 复合唯一索引 export class Book { Property({ type: json, nullable: true }) metaData?: { foo: string; bar: number }; }MySQL显式指定返回类型MySQL 对 JSON 路径索引的实现依赖json_value()函数它要求显式指定返回类型。为此Index()支持options选项Entity() Index({ properties: metaData.foo, options: { returning: char(200) } }) export class Book { Property({ type: json, nullable: true }) metaData?: { foo: string; bar: number }; }生成的 SQL 如下alter table book add index book_meta_data_foo_index((json_value(meta_data, $.foo returning char(200))));从 packages/sql/src/dialects/mysql/BaseMySqlPlatform.ts 可以看到returning未指定时默认值为char(255)。之所以需要这个参数是因为json_value()的返回类型会影响索引的比较语义——对字符串类型的 JSON 字段选择足够大的字符类型才能正确覆盖实际数据长度。各平台的索引实现JSON 索引的定义同样由各平台的getJsonIndexDefinition()负责schema 生成器packages/sql/src/schema/SchemaHelper.ts在生成索引 DDL 时会调用它通用 SQL 平台生成(json_extract(root, $.path))表达式PostgreSQL生成(first-last)或带中间路径的(first-path1-last)表达式MySQL生成(json_value(root, $.path returning char(255)))支持options.returning覆盖默认返回类型MariaDB不支持此特性文档明确说明MariaDB driver does not support this feature因为 MariaDB 的json_value()不支持在索引表达式中使用。注意事项与最佳实践nullable 与 null 值声明 JSON 属性时通常配合nullable: trueJsonType.convertToDatabaseValue对null/undefined会原样透传不会参与序列化因此空值场景不会产生额外开销。嵌套路径查询的性能对 JSON 内部路径的过滤在大多数数据库上属于表达式扫描。当某条 JSON 路径是高频查询条件时应像上文那样为其建立表达式索引否则查询会退化为全表扫描。数字与布尔值的类型匹配PostgreSQL 会对数字/布尔右侧值自动 cast因此查询条件里写bar: 321数字与数据库中存储的321能正确匹配在其他驱动上则依赖json_extract/json_value的隐式比较建议保持查询值类型与存储值一致。操作符支持JSON 条件内可以使用$in等操作符见 GH #1487 相关测试但操作符应位于 JSON 路径的叶子层级。序列化一致性JsonType通过convertsJsonAutomatically()标志避免对已由驱动解析好的值做二次JSON.parse这也是为什么不同驱动下从数据库读出的 JSON 属性始终是 JS 对象、而不是字符串。MariaDB 的索引限制如果你的目标库是 MariaDB请勿在 JSON 路径上声明Index/Unique该能力不被支持可在应用层通过其他列或冗余字段替代。小结MikroORM 对 JSON 属性的支持覆盖了定义 → 查询 → 排序 → 索引的完整链路type: json触发内置JsonType统一序列化语义查询条件用对象字面量即可按任意嵌套路径过滤并自动适配各驱动的路径表达式与类型转换Index/Unique配合点路径即可为高频 JSON 路径建立表达式索引。理解 Platform.ts 中getSearchJsonPropertyKey、getJsonIndexDefinition、convertsJsonAutomatically等钩子的分工就能在跨数据库迁移时准确预判 SQL 形态与能力边界——这正是一套实体、处处可用的 MikroORM 风格 JSON 实践。赞分享后端【免费下载链接】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点击查看免费下载相关推荐Win11禁用窗口圆角终极指南快速恢复经典直角设计Win11禁用窗口圆角终极指南快速恢复经典直角设计 想要让Windows 11的现代化界面回归经典的直角设计吗Win11DisableRoundedCorn操作系统逆向工程Apache SeaTunnel Web UI打开浏览器5分钟看清数据同步作业在干什么 完整指南Apache SeaTunnel Web UI打开浏览器5分钟看清数据同步作业在干什么 完整指南 上次一个千万行的同步作业跑到一半卡住我翻了一下午日志才定数据集成ETL大数据批处理流处理变更数据捕获告别繁琐CSSUnoCSS属性化模式与自定义属性实战指南告别繁琐CSSUnoCSS属性化模式与自定义属性实战指南 还在为冗长的CSS类名而烦恼吗UnoCSS属性化模式为你带来全新的开发体验作为一款 即时原子CS前端构建工具上一篇免费数据恢复完整指南误删文件怎么找回来分区修复手把手教你用 TestDisk 和 PhotoRec下一篇novel-downloader小说下载器油猴脚本一键批量离线保存全网小说创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考