implementing entity schemas - step 3

This commit is contained in:
Umed Khudoiberdiev 2016-06-03 04:22:45 +05:00
parent efaf61b9d0
commit 20afae93ef
28 changed files with 407 additions and 155 deletions

View File

@ -15,15 +15,15 @@ const options: CreateConnectionOptions = {
},
// entitySchemaDirectories: [__dirname + "/schemas"],
entitySchemas: [
require("./schemas/post.json"),
require("./schemas/post-details.json"),
require("./schemas/category.json"),
require("./schemas/image.json")
require(__dirname + "/../../../../sample/sample24-schemas/schemas/post.json"),
require(__dirname + "/../../../../sample/sample24-schemas/schemas/post-details.json"),
require(__dirname + "/../../../../sample/sample24-schemas/schemas/category.json"),
require(__dirname + "/../../../../sample/sample24-schemas/schemas/image.json")
]
};
createConnection(options).then(connection => {
let postRepository = connection.getRepository<Post>("post");
let postRepository = connection.getRepository<Post>("Post");
let post: Post = {
title: "Hello post",

View File

@ -22,8 +22,7 @@
"post": {
"target": "Post",
"type": "one-to-one",
"owner": false,
"inverseSide": "postDetails"
"inverseSide": "details"
}
}
}

View File

@ -22,7 +22,7 @@
"details": {
"target": "PostDetails",
"type": "one-to-one",
"owner": true,
"joinColumn": true,
"inverseSide": "post",
"cascadeInsert": true,
"cascadeUpdate": true,
@ -44,7 +44,7 @@
"categories": {
"target": "Category",
"type": "many-to-many",
"owner": true,
"joinTable": true,
"cascadeInsert": true,
"cascadeUpdate": true,
"cascadeRemove": true,

View File

@ -33,7 +33,7 @@ export class ConnectionManager {
connection.importEntitySchemaFromDirectories(options.entitySchemaDirectories);
if (options.entitySchemas)
connection.importEntities(options.entitySchemas);
connection.importSchemas(options.entitySchemas);
if (options.entityDirectories && options.entityDirectories.length > 0)
connection.importEntitiesFromDirectories(options.entityDirectories);

View File

@ -1,4 +1,5 @@
import {ConnectionOptions} from "../connection/ConnectionOptions";
import {EntitySchema} from "../metadata/entity-schema/EntitySchema";
/**
* All options to help to create a new connection.
@ -38,7 +39,7 @@ export interface CreateConnectionOptions {
/**
* Entity schemas to be loaded for the new connection.
*/
entitySchemas?: any[];
entitySchemas?: EntitySchema[];
/**
* List of directories from where entities will be loaded.

View File

@ -280,12 +280,15 @@ export class Connection {
* Gets repository for the given entity class.
*/
getRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function): Repository<Entity>;
// getRepository<Entity>(entityClass: Function): Repository<Entity>;
/**
* Gets repository for the given entity name.
*/
getRepository<Entity>(entityClass: string): Repository<Entity>;
/**
* Gets repository for the given entity name.
*/
getRepository<Entity>(entityClassOrName: ConstructorFunction<Entity>|Function|string): Repository<Entity>;
/**
* Gets repository for the given entity class or name.
@ -294,7 +297,7 @@ export class Connection {
if (!this.isConnected)
throw new NoConnectionForRepositoryError(this.name);
const metadata = this.entityMetadatas.findByNameOrTarget(entityClassOrName);
const metadata = this.entityMetadatas.findByTarget(entityClassOrName);
const repository = this.repositories.find(repository => Repository.ownsMetadata(repository, metadata));
if (!repository)
throw new RepositoryNotFoundError(this.name, entityClassOrName);
@ -320,7 +323,7 @@ export class Connection {
if (!this.isConnected)
throw new NoConnectionForRepositoryError(this.name);
const metadata = this.entityMetadatas.findByNameOrTarget(entityClassOrName);
const metadata = this.entityMetadatas.findByTarget(entityClassOrName);
const repository = this.repositories.find(repository => Repository.ownsMetadata(repository, metadata));
if (!repository)
throw new RepositoryNotFoundError(this.name, entityClassOrName);
@ -337,7 +340,7 @@ export class Connection {
if (!this.isConnected)
throw new NoConnectionForRepositoryError(this.name);
const metadata = this.entityMetadatas.findByNameOrTarget(entityClass);
const metadata = this.entityMetadatas.findByTarget(entityClass);
const repository = this.reactiveRepositories.find(repository => ReactiveRepository.ownsMetadata(repository, metadata));
if (!repository)
throw new ReactiveRepositoryNotFoundError(this.name, entityClass);
@ -352,7 +355,7 @@ export class Connection {
if (!this.isConnected)
throw new NoConnectionForRepositoryError(this.name);
const metadata = this.entityMetadatas.findByNameOrTarget(entityClass);
const metadata = this.entityMetadatas.findByTarget(entityClass);
const repository = this.reactiveRepositories.find(repository => ReactiveRepository.ownsMetadata(repository, metadata));
if (!repository)
throw new RepositoryNotFoundError(this.name, entityClass);

View File

@ -34,6 +34,11 @@ export class EntityManager {
*/
getRepository<Entity>(entityClass: string): Repository<Entity>;
/**
* Gets repository for the given entity class or name.
*/
getRepository<Entity>(entityClassOrName: ConstructorFunction<Entity>|Function|string): Repository<Entity>;
/**
* Gets repository for the given entity class or name.
*/
@ -77,8 +82,12 @@ export class EntityManager {
/**
* Checks if entity has an id.
*/
hasId(entity: Function): boolean {
return this.getRepository(entity.constructor).hasId(entity);
hasId(entity: Function): boolean;
hasId(target: Function|string, entity: Function): boolean;
hasId(targetOrEntity: Function|string, maybeEntity?: Function): boolean {
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
const entity = arguments.length === 2 ? <Function> maybeEntity : <Function> targetOrEntity;
return this.getRepository(target).hasId(entity);
}
/**
@ -123,17 +132,26 @@ export class EntityManager {
/**
* Persists (saves) a given entity in the database.
*/
persist<Entity>(entity: Entity): Promise<Entity> {
return this.getRepository(<any> entity.constructor).persist(entity);
persist<Entity>(entity: Entity): Promise<Entity>;
persist<Entity>(targetOrEntity: Function|string, entity: Entity): Promise<Entity>;
persist<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Promise<Entity> {
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
return <any> this.getRepository(<any> target).persist(entity);
}
/**
* Removes a given entity from the database.
*/
remove<Entity>(entity: Entity) {
return this.getRepository(<any> entity.constructor).remove(entity);
remove<Entity>(entity: Entity): Promise<Entity>;
remove<Entity>(targetOrEntity: Function|string, entity: Entity): Promise<Entity>;
remove<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Promise<Entity> {
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
return <any> this.getRepository(<any> target).remove(entity);
}
/**
* Finds entities that match given conditions.
*/

View File

@ -27,29 +27,33 @@ export class ReactiveEntityManager {
/**
* Gets repository of the given entity.
*/
getRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function): Repository<Entity> {
getRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function|string): Repository<Entity> {
return this.connection.getRepository(entityClass);
}
/**
* Gets reactive repository of the given entity.
*/
getReactiveRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function): ReactiveRepository<Entity> {
getReactiveRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function|string): ReactiveRepository<Entity> {
return this.connection.getReactiveRepository(entityClass);
}
/**
* Gets reactive tree repository of the given entity.
*/
getReactiveTreeRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function): ReactiveTreeRepository<Entity> {
getReactiveTreeRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function|string): ReactiveTreeRepository<Entity> {
return this.connection.getReactiveTreeRepository(entityClass);
}
/**
* Checks if entity has an id.
*/
hasId(entity: Function): boolean {
return this.getReactiveRepository(entity.constructor).hasId(entity);
hasId(entity: Function): boolean;
hasId(target: Function|string, entity: Function): boolean;
hasId(targetOrEntity: Function|string, maybeEntity?: Function): boolean {
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
const entity = arguments.length === 2 ? <Function> maybeEntity : <Function> targetOrEntity;
return this.getReactiveRepository(target).hasId(entity);
}
/**
@ -94,17 +98,25 @@ export class ReactiveEntityManager {
/**
* Persists (saves) a given entity in the database.
*/
persist<Entity>(entity: Entity): Rx.Observable<Entity> {
persist<Entity>(entity: Entity): Rx.Observable<Entity>;
persist<Entity>(targetOrEntity: Function|string, entity: Entity): Rx.Observable<Entity>;
persist<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Rx.Observable<Entity> {
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
return <any> this.getReactiveRepository(<any> entity.constructor).persist(entity);
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
return <any> this.getReactiveRepository(<any> target).persist(entity);
}
/**
* Removes a given entity from the database.
*/
remove<Entity>(entity: Entity): Rx.Observable<Entity[]> {
remove<Entity>(entity: Entity): Rx.Observable<Entity>;
remove<Entity>(targetOrEntity: Function|string, entity: Entity): Rx.Observable<Entity>;
remove<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Rx.Observable<Entity> {
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
return <any> this.getReactiveRepository(<any> entity.constructor).remove(entity);
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
return <any> this.getReactiveRepository(<any> target).remove(entity);
}
/**

View File

@ -8,7 +8,7 @@ export interface JoinTableMetadataArgs {
/**
* 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

@ -46,9 +46,9 @@ export class MetadataArgsStorage {
/**
* Gets merged (with all abstract classes) table metadatas for the given classes.
*/
getMergedTableMetadatas(classes: Function[]) {
const allTableMetadataArgs = this.tables.filterByTargets(classes);
const tableMetadatas = this.tables.filterByTargets(classes).filter(table => table.type === "regular" || table.type === "closure");
getMergedTableMetadatas(classes?: Function[]) {
const allTableMetadataArgs = classes ? this.tables.filterByTargets(classes) : this.tables;
const tableMetadatas = allTableMetadataArgs.filter(table => table.type === "regular" || table.type === "closure");
return tableMetadatas.map(tableMetadata => {
return this.mergeWithAbstract(allTableMetadataArgs, tableMetadata);
@ -58,8 +58,9 @@ export class MetadataArgsStorage {
/**
* Gets merged (with all abstract classes) embeddable table metadatas for the given classes.
*/
getMergedEmbeddableTableMetadatas(classes: Function[]) {
const embeddableTableMetadatas = this.tables.filterByTargets(classes).filter(table => table.type === "embeddable");
getMergedEmbeddableTableMetadatas(classes?: Function[]) {
const tables = classes ? this.tables.filterByTargets(classes) : this.tables;
const embeddableTableMetadatas = tables.filter(table => table.type === "embeddable");
return embeddableTableMetadatas.map(embeddableTableMetadata => {
return this.mergeWithEmbeddable(embeddableTableMetadatas, embeddableTableMetadata);

View File

@ -21,14 +21,6 @@ export class EntityMetadataCollection extends Array<EntityMetadata> {
return metadata;
}
findByNameOrTarget(nameOrTarget: Function|string) {
if (typeof nameOrTarget === "string") {
return this.findByName(nameOrTarget);
} else {
return this.findByTarget(nameOrTarget);
}
}
findByName(name: string) {
const metadata = this.find(metadata => metadata.name === name);

View File

@ -1,6 +1,6 @@
import {EntityMetadata} from "../metadata/EntityMetadata";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {ColumnMetadata, ColumnMode} from "../metadata/ColumnMetadata";
import {ColumnOptions} from "../decorator/options/ColumnOptions";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {EntityMetadataValidator} from "./EntityMetadataValidator";
@ -18,6 +18,9 @@ import {MetadataArgsStorage} from "../metadata-args/MetadataArgsStorage";
import {TableMetadataArgs} from "../metadata-args/TableMetadataArgs";
import {ColumnMetadataArgs} from "../metadata-args/ColumnMetadataArgs";
import {RelationMetadataArgs} from "../metadata-args/RelationMetadataArgs";
import {JoinColumnMetadataArgs} from "../metadata-args/JoinColumnMetadataArgs";
import {JoinTableMetadataArgs} from "../metadata-args/JoinTableMetadataArgs";
import {JoinColumnOptions} from "../decorator/options/JoinColumnOptions";
/**
* Aggregates all metadata: table, column, relation into one collection grouped by tables for a given set of classes.
@ -57,10 +60,24 @@ export class EntityMetadataBuilder {
// add columns metadata args from the schema
Object.keys(schema.columns).forEach(columnName => {
const columnSchema = schema.columns[columnName];
let mode: ColumnMode = "regular";
if (columnSchema.primary)
mode = "primary";
if (columnSchema.createDate)
mode = "createDate";
if (columnSchema.updateDate)
mode = "updateDate";
if (columnSchema.version)
mode = "version";
if (columnSchema.treeChildrenCount)
mode = "treeChildrenCount";
if (columnSchema.treeLevel)
mode = "treeLevel";
const column: ColumnMetadataArgs = {
target: schema.target || schema.name,
// targetId: schema.name,
mode: columnSchema.mode,
mode: mode,
propertyName: columnName,
// todo: what to do with it?: propertyType:
options: {
@ -90,8 +107,8 @@ export class EntityMetadataBuilder {
// targetId: schema.name,
propertyName: relationName,
// todo: what to do with it?: propertyType:
relationType: relationSchema.relationType,
type: relationSchema.type,
relationType: relationSchema.type,
type: relationSchema.target,
inverseSideProperty: relationSchema.inverseSide,
isTreeParent: relationSchema.isTreeParent,
isTreeChildren: relationSchema.isTreeChildren,
@ -107,20 +124,63 @@ export class EntityMetadataBuilder {
};
metadataArgsStorage.relations.add(relation);
// add join column
if (relationSchema.joinColumn) {
if (typeof relationSchema.joinColumn === "boolean") {
const joinColumn: JoinColumnMetadataArgs = {
target: schema.target || schema.name,
// targetId: schema.name,
propertyName: relationName
};
metadataArgsStorage.joinColumns.push(joinColumn);
} else {
const joinColumn: JoinColumnMetadataArgs = {
target: schema.target || schema.name,
// targetId: schema.name,
propertyName: relationName,
name: relationSchema.joinColumn.name,
referencedColumnName: relationSchema.joinColumn.referencedColumnName
};
metadataArgsStorage.joinColumns.push(joinColumn);
}
}
// add join table
if (relationSchema.joinTable) {
if (typeof relationSchema.joinTable === "boolean") {
const joinTable: JoinTableMetadataArgs = {
target: schema.target || schema.name,
// targetId: schema.name,
propertyName: relationName
};
metadataArgsStorage.joinTables.push(joinTable);
} else {
const joinTable: JoinTableMetadataArgs = {
target: schema.target || schema.name,
// targetId: schema.name,
propertyName: relationName,
name: relationSchema.joinTable.name,
joinColumn: relationSchema.joinTable.joinColumn,
inverseJoinColumn: relationSchema.joinTable.inverseJoinColumn
};
metadataArgsStorage.joinTables.push(joinTable);
}
}
});
});
return this.buildFromAnyMetadataArgsStorage(metadataArgsStorage, namingStrategy, []);
return this.buildFromAnyMetadataArgsStorage(metadataArgsStorage, namingStrategy);
}
/**
* Builds a complete metadata aggregations for the given entity classes.
*/
buildFromMetadataArgsStorage(namingStrategy: NamingStrategyInterface, entityClasses: Function[]): EntityMetadata[] {
buildFromMetadataArgsStorage(namingStrategy: NamingStrategyInterface, entityClasses?: Function[]): EntityMetadata[] {
return this.buildFromAnyMetadataArgsStorage(getMetadataArgsStorage(), namingStrategy, entityClasses);
}
buildFromAnyMetadataArgsStorage(metadataArgsStorage: MetadataArgsStorage, namingStrategy: NamingStrategyInterface, entityClasses: Function[]): EntityMetadata[] {
buildFromAnyMetadataArgsStorage(metadataArgsStorage: MetadataArgsStorage, namingStrategy: NamingStrategyInterface, entityClasses?: Function[]): EntityMetadata[] {
const embeddableMergedArgs = metadataArgsStorage.getMergedEmbeddableTableMetadatas(entityClasses);
const entityMetadatas = metadataArgsStorage.getMergedTableMetadatas(entityClasses).map(mergedArgs => {

View File

@ -329,7 +329,7 @@ export class EntityMetadata {
* Returns entity id of the given entity.
*/
getEntityId(entity: any) {
return entity[this.primaryColumn.propertyName];
return entity ? entity[this.primaryColumn.propertyName] : undefined;
}
/**

View File

@ -9,7 +9,7 @@ import {RelationMetadataArgs} from "../metadata-args/RelationMetadataArgs";
/**
* Function that returns a type of the field. Returned value must be a class used on the relation.
*/
export type RelationTypeInFunction = ((type?: any) => Function)|string; // todo: |string ?
export type RelationTypeInFunction = ((type?: any) => Function)|Function|string; // todo: |string ?
/**

View File

@ -3,6 +3,7 @@ import {ColumnType} from "../types/ColumnTypes";
import {TableType} from "../TableMetadata";
import {RelationType} from "../types/RelationTypes";
import {OnDeleteType} from "../ForeignKeyMetadata";
import {JoinColumnOptions} from "../../decorator/options/JoinColumnOptions";
export interface EntitySchema {
@ -54,11 +55,34 @@ export interface EntitySchema {
[columnName: string]: {
/**
* Column mode in which column will work.
* For example, "primary" means that it will be a primary column, or "createDate" means that it will create a create
* date column.
* Indicates if this column is a primary column.
*/
mode: ColumnMode;
primary: boolean;
/**
* Indicates if this column is a created date column.
*/
createDate: boolean;
/**
* Indicates if this column is an update date column.
*/
updateDate: boolean;
/**
* Indicates if this column is a version column.
*/
version: boolean;
/**
* Indicates if this column is a treeChildrenCount column.
*/
treeChildrenCount: boolean;
/**
* Indicates if this column is a treeLevel column.
*/
treeLevel: boolean;
/**
* Column type. Must be one of the value from the ColumnTypes class.
@ -135,15 +159,14 @@ export interface EntitySchema {
[relationName: string]: {
/**
* Type of relation. Can be one of the value of the RelationTypes class.
* Indicates with which entity this relation is made.
*/
relationType: RelationType;
target: Function|string;
/**
* Type of the relation. This type is in function because of language specifics and problems with recursive
* referenced classes.
* Type of relation. Can be one of the value of the RelationTypes class.
*/
type: string;
type: RelationType;
/**
* Inverse side of the relation.
@ -154,6 +177,22 @@ export interface EntitySchema {
* Join table options of this column. If set to true then it simply means that it has a join table.
*/
joinTable?: boolean|{
/**
* Name of the table that will be created to store values of the both tables (join table).
* By default is auto generated.
*/
name?: string;
/**
* First column of the join table.
*/
joinColumn?: JoinColumnOptions;
/**
* Second (inverse) column of the join table.
*/
inverseJoinColumn?: JoinColumnOptions;
};

View File

@ -9,6 +9,7 @@ import {CascadesNotAllowedError} from "./error/CascadesNotAllowedError";
import {RemoveOperation} from "./operation/RemoveOperation";
import {EntityMetadataCollection} from "../metadata-args/collection/EntityMetadataCollection";
import {UpdateByInverseSideOperation} from "./operation/UpdateByInverseSideOperation";
import {JunctionRemoveOperation} from "./operation/JunctionRemoveOperation";
/**
* 1. collect all exist objects from the db entity
@ -57,8 +58,8 @@ export class EntityPersistOperationBuilder {
* Finds columns and relations from entity2 which does not exist or does not match in entity1.
*/
buildFullPersistment(metadata: EntityMetadata,
dbEntity: any,
persistedEntity: any,
dbEntity: EntityWithId,
persistedEntity: EntityWithId,
dbEntities: EntityWithId[],
allPersistedEntities: EntityWithId[]): PersistOperation {
@ -87,8 +88,8 @@ export class EntityPersistOperationBuilder {
* Finds columns and relations from entity2 which does not exist or does not match in entity1.
*/
buildOnlyRemovement(metadata: EntityMetadata,
dbEntity: any,
persistedEntity: any,
dbEntity: EntityWithId,
persistedEntity: EntityWithId,
dbEntities: EntityWithId[],
allPersistedEntities: EntityWithId[]): PersistOperation {
// const dbEntities = this.extractObjectsById(dbEntity, metadata);
@ -109,12 +110,12 @@ export class EntityPersistOperationBuilder {
// Private Methods
// -------------------------------------------------------------------------
private findCascadeInsertedEntities(newEntity: any,
private findCascadeInsertedEntities(newEntityWithId: EntityWithId,
dbEntities: EntityWithId[],
fromRelation?: RelationMetadata,
operations: InsertOperation[] = []): InsertOperation[] {
// const metadata = this.connection.getEntityMetadata(newEntity.constructor);
const metadata = this.entityMetadatas.findByTarget(newEntity.constructor);
const newEntity = newEntityWithId.entity;
const metadata = this.entityMetadatas.findByTarget(newEntityWithId.entityTarget);
const isObjectNew = !this.findEntityWithId(dbEntities, metadata.target, newEntity[metadata.primaryColumn.propertyName]);
// if object is new and should be inserted, we check if cascades are allowed before add it to operations list
@ -122,17 +123,30 @@ export class EntityPersistOperationBuilder {
return operations; // looks like object is new here, but cascades are not allowed - then we should stop iteration
} else if (isObjectNew && !operations.find(o => o.entity === newEntity)) { // object is new and cascades are allow here
operations.push(new InsertOperation(newEntity));
operations.push(new InsertOperation(newEntityWithId.entityTarget, newEntity));
}
metadata.relations.forEach(relation => {
const value = this.getEntityRelationValue(relation, newEntity);
const inverseMetadata = relation.inverseEntityMetadata;
if (!value) return;
if (value instanceof Array) {
value.map((subValue: any) => this.findCascadeInsertedEntities(subValue, dbEntities, relation, operations));
value.forEach((subValue: any) => {
const subValueWithId: EntityWithId = {
id: inverseMetadata.getEntityId(subValue),
entity: subValue,
entityTarget: inverseMetadata.target
};
this.findCascadeInsertedEntities(subValueWithId, dbEntities, relation, operations);
});
} else {
this.findCascadeInsertedEntities(value, dbEntities, relation, operations);
const valueWithId: EntityWithId = {
id: inverseMetadata.getEntityId(value),
entity: value,
entityTarget: inverseMetadata.target
};
this.findCascadeInsertedEntities(valueWithId, dbEntities, relation, operations);
}
});
@ -141,11 +155,14 @@ export class EntityPersistOperationBuilder {
private findCascadeUpdateEntities(updatesByRelations: UpdateByRelationOperation[],
metadata: EntityMetadata,
dbEntity: any,
newEntity: any,
dbEntityWithId: EntityWithId,
newEntityWithId: EntityWithId,
dbEntities: EntityWithId[],
fromRelation?: RelationMetadata,
operations: UpdateOperation[] = []): UpdateOperation[] {
const dbEntity = dbEntityWithId.entity;
const newEntity = newEntityWithId.entity;
if (!dbEntity)
return operations;
@ -157,7 +174,7 @@ export class EntityPersistOperationBuilder {
} else if (diffColumns.length || diffRelations.length) {
const entityId = newEntity[metadata.primaryColumn.propertyName];
if (entityId)
operations.push(new UpdateOperation(newEntity, entityId, diffColumns, diffRelations));
operations.push(new UpdateOperation(newEntityWithId.entityTarget, newEntity, entityId, diffColumns, diffRelations));
}
metadata.relations.forEach(relation => {
@ -179,16 +196,40 @@ export class EntityPersistOperationBuilder {
const dbValue = dbEntities.find(dbValue => {
return dbValue.entityTarget === valueTarget && dbValue.entity[referencedColumnName] === subEntity[relationIdColumnName];
});
if (dbValue)
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValue.entity, subEntity, dbEntities, relation, operations);
if (dbValue) {
const dbValueWithId: EntityWithId = {
id: relMetadata.getEntityId(dbValue.entity),
entity: dbValue.entity,
entityTarget: relMetadata.target
};
const subEntityWithId: EntityWithId = {
id: relMetadata.getEntityId(subEntity),
entity: subEntity,
entityTarget: relMetadata.target
};
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValueWithId, subEntityWithId, dbEntities, relation, operations);
}
});
} else {
const dbValue = dbEntities.find(dbValue => {
return dbValue.entityTarget === valueTarget && dbValue.entity[referencedColumnName] === value[relationIdColumnName];
});
if (dbValue)
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValue.entity, value, dbEntities, relation, operations);
if (dbValue) {
const dbValueWithId: EntityWithId = {
id: relMetadata.getEntityId(dbValue.entity),
entity: dbValue.entity,
entityTarget: relMetadata.target
};
const valueWithId: EntityWithId = {
id: relMetadata.getEntityId(value),
entity: value,
entityTarget: relMetadata.target
};
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValueWithId, valueWithId, dbEntities, relation, operations);
}
}
});
@ -196,12 +237,14 @@ export class EntityPersistOperationBuilder {
}
private findCascadeRemovedEntities(metadata: EntityMetadata,
dbEntity: any,
dbEntityWithId: EntityWithId,
allPersistedEntities: EntityWithId[],
fromRelation: RelationMetadata|undefined,
fromMetadata: EntityMetadata|undefined,
fromEntityId: any,
parentAlreadyRemoved: boolean = false): RemoveOperation[] {
const dbEntity = dbEntityWithId.entity;
let operations: RemoveOperation[] = [];
if (!dbEntity)
return operations;
@ -214,7 +257,7 @@ export class EntityPersistOperationBuilder {
return operations; // looks like object is removed here, but cascades are not allowed - then we should stop iteration
} else if (isObjectRemoved) { // object is remove and cascades are allow here
operations.push(new RemoveOperation(dbEntity, entityId, <EntityMetadata> fromMetadata, fromRelation, fromEntityId));
operations.push(new RemoveOperation(dbEntityWithId.entityTarget, dbEntity, entityId, <EntityMetadata> fromMetadata, fromRelation, fromEntityId));
}
metadata.relations.forEach(relation => {
@ -224,11 +267,23 @@ export class EntityPersistOperationBuilder {
if (dbValue instanceof Array) {
dbValue.forEach((subDbEntity: any) => {
const relationOperations = this.findCascadeRemovedEntities(relMetadata, subDbEntity, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
const subDbEntityWithId: EntityWithId = {
id: relMetadata.getEntityId(subDbEntity),
entity: subDbEntity,
entityTarget: relMetadata.target
};
const relationOperations = this.findCascadeRemovedEntities(relMetadata, subDbEntityWithId, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
relationOperations.forEach(o => operations.push(o));
});
} else {
const relationOperations = this.findCascadeRemovedEntities(relMetadata, dbValue, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
const dbValueWithId: EntityWithId = {
id: relMetadata.getEntityId(dbValue),
entity: dbValue,
entityTarget: relMetadata.target
};
const relationOperations = this.findCascadeRemovedEntities(relMetadata, dbValueWithId, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
relationOperations.forEach(o => operations.push(o));
}
}, []);
@ -237,13 +292,16 @@ export class EntityPersistOperationBuilder {
}
private updateInverseRelations(metadata: EntityMetadata,
dbEntity: any,
newEntity: any,
dbEntityWithId: EntityWithId,
newEntityWithId: EntityWithId,
operations: UpdateByInverseSideOperation[] = []): UpdateByInverseSideOperation[] {
const dbEntity = dbEntityWithId.entity;
const newEntity = newEntityWithId.entity;
metadata.relations
.filter(relation => relation.isOneToMany) // todo: maybe need to check isOneToOne and not owner
// .filter(relation => newEntity[relation.propertyName] instanceof Array) // todo: what to do with empty relations? need to set to NULL from inverse side?
.forEach(relation => {
const relationMetadata = relation.inverseEntityMetadata;
// to find new objects in relation go throw all objects in newEntity and check if they don't exist in dbEntity
if (newEntity && newEntity[relation.propertyName] instanceof Array) {
@ -255,7 +313,7 @@ export class EntityPersistOperationBuilder {
return relation.inverseEntityMetadata.getEntityId(newSubEntity) === relation.inverseEntityMetadata.getEntityId(dbSubEntity);
});
}).forEach((subEntity: any) => {
operations.push(new UpdateByInverseSideOperation("update", subEntity, newEntity, relation));
operations.push(new UpdateByInverseSideOperation(relationMetadata.target, newEntityWithId.entityTarget, "update", subEntity, newEntity, relation));
});
}
@ -269,7 +327,7 @@ export class EntityPersistOperationBuilder {
return relation.inverseEntityMetadata.getEntityId(dbSubEntity) === relation.inverseEntityMetadata.getEntityId(newSubEntity);
});
}).forEach((subEntity: any) => {
operations.push(new UpdateByInverseSideOperation("remove", subEntity, newEntity, relation));
operations.push(new UpdateByInverseSideOperation(relationMetadata.target, newEntityWithId.entityTarget, "remove", subEntity, newEntity, relation));
});
}
@ -286,37 +344,49 @@ export class EntityPersistOperationBuilder {
*
*/
private updateRelations(insertOperations: InsertOperation[], newEntity: any): UpdateByRelationOperation[] {
private updateRelations(insertOperations: InsertOperation[], newEntity: EntityWithId): UpdateByRelationOperation[] {
return insertOperations.reduce((operations, insertOperation) => {
return operations.concat(this.findRelationsWithEntityInside(insertOperation, newEntity));
}, <UpdateByRelationOperation[]> []);
}
private findRelationsWithEntityInside(insertOperation: InsertOperation, entityToSearchIn: any): UpdateByRelationOperation[] {
// updateByt metadata = this.connection.getEntityMetadata(entityToSearchIn.constructor);
const metadata = this.entityMetadatas.findByTarget(entityToSearchIn.constructor);
private findRelationsWithEntityInside(insertOperation: InsertOperation, entityToSearchInWithId: EntityWithId): UpdateByRelationOperation[] {
const entityToSearchIn = entityToSearchInWithId.entity;
const metadata = this.entityMetadatas.findByTarget(entityToSearchInWithId.entityTarget);
const operations: UpdateByRelationOperation[] = [];
metadata.relations.forEach(relation => {
const value = this.getEntityRelationValue(relation, entityToSearchIn);
const inverseMetadata = relation.inverseEntityMetadata;
if (!value) return;
if (value instanceof Array) {
value.forEach((sub: any) => {
if (!relation.isManyToMany && sub === insertOperation.entity)
operations.push(new UpdateByRelationOperation(entityToSearchIn, insertOperation, relation));
operations.push(new UpdateByRelationOperation(entityToSearchInWithId.entityTarget, entityToSearchIn, insertOperation, relation));
const subOperations = this.findRelationsWithEntityInside(insertOperation, sub);
const subWithId: EntityWithId = {
id: inverseMetadata.getEntityId(sub),
entity: sub,
entityTarget: inverseMetadata.target
};
const subOperations = this.findRelationsWithEntityInside(insertOperation, subWithId);
subOperations.forEach(o => operations.push(o));
});
} else if (value) {
if (value === insertOperation.entity) {
operations.push(new UpdateByRelationOperation(entityToSearchIn, insertOperation, relation));
operations.push(new UpdateByRelationOperation(entityToSearchInWithId.entityTarget, entityToSearchIn, insertOperation, relation));
}
const subOperations = this.findRelationsWithEntityInside(insertOperation, value);
const valueWithId: EntityWithId = {
id: inverseMetadata.getEntityId(value),
entity: value,
entityTarget: inverseMetadata.target
};
const subOperations = this.findRelationsWithEntityInside(insertOperation, valueWithId);
subOperations.forEach(o => operations.push(o));
}
@ -325,7 +395,8 @@ export class EntityPersistOperationBuilder {
return operations;
}
private findJunctionInsertOperations(metadata: EntityMetadata, newEntity: any, dbEntities: EntityWithId[], isRoot = true): JunctionInsertOperation[] {
private findJunctionInsertOperations(metadata: EntityMetadata, newEntityWithId: EntityWithId, dbEntities: EntityWithId[], isRoot = true): JunctionInsertOperation[] {
const newEntity = newEntityWithId.entity;
const dbEntity = dbEntities.find(dbEntity => {
return dbEntity.id === newEntity[metadata.primaryColumn.propertyName] && dbEntity.entityTarget === metadata.target;
});
@ -347,19 +418,31 @@ export class EntityPersistOperationBuilder {
operations.push({
metadata: relation.junctionEntityMetadata,
entity1: newEntity,
entity2: subEntity
entity2: subEntity,
entity1Target: newEntityWithId.entityTarget,
entity2Target: relationMetadata.target
});
}
}
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
const subOperations = this.findJunctionInsertOperations(relationMetadata, subEntity, dbEntities, false);
const subEntityWithId: EntityWithId = {
id: relationMetadata.getEntityId(subEntity),
entity: subEntity,
entityTarget: relationMetadata.target
};
const subOperations = this.findJunctionInsertOperations(relationMetadata, subEntityWithId, dbEntities, false);
subOperations.forEach(o => operations.push(o));
}
});
} else {
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
const subOperations = this.findJunctionInsertOperations(relationMetadata, value, dbEntities, false);
const valueWithId: EntityWithId = {
id: relationMetadata.getEntityId(value),
entity: value,
entityTarget: relationMetadata.target
};
const subOperations = this.findJunctionInsertOperations(relationMetadata, valueWithId, dbEntities, false);
subOperations.forEach(o => operations.push(o));
}
}
@ -368,7 +451,8 @@ export class EntityPersistOperationBuilder {
}, <JunctionInsertOperation[]> []);
}
private findJunctionRemoveOperations(metadata: EntityMetadata, dbEntity: any, newEntities: EntityWithId[], isRoot = true): JunctionInsertOperation[] {
private findJunctionRemoveOperations(metadata: EntityMetadata, dbEntityWithId: EntityWithId, newEntities: EntityWithId[], isRoot = true): JunctionInsertOperation[] {
const dbEntity = dbEntityWithId.entity;
if (!dbEntity) // if new entity is persisted then it does not have anything to be deleted
return [];
@ -393,25 +477,39 @@ export class EntityPersistOperationBuilder {
operations.push({
metadata: relation.junctionEntityMetadata,
entity1: dbEntity,
entity2: subEntity
entity2: subEntity,
entity1Target: dbEntityWithId.entityTarget,
entity2Target: relationMetadata.target
});
}
}
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
const subOperations = this.findJunctionRemoveOperations(relationMetadata, subEntity, newEntities, false);
const subEntityWithId: EntityWithId = {
id: relationMetadata.getEntityId(subEntity),
entity: subEntity,
entityTarget: relationMetadata.target
};
const subOperations = this.findJunctionRemoveOperations(relationMetadata, subEntityWithId, newEntities, false);
subOperations.forEach(o => operations.push(o));
}
});
} else {
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
const subOperations = this.findJunctionRemoveOperations(relationMetadata, dbValue, newEntities, false);
const dbValueWithId: EntityWithId = {
id: relationMetadata.getEntityId(dbValue),
entity: dbValue,
entityTarget: relationMetadata.target
};
const subOperations = this.findJunctionRemoveOperations(relationMetadata, dbValueWithId, newEntities, false);
subOperations.forEach(o => operations.push(o));
}
}
return operations;
}, <JunctionInsertOperation[]> []);
}, <JunctionRemoveOperation[]> []);
}
private diffColumns(metadata: EntityMetadata, newEntity: any, dbEntity: any) {
@ -441,14 +539,12 @@ export class EntityPersistOperationBuilder {
const entityTarget = relation.target;
const newEntityRelationMetadata = this.entityMetadatas.findByTarget(entityTarget);
const dbEntityRelationMetadata = this.entityMetadatas.findByTarget(dbEntity[relation.propertyName].constructor);
const dbEntityRelationMetadata = this.entityMetadatas.findByTarget(entityTarget);
return newEntityRelationMetadata.getEntityId(newEntity[relation.propertyName]) !== dbEntityRelationMetadata.getEntityId(dbEntity[relation.propertyName]);
});
}
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);
}

View File

@ -136,7 +136,7 @@ export class PersistOperationExecutor {
private executeInsertClosureTableOperations(persistOperation: PersistOperation) {
const promises = persistOperation.inserts
.filter(operation => {
const metadata = this.entityMetadatas.findByTarget(operation.entity.constructor);
const metadata = this.entityMetadatas.findByTarget(operation.target);
return metadata.table.isClosure;
})
.map(operation => {
@ -221,7 +221,7 @@ export class PersistOperationExecutor {
*/
private executeRemoveOperations(persistOperation: PersistOperation) {
return Promise.all(persistOperation.removes.map(operation => {
return this.delete(operation.entity);
return this.delete(operation.target, operation.entity);
}));
}
@ -230,7 +230,7 @@ export class PersistOperationExecutor {
*/
private updateIdsOfInsertedEntities(persistOperation: PersistOperation) {
persistOperation.inserts.forEach(insertOperation => {
const metadata = this.entityMetadatas.findByTarget(insertOperation.entity.constructor);
const metadata = this.entityMetadatas.findByTarget(insertOperation.target);
insertOperation.entity[metadata.primaryColumn.propertyName] = insertOperation.entityId;
});
}
@ -240,7 +240,7 @@ export class PersistOperationExecutor {
*/
private updateSpecialColumnsInEntities(persistOperation: PersistOperation) {
persistOperation.inserts.forEach(insertOperation => {
const metadata = this.entityMetadatas.findByTarget(insertOperation.entity.constructor);
const metadata = this.entityMetadatas.findByTarget(insertOperation.target);
if (metadata.hasUpdateDateColumn)
insertOperation.entity[metadata.updateDateColumn.propertyName] = insertOperation.date;
if (metadata.hasCreateDateColumn)
@ -257,7 +257,7 @@ export class PersistOperationExecutor {
}*/
});
persistOperation.updates.forEach(updateOperation => {
const metadata = this.entityMetadatas.findByTarget(updateOperation.entity.constructor);
const metadata = this.entityMetadatas.findByTarget(updateOperation.target);
if (metadata.hasUpdateDateColumn)
updateOperation.entity[metadata.updateDateColumn.propertyName] = updateOperation.date;
if (metadata.hasCreateDateColumn)
@ -272,9 +272,9 @@ export class PersistOperationExecutor {
*/
private updateIdsOfRemovedEntities(persistOperation: PersistOperation) {
persistOperation.removes.forEach(removeOperation => {
const metadata = this.entityMetadatas.findByTarget(removeOperation.entity.constructor);
const metadata = this.entityMetadatas.findByTarget(removeOperation.target);
const removedEntity = persistOperation.allPersistedEntities.find(allNewEntity => {
return allNewEntity.entity.constructor === removeOperation.entity.constructor && allNewEntity.id === removeOperation.entity[metadata.primaryColumn.propertyName];
return allNewEntity.entityTarget === removeOperation.target && allNewEntity.id === removeOperation.entity[metadata.primaryColumn.propertyName];
});
if (removedEntity)
removedEntity.entity[metadata.primaryColumn.propertyName] = undefined;
@ -289,7 +289,7 @@ export class PersistOperationExecutor {
const relatedInsertOperation = insertOperations.find(o => o.entity === operation.targetEntity);
const idInInserts = relatedInsertOperation ? relatedInsertOperation.entityId : null;
if (operation.updatedRelation.isOneToMany) {
const metadata = this.entityMetadatas.findByTarget(operation.insertOperation.entity.constructor);
const metadata = this.entityMetadatas.findByTarget(operation.insertOperation.target);
if (operation.insertOperation.entity === target)
updateMap[operation.updatedRelation.inverseRelation.propertyName] = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
@ -307,7 +307,7 @@ export class PersistOperationExecutor {
const relatedInsertOperation = insertOperations.find(o => o.entity === operation.targetEntity);
const idInInserts = relatedInsertOperation ? relatedInsertOperation.entityId : null;
if (operation.updatedRelation.isOneToMany || operation.updatedRelation.isOneToOneNotOwner) {
const metadata = this.entityMetadatas.findByTarget(operation.insertOperation.entity.constructor);
const metadata = this.entityMetadatas.findByTarget(operation.insertOperation.target);
tableName = metadata.table.name;
relationName = operation.updatedRelation.inverseRelation.name;
relationId = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
@ -315,7 +315,7 @@ export class PersistOperationExecutor {
id = operation.insertOperation.entityId;
} else {
const metadata = this.entityMetadatas.findByTarget(operation.targetEntity.constructor);
const metadata = this.entityMetadatas.findByTarget(operation.entityTarget);
tableName = metadata.table.name;
relationName = operation.updatedRelation.name;
relationId = operation.insertOperation.entityId;
@ -326,8 +326,8 @@ export class PersistOperationExecutor {
}
private updateInverseRelation(operation: UpdateByInverseSideOperation, insertOperations: InsertOperation[]) {
const targetEntityMetadata = this.entityMetadatas.findByTarget(operation.targetEntity.constructor);
const fromEntityMetadata = this.entityMetadatas.findByTarget(operation.fromEntity.constructor);
const targetEntityMetadata = this.entityMetadatas.findByTarget(operation.entityTarget);
const fromEntityMetadata = this.entityMetadatas.findByTarget(operation.fromEntityTarget);
const tableName = targetEntityMetadata.table.name;
const targetRelation = operation.fromRelation.inverseRelation;
const idColumn = targetEntityMetadata.primaryColumn.name;
@ -350,7 +350,7 @@ export class PersistOperationExecutor {
private update(updateOperation: UpdateOperation) {
const entity = updateOperation.entity;
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
const metadata = this.entityMetadatas.findByTarget(updateOperation.target);
const values: { [key: string]: any } = {};
updateOperation.columns.forEach(column => {
@ -386,14 +386,14 @@ export class PersistOperationExecutor {
throw new Error("Remove operation relation is not set"); // todo: find out how its possible
}
private delete(entity: any) {
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
private delete(target: Function|string, entity: any) {
const metadata = this.entityMetadatas.findByTarget(target);
return this.driver.delete(metadata.table.name, { [metadata.primaryColumn.name]: entity[metadata.primaryColumn.propertyName] });
}
private insert(operation: InsertOperation) {
const entity = operation.entity;
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
const metadata = this.entityMetadatas.findByTarget(operation.target);
const columns = metadata.columns
.filter(column => !column.isVirtual && column.hasEntityValue(entity));
@ -449,7 +449,7 @@ export class PersistOperationExecutor {
private insertIntoClosureTable(operation: InsertOperation, updateMap: { [key: string]: any }) {
const entity = operation.entity;
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
const metadata = this.entityMetadatas.findByTarget(operation.target);
const parentEntity = entity[metadata.treeParentRelation.propertyName];
let parentEntityId: any = 0;
@ -472,7 +472,7 @@ export class PersistOperationExecutor {
}
private updateTreeLevel(operation: InsertOperation) {
const metadata = this.entityMetadatas.findByTarget(operation.entity.constructor);
const metadata = this.entityMetadatas.findByTarget(operation.target);
if (metadata.hasTreeLevelColumn && operation.treeLevel) {
const values = { [metadata.treeLevelColumn.name]: operation.treeLevel };
@ -485,8 +485,8 @@ export class PersistOperationExecutor {
private insertJunctions(junctionOperation: JunctionInsertOperation, insertOperations: InsertOperation[]) {
const junctionMetadata = junctionOperation.metadata;
const metadata1 = this.entityMetadatas.findByTarget(junctionOperation.entity1.constructor);
const metadata2 = this.entityMetadatas.findByTarget(junctionOperation.entity2.constructor);
const metadata1 = this.entityMetadatas.findByTarget(junctionOperation.entity1Target);
const metadata2 = this.entityMetadatas.findByTarget(junctionOperation.entity2Target);
const columns = junctionMetadata.columns.map(column => column.name);
const insertOperation1 = insertOperations.find(o => o.entity === junctionOperation.entity1);
const insertOperation2 = insertOperations.find(o => o.entity === junctionOperation.entity2);
@ -523,8 +523,8 @@ export class PersistOperationExecutor {
private removeJunctions(junctionOperation: JunctionRemoveOperation) {
const junctionMetadata = junctionOperation.metadata;
const metadata1 = this.entityMetadatas.findByTarget(junctionOperation.entity1.constructor);
const metadata2 = this.entityMetadatas.findByTarget(junctionOperation.entity2.constructor);
const metadata1 = this.entityMetadatas.findByTarget(junctionOperation.entity1Target);
const metadata2 = this.entityMetadatas.findByTarget(junctionOperation.entity2Target);
const columns = junctionMetadata.columns.map(column => column.name);
const id1 = junctionOperation.entity1[metadata1.primaryColumn.propertyName];
const id2 = junctionOperation.entity2[metadata2.primaryColumn.propertyName];

View File

@ -5,7 +5,8 @@ export class InsertOperation {
public treeLevel: number;
constructor(public entity: any,
constructor(public target: Function|string, // todo: probably should be metadata here
public entity: any,
public entityId?: number,
public date = new Date()) {
}

View File

@ -6,6 +6,8 @@ import {EntityMetadata} from "../../metadata/EntityMetadata";
export class JunctionInsertOperation {
constructor(public metadata: EntityMetadata,
public entity1: any,
public entity2: any) {
public entity2: any,
public entity1Target: Function|string,
public entity2Target: Function|string) {
}
}

View File

@ -6,6 +6,8 @@ import {EntityMetadata} from "../../metadata/EntityMetadata";
export class JunctionRemoveOperation {
constructor(public metadata: EntityMetadata,
public entity1: any,
public entity2: any) {
public entity2: any,
public entity1Target: any,
public entity2Target: any) {
}
}

View File

@ -22,8 +22,8 @@ export class PersistOperation {
// todo: what if we have two same entities in the insert operations?
dbEntity: any;
persistedEntity: any;
dbEntity: EntityWithId;
persistedEntity: EntityWithId;
allDbEntities: EntityWithId[];
allPersistedEntities: EntityWithId[];
inserts: InsertOperation[] = [];

View File

@ -5,7 +5,8 @@ import {EntityMetadata} from "../../metadata/EntityMetadata";
* @internal
*/
export class RemoveOperation {
constructor(public entity: any,
constructor(public target: Function|string, // todo: probably should be metadata here
public entity: any,
public entityId: any,
public fromMetadata: EntityMetadata, // todo: use relation.metadata instead?
public relation: RelationMetadata|undefined,

View File

@ -4,7 +4,9 @@ import {RelationMetadata} from "../../metadata/RelationMetadata";
* @internal
*/
export class UpdateByInverseSideOperation {
constructor(public operationType: "update"|"remove",
constructor(public entityTarget: Function|string, // todo: probably must be entity metadata here?
public fromEntityTarget: Function|string,
public operationType: "update"|"remove",
public targetEntity: any,
public fromEntity: any,
public fromRelation: RelationMetadata) {

View File

@ -5,7 +5,8 @@ import {RelationMetadata} from "../../metadata/RelationMetadata";
* @internal
*/
export class UpdateByRelationOperation {
constructor(public targetEntity: any,
constructor(public entityTarget: Function|string, // todo: probably must be entity metadata here?
public targetEntity: any,
public insertOperation: InsertOperation,
public updatedRelation: RelationMetadata) {
}

View File

@ -5,7 +5,8 @@ import {RelationMetadata} from "../../metadata/RelationMetadata";
* @internal
*/
export class UpdateOperation {
constructor(public entity: any,
constructor(public target: Function|string,
public entity: any,
public entityId: any,
public columns: ColumnMetadata[],
public relations: RelationMetadata[],

View File

@ -366,7 +366,7 @@ export class QueryBuilder<Entity> {
})
.then(results => this.rawResultsToEntities(results))
.then(results => this.addLazyProperties(results))
.then(results => this.broadcaster.broadcastLoadEventsForAll(results).then(() => results))
.then(results => this.broadcaster.broadcastLoadEventsForAll(this.aliasMap.mainAlias.target, results).then(() => results))
.then(results => {
return {
entities: results,
@ -384,7 +384,7 @@ export class QueryBuilder<Entity> {
.then(results => this.addLazyProperties(results))
.then(results => {
return this.broadcaster
.broadcastLoadEventsForAll(results)
.broadcastLoadEventsForAll(this.aliasMap.mainAlias.target, results)
.then(() => results);
})
.then(results => {

View File

@ -126,7 +126,17 @@ export class Repository<Entity> {
return this.findNotLoadedIds(entityWithIds, allPersistedEntities);
}) // need to find db entities that were not loaded by initialize method
.then(allDbEntities => {
return this.entityPersistOperationBuilder.buildFullPersistment(this.metadata, loadedDbEntity, entity, allDbEntities, allPersistedEntities);
const persistedEntity: EntityWithId = {
id: this.metadata.getEntityId(entity),
entityTarget: this.metadata.target,
entity: entity
};
const dbEntity: EntityWithId = {
id: this.metadata.getEntityId(loadedDbEntity),
entityTarget: this.metadata.target,
entity: loadedDbEntity
};
return this.entityPersistOperationBuilder.buildFullPersistment(this.metadata, dbEntity, persistedEntity, allDbEntities, allPersistedEntities);
})
.then(persistOperation => {
return this.persistOperationExecutor.executePersistOperation(persistOperation);
@ -151,7 +161,18 @@ export class Repository<Entity> {
})
// .then(([dbEntities, allPersistedEntities]: [EntityWithId[], EntityWithId[]]) => {
.then(results => {
const persistOperation = this.entityPersistOperationBuilder.buildOnlyRemovement(this.metadata, dbEntity, entity, results[0], results[1]);
const entityWithId: EntityWithId = {
id: this.metadata.getEntityId(entity),
entityTarget: this.metadata.target,
entity: entity
};
const dbEntityWithId: EntityWithId = {
id: this.metadata.getEntityId(dbEntity),
entityTarget: this.metadata.target,
entity: dbEntity
};
const persistOperation = this.entityPersistOperationBuilder.buildOnlyRemovement(this.metadata, dbEntityWithId, entityWithId, results[0], results[1]);
return this.persistOperationExecutor.executePersistOperation(persistOperation);
}).then(() => entity);
}
@ -438,10 +459,10 @@ export class Repository<Entity> {
private findNotLoadedIds(dbEntities: EntityWithId[], persistedEntities: EntityWithId[]): Promise<EntityWithId[]> {
const missingDbEntitiesLoad = persistedEntities
.filter(entityWithId => entityWithId.id !== null && entityWithId.id !== undefined)
.filter(entityWithId => !dbEntities.find(dbEntity => dbEntity.entity.constructor === entityWithId.entity.constructor && dbEntity.id === entityWithId.id))
.filter(entityWithId => !dbEntities.find(dbEntity => dbEntity.entityTarget === entityWithId.entityTarget && dbEntity.id === entityWithId.id))
.map(entityWithId => {
const metadata = this.entityMetadatas.findByTarget(entityWithId.entity.constructor);
const repository = this.connection.getRepository(entityWithId.entity.constructor);
const metadata = this.entityMetadatas.findByTarget(entityWithId.entityTarget);
const repository = this.connection.getRepository(entityWithId.entityTarget as any); // todo: fix type
return repository.findOneById(entityWithId.id).then(loadedEntity => {
if (!loadedEntity) return undefined;

View File

@ -139,22 +139,22 @@ export class Broadcaster {
return Promise.all(subscribers.concat(listeners)).then(() => {});
}
broadcastLoadEvents(entity: any): Promise<void> {
broadcastLoadEvents(target: Function|string, entity: any): Promise<void> {
if (entity instanceof Promise)
return Promise.resolve();
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
const metadata = this.entityMetadatas.findByTarget(target);
let promises: Promise<any>[] = [];
metadata
.relations
.filter(relation => entity.hasOwnProperty(relation.propertyName))
.map(relation => entity[relation.propertyName])
.map(value => {
.map(relation => {
const value = entity[relation.propertyName];
if (value instanceof Array) {
promises = promises.concat(this.broadcastLoadEventsForAll(value));
promises = promises.concat(this.broadcastLoadEventsForAll(relation.inverseEntityMetadata.target, value));
} else {
promises.push(this.broadcastLoadEvents(value));
promises.push(this.broadcastLoadEvents(relation.inverseEntityMetadata.target, value));
}
});
@ -173,8 +173,8 @@ export class Broadcaster {
return Promise.all(promises).then(() => {});
}
broadcastLoadEventsForAll(entities: any[]): Promise<void> {
const promises = entities.map(entity => this.broadcastLoadEvents(entity));
broadcastLoadEventsForAll(target: Function|string, entities: any[]): Promise<void> {
const promises = entities.map(entity => this.broadcastLoadEvents(target, entity));
return Promise.all(promises).then(() => {});
}