From dd7b48fefd9149653a921afade005c70a73e3a2f Mon Sep 17 00:00:00 2001 From: Cong Liu Date: Sat, 5 Aug 2023 08:42:35 +0800 Subject: [PATCH] feat: add InjectEntityManager decorator for MikroORM and upgrade document (#3114) * feat: added `InjectEntityManager` for mikro-orm * chore(docs): updated documents for mikro-orm * chore(test): added test case for `InjectEntityManager` * chore(test): updated test according to the guideline of mikro-orm --- packages/mikro/src/configuration.ts | 20 ++++++++- packages/mikro/src/decorator.ts | 7 +++ .../base-fn-origin/src/configuration.ts | 38 +++++++++------- site/docs/extensions/mikro.md | 38 +++++++++++----- .../current/extensions/mikro.md | 45 +++++++++++++------ 5 files changed, 104 insertions(+), 44 deletions(-) diff --git a/packages/mikro/src/configuration.ts b/packages/mikro/src/configuration.ts index 55f47f639be1..27813c9ba5fa 100644 --- a/packages/mikro/src/configuration.ts +++ b/packages/mikro/src/configuration.ts @@ -9,7 +9,11 @@ import { Init, Inject, } from '@midwayjs/core'; -import { DATA_SOURCE_KEY, ENTITY_MODEL_KEY } from './decorator'; +import { + DATA_SOURCE_KEY, + ENTITY_MANAGER_KEY, + ENTITY_MODEL_KEY, +} from './decorator'; import { MikroDataSourceManager } from './dataSourceManager'; import { EntityName, RequestContext } from '@mikro-orm/core'; @@ -62,6 +66,20 @@ export class MikroConfiguration implements ILifeCycle { } ); + this.decoratorService.registerPropertyHandler( + ENTITY_MANAGER_KEY, + (propertyName, meta: { connectionName?: string }) => { + if (RequestContext.getEntityManager()) { + return RequestContext.getEntityManager(); + } else { + return this.dataSourceManager.getDataSource( + meta.connectionName || + this.dataSourceManager.getDefaultDataSourceName() + ).em; + } + } + ); + this.decoratorService.registerPropertyHandler( DATA_SOURCE_KEY, ( diff --git a/packages/mikro/src/decorator.ts b/packages/mikro/src/decorator.ts index 2823f546a839..901eb2da9c62 100644 --- a/packages/mikro/src/decorator.ts +++ b/packages/mikro/src/decorator.ts @@ -2,6 +2,7 @@ import { createCustomPropertyDecorator } from '@midwayjs/core'; import { EntityName } from '@mikro-orm/core'; export const ENTITY_MODEL_KEY = 'mikro:entity_model_key'; +export const ENTITY_MANAGER_KEY = 'mikro:entity_manager_key'; export const DATA_SOURCE_KEY = 'mikro:data_source_key'; export function InjectRepository( @@ -14,6 +15,12 @@ export function InjectRepository( }); } +export function InjectEntityManager(connectionName?: string) { + return createCustomPropertyDecorator(ENTITY_MANAGER_KEY, { + connectionName, + }); +} + export function InjectDataSource(dataSourceName?: string) { return createCustomPropertyDecorator(DATA_SOURCE_KEY, { dataSourceName, diff --git a/packages/mikro/test/fixtures/base-fn-origin/src/configuration.ts b/packages/mikro/test/fixtures/base-fn-origin/src/configuration.ts index 9c0acca78546..1f778cb6439f 100644 --- a/packages/mikro/test/fixtures/base-fn-origin/src/configuration.ts +++ b/packages/mikro/test/fixtures/base-fn-origin/src/configuration.ts @@ -1,29 +1,33 @@ import { App, Configuration, Inject } from '@midwayjs/core'; import * as mikro from '../../../../src'; import { join } from 'path'; -import { InjectDataSource, InjectRepository } from '../../../../src'; +import { + InjectDataSource, + InjectRepository, + InjectEntityManager, + MikroDataSourceManager, +} from '../../../../src'; import { IMidwayApplication } from '@midwayjs/core'; import { Book } from './entity'; -import { EntityRepository, QueryOrder, wrap } from '@mikro-orm/core'; -import { MikroORM, IDatabaseDriver, Connection } from '@mikro-orm/core'; +import { EntityManager, EntityRepository } from '@mikro-orm/sqlite'; +import { MikroORM, IDatabaseDriver, Connection, QueryOrder } from '@mikro-orm/core'; @Configuration({ - imports: [ - mikro - ], - importConfigs: [ - join(__dirname, './config') - ] + imports: [mikro], + importConfigs: [join(__dirname, './config')], }) export class ContainerConfiguration { @InjectRepository(Book) bookRepository: EntityRepository; + @InjectEntityManager() + em: EntityManager; + @App() app: IMidwayApplication; @Inject() - mikroDataSourceManager: mikro.MikroDataSourceManager; + mikroDataSourceManager: MikroDataSourceManager; @InjectDataSource() defaultDataSource: MikroORM>; @@ -32,17 +36,17 @@ export class ContainerConfiguration { namedDataSource: MikroORM>; async onReady() { - expect(this.defaultDataSource).toBeDefined(); expect(this.defaultDataSource).toEqual(this.namedDataSource); - const orm = this.mikroDataSourceManager.getDataSource('default'); - const connection = orm.em.getConnection(); - await (connection as any).loadFile(join(__dirname, '../sqlite-schema.sql')); + const connection = this.em.getConnection(); + await connection.loadFile(join(__dirname, '../sqlite-schema.sql')); - const book = this.bookRepository.create({ title: 'b1', author: { name: 'a1', email: 'e1' } }); - wrap(book.author, true).__initialized = true; - await this.bookRepository.persist(book).flush(); + const book = this.bookRepository.create({ + title: 'b1', + author: { name: 'a1', email: 'e1' }, + }); + await this.em.persist(book).flush(); const findResult = await this.bookRepository.findAll({ populate: ['author'], diff --git a/site/docs/extensions/mikro.md b/site/docs/extensions/mikro.md index 53391b8c2f09..9f782abc17fa 100644 --- a/site/docs/extensions/mikro.md +++ b/site/docs/extensions/mikro.md @@ -197,39 +197,53 @@ export default (appInfo) => { -### 调用 Repository +### 增删查改 -在业务代码中使用 `InjectRepository` 注入 `repository` 对象,执行数据库操作。 +在业务代码中,可以使用`InjectRepository`注入`Repository`对象执行简单的查询操作。其它的增删改操作可以通过配合`EntityManger`的`persist`和`flush`接口来实现,使用`InjectEntityManager`可以直接注入`EntityManager`对象,也可以通过`repository.getEntityManager()`获取。 + +:::caution + +从5.7版本开始,MikroORM将原来`Repository`上`persist`和`flush`等接口标为*弃用*,并计划在v6版本中[彻底移除](https://github.com/mikro-orm/mikro-orm/discussions/3989),建议直接调用`EntityManager`上的相关接口。 + +::: ```typescript import { Book } from './entity'; import { Provide } from '@midwayjs/core'; -import { InjectRepository } from '@midwayjs/mikro'; -import { EntityRepository, QueryOrder, wrap } from '@mikro-orm/core'; +import { InjectEntityManager, InjectRepository } from '@midwayjs/mikro'; +import { QueryOrder } from '@mikro-orm/core'; +import { EntityManager, EntityRepository } from '@mikro-orm/mysql'; // 需要使用数据库驱动对应的类来执行操作 @Provide() -export class BookController { +export class BookService { @InjectRepository(Book) bookRepository: EntityRepository; - async findBookAndQuery() { - const book = this.bookRepository.create({ title: 'b1', author: { name: 'a1', email: 'e1' } }); - wrap(book.author, true).__initialized = true; - await this.bookRepository.persist(book).flush(); + @InjectEntityManager() + em: EntityManager; - const findResult = await this.bookRepository.findAll({ + async queryByRepo() { + // 使用Repository查询 + const books = await this.bookRepository.findAll({ populate: ['author'], orderBy: { title: QueryOrder.DESC }, limit: 20, }); + return books; + } + async createBook() { + const book = new Book({ title: 'b1', author: { name: 'a1', email: 'e1' } }); + // 标记保存Book + this.em.persist(book); + // 执行所有变更 + await this.em.flush(); + return book; } } ``` - - ## 高级功能 ### 获取数据源 diff --git a/site/i18n/en/docusaurus-plugin-content-docs/current/extensions/mikro.md b/site/i18n/en/docusaurus-plugin-content-docs/current/extensions/mikro.md index 324beb991554..2aa32f3487c4 100644 --- a/site/i18n/en/docusaurus-plugin-content-docs/current/extensions/mikro.md +++ b/site/i18n/en/docusaurus-plugin-content-docs/current/extensions/mikro.md @@ -198,33 +198,50 @@ For association in the form of a directory scan, please refer to [Data Source Ma -### Call Repository +### CRUD Operations -Use `InjectRepository` injection `repository` objects in business code to perform database operations. +Use `InjectRepository` to inject `Repository` to perform simple query operations. And use `InjectEntityManager` to get the instance of `EntityManager`, to perform creating, updating and deleting operations. +You can also get `EntityManager` by calling `repository.getEntityManger()`. + +:::caution + +Since v5.7, `persist` and `flush` etc. on `Repository` (shortcuts to methods on `EntityManager`) were marked as *deprecated*, and [planned to remove them in v6](https://github.com/mikro-orm/mikro-orm/discussions/3989). Please use those APIs on `EntityManger` directly instead of on `Repository`. + +::: ```typescript import { Book } from './entity'; import { Provide } from '@midwayjs/core'; -import { InjectRepository } from '@midwayjs/mikro'; -import { EntityRepository, QueryOrder, wrap } from '@mikro-orm/core'; +import { InjectEntityManager, InjectRepository } from '@midwayjs/mikro'; +import { QueryOrder } from '@mikro-orm/core'; +import { EntityManager, EntityRepository } from '@mikro-orm/mysql'; // should be imported from driver specific packages @Provide() -export class BookController { +export class BookService { @InjectRepository(Book) bookRepository: EntityRepository; - async findBookAndQuery() { - const book = this.bookRepository.create({ title: 'b1', author: { name: 'a1', email: 'e1' } }); - wrap(book.author, true).__initialized = true; - await this.bookRepository.persist(book).flush(); - - const findResult = await this.bookRepository.findAll({ - populate: ['author'] - orderBy: { title: QueryOrder.DESC} - limit: 20 + @InjectEntityManager() + em: EntityManager; + + async queryByRepo() { + // query with Repository + const books = await this.bookRepository.findAll({ + populate: ['author'], + orderBy: { title: QueryOrder.DESC }, + limit: 20, }); + return books; + } + async createBook() { + const book = new Book({ title: 'b1', author: { name: 'a1', email: 'e1' } }); + // mark book as persisted + this.em.persist(book); + // persist all changes to database + await this.em.flush(); + return book; } } ```