added build from entity schemas - second stop

This commit is contained in:
Umed Khudoiberdiev 2016-06-03 02:03:47 +05:00
parent 2212868554
commit efaf61b9d0
22 changed files with 93 additions and 92 deletions

View File

@ -374,20 +374,20 @@ export class Connection {
// take imported naming strategy metadatas
getMetadataArgsStorage()
.namingStrategies
.filterByClasses(this.namingStrategyClasses)
.filterByTargets(this.namingStrategyClasses)
.forEach(metadata => this.namingStrategyMetadatas.push(new NamingStrategyMetadata(metadata)));
// take imported event subscribers
getMetadataArgsStorage()
.entitySubscribers
.filterByClasses(this.subscriberClasses)
.filterByTargets(this.subscriberClasses)
.map(metadata => getFromContainer(metadata.target))
.forEach(subscriber => this.entitySubscribers.push(subscriber));
// take imported entity listeners
getMetadataArgsStorage()
.entityListeners
.filterByClasses(this.entityClasses)
.filterByTargets(this.entityClasses)
.forEach(metadata => this.entityListeners.push(new EntityListenerMetadata(metadata)));
// build entity metadatas from metadata args storage (collected from decorators)

View File

@ -9,13 +9,13 @@ export interface ColumnMetadataArgs {
/**
* Class to which column is applied.
*/
readonly target?: Function;
readonly target?: Function|string;
/**
* In the case if this column is without a target, targetId must be specified.
* This is used for entity schemas without classes.
*/
readonly targetId?: string;
// readonly targetId?: string;
/**
* Class's property name to which column is applied.

View File

@ -6,7 +6,7 @@ export interface JoinColumnMetadataArgs {
/**
* Class to which this column is applied.
*/
readonly target: Function;
readonly target: Function|string;
/**
* Class's property name to which this column is applied.

View File

@ -47,8 +47,8 @@ export class MetadataArgsStorage {
* Gets merged (with all abstract classes) table metadatas for the given classes.
*/
getMergedTableMetadatas(classes: Function[]) {
const allTableMetadataArgs = this.tables.filterByClasses(classes);
const tableMetadatas = this.tables.filterByClasses(classes).filter(table => table.type === "regular" || table.type === "closure");
const allTableMetadataArgs = this.tables.filterByTargets(classes);
const tableMetadatas = this.tables.filterByTargets(classes).filter(table => table.type === "regular" || table.type === "closure");
return tableMetadatas.map(tableMetadata => {
return this.mergeWithAbstract(allTableMetadataArgs, tableMetadata);
@ -59,7 +59,7 @@ export class MetadataArgsStorage {
* Gets merged (with all abstract classes) embeddable table metadatas for the given classes.
*/
getMergedEmbeddableTableMetadatas(classes: Function[]) {
const embeddableTableMetadatas = this.tables.filterByClasses(classes).filter(table => table.type === "embeddable");
const embeddableTableMetadatas = this.tables.filterByTargets(classes).filter(table => table.type === "embeddable");
return embeddableTableMetadatas.map(embeddableTableMetadata => {
return this.mergeWithEmbeddable(embeddableTableMetadatas, embeddableTableMetadata);
@ -75,19 +75,20 @@ export class MetadataArgsStorage {
private mergeWithAbstract(allTableMetadatas: TargetMetadataArgsCollection<TableMetadataArgs>,
tableMetadata: TableMetadataArgs) {
const indices = this.indices.filterByClass(tableMetadata.target);
const columns = this.columns.filterByClass(tableMetadata.target);
const relations = this.relations.filterByClass(tableMetadata.target);
const joinColumns = this.joinColumns.filterByClass(tableMetadata.target);
const joinTables = this.joinTables.filterByClass(tableMetadata.target);
const entityListeners = this.entityListeners.filterByClass(tableMetadata.target);
const relationCounts = this.relationCounts.filterByClass(tableMetadata.target);
const embeddeds = this.embeddeds.filterByClass(tableMetadata.target);
const indices = this.indices.filterByTarget(tableMetadata.target);
const columns = this.columns.filterByTarget(tableMetadata.target);
const relations = this.relations.filterByTarget(tableMetadata.target);
const joinColumns = this.joinColumns.filterByTarget(tableMetadata.target);
const joinTables = this.joinTables.filterByTarget(tableMetadata.target);
const entityListeners = this.entityListeners.filterByTarget(tableMetadata.target);
const relationCounts = this.relationCounts.filterByTarget(tableMetadata.target);
const embeddeds = this.embeddeds.filterByTarget(tableMetadata.target);
allTableMetadatas
.filter(metadata => {
if (!tableMetadata.target || !metadata.target) return false;
return this.isInherited(tableMetadata.target, metadata.target);
if (!(tableMetadata.target instanceof Function) || !(metadata.target instanceof Function)) return false;
return this.isInherited(tableMetadata.target, metadata.target); // todo: fix it
})
.forEach(parentMetadata => {
const metadatasFromAbstract = this.mergeWithAbstract(allTableMetadatas, parentMetadata);
@ -138,12 +139,13 @@ export class MetadataArgsStorage {
*/
private mergeWithEmbeddable(allTableMetadatas: TargetMetadataArgsCollection<TableMetadataArgs>,
tableMetadata: TableMetadataArgs) {
const columns = this.columns.filterByClass(tableMetadata.target);
const columns = this.columns.filterByTarget(tableMetadata.target);
allTableMetadatas
.filter(metadata => {
if (!tableMetadata.target || !metadata.target) return false;
return this.isInherited(tableMetadata.target, metadata.target);
if (!(tableMetadata.target instanceof Function) || !(metadata.target instanceof Function)) return false;
return this.isInherited(tableMetadata.target, metadata.target); // todo: fix it for entity schema
})
.forEach(parentMetadata => {
const metadatasFromParents = this.mergeWithEmbeddable(allTableMetadatas, parentMetadata);

View File

@ -10,13 +10,13 @@ export interface RelationMetadataArgs {
/**
* Class to which this relation is applied.
*/
readonly target?: Function;
readonly target?: Function|string;
/**
* In the case if this relation is without a target, targetId must be specified.
* This is used for entity schemas without classes.
*/
readonly targetId?: string;
// readonly targetId?: string;
/**
* Class's property name to which this relation is applied.

View File

@ -8,13 +8,13 @@ export interface TableMetadataArgs {
/**
* Class to which table is applied.
*/
readonly target?: Function;
readonly target?: Function|string;
/**
* In the case if this table is without a target, targetId must be specified.
* This is used for entity schemas without classes.
*/
readonly targetId?: string;
// readonly targetId?: string;
/**
* Table name.

View File

@ -10,11 +10,11 @@ export class EntityMetadataCollection extends Array<EntityMetadata> {
// Public Methods
// -------------------------------------------------------------------------
hasTarget(target: Function) {
hasTarget(target: Function|string) {
return !!this.find(metadata => metadata.target === target);
}
findByTarget(target: Function) {
findByTarget(target: Function|string) {
const metadata = this.find(metadata => metadata.target === target);
if (!metadata)
throw new EntityMetadataNotFound(target);

View File

@ -1,6 +1,6 @@
import {TargetMetadataArgsCollection} from "./TargetMetadataArgsCollection";
export class PropertyMetadataArgsCollection<T extends { target?: Function, propertyName?: string }> extends TargetMetadataArgsCollection<T> {
export class PropertyMetadataArgsCollection<T extends { target?: Function|string, propertyName?: string }> extends TargetMetadataArgsCollection<T> {
// -------------------------------------------------------------------------
// Public Methods

View File

@ -1,6 +1,6 @@
import {MetadataAlreadyExistsError} from "../../metadata-builder/error/MetadataAlreadyExistsError";
export class TargetMetadataArgsCollection<T extends { target?: Function }> extends Array<T> {
export class TargetMetadataArgsCollection<T extends { target?: Function|string }> extends Array<T> {
// -------------------------------------------------------------------------
// Public Methods
@ -13,16 +13,16 @@ export class TargetMetadataArgsCollection<T extends { target?: Function }> exten
return collection;
}
filterByClass(cls?: Function): this {
filterByTarget(cls?: Function|string): this {
// if no class specified then simply return empty collection
if (!cls)
return new (<any> this.constructor)();
return this.filterByClasses([cls]);
return this.filterByTargets([cls]);
}
filterByClasses(classes: Function[]): this {
filterByTargets(classes: Array<Function|string>): this { // Function[]|string[] ?
return this.filter(metadata => {
if (!metadata.target) return false;
return classes.indexOf(metadata.target) !== -1;
@ -31,7 +31,7 @@ export class TargetMetadataArgsCollection<T extends { target?: Function }> exten
add(metadata: T, checkForDuplicateTargets = false) {
if (checkForDuplicateTargets) {
if (!metadata.target)
if (!metadata.target || !(metadata.target instanceof Function))
throw new Error(`Target is not set in the given metadata.`);
if (this.hasWithTarget(metadata.target))

View File

@ -45,10 +45,10 @@ export class EntityMetadataBuilder {
// add table metadata args from the schema
const tableSchema = schema.table || {} as any;
const table: TableMetadataArgs = {
target: schema.target,
target: schema.target || schema.name,
name: tableSchema.name,
type: tableSchema.type || "regular",
targetId: schema.name,
// targetId: schema.name,
orderBy: tableSchema.orderBy,
primaryKeys: tableSchema.primaryKeys
};
@ -58,8 +58,8 @@ export class EntityMetadataBuilder {
Object.keys(schema.columns).forEach(columnName => {
const columnSchema = schema.columns[columnName];
const column: ColumnMetadataArgs = {
target: schema.target,
targetId: schema.name,
target: schema.target || schema.name,
// targetId: schema.name,
mode: columnSchema.mode,
propertyName: columnName,
// todo: what to do with it?: propertyType:
@ -86,8 +86,8 @@ export class EntityMetadataBuilder {
Object.keys(schema.relations).forEach(relationName => {
const relationSchema = schema.relations[relationName];
const relation: RelationMetadataArgs = {
target: schema.target,
targetId: schema.name,
target: schema.target || schema.name,
// targetId: schema.name,
propertyName: relationName,
// todo: what to do with it?: propertyType:
relationType: relationSchema.relationType,

View File

@ -97,6 +97,9 @@ export class EntityMetadata {
if (!this.table)
throw new Error("No table target set to the entity metadata.");
if (typeof this.table.target === "string")
return this.table.target;
if (this.target)
return (<any> this.target).name;
@ -117,7 +120,7 @@ export class EntityMetadata {
/**
* Target class to which this entity metadata is bind.
*/
get target(): Function {
get target(): Function|string {
return this.table.target;
}

View File

@ -58,7 +58,7 @@ export class IndexMetadata extends TargetMetadata {
* Gets index's name.
*/
get name() {
return this.entityMetadata.namingStrategy.indexName(this.target, this._name, this.columns);
return this.entityMetadata.namingStrategy.indexName(this._name, this.columns);
}
/**

View File

@ -4,12 +4,17 @@ import {NamingStrategyMetadataArgs} from "../metadata-args/NamingStrategyMetadat
/**
* This metadata interface contains all information about naming strategy.
*/
export class NamingStrategyMetadata extends TargetMetadata {
export class NamingStrategyMetadata {
// ---------------------------------------------------------------------
// Readonly Properties
// ---------------------------------------------------------------------
/**
* Target class to which metadata is applied.
*/
readonly target: Function;
/**
* Naming strategy name.
*/
@ -20,7 +25,7 @@ export class NamingStrategyMetadata extends TargetMetadata {
// ---------------------------------------------------------------------
constructor(args: NamingStrategyMetadataArgs) {
super(args.target);
this.target = args.target;
this.name = args.name;
}

View File

@ -18,7 +18,7 @@ export class PropertyMetadata extends TargetMetadata {
// Constructor
// ---------------------------------------------------------------------
constructor(target?: Function, propertyName?: string) {
constructor(target?: Function|string, propertyName?: string) {
super(target);
if (propertyName)

View File

@ -74,8 +74,10 @@ export class TableMetadata extends TargetMetadata {
return this.entityMetadata.namingStrategy.tableNameCustomized(this._name);
// otherwise use target's table name
if (this.target)
return this.entityMetadata.namingStrategy.tableName((this.target as any).name);
if (this.target) {
const name = typeof this.target === "string" ? this.target : (this.target as any).name;
return this.entityMetadata.namingStrategy.tableName(name);
}
// in the case if error
throw new Error("Table does not have neither table name neither target specified.");

View File

@ -10,13 +10,13 @@ export abstract class TargetMetadata {
/**
* Target class to which metadata is applied.
*/
readonly target: Function;
readonly target: Function|string;
// ---------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------
constructor(target?: Function) {
constructor(target?: Function|string) {
if (target)
this.target = target;
}

View File

@ -38,7 +38,7 @@ export class DefaultNamingStrategy implements NamingStrategyInterface {
return customName;
}
indexName(target: Function, name: string|undefined, columns: string[]): string {
indexName(name: string|undefined, columns: string[]): string {
if (name)
return name;

View File

@ -47,7 +47,7 @@ export interface NamingStrategyInterface {
/**
* Gets the name of the index - simple and compose index.
*/
indexName(target: Function, name: string|undefined, columns: string[]): string;
indexName(name: string|undefined, columns: string[]): string;
/**
* Gets the name of the join column used in the one-to-one and many-to-one relations.

View File

@ -164,6 +164,7 @@ export class EntityPersistOperationBuilder {
const relMetadata = relation.inverseEntityMetadata;
const relationIdColumnName = relMetadata.primaryColumn.propertyName;
const value = this.getEntityRelationValue(relation, newEntity);
const valueTarget = relation.target;
const referencedColumnName = relation.isOwning ? relation.referencedColumnName : relation.inverseRelation.referencedColumnName;
// const dbValue = this.getEntityRelationValue(relation, dbEntity);
@ -176,7 +177,7 @@ export class EntityPersistOperationBuilder {
return subDbEntity[relationIdColumnName] === subEntity[relationIdColumnName];
});*/
const dbValue = dbEntities.find(dbValue => {
return dbValue.entity.constructor === subEntity.constructor && dbValue.entity[referencedColumnName] === subEntity[relationIdColumnName];
return dbValue.entityTarget === valueTarget && dbValue.entity[referencedColumnName] === subEntity[relationIdColumnName];
});
if (dbValue)
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValue.entity, subEntity, dbEntities, relation, operations);
@ -184,7 +185,7 @@ export class EntityPersistOperationBuilder {
} else {
const dbValue = dbEntities.find(dbValue => {
return dbValue.entity.constructor === value.constructor && dbValue.entity[referencedColumnName] === value[relationIdColumnName];
return dbValue.entityTarget === valueTarget && dbValue.entity[referencedColumnName] === value[relationIdColumnName];
});
if (dbValue)
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValue.entity, value, dbEntities, relation, operations);
@ -326,7 +327,7 @@ export class EntityPersistOperationBuilder {
private findJunctionInsertOperations(metadata: EntityMetadata, newEntity: any, dbEntities: EntityWithId[], isRoot = true): JunctionInsertOperation[] {
const dbEntity = dbEntities.find(dbEntity => {
return dbEntity.id === newEntity[metadata.primaryColumn.propertyName] && dbEntity.entity.constructor === metadata.target;
return dbEntity.id === newEntity[metadata.primaryColumn.propertyName] && dbEntity.entityTarget === metadata.target;
});
return metadata.relations
.filter(relation => newEntity[relation.propertyName] !== null && newEntity[relation.propertyName] !== undefined)
@ -372,7 +373,7 @@ export class EntityPersistOperationBuilder {
return [];
const newEntity = newEntities.find(newEntity => {
return newEntity.id === dbEntity[metadata.primaryColumn.propertyName] && newEntity.entity.constructor === metadata.target;
return newEntity.id === dbEntity[metadata.primaryColumn.propertyName] && newEntity.entityTarget === metadata.target;
});
return metadata.relations
.filter(relation => dbEntity[relation.propertyName] !== null && dbEntity[relation.propertyName] !== undefined)
@ -437,15 +438,18 @@ export class EntityPersistOperationBuilder {
return false;
if (!newEntity[relation.propertyName] || !dbEntity[relation.propertyName])
return true;
const entityTarget = relation.target;
const newEntityRelationMetadata = this.entityMetadatas.findByTarget(newEntity[relation.propertyName].constructor);
const newEntityRelationMetadata = this.entityMetadatas.findByTarget(entityTarget);
const dbEntityRelationMetadata = this.entityMetadatas.findByTarget(dbEntity[relation.propertyName].constructor);
return newEntityRelationMetadata.getEntityId(newEntity[relation.propertyName]) !== dbEntityRelationMetadata.getEntityId(dbEntity[relation.propertyName]);
});
}
private findEntityWithId(entityWithIds: EntityWithId[], entityClass: Function, id: any) {
return entityWithIds.find(entityWithId => entityWithId.id === id && entityWithId.entity.constructor === entityClass);
private findEntityWithId(entityWithIds: EntityWithId[], entityTarget: Function|string, id: any) {
// console.log(entityWithId.entityTarget);
// console.log(entityWithId.entity.constructor);
return entityWithIds.find(entityWithId => entityWithId.id === id && entityWithId.entityTarget === entityTarget);
}
private checkCascadesAllowed(type: "insert"|"update"|"remove", metadata: EntityMetadata, relation: RelationMetadata) {

View File

@ -11,6 +11,7 @@ import {UpdateByInverseSideOperation} from "./UpdateByInverseSideOperation";
*/
export interface EntityWithId {
id: any;
entityTarget: Function|string;
entity: any;
}

View File

@ -142,6 +142,7 @@ export class QueryBuilder<Entity> {
from(tableName: string, alias: string): this;
from(entity: Function, alias: string): this;
from(entity: Function|string, alias: string): this;
from(entityOrTableName: Function|string, alias: string): this {
if (entityOrTableName instanceof Function) {
const aliasObj = new Alias(alias);
@ -517,6 +518,7 @@ export class QueryBuilder<Entity> {
if (entity[index])
return Promise.resolve(entity[index]);
// find object metadata and try to load
// const target = relation.target instanceof Function ? <Function> relation.target : <string> relation.target;
return new QueryBuilder(this.driver, this.entityMetadatas, this.broadcaster)
.select(relation.propertyName)
.from(relation.target, relation.propertyName) // todo: change `id` after join column implemented

View File

@ -447,6 +447,7 @@ export class Repository<Entity> {
return <EntityWithId> {
id: (<any> loadedEntity)[metadata.primaryColumn.name],
entityTarget: metadata.target,
entity: loadedEntity
};
});
@ -461,48 +462,29 @@ export class Repository<Entity> {
* Extracts unique objects from given entity and all its downside relations.
*/
private extractObjectsById(entity: any, metadata: EntityMetadata, entityWithIds: EntityWithId[] = []): Promise<EntityWithId[]> {
const promises = metadata.relations
// .filter(relation => !!entity[relation.propertyName])
.map(relation => {
const relMetadata = relation.inverseEntityMetadata;
// const value = ;
const promises = metadata.relations.map(relation => {
const relMetadata = relation.inverseEntityMetadata;
const value = (entity[relation.propertyName] instanceof Promise && relation.isLazy) ? entity["__" + relation.propertyName + "__"] : entity[relation.propertyName];
if (!value)
return undefined;
const value = (entity[relation.propertyName] instanceof Promise && relation.isLazy) ? entity["__" + relation.propertyName + "__"] : entity[relation.propertyName];
if (!value)
return undefined;
if (value instanceof Array) {
const subPromises = value.map((subEntity: any) => {
return this.extractObjectsById(subEntity, relMetadata, entityWithIds);
});
return Promise.all(subPromises);
if (value instanceof Array) {
const subPromises = value.map((subEntity: any) => {
return this.extractObjectsById(subEntity, relMetadata, entityWithIds);
});
return Promise.all(subPromises);
/*} else if (value instanceof Promise && relation.isLazy) {
return value.then((resolvedValue: any) => { // todo: duplicate logic
if (resolvedValue && resolvedValue instanceof Array) {
const promises = resolvedValue.map((subEntity: any) => {
return this.extractObjectsById(subEntity, relMetadata, entityWithIds);
});
return Promise.all(promises);
} else if (resolvedValue) {
return this.extractObjectsById(resolvedValue, relMetadata, entityWithIds);
}
});*/
} else {
return this.extractObjectsById(value, relMetadata, entityWithIds);
}
})
.filter(result => !!result);
} else {
return this.extractObjectsById(value, relMetadata, entityWithIds);
}
});
return Promise.all<any>(promises).then(() => {
return Promise.all<any>(promises.filter(result => !!result)).then(() => {
if (!entityWithIds.find(entityWithId => entityWithId.entity === entity)) {
entityWithIds.push({
id: entity[metadata.primaryColumn.name],
entityTarget: metadata.target,
entity: entity
});
}