介绍
MikroORM
是基于数据映射器、工作单元和身份映射模式的Node.js
的TypeScript ORM
。在本指南中,您将了解这些单词的含义、如何建立一个简单的API项目、如何测试它等等。
本指南的目的是展示MikroORM
最重要的功能以及一些更小众的功能。它将引导您使用以下技术为博客创建一个简单的API
:
- 带
SQLite
驱动程序的MikroORM
Fastify
作为网络框架- 测试用
Vitest
ECMAScript
模块JWT
身份验证- 通过
ts
变形进行反射
ORM由几个包组成,我们将使用其中的重要包:
@mikro-orm/core
: 带有ORM代码的主包@mikro-orm/cli
: CLI程序包,需要在本地安装@mikro-orm/sqlite
: sqlite驱动程序包(您也可以使用不同的驱动程序)@mikro-orm/reflection
: 启用具有ts变形反射的DRY实体@mikro-orm/migrations
: 用于管理架构迁移的包@mikro-orm/seeder
: 用于为数据库设定测试数据种子的包
核心和驱动程序包是必需的,此列表的其余部分是可选的,如果您愿意,可以是开发依赖项。我们将使用sqlite
驱动程序,主要是为了简单起见,因为它不需要任何额外的设置,并且提供了一个方便的内存数据库,我们将在测试中使用它。
还有更多的程序包,有些程序包也位于
mikro-orm/mikro-ormmonorepo
之外,如@mikro-or/nestjs
或@mikro-orm/sql-highlighter
-与monorepo
中的程序包不同,这些程序包通常具有不同的版本行。
当前可用驱动程序的完整列表:
@mikro-orm/mysql
@mikro-orm/mariadb
@mikro-orm/postgresql
@mikro-orm/sqlite
@mikro-orm/better-sqlite
@mikro-orm/mongodb
安装
首先通过您选择的软件包管理器安装模块,可以使用如下任一工具。也不要忘记安装数据库驱动程序:
1、使用npm
# for mongodb
npm install @mikro-orm/core @mikro-orm/mongodb
# for mysql (works with mariadb too)
npm install @mikro-orm/core @mikro-orm/mysql
# for mariadb (works with mysql too)
npm install @mikro-orm/core @mikro-orm/mariadb
# for postgresql (works with cockroachdb too)
npm install @mikro-orm/core @mikro-orm/postgresql
# for sqlite
npm install @mikro-orm/core @mikro-orm/sqlite
# for better-sqlite
npm install @mikro-orm/core @mikro-orm/better-sqlite
2、使用yarn
# for mongodb
yarn add @mikro-orm/core @mikro-orm/mongodb
# for mysql (works with mariadb too)
yarn add @mikro-orm/core @mikro-orm/mysql
# for mariadb (works with mysql too)
yarn add @mikro-orm/core @mikro-orm/mariadb
# for postgresql (works with cockroachdb too)
yarn add @mikro-orm/core @mikro-orm/postgresql
# for sqlite
yarn add @mikro-orm/core @mikro-orm/sqlite
# for better-sqlite
yarn add @mikro-orm/core @mikro-orm/better-sqlite
3、使用pnpm
# for mongodb
pnpm add @mikro-orm/core @mikro-orm/mongodb
# for mysql (works with mariadb too)
pnpm add @mikro-orm/core @mikro-orm/mysql
# for mariadb (works with mysql too)
pnpm add @mikro-orm/core @mikro-orm/mariadb
# for postgresql (works with cockroachdb too)
pnpm add @mikro-orm/core @mikro-orm/postgresql
# for sqlite
pnpm add @mikro-orm/core @mikro-orm/sqlite
# for better-sqlite
pnpm add @mikro-orm/core @mikro-orm/better-sqlite
接下来,您需要通过以下方式在tsconfig.json
中启用对decorator
以及esModuleInterop
的支持(装饰器是选择性的,如果您使用不同的方式来定义实体元数据(如EntitySchema
),则不需要启用它们。):
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true
然后调用MikroORM.init
作为引导应用程序的一部分:
import { MikroORM } from '@mikro-orm/postgresql'; // or any other driver package
const orm = await MikroORM.init({
entities: ['./dist/entities'], // path to your JS entities (dist), relative to `baseDir`
dbName: 'my-db-name',
});
console.log(orm.em); // access EntityManager via `em` property
要访问特定于驱动程序的方法,如em.createQueryBuilder()
,您需要从驱动程序包导入MikroORM/EntityManager/EntityRepository
类。或者,您可以将orm.em
强制转换为从驱动程序包导出的EntityManager
:
import { EntityManager } from '@mikro-orm/postgresql';
const em = orm.em as EntityManager;
const qb = em.createQueryBuilder(...);
基于文件位置配置entity
还可以通过实体阵列提供存储实体的路径。路径是通过内部globby
解析的,因此可以使用globbing
模式,包括负globs
。
const orm = await MikroORM.init({
entities: ['./dist/app/**/*.entity.js'], // 基于项目目录配置最终部署文件位置
entitiesTs: ['./src/app/**/*.entity.ts'], // 基于当前项目目录配置ts所在源码位置
// ...
});
如果在基于文件夹的查找中遇到问题,请尝试使用mikro orm debug CLI
命令检查实际使用的路径。
TypeScript中的实体配置
默认的元数据提供程序是ReflectMetadataProvider。如果要使用基于ts-morph的发现(通过编译器API读取实际的ts类型),则需要安装@mikro-orm/reflection包。
import { MikroORM } from '@mikro-orm/postgresql';
import { TsMorphMetadataProvider } from '@mikro-orm/reflection';
const orm = await MikroORM.init({
metadataProvider: TsMorphMetadataProvider,
// ...
});
您也可以使用不同的默认ReflectMetadataProvider
,甚至可以编写自定义的ReflectMetadata Provider
。使用EntitySchema
是定义实体的另一种方式,完全不依赖于元数据提供程序。
import { MikroORM } from '@mikro-orm/postgresql';
const orm = await MikroORM.init({
// default since v4, so not needed to specify explicitly
metadataProvider: ReflectMetadataProvider,
// ...
});
同步初始化
与异步MikroORM.init
方法相反,您可以更喜欢使用同步变体initSync
。这种方法有一些局限性:
- 数据库连接将在您第一次与数据库交互时建立(或者您可以显式使用
orm.connect()
) - 没有加载配置文件,
options
参数是必需的 - 不支持基于文件夹的发现
- 不检查不匹配的包版本
RequestContext
现在,您需要为每个请求派生实体管理器,这样它们的标识映射就不会发生冲突。要执行此操作,请使用RequestContext帮助程序:
const app = express();
app.use((req, res, next) => {
RequestContext.create(orm.em, next);
});
您应该在请求处理程序之前和任何使用ORM
的自定义中间件之前将此中间件注册为最后一个中间件。当您在queryParser
或bodyParser
等请求处理中间件之前注册它时,可能会出现问题,所以一定要在它们之后注册上下文。
Entity定义
现在,您可以开始定义Entity(在其中一个Entity文件夹中)。如下是一个简单Entity定义的实例:
// ./entities/Book.ts
@Entity()
export class Book {
@PrimaryKey()
id: bigint;
@Property()
title: string;
@ManyToOne(() => Author)
author: Author;
@ManyToMany(() => BookTag)
tags = new Collection<BookTag>(this);
constructor(title: string, author: Author) {
this.title = title;
this.author = author;
}
}
或者,如果要使用UUID
主键:
// ./entities/Book.ts
import { v4 } from 'uuid';
@Entity()
export class Book {
@PrimaryKey({ type: 'uuid' })
uuid = v4();
// ...
}
EntityManager
定义实体后,可以通过EntityManager
开始使用ORM
。
若要将实体状态保存到数据库,您需要将其持久化。persist
确定是使用insert
还是update,并计算适当的更改集。尚未持久化(没有标识符)的实体引用将自动级联持久化。
// use constructors in your entities for required parameters
const author = new Author('Jon Snow', 'snow@wall.st');
author.born = new Date();
const publisher = new Publisher('7K publisher');
const book1 = new Book('My Life on The Wall, part 1', author);
book1.publisher = publisher;
const book2 = new Book('My Life on The Wall, part 2', author);
book2.publisher = publisher;
const book3 = new Book('My Life on The Wall, part 3', author);
book3.publisher = publisher;
// just persist books, author and publisher will be automatically cascade persisted
await em.persist([book1, book2, book3]).flush();
要从数据库中获取实体,可以使用EntityManager
的find()
和findOne()
:
const authors = em.find(Author, {});
for (const author of authors) {
console.log(author); // instance of Author entity
console.log(author.name); // Jon Snow
for (const book of author.books) { // iterating books collection
console.log(book); // instance of Book entity
console.log(book.title); // My Life on The Wall, part 1/2/3
}
}
设置命令行工具
MikroORM
附带了许多在开发过程中非常有用的命令行工具,如SchemaGenerator
和EntityGenerator
。您可以从NPM
二进制目录调用此命令,也可以使用npx
:
# install the CLI package first!
$ yarn add @mikro-orm/cli
# manually
$ node node_modules/.bin/mikro-orm
# via npx
$ npx mikro-orm
# or via yarn
$ yarn mikro-orm
为了使CLI
能够访问您的数据库,您需要创建导出orm
配置的mikro-orm.config.js
文件。
要启用TypeScript
支持,请将useTsNode
标志添加到package.json
文件的mikro-orm
部分。默认情况下,当未启用useTsNode
时,CLI
将忽略.ts
文件,因此,如果您想排除这种行为,请启用alwaysAllowTs
选项。如果您想将MikroORM
与Bun
一起使用,这将非常有用,Bun
具有开箱即用的TypeScript
支持。
您还可以在package.json
中设置mikro-orm.config.*
文件的可能路径阵列,以及使用不同的文件名。package.json
文件可以位于当前工作目录中,也可以位于其父文件夹中。
// package.json
{
"name": "your-app",
"dependencies": { ... },
"mikro-orm": {
"useTsNode": true,
"configPaths": [
"./src/mikro-orm.config.ts",
"./dist/mikro-orm.config.js"
]
}
}
控制这些CLI相关设置的另一种方法是使用环境变量:
MIKRO_ORM_CLI_CONFIG
:ORM
配置文件的路径MIKRO_ORM_CLI_USE_TS_NODE
:为TypeScript
支持注册TS
节点MIKRO_ORM_CLI_TS_CONFIG_PATH
:tsconfig.json
的路径(用于TS
节点)MIKRO_ORM_CLI_ALWAYS_ALLOW_TS
:启用不使用TS
节点的.TS
文件MIKRO_ORM_CLI_VERBOSE
:启用详细日志记录(例如,打印种子程序或模式困难中使用的查询)
或者,您也可以通过--config
选项指定配置路径:
$ npx mikro-orm debug --config ./my-config.ts
当您运行应用程序时(只要它是process.argv的一部分),而不仅仅是当您使用CLI时,–config标志也会受到尊重。
MikroORM将始终尝试根据configPaths中的顺序加载第一个可用的配置文件。如果禁用了useTsNode,或者尚未注册或检测到ts节点,则ts配置文件将被忽略。
创建配置对象的首选方式是使用defineConfig助手。即使在JavaScript文件中,它也将提供智能感知,而不需要通过jsdoc进行类型提示:
import { defineConfig } from '@mikro-orm/sqlite';
export default defineConfig({
entities: [Author, Book, BookTag],
dbName: 'my-db-name',
// this is inferred as you import `defineConfig` from sqlite package
// driver: SqliteDriver,
});
如果从驱动程序包导入帮助程序,则使用defineConfig还会自动为您推断驱动程序选项。这意味着您不必显式地提供驱动程序选项。
或者,可以使用“选项”类型:
// ./src/mikro-orm.config.ts
import { Options } from '@mikro-orm/sqlite';
const config: Options = {
entities: [Author, Book, BookTag],
dbName: 'my-db-name',
driver: SqliteDriver,
};
export default config;
正确设置CLI
配置后,可以省略MikroORM.init()
的options
参数,CLI
配置将自动使用。如果您使用使用摇树的捆扎机,此过程可能会失败。由于配置文件没有在任何地方静态引用,因此不会对其进行编译,因此最好的方法是显式提供配置:
import config from './mikro-orm.config';
const orm = await MikroORM.init(config);
现在您应该可以开始使用CLI
了。CLI
帮助中列出了所有可用的命令:
$ npx mikro-orm
Usage: mikro-orm <command> [options]
Commands:
mikro-orm cache:clear Clear metadata cache
mikro-orm cache:generate Generate metadata cache
mikro-orm generate-entities Generate entities based on current database
schema
mikro-orm database:create Create your database if it does not exist
mikro-orm database:import <file> Imports the SQL file to the database
mikro-orm seeder:run Seed the database using the seeder class
mikro-orm seeder:create <seeder> Create a new seeder class
mikro-orm schema:create Create database schema based on current
metadata
mikro-orm schema:drop Drop database schema based on current
metadata
mikro-orm schema:update Update database schema based on current
metadata
mikro-orm schema:fresh Drop and recreate database schema based on
current metadata
mikro-orm migration:create Create new migration with current schema
diff
mikro-orm migration:up Migrate up to the latest version
mikro-orm migration:down Migrate one step down
mikro-orm migration:list List all executed migrations
mikro-orm migration:check Check if migrations are needed. Useful for
bash scripts.
mikro-orm migration:pending List all pending migrations
mikro-orm migration:fresh Clear the database and rerun all migrations
mikro-orm debug Debug CLI configuration
Options:
--config Set path to the ORM configuration file [string]
-v, --version Show version number [boolean]
-h, --help Show help [boolean]
Examples:
mikro-orm schema:update --run Runs schema synchronization