Permalink
Browse files

feat(EntityManager): add performance repositories

Add cached repositories to the EntityManager for repetitive simple tasks such as quering the database for HTTP(s) requests.

If you're planning on doing work with the unit of work (`wetland.getManager()` / `EntityManager.createScope()`) you must use the scope to get the repository, instead.

Repositories fetched from the entity manager are a more performant way of querying the database, but they're never linked to a scope.
If all you're planning on doing is fetching data or performing simple queries, this method is for you.

The queries performed on a repository fetched from the EntityManager _do_ run in an internal scope, so they're safe to use in for example APIs.

BREAKING CHANGE: The EntityRepository has a method called getEntityManager, which now conditionally returns the EntityManager or an instance of Scope.
If you must have a Scope, call EntityRepository.getScope() instead.
  • Loading branch information...
RWOverdijk committed Sep 23, 2018
1 parent fac2cae commit 569a3d1ba8a05e270465d42e911e4ff3bbacc873
Showing with 88 additions and 27 deletions.
  1. +54 −2 src/EntityManager.ts
  2. +29 −9 src/EntityRepository.ts
  3. +5 −16 src/Scope.ts
@@ -3,6 +3,8 @@ import {Wetland} from './Wetland';
import {Scope, Entity} from './Scope';
import {EntityInterface, EntityCtor} from './EntityInterface';
import {Homefront} from 'homefront';
import { EntityRepository } from './EntityRepository';
import { Store } from './Store';
/**
* The main entity manager for wetland.
@@ -13,9 +15,9 @@ export class EntityManager {
/**
* The wetland instance this entity manager belongs to.
*
* @type {Wetland}
* @type { Wetland }
*/
private wetland: Wetland = null;
private readonly wetland: Wetland = null;
/**
* Holds the entities registered with the entity manager indexed on name.
@@ -24,6 +26,13 @@ export class EntityManager {
*/
private entities: {[key: string]: {entity: EntityCtor<EntityInterface>, mapping: Mapping<EntityInterface>}} = {};
/**
* Holds instances of repositories that have been instantiated before, as a cache.
*
* @type { Map }
*/
private repositories: Map<EntityCtor<EntityInterface>, EntityRepository<any>> = new Map();
/**
* Construct a new core entity manager.
* @constructor
@@ -69,6 +78,49 @@ export class EntityManager {
return entity.entity;
}
/**
* Get a repository instance for the provided Entity reference.
*
* @param {string|Entity} entity
* @param {Scope} scope
*
* @returns {EntityRepository}
*/
public getRepository<T>(entity: string | EntityCtor<T>, scope?: Scope): EntityRepository<T> {
const entityReference = this.resolveEntityReference(entity) as EntityCtor<T>;
if (!this.repositories.has(entityReference) || scope) {
const Repository = Mapping.forEntity(entityReference).getRepository();
if (scope) {
return new Repository(scope, entityReference)
}
this.repositories.set(entityReference, new Repository(this, entityReference));
}
return this.repositories.get(entityReference) as EntityRepository<T>;
}
/**
* Get store for provided entity.
*
* @param {EntityInterface} entity
*
* @returns {Store}
*/
public getStore(entity?: EntityInterface | string): Store {
let storeName = null;
if (typeof entity === 'string') {
storeName = entity;
} else if (entity) {
storeName = this.getMapping(entity).getStoreName();
}
return this.wetland.getStore(storeName);
}
/**
* Get all registered entities.
*
@@ -1,16 +1,17 @@
import {Mapping} from './Mapping';
import {QueryBuilder} from './QueryBuilder';
import * as knex from 'knex';
import {Scope} from './Scope';
import {Store} from './Store';
import {EntityCtor} from './EntityInterface';
import { EntityManager } from './EntityManager';
import { Scope } from './Scope';
export class EntityRepository<T> {
/**
* @type {Scope}
* @type {EntityManager|Scope}
*/
protected entityManager: Scope;
protected entityManager: EntityManager | Scope;
/**
* @type {{}}
@@ -22,15 +23,20 @@ export class EntityRepository<T> {
*/
protected mapping: Mapping<T>;
/**
* Holds the query options.
*
* @type { string[] }
*/
protected queryOptions: Array<string> = ['orderBy', 'limit', 'offset', 'groupBy', 'select'];
/**
* Construct a new EntityRepository.
*
* @param {Scope} entityManager
* @param {{}} entity
* @param {EntityManager|Scope} entityManager
* @param {{}} entity
*/
public constructor(entityManager: Scope, entity: EntityCtor<T>) {
public constructor(entityManager: EntityManager | Scope, entity: EntityCtor<T>) {
this.entityManager = entityManager;
this.entity = entity;
this.mapping = Mapping.forEntity(entity);
@@ -48,12 +54,25 @@ export class EntityRepository<T> {
/**
* Get a reference to the entity manager.
*
* @returns {Scope}
* @returns {EntityManager | Scope}
*/
protected getEntityManager(): Scope {
protected getEntityManager(): EntityManager | Scope{
return this.entityManager;
}
/**
* Get a scope. If this repository was constructed within a scope, you get said scope.
*
* @returns {Scope}
*/
protected getScope(): Scope {
if (this.entityManager instanceof Scope) {
return this.entityManager;
}
return this.entityManager.createScope();
}
/**
* Get a new query builder.
*
@@ -71,7 +90,8 @@ export class EntityRepository<T> {
statement = connection(`${this.mapping.getTableName()} as ${alias}`);
}
return new QueryBuilder(this.entityManager, statement, this.mapping, alias);
// Create a new QueryBuilder, pass in a scoped entity manager.
return new QueryBuilder(this.getScope(), statement, this.mapping, alias);
}
/**
@@ -15,7 +15,7 @@ export class Scope {
/**
* @type {UnitOfWork}
*/
private unitOfWork: UnitOfWork;
private readonly unitOfWork: UnitOfWork;
/**
* @type {EntityManager}
@@ -58,15 +58,12 @@ export class Scope {
/**
* Proxy method the entityManager getRepository method.
*
* @param {Entity} entity
* @param {string|Entity} entity
*
* @returns {EntityRepository}
*/
public getRepository<T>(entity: EntityCtor<T>): EntityRepository<T> {
let entityReference = this.manager.resolveEntityReference(entity) as EntityCtor<T>;
let Repository = Mapping.forEntity(entityReference).getRepository();
return new Repository(this, entityReference);
public getRepository<T>(entity: string | EntityCtor<T>): EntityRepository<T> {
return this.manager.getRepository(entity, this);
}
/**
@@ -190,15 +187,7 @@ export class Scope {
* @returns {Store}
*/
public getStore(entity?: EntityInterface | string): Store {
let storeName = null;
if (typeof entity === 'string') {
storeName = entity;
} else if (entity) {
storeName = this.manager.getMapping(entity).getStoreName();
}
return this.wetland.getStore(storeName);
return this.manager.getStore(entity);
}
/**

0 comments on commit 569a3d1

Please sign in to comment.