- 后端
【免费下载链接】mikro-orm
TypeScript 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.
本文以 MikroORM 6.x 官方文档 custom-driver.md 为核心骨架,讲解如何为 MikroORM 接入一个官方尚未支持的数据库。你将掌握驱动架构中 Platform、SchemaHelper、Connection、Driver 四个核心类的职责与实现要点,并学会结合@mikro-orm/sql的抽象基类快速搭建 SQL 驱动,以及直接继承核心基类实现非 SQL 驱动的完整路径。
驱动架构总览:四个类各司其职
MikroORM 的驱动层被刻意拆分为多个关注点单一的类,任何一个数据库的接入都需要(或可以)实现以下四个部分:
| 类 | 职责 | 核心位置 |
|---|---|---|
Platform | 描述目标数据库的能力与特性(事务、命名策略、主键规范化、标识符引用等) | packages/core/src/platforms/Platform.ts |
SchemaHelper | 提供建表/改表等 schema 相关 SQL 片段的生成方式,是 Platform 的一部分 | @mikro-orm/core的SchemaHelper |
Connection | 负责与数据库建立连接并执行查询 | packages/core/src/connections/Connection.ts |
Driver | 编排 Connection 与 Platform,把实体操作(find、insert、update、delete、count 等)持久化到数据库 | packages/core/src/drivers/DatabaseDriver.ts |
这套分层设计与 MikroORM 的 Data Mapper、Unit of Work、Identity Map 模式一脉相承:EntityManager 只与IDatabaseDriver接口打交道,而驱动内部如何连接数据库、如何生成 SQL、如何处理方言差异,全部封装在这四个类中。因此只要实现这四个类,MikroORM 的实体定义、关系映射、事务、级联、Identity Map 等上层能力即可原样复用。
Platform:定义数据库的能力边界
Platform是驱动能力的"说明书",MikroORM 通过它判断某个数据库支持哪些特性。从源码看,Platform.ts 是一个抽象基类,绝大多数方法都有默认实现(默认返回false或保守值),你需要按目标数据库的真实能力覆盖:
import { Platform } from '@mikro-orm/core'; export class MyCustomPlatform extends Platform { protected abstract schemaHelper: MyCustomSchemaHelper; // 在这里覆盖默认设置 usesPivotTable(): boolean; supportsTransactions(): boolean; supportsSavePoints(): boolean; getNamingStrategy(): { new (): NamingStrategy; }; getIdentifierQuoteCharacter(): string; getParameterPlaceholder(index?: number): string; usesReturningStatement(): boolean; normalizePrimaryKey<T = number | string>(data: IPrimaryKey): T; denormalizePrimaryKey(data: IPrimaryKey): IPrimaryKey; getSerializedPrimaryKeyField(field: string): string; }各方法含义与默认行为如下:
usesPivotTable():M:N 关系是否使用中间表。SQL 驱动返回true,MongoDB 这类文档数据库返回false(源码默认即false)。supportsTransactions():是否支持事务。默认值受全局配置disableTransactions影响(见 Platform.ts),若目标数据库不支持事务可固定返回false。supportsSavePoints():是否支持保存点(嵌套事务)。getNamingStrategy():返回默认命名策略构造器,基类默认返回UnderscoreNamingStrategy。getIdentifierQuoteCharacter():标识符引用字符,如 PostgreSQL 用双引号。getParameterPlaceholder(index?):SQL 参数占位符,如?或$1。usesReturningStatement():是否支持INSERT ... RETURNING子句(如 PostgreSQL 支持、MySQL 不支持),默认false。normalizePrimaryKey()/denormalizePrimaryKey():主键在数据库原始形态与 ORM 内部形态之间的双向转换(例如把 MongoDB 的ObjectId归一化为字符串)。getSerializedPrimaryKeyField(field):序列化时主键字段的展示名称。
除上述方法外,Platform.ts 还提供了一系列特性开关,包括usesOutputStatement()(MSSQL 的 OUTPUT 子句)、supportsNativeEnums()(PostgreSQL 原生枚举)、usesEnumCheckConstraints()、supportsMaterializedViews()、supportsPartitionedTables()(声明式表分区)、indexForeignKeys()(是否自动为外键建索引)等,均可在自定义 Platform 中按需覆盖。
SchemaHelper:驱动 schema 生成逻辑
SchemaHelper是 Platform 的组成部分,负责提供"如何构建 schema"的信息——即 MikroORM 的 SchemaGenerator 在生成建表、改表 SQL 时所需的各种 SQL 片段:
import { SchemaHelper } from '@mikro-orm/core'; export class MyCustomSchemaHelper extends SchemaHelper { // 在这里覆盖默认设置 getIdentifierQuoteCharacter(): string; getSchemaBeginning(): string; getSchemaEnd(): string; getSchemaTableEnd(): string; getAutoIncrementStatement(meta: EntityMetadata): string; getPrimaryKeySubtype(meta: EntityMetadata): string; getTypeDefinition(prop: EntityProperty, types?: Record<string, string>, lengths?: Record<string, number>): string; getUnsignedSuffix(prop: EntityProperty): string; supportsSchemaConstraints(): boolean; supportsSchemaMultiAlter(): boolean; supportsSequences(): boolean; quoteIdentifier(field: string): string; dropTable(meta: EntityMetadata): string; indexForeignKeys(): boolean; }关键方法的职责:
getTypeDefinition():把实体属性的 TypeScript/ORM 类型翻译成目标数据库的列类型定义,是 schema 生成最核心的方法之一。getAutoIncrementStatement():返回自增主键(AUTO_INCREMENT / IDENTITY / SERIAL)的 SQL 片段。supportsSequences():是否支持序列(如 PostgreSQL、Oracle)。supportsSchemaConstraints()/supportsSchemaMultiAlter():schema 约束能力与多条 ALTER 语句的合并能力。quoteIdentifier():标识符引用封装。dropTable():生成 DROP TABLE 语句。
需要 schema 生成能力时,可参照现有驱动的 SchemaHelper 实现(例如PostgreSqlSchemaHelper、MySqlSchemaHelper,位于对应驱动包的schema目录下)来编写你自己的版本。
Connection:负责与数据库通信
第三个部分是连接包装器,负责真正与数据库通信。继承核心的Connection抽象类,需要实现以下抽象方法(签名与源码一致,见 Connection.ts):
import { Connection } from '@mikro-orm/core'; export class MyCustomConnection extends Connection { // 实现抽象方法 connect(): Promise<void>; isConnected(): Promise<boolean>; close(force?: boolean): Promise<void>; getDefaultClientUrl(): string; execute(query: string, params?: any[], method?: 'all' | 'get' | 'run'): Promise<QueryResult | any | any[]>; }从源码看,Connection基类还提供了checkConnection()、ensureConnection()、executeDump()、getNativeClient()等方法作为可选能力(默认抛出"不支持"错误),自定义连接可以按需覆盖。例如checkConnection()返回{ ok: true }或{ ok: false; reason: string; error?: Error },用于连接健康检查;getNativeClient()用于向用户暴露底层原生客户端(如pg的连接池)。
Driver:把实体操作持久化到数据库
最后一部分是Driver,它负责使用 Connection 与 Platform 将实体变更持久化到数据库。官方文档给出了两条实现路线:
- SQL 驱动:优先继承
AbstractSqlDriver(来自@mikro-orm/sql),可以复用完整的 SQL 查询构建器、连接策略、批量操作等能力; - 非 SQL 驱动:继承核心的
DatabaseDriver抽象类(位于 packages/core/src/drivers/DatabaseDriver.ts),自行实现查询语义; - 绝对控制:直接实现
IDatabaseDriver接口(定义于 packages/core/src/drivers/IDatabaseDriver.ts),接口包含find、findOne、findVirtual、stream、nativeInsert、nativeInsertMany、nativeUpdate、nativeDelete、count、createEntityManager、connect、close、reconnect、getConnection等完整契约。
继承DatabaseDriver的最小实现如下(抽象方法签名与 DatabaseDriver.ts 一致):
import { DatabaseDriver } from '@mikro-orm/core'; export class MyCustomDriver extends DatabaseDriver { // 初始化连接与平台 protected readonly connection = new MyCustomConnection(this.config); protected readonly platform = new MyCustomPlatform; // 并实现抽象方法 find<T extends AnyEntity>(entityName: string, where: FilterQuery<T>, populate?: string[], orderBy?: Record<string, QueryOrder>, limit?: number, offset?: number): Promise<T[]>; findOne<T extends AnyEntity>(entityName: string, where: FilterQuery<T> | string, populate: string[]): Promise<T | null>; nativeInsert<T extends AnyEntityType<T>>(entityName: string, data: EntityData<T>): Promise<QueryResult>; nativeUpdate<T extends AnyEntity>(entityName: string, where: FilterQuery<T> | IPrimaryKey, data: EntityData<T>): Promise<QueryResult>; nativeDelete<T extends AnyEntity>(entityName: string, where: FilterQuery<T> | IPrimaryKey): Promise<QueryResult>; count<T extends AnyEntity>(entityName: string, where: FilterQuery<T>): Promise<number>; }需要注意DatabaseDriver的构造函数签名是(config: Configuration, dependencies: string[]),第二个参数是驱动依赖的 npm 包名列表,MikroORM 启动时会据此校验依赖是否安装并给出友好报错(详见 DatabaseDriver.ts)。此外基类还提供了若干可选方法(如nativeUpdateMany、nativeClone、findVirtual、countVirtual、stream等),默认实现会抛出"当前驱动不支持"的错误,需要时再覆盖。
基于 @mikro-orm/sql 快速搭建 SQL 驱动
大多数自定义驱动面向的是 SQL 数据库。此时强烈建议继承@mikro-orm/sql包中的抽象基类——SQL 层内部基于 Kysely 构建查询,连接类只需提供一个 Kysely dialect 即可复用绝大部分能力。从源码看,AbstractSqlConnection.ts 中声明了抽象方法createKyselyDialect(overrides: Dictionary),连接生命周期、事务、保存点、查询执行与流式读取均由基类通过 Kysely 统一处理。
Connection:提供 Kysely dialect
import { AbstractSqlConnection } from '@mikro-orm/sql'; import type { Dialect, Dictionary } from 'kysely'; export class MyConnection extends AbstractSqlConnection { createKyselyDialect(overrides: Dictionary): Dialect { // 返回目标数据库的 Kysely dialect // overrides 中包含来自 MikroORM 配置的 driverOptions return new MyKyselyDialect({ ... }); } }Platform:描述 SQL 数据库特性
继承AbstractSqlPlatform(它已继承核心Platform并提供合理的 SQL 默认值),按需覆盖能力方法:
import { AbstractSqlPlatform } from '@mikro-orm/sql'; export class MyPlatform extends AbstractSqlPlatform { // 覆盖方法以描述数据库能力,例如: supportsTransactions(): boolean { return true; } usesReturningStatement(): boolean { return false; } getDefaultSchemaName(): string | undefined { return undefined; } }AbstractSqlPlatform之上常见的可覆盖方法及用途:
| 方法 | 用途 |
|---|---|
supportsTransactions() | 数据库是否支持事务 |
usesReturningStatement() | 是否支持INSERT ... RETURNING |
supportsSchemas() | 是否支持命名 schema(如 PostgreSQL) |
getDefaultSchemaName() | 支持 schema 时的默认 schema 名 |
quoteIdentifier(id) | 标识符引用方式(默认"id"双引号) |
quoteValue(value) | 字面量值的引用方式 |
getCurrentTimestampSQL(length) | 当前时间戳的 SQL 表达式 |
getSearchJsonPropertySQL(path, type, aliased) | JSON 属性访问语法 |
escape(value) | SQL 字面量转义 |
若需要 schema 生成支持,同样可以提供一个自定义SchemaHelper,参考现有驱动(如PostgreSqlSchemaHelper、MySqlSchemaHelper)。
Driver:串联一切并声明原生依赖
import { type Configuration, EntityManagerType } from '@mikro-orm/core'; import { AbstractSqlDriver } from '@mikro-orm/sql'; import { MyConnection } from './MyConnection.js'; import { MyPlatform } from './MyPlatform.js'; export class MyDriver extends AbstractSqlDriver<MyConnection> { constructor(config: Configuration) { super(config, new MyPlatform(), MyConnection, ['kysely', 'my-native-driver']); } }super()的最后一个参数是必须安装的 npm 包名数组——MikroORM 启动时会检查这些依赖,缺失时给出友好的错误提示。AbstractSqlDriver承担了全部重活:find、findOne、nativeInsert、nativeUpdate、nativeDelete、count、查询构建、joined 加载策略等。
使用自定义驱动
通过driver配置项把驱动类交给 MikroORM:
const orm = await MikroORM.init({ driver: MyDriver, dbName: 'my-database', entities: [Author, Book], });非 SQL 驱动实现要点
对于文档型、键值型等非 SQL 数据库,没有查询构建器可以依赖,需要直接继承核心基类并实现更多方法:
- Connection:继承
Connection,实现connect()、isConnected()、checkConnection()、close(force?),并自行添加目标数据库特有的查询方法; - Platform:继承
Platform并覆盖特性开关,例如usesPivotTable(): false、supportsTransactions(): false、自定义getNamingStrategy()、normalizePrimaryKey()与denormalizePrimaryKey()等; - Driver:继承
DatabaseDriver<MyConnection>,在构造器中实例化connection与platform,并实现find、findOne、nativeInsert、nativeInsertMany、nativeUpdate、nativeDelete、count等 CRUD 抽象方法。
仓库中的 MongoDB 驱动是完整的非 SQL 驱动参考实现,其源码位于 packages/mongodb/src,包含MongoConnection.ts、MongoDriver.ts、MongoPlatform.ts、MongoExceptionConverter.ts、MongoSchemaGenerator.ts等文件,展示了连接、平台、驱动、异常转换、schema 生成的全套做法,是学习自定义非 SQL 驱动的最佳范例。
参考实现:从现有驱动包学习
仓库中已内置多个生产级驱动,可作为自定义驱动的直接参考:
- PostgreSQL(
@mikro-orm/postgresql):功能最完整的 SQL 驱动,含 schema 支持、JSON 操作符、数组类型等; - MySQL(
@mikro-orm/mysql):带 MySQL 特定平台特性的 SQL 驱动; - SQLite(
@mikro-orm/sqlite):极简 SQL 驱动,是入门自定义 SQL 驱动的良好起点; - MongoDB(
@mikro-orm/mongodb):直接继承DatabaseDriver的非 SQL 驱动范本。
所有驱动包均位于仓库的 packages 目录下。编写自定义驱动时,建议先对照这些现有实现理解四个类的分工,再按"先 Connection 后 Platform、最后 Driver"的顺序逐步补齐,并用真实的实体读写测试验证每条路径。
- 后端
【免费下载链接】mikro-orm
TypeScript 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.
相关推荐
MikroORM 自定义数据库驱动开发指南:基于 Platform/Connection/Driver 三层架构扩展 SQL 与非 SQL 数据库
MikroORM 自定义数据库驱动开发指南:基于 Platform/Connection/Driver 三层架构扩展 SQL 与非 SQL 数据库 如果你希望让
后端深入剖析 MikroORM 自定义 Driver 开发:从 Platform、Connection 到 Driver 的三层架构与实战实现
深入剖析 MikroORM 自定义 Driver 开发:从 Platform、Connection 到 Driver 的三层架构与实战实现 导读 :MikroO
后端MikroORM 自定义驱动(Custom Driver)开发指南:基于 Platform、SchemaHelper、Connection、Driver 四大组件实现全新数据库支持
MikroORM 自定义驱动(Custom Driver)开发指南:基于 Platform、SchemaHelper、Connection、Driver 四大组
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考