Skip to content

Commit

Permalink
feat(metadata): create instance of metadata instead of static one [BC] (
Browse files Browse the repository at this point in the history
#91)

Previously there was only static metadata storage, used across the whole ORM. It is still needed for initial gathering of decorator metadata, but then ORM will create new instance based on this global state and work with that internally.

BREAKING CHANGES:
MetadatStorage.getMetadata() will contain only decorator data, not the computed one like naming strategy defaults, etc. Those are now stored only in the instance one, accessible via `orm.getMetadata()` and `em.getMetadata()`.
  • Loading branch information
B4nan committed Aug 9, 2019
1 parent f05d343 commit 3abc034
Show file tree
Hide file tree
Showing 37 changed files with 289 additions and 233 deletions.
37 changes: 23 additions & 14 deletions lib/EntityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ export class EntityManager {
private readonly validator = new EntityValidator(this.config.get('strict'));
private readonly repositoryMap: Record<string, EntityRepository<IEntity>> = {};
private readonly entityLoader = new EntityLoader(this);
private readonly metadata = MetadataStorage.getMetadata();
private readonly unitOfWork = new UnitOfWork(this);
private readonly entityFactory = new EntityFactory(this.unitOfWork, this.driver, this.config);
private readonly entityFactory = new EntityFactory(this.unitOfWork, this.driver, this.config, this.metadata);
private transactionContext: Transaction;

constructor(readonly config: Configuration,
private readonly driver: IDatabaseDriver) { }
private readonly driver: IDatabaseDriver,
private readonly metadata: MetadataStorage) { }

getDriver<D extends IDatabaseDriver = IDatabaseDriver>(): D {
return this.driver as D;
Expand All @@ -32,7 +32,7 @@ export class EntityManager {
entityName = Utils.className(entityName);

if (!this.repositoryMap[entityName]) {
const meta = this.metadata[entityName];
const meta = this.metadata.get(entityName);
const RepositoryClass = this.config.getRepositoryClass(meta.customRepository);
this.repositoryMap[entityName] = new RepositoryClass(this, entityName);
}
Expand All @@ -53,7 +53,7 @@ export class EntityManager {
async find<T extends IEntityType<T>>(entityName: EntityName<T>, where?: FilterQuery<T>, populate?: string[], orderBy?: QueryOrderMap, limit?: number, offset?: number): Promise<T[]>;
async find<T extends IEntityType<T>>(entityName: EntityName<T>, where = {} as FilterQuery<T>, populate?: string[] | FindOptions, orderBy?: QueryOrderMap, limit?: number, offset?: number): Promise<T[]> {
entityName = Utils.className(entityName);
where = SmartQueryHelper.processWhere(where, entityName);
where = SmartQueryHelper.processWhere(where, entityName, this.metadata.get(entityName));
this.validator.validateParams(where);
const options = Utils.isObject<FindOptions>(populate) ? populate : { populate, orderBy, limit, offset };
const results = await this.driver.find(entityName, where, options.populate || [], options.orderBy || {}, options.limit, options.offset, this.transactionContext);
Expand All @@ -79,9 +79,10 @@ export class EntityManager {
async findOne<T extends IEntityType<T>>(entityName: EntityName<T>, where: FilterQuery<T> | IPrimaryKey, populate?: string[] | FindOneOptions, orderBy?: QueryOrderMap): Promise<T | null> {
entityName = Utils.className(entityName);
const options = Utils.isObject<FindOneOptions>(populate) ? populate : { populate, orderBy };
const meta = this.metadata.get(entityName);
this.validator.validateEmptyWhere(where);
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName);
this.checkLockRequirements(options.lockMode, this.metadata[entityName]);
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName, meta);
this.checkLockRequirements(options.lockMode, meta);
let entity = this.getUnitOfWork().tryGetById<T>(entityName, where);
const isOptimisticLocking = !Utils.isDefined(options.lockMode) || options.lockMode === LockMode.OPTIMISTIC;

Expand Down Expand Up @@ -129,7 +130,7 @@ export class EntityManager {
async nativeUpdate<T extends IEntityType<T>>(entityName: EntityName<T>, where: FilterQuery<T>, data: EntityData<T>): Promise<number> {
entityName = Utils.className(entityName);
data = SmartQueryHelper.processParams(data);
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName);
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName, this.metadata.get(entityName));
this.validator.validateParams(data, 'update data');
this.validator.validateParams(where, 'update condition');
const res = await this.driver.nativeUpdate(entityName, where, data, this.transactionContext);
Expand All @@ -139,7 +140,7 @@ export class EntityManager {

async nativeDelete<T extends IEntityType<T>>(entityName: EntityName<T>, where: FilterQuery<T> | string | any): Promise<number> {
entityName = Utils.className(entityName);
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName);
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName, this.metadata.get(entityName));
this.validator.validateParams(where, 'delete condition');
const res = await this.driver.nativeDelete(entityName, where, this.transactionContext);

Expand All @@ -148,7 +149,7 @@ export class EntityManager {

map<T extends IEntityType<T>>(entityName: EntityName<T>, result: EntityData<T>): T {
entityName = Utils.className(entityName);
const meta = this.metadata[entityName];
const meta = this.metadata.get(entityName);
const data = this.driver.mapResult(result, meta)!;

return this.merge<T>(entityName, data, true);
Expand All @@ -170,9 +171,13 @@ export class EntityManager {
}

entityName = Utils.className(entityName);
this.validator.validatePrimaryKey(data as EntityData<T>, this.metadata[entityName]);
this.validator.validatePrimaryKey(data as EntityData<T>, this.metadata.get(entityName));
let entity = this.getUnitOfWork().tryGetById<T>(entityName, data as EntityData<T>);

if (entity) {
entity.__em = this;
}

if (entity && entity.isInitialized() && !refresh) {
return entity;
}
Expand Down Expand Up @@ -206,7 +211,7 @@ export class EntityManager {

async count<T extends IEntityType<T>>(entityName: EntityName<T>, where: FilterQuery<T>): Promise<number> {
entityName = Utils.className(entityName);
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName);
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName, this.metadata.get(entityName));
this.validator.validateParams(where);

return this.driver.count(entityName, where, this.transactionContext);
Expand Down Expand Up @@ -283,7 +288,7 @@ export class EntityManager {
canPopulate(entityName: string | Function, property: string): boolean {
entityName = Utils.className(entityName);
const [p, ...parts] = property.split('.');
const props = this.metadata[entityName].properties;
const props = this.metadata.get(entityName).properties;
const ret = p in props && props[p].reference !== ReferenceType.SCALAR;

if (!ret) {
Expand All @@ -298,7 +303,7 @@ export class EntityManager {
}

fork(clear = true): EntityManager {
const em = new EntityManager(this.config, this.driver);
const em = new EntityManager(this.config, this.driver, this.metadata);

if (!clear) {
Object.values(this.getUnitOfWork().getIdentityMap()).forEach(entity => em.merge(entity));
Expand All @@ -325,6 +330,10 @@ export class EntityManager {
return this.transactionContext as T;
}

getMetadata(): MetadataStorage {
return this.metadata;
}

private checkLockRequirements(mode: LockMode | undefined, meta: EntityMetadata): void {
if (!mode) {
return;
Expand Down
15 changes: 8 additions & 7 deletions lib/MikroORM.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import { EntityManager } from './EntityManager';
import { IDatabaseDriver } from './drivers';
import { MetadataDiscovery } from './metadata';
import { MetadataDiscovery, MetadataStorage } from './metadata';
import { Configuration, Logger, Options } from './utils';
import { EntityMetadata } from './decorators';

export class MikroORM {

em: EntityManager;
readonly config: Configuration;
private metadata: Record<string, EntityMetadata>;
private metadata: MetadataStorage;
private readonly driver: IDatabaseDriver;
private readonly logger: Logger;

static async init(options: Options): Promise<MikroORM> {
const orm = new MikroORM(options);
const driver = await orm.connect();
orm.em = new EntityManager(orm.config, driver);

try {
const storage = new MetadataDiscovery(orm.em, orm.config, orm.logger);
orm.metadata = await storage.discover();
const discovery = new MetadataDiscovery(MetadataStorage.init(), orm.driver.getPlatform(), orm.config, orm.logger);
orm.metadata = await discovery.discover();
orm.em = new EntityManager(orm.config, driver, orm.metadata);
orm.metadata.decorate(orm.em);
driver.setMetadata(orm.metadata);

return orm;
} catch (e) {
Expand Down Expand Up @@ -57,7 +58,7 @@ export class MikroORM {
return this.driver.getConnection().close(force);
}

getMetadata(): Record<string, EntityMetadata> {
getMetadata(): MetadataStorage {
return this.metadata;
}

Expand Down
6 changes: 5 additions & 1 deletion lib/connections/Connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { MetadataStorage } from '../metadata';
export abstract class Connection {

protected readonly logger = this.config.getLogger();
protected readonly metadata = MetadataStorage.getMetadata();
protected metadata: MetadataStorage;
protected abstract client: any;

constructor(protected readonly config: Configuration) { }
Expand Down Expand Up @@ -56,6 +56,10 @@ export abstract class Connection {
return `${url.protocol}//${options.user}${options.password ? ':*****' : ''}@${options.host}:${options.port}`;
}

setMetadata(metadata: MetadataStorage): void {
this.metadata = metadata;
}

protected async executeQuery<T>(query: string, params: any[], cb: () => Promise<T>): Promise<T> {
const now = Date.now();
const res = await cb();
Expand Down
4 changes: 3 additions & 1 deletion lib/connections/MongoConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ export class MongoConnection extends Connection {

private getCollectionName(name: EntityName<IEntity>): string {
name = Utils.className(name);
return this.metadata[name] ? this.metadata[name].collection : name;
const meta = this.metadata.get(name);

return meta ? meta.collection : name;
}

}
14 changes: 7 additions & 7 deletions lib/drivers/AbstractSqlDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra
}

async findOne<T extends IEntityType<T>>(entityName: string, where: FilterQuery<T> | string, populate: string[] = [], orderBy: QueryOrderMap = {}, fields?: string[], lockMode?: LockMode, ctx?: Transaction): Promise<T | null> {
const pk = this.metadata[entityName].primaryKey;
const pk = this.metadata.get(entityName).primaryKey;

if (Utils.isPrimaryKey(where)) {
where = { [pk]: where };
Expand All @@ -45,7 +45,7 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra

async count(entityName: string, where: any, ctx?: Transaction): Promise<number> {
const qb = this.createQueryBuilder(entityName, ctx);
const pk = this.metadata[entityName].primaryKey;
const pk = this.metadata.get(entityName).primaryKey;
const res = await qb.count(pk, true).where(where).execute('get', false);

return +res.count;
Expand Down Expand Up @@ -78,7 +78,7 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra
res = await qb.update(data).where(where).execute('run', false);
}

await this.processManyToMany(entityName, Utils.extractPK(data[pk] || where, this.metadata[entityName])!, collections, ctx);
await this.processManyToMany(entityName, Utils.extractPK(data[pk] || where, this.metadata.get(entityName))!, collections, ctx);

return res;
}
Expand All @@ -97,11 +97,11 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra
}

protected extractManyToMany<T extends IEntityType<T>>(entityName: string, data: EntityData<T>): EntityData<T> {
if (!this.metadata[entityName]) {
if (!this.metadata.get(entityName)) {
return {};
}

const props = this.metadata[entityName].properties;
const props = this.metadata.get(entityName).properties;
const ret: EntityData<T> = {};

for (const k of Object.keys(data)) {
Expand All @@ -117,11 +117,11 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra
}

protected async processManyToMany<T extends IEntityType<T>>(entityName: string, pk: IPrimaryKey, collections: EntityData<T>, ctx?: Transaction) {
if (!this.metadata[entityName]) {
if (!this.metadata.get(entityName)) {
return;
}

const props = this.metadata[entityName].properties;
const props = this.metadata.get(entityName).properties;
const owners = Object.keys(collections).filter(k => props[k].owner);

for (const k of owners) {
Expand Down
14 changes: 10 additions & 4 deletions lib/drivers/DatabaseDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export abstract class DatabaseDriver<C extends Connection> implements IDatabaseD

protected readonly connection: C;
protected readonly platform: Platform;
protected readonly metadata = MetadataStorage.getMetadata();
protected readonly logger = this.config.getLogger();
protected metadata: MetadataStorage;

constructor(protected readonly config: Configuration) { }

Expand All @@ -39,8 +39,8 @@ export abstract class DatabaseDriver<C extends Connection> implements IDatabaseD

const fk1 = prop.joinColumn;
const fk2 = prop.inverseJoinColumn;
const pivotTable = prop.owner ? prop.pivotTable : this.metadata[prop.type].properties[prop.mappedBy].pivotTable;
const orderBy = { [`${pivotTable}.${this.metadata[pivotTable].primaryKey}`]: QueryOrder.ASC };
const pivotTable = prop.owner ? prop.pivotTable : this.metadata.get(prop.type).properties[prop.mappedBy].pivotTable;
const orderBy = { [`${pivotTable}.${this.metadata.get(pivotTable).primaryKey}`]: QueryOrder.ASC };
const items = owners.length ? await this.find(prop.type, { [fk1]: { $in: owners } }, [pivotTable], orderBy, undefined, undefined, ctx) : [];

const map: Record<string, T[]> = {};
Expand Down Expand Up @@ -82,8 +82,14 @@ export abstract class DatabaseDriver<C extends Connection> implements IDatabaseD
return this.platform;
}

setMetadata(metadata: MetadataStorage): void {
this.metadata = metadata;
this.connection.setMetadata(metadata);
}

protected getPrimaryKeyField(entityName: string): string {
return this.metadata[entityName] ? this.metadata[entityName].primaryKey : this.config.getNamingStrategy().referenceColumnName();
const meta = this.metadata.get(entityName);
return meta ? meta.primaryKey : this.config.getNamingStrategy().referenceColumnName();
}

}
3 changes: 3 additions & 0 deletions lib/drivers/IDatabaseDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Connection, QueryResult, Transaction } from '../connections';
import { QueryOrderMap } from '../query';
import { Platform } from '../platforms';
import { LockMode } from '../unit-of-work';
import { MetadataStorage } from '../metadata';

export interface IDatabaseDriver<C extends Connection = Connection> {

Expand Down Expand Up @@ -37,6 +38,8 @@ export interface IDatabaseDriver<C extends Connection = Connection> {

getPlatform(): Platform;

setMetadata(metadata: MetadataStorage): void;

}

export type FilterQuery<T> = Partial<T> | Record<string, any>;
6 changes: 3 additions & 3 deletions lib/drivers/MongoDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class MongoDriver extends DatabaseDriver<MongoConnection> {
where = this.renameFields(entityName, where);
const res = await this.connection.find<T>(entityName, where, orderBy, limit, offset);

return res.map((r: T) => this.mapResult<T>(r, this.metadata[entityName])!);
return res.map((r: T) => this.mapResult<T>(r, this.metadata.get(entityName))!);
}

async findOne<T extends IEntityType<T>>(entityName: string, where: FilterQuery<T> | IPrimaryKey, populate: string[] = [], orderBy: QueryOrderMap = {}, fields?: string[], lockMode?: LockMode): Promise<T | null> {
Expand All @@ -28,7 +28,7 @@ export class MongoDriver extends DatabaseDriver<MongoConnection> {
where = this.renameFields(entityName, where) as FilterQuery<T>;
const res = await this.connection.find<T>(entityName, where, orderBy, 1, undefined, fields);

return this.mapResult<T>(res[0], this.metadata[entityName]);
return this.mapResult<T>(res[0], this.metadata.get(entityName));
}

async count<T extends IEntityType<T>>(entityName: string, where: FilterQuery<T>): Promise<number> {
Expand Down Expand Up @@ -69,7 +69,7 @@ export class MongoDriver extends DatabaseDriver<MongoConnection> {
private renameFields(entityName: string, data: any): any {
data = Object.assign({}, data); // copy first
Utils.renameKey(data, 'id', '_id');
const meta = this.metadata[entityName];
const meta = this.metadata.get(entityName);

Object.keys(data).forEach(k => {
if (meta && meta.properties[k]) {
Expand Down
5 changes: 2 additions & 3 deletions lib/entity/ArrayCollection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { EntityProperty, IEntityType, IPrimaryKey } from '../decorators';
import { MetadataStorage } from '../metadata';
import { ReferenceType } from './enums';
import { Collection } from './Collection';

Expand All @@ -23,7 +22,7 @@ export class ArrayCollection<T extends IEntityType<T>> {

toArray(): Record<string, any>[] {
return this.getItems().map(item => {
const meta = MetadataStorage.getMetadata(item.constructor.name);
const meta = this.owner.__em.getMetadata().get(item.constructor.name);
const args = [...meta.toJsonParams.map(() => undefined), [this.property.name]];

return item.toJSON(...args);
Expand Down Expand Up @@ -141,7 +140,7 @@ export class ArrayCollection<T extends IEntityType<T>> {

protected get property() {
if (!this._property) {
const meta = MetadataStorage.getMetadata(this.owner.constructor.name);
const meta = this.owner.__em.getMetadata().get(this.owner.constructor.name);
const field = Object.keys(meta.properties).find(k => this.owner[k] === this);
this._property = meta.properties[field!];
}
Expand Down
3 changes: 1 addition & 2 deletions lib/entity/EntityAssigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import { SCALAR_TYPES } from './EntityFactory';
import { EntityManager } from '../EntityManager';
import { EntityData, EntityProperty, IEntity, IEntityType } from '../decorators';
import { Utils } from '../utils';
import { MetadataStorage } from '../metadata';
import { ReferenceType } from './enums';

export class EntityAssigner {

static assign<T extends IEntityType<T>>(entity: T, data: EntityData<T>, options?: AssignOptions): void;
static assign<T extends IEntityType<T>>(entity: T, data: EntityData<T>, onlyProperties?: boolean): void;
static assign<T extends IEntityType<T>>(entity: T, data: EntityData<T>, onlyProperties: AssignOptions | boolean = false): void {
const meta = MetadataStorage.getMetadata(entity.constructor.name);
const meta = entity.__em.getMetadata().get(entity.constructor.name);
const props = meta.properties;
const options = (typeof onlyProperties === 'boolean' ? { onlyProperties } : onlyProperties);

Expand Down

0 comments on commit 3abc034

Please sign in to comment.