ARTICLE DETAIL

资讯详情

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

mikro-orm 命名策略(Naming Strategy)实战指南:表名、列名、索引名的映射规则与自定义实现

mikro-orm 命名策略(Naming Strategy)实战指南:表名、列名、索引名的映射规则与自定义实现 后端【免费下载链接】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点击查看免费下载本文基于 mikro-orm 官方文档《Naming Strategy》展开系统讲解 mikro-orm 如何将实体类、属性名映射为数据库表名与列名内置的三种命名策略UnderscoreNamingStrategy、MongoNamingStrategy、EntityCaseNamingStrategy各自的适用驱动与转换规则、NamingStrategy接口的完整 API 参考、在MikroORM.init中覆盖命名策略的方法以及如何通过继承AbstractNamingStrategy编写符合团队规范的自定义策略。读完本文你可以准确预判任意实体类最终生成的建表语句并在 SchemaGenerator、Migrations 与 EntityGenerator 等场景中统一掌控命名规则。一、命名策略的定位与三大内置实现在 mikro-orm 中实体类到数据库表、属性到列的命名并非随意决定而是由**命名策略Naming Strategy**统一裁决。文档明确指出可选择的三种基本命名策略为策略类默认适用场景命名行为UnderscoreNamingStrategy所有 SQL 驱动的默认策略表名/列名统一转为小写 snake_caseMongoNamingStrategyMongoDriver的默认策略字段名原样保留集合名转为小写连字符形式EntityCaseNamingStrategy需手动指定表名/列名直接使用实体与属性原名不做转换从源码可以确认默认值的选择逻辑Platform 基类的getNamingStrategy()返回UnderscoreNamingStrategy见 Platform.ts#L93-L95而 MongoPlatform 通过override getNamingStrategy()返回MongoNamingStrategy。运行时Configuration 的最终取值逻辑为Configuration.ts#L388-L390getNamingStrategy(): NamingStrategy { return this.getCachedService(this.#options.namingStrategy || this.#platform.getNamingStrategy()); }也就是说初始化时传入namingStrategy选项则优先使用否则回落到当前平台的默认策略且实例通过getCachedService缓存复用。namingStrategy选项的签名是构造函数形式namingStrategy?: { new (): NamingStrategy }Configuration.ts#L959。二、在初始化时覆盖命名策略与自定义策略文档给出的覆盖方式是在初始化 ORM 时传入namingStrategy配置项class MyCustomNamingStrategy implements NamingStrategy { ... } const orm await MikroORM.init({ ... namingStrategy: MyCustomNamingStrategy, ... });如果你希望复用默认行为、只改个别规则可以直接继承AbstractNamingStrategy。文档中特别提示你也可以扩展AbstractNamingStrategy它替你实现了getClassName()方法——该方法用于将实体文件名映射为类名。实际上 AbstractNamingStrategy 的默认实现远不止getClassName()一个方法它是自定义策略的最佳起点。下文 API 参考一节会逐一说明这些默认实现。三、Mongo 驱动下的命名规则MongoNamingStrategy的字段名原样使用propertyToColumnName直接返回入参只有集合名会被转换驼峰转为小写连字符形式。例如MyCoolEntity会被翻译为my-cool-entity集合名。对应源码MongoNamingStrategy.tsclassToTableName(entityName: string, tableName?: string): string { return tableName ?? entityName.replace(/([a-z])([A-Z])/g, $1-$2).toLowerCase(); } propertyToColumnName(propertyName: string): string { return propertyName; // 字段名保持不变 } referenceColumnName(): string { return _id; // 主键字段名为 _id }可以注意两个细节转换正则([a-z])([A-Z])只在小写字母紧跟大写字母的边界处插入分隔符因此MyCoolEntity→my-cool-entityreferenceColumnName()返回_id与 SQL 策略的id不同这直接影响 Mongo 集合中主键与引用字段的命名。四、SQL 驱动下的命名规则以 MySQL 为例MySqlDriver默认使用UnderscoreNamingStrategy所有表和列名都会被转为小写、以下划线分词。文档中给出的author表建表语句示例如下CREATE TABLE author ( id int(11) unsigned NOT NULL AUTO_INCREMENT, created_at datetime(3) DEFAULT NULL, updated_at datetime(3) DEFAULT NULL, terms_accepted tinyint(1) DEFAULT NULL, name varchar(255) DEFAULT NULL, email varchar(255) DEFAULT NULL, born datetime DEFAULT NULL, favourite_book_id int(11) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8;其中createdAt→created_at、favouriteBookm:1 关系→favourite_book_id正是 UnderscoreNamingStrategy 各方法协同的结果// 核心转换小写字母后紧跟大写字母时插入下划线再整体转小写 private underscore(name: string): string { return name.replace(/([a-z])([A-Z])/g, $1_$2).toLowerCase(); } classToTableName(entityName: string, tableName?: string): string { return tableName ?? this.underscore(entityName); } joinColumnName(propertyName: string): string { return this.underscore(propertyName) _ this.referenceColumnName(); // 如 bookTag - book_tag_id } joinKeyColumnName(entityName, referencedColumnName?, ...): string { return this.classToTableName(entityName, tableName) _ (referencedColumnName || this.referenceColumnName()); } joinTableName(sourceEntity, targetEntity, propertyName, ...): string { return this.classToTableName(sourceEntity, tableName) _ this.classToTableName(propertyName); } referenceColumnName(): string { return id; }单元测试 UnderscoreNamingStrategy.test.ts 精确固化了这些行为例如expect(ns.classToTableName(BookTag)).toBe(book_tag); expect(ns.joinColumnName(bookTag)).toBe(book_tag_id); expect(ns.joinKeyColumnName(BookTag)).toBe(book_tag_id); expect(ns.joinTableName(bookTag, foo_bar, foo_baz)).toBe(book_tag_foo_baz); expect(ns.propertyToColumnName(bookTag)).toBe(book_tag); expect(ns.referenceColumnName()).toBe(id);反向转换列名转属性名同样经过测试验证book_tag、book tag、book-tag乃至Book__-- _- tag这类脏数据都会归一为驼峰属性名columnNameToPropertyAbstractNamingStrategy.ts#L69-L75。EntityCaseNamingStrategy的行为则由 EntityCaseNamingStrategy.test.ts 固化BookTag类 →BookTag表bookTag属性 →bookTag列唯一特殊之处是joinKeyColumnName会将实体类名首字母小写BookTag→bookTag且复合主键外键会拼上被引用列名bookTag_name。五、NamingStrategy API 参考以下为文档列出的接口方法签名与描述结合 NamingStrategy 接口源码进行了核对。NamingStrategy.getClassName(file: string, separator?: string): string根据文件名返回类名。默认实现在AbstractNamingStrategy中取扩展名前的部分把分隔符默认-后的字符大写再首字母大写。例如my-cool-entity.ts→MyCoolEntity。NamingStrategy.classToTableName(entityName: string): string为实体类返回表名。这是各内置策略差异最大的方法snake_case / 原样 / 连字符小写。源码签名带可选tableName参数若实体已通过table()/ EntitySchema 显式指定表名该值会优先返回。NamingStrategy.getEntityName(tableName: string, schemaName?: string): string根据数据库表名返回实体类名EntityGenerator使用。默认实现忽略 schema 名当发现重名时生成的名称会自动加前缀。源码中还可看到防御性处理表名若以数字等非标识符字符开头会先加E_前缀AbstractNamingStrategy.ts#L56-L67保证结果是合法且可排序的类名。NamingStrategy.propertyToColumnName(propertyName: string): string为属性返回列名EntityGenerator亦使用。NamingStrategy.getEnumClassName(columnName: string, tableName: string, schemaName?: string): string为某个列生成枚举类名EntityGenerator使用。默认实现基于table_column组合名走getEntityName。NamingStrategy.getEnumTypeName(columnName: string, tableName: string, schemaName?: string): string获取枚举类型名配合实体生成器的enumType: dictionary与enumType: union-type选项使用。默认实现是在枚举类名前加T前缀。NamingStrategy.enumValueToEnumProperty(enumValue: string, columnName: string, tableName: string, schemaName?: string): string为枚举值返回枚举属性名EntityGenerator使用。默认实现将枚举值转大写。NamingStrategy.referenceColumnName(): string返回默认的引用列名。UnderscoreNamingStrategy/EntityCaseNamingStrategy返回idMongoNamingStrategy返回_id。NamingStrategy.joinColumnName(propertyName: string): string为属性返回关联列名m:1 / 1:1 关系外键列。UnderscoreNamingStrategy的产物形如book_tag_id即underscore(propertyName) _ referenceColumnName()。NamingStrategy.joinTableName(sourceEntity: string, targetEntity: string, propertyName: string): string返回连接表pivot 表名该返回值同时是ManyToMany()装饰器pivotTable选项的默认值。UnderscoreNamingStrategy生成源表_属性snake如book_tag_foo_bazEntityCaseNamingStrategy生成源表_原属性名如BookTag_fooBaz。NamingStrategy.joinKeyColumnName(entityName: string, referencedColumnName?: string): string返回给定参数下的外键列名。UnderscoreNamingStrategy生成实体表名_被引用列名如book_tag_idMongo 策略则直接使用或返回表名本身。NamingStrategy.indexName(tableName: string, columns: string[], type: primary | foreign | unique | index | sequence): string返回指定类型的键/约束名。文档特别提醒部分驱动并非支持全部类型例如 MySQL 与 SQLite 会强制主键名PRIMARY KEY/_pkey约定。默认实现AbstractNamingStrategy.ts#L26-L51的命名规则primary→{table}_pkeysequence→{table}_{cols}_seq其余 →{table}_{cols}_{type}无列时退化为{table}_{type}带 schema 的表名会先截掉.前的部分列名中的.会被替换为_。该约束名在 MS SQL 的默认值约束等场景会被真实引用例如 MsSqlSchemaHelper 调用indexName(..., default)为列默认值命名。NamingStrategy.aliasName(entityName: string, index: number): string为查询中的实体返回别名。别名必须在整个查询内唯一——默认通过追加递增的index参数保证只要你自行保证唯一也可以不使用它。默认实现只取类名首字母小写加序号如a0、a1源码注释说明这是为了规避部分数据库引擎的标识符长度限制。SQL 查询构造器 QueryBuilder 即通过config.getNamingStrategy().aliasName(entityName, aliasCounter)为每个 join 分配别名。NamingStrategy.inverseSideName(entityName: string, propertyName: string, kind: ReferenceKind): string返回双向关系中对侧属性名供EntityGenerator的bidirectionalRelations选项使用。默认实现按关系类型区分AbstractNamingStrategy.ts#L106-L118M:N关系命名为${propertyName}Inverse属性名从 pivot 表名推断其他关系类型使用目标实体类名首字母小写若是 1:M 集合则追加Collection后缀如BookTag→bookTag1:M 时 →bookTagCollection。该行为在 v6.3 中发生变化在此之前所有属性包括非 M:N 关系都统一加Inverse后缀。单测UnderscoreNamingStrategy.test.ts#L24-L30固化了上述规则expect(ns.inverseSideName(BookTag, book, ReferenceKind.MANY_TO_ONE)).toBe(bookTag); expect(ns.inverseSideName(BookTag, book, ReferenceKind.ONE_TO_MANY)).toBe(bookTagCollection); expect(ns.inverseSideName(User, friends, ReferenceKind.MANY_TO_MANY)).toBe(friendsInverse);六、自定义策略实战建议基于 AbstractNamingStrategy 的默认实现编写自定义策略前值得先了解AbstractNamingStrategy已经替实现了哪些方法——你只需覆写与目标风格冲突的少量抽象方法。从源码结构看默认实现还包括getClassName(file, separator -)文件 → 类名分隔符可自定义如my_entity.ts传_classToMigrationName(timestamp, customMigrationName?)生成迁移类名Migration{timestamp}自定义名称部分会把非标识符合法字符替换为_确保产物可作为合法类名用于迁移文件indexName(...)如上所述的约束命名规则getEntityName / columnNameToProperty反向命名用于 EntityGenerator其中还处理了 populate 路径保留字如$**、$*等PopulatePath成员的前缀保护aliasName首字母 序号的短别名manyToManyPropertyName(...)从 pivot 表名去掉所有方表名前缀后转属性名例如author_books→booksuser_roles→roles无所有方前缀时返回全名如booksAuthorsdiscriminatorColumnName(baseName)多态关系的鉴别器列名默认为{baseName}Type经列名转换后的结果。因此一个典型的自定义策略如「全小写无分隔符」风格可以写成import { AbstractNamingStrategy } from mikro-orm/core; class LowerNamingStrategy extends AbstractNamingStrategy { classToTableName(entityName: string, tableName?: string): string { return (tableName ?? entityName).toLowerCase(); } propertyToColumnName(propertyName: string): string { return propertyName.toLowerCase(); } joinColumnName(propertyName: string): string { return propertyName.toLowerCase() _ this.referenceColumnName(); } joinTableName(sourceEntity: string, targetEntity: string, propertyName: string, tableName?: string): string { return this.classToTableName(sourceEntity, tableName) _ propertyName.toLowerCase(); } joinKeyColumnName(entityName: string, referencedColumnName?: string, composite?: boolean, tableName?: string): string { return this.classToTableName(entityName, tableName).toLowerCase() _ (referencedColumnName ?? this.referenceColumnName()); } referenceColumnName(): string { return id; } }七、命名策略在整个系统中的生效位置从源码的调用点可以确认命名策略不只是「建表时」的规则它贯穿了 mikro-orm 的多个子系统元数据发现MetadataDiscovery 在解析实体元数据时就取用命名策略把属性映射为列查询构造SQL 的 QueryBuilder 用aliasName生成 join 别名Dataloader 场景DataloaderUtils也用aliasName为 pivot 实体分配别名Schema 工具各平台 SchemaHelper 用indexName为约束、默认值命名如 SqliteSchemaHelper 的 check 约束迁移Migrator 把config.getNamingStrategy()注入迁移生成器实体生成器EntityGenerator 用getEntityName、columnNameToProperty、inverseSideName、manyToManyPropertyName等反向命名方法从现有数据库逆向生成实体代码。这意味着切换命名策略会同时影响查询别名、schema 对比与迁移 diff在已落库的项目中更换策略需谨慎评估对既有表结构比对的影响。八、小结要点说明默认策略SQL 驱动 →UnderscoreNamingStrategyMongo 驱动 →MongoNamingStrategyEntityCaseNamingStrategy需显式指定覆盖方式MikroORM.init({ namingStrategy: MyNamingStrategy })实例经Configuration.getCachedService缓存自定义方式实现NamingStrategy接口或继承AbstractNamingStrategy只覆写少数抽象方法影响面表名、列名、外键/关联列、pivot 表名、索引与约束名、查询别名、迁移名、EntityGenerator 产物结合 NamingStrategy 接口定义、三种内置策略实现 与 配套单测你可以对任意实体准确推导出其在目标数据库中的最终命名并在 schema 生成、迁移与实体生成工具链中保持命名规则的一致性。赞分享后端【免费下载链接】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点击查看免费下载相关推荐GORM命名策略表名与列名的自定义规则终极指南GORM命名策略表名与列名的自定义规则终极指南 GORM作为Go语言中最流行的ORM框架其强大的命名策略功能让开发者能够灵活控制数据库表名和列名的生成规则。后端数据库ORMDoctrine ORM NamingStrategy 完全指南自定义表名与列名生成规则Doctrine ORM NamingStrategy 完全指南自定义表名与列名生成规则 本指南围绕 Doctrine ORM 的命名策略NamingStr数据库ORM后端Regal 自定义命名规范规则naming-convention实战指南用配置而非代码统一 Rego 项目命名Regal 自定义命名规范规则naming convention实战指南用配置而非代码统一 Rego 项目命名 Regal 的 custom 类别中提供了后端认证鉴权云原生上一篇x402 erc20ApprovalGasSponsoring 扩展详解面向 exact EVM 方案的 ERC-20 无 Gas 审批与原子结算下一篇终极HiGHS线性规划求解器完整指南从零到精通创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表