mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
replaced all single primary keys usages
This commit is contained in:
parent
e64dd9576d
commit
f8278ebc04
@ -19,21 +19,21 @@ const options: ConnectionOptions = {
|
||||
entities: [Post]
|
||||
};
|
||||
|
||||
createConnection(options).then(connection => {
|
||||
createConnection(options).then(async connection => {
|
||||
|
||||
let postRepository = connection.getRepository(Post);
|
||||
|
||||
const post = new Post();
|
||||
post.id = 1;
|
||||
post.type = "person";
|
||||
post.text = "this is test post";
|
||||
post.text = "this is test post!";
|
||||
|
||||
postRepository.persist(post)
|
||||
.then(savedPost => {
|
||||
console.log("Post has been saved: ", savedPost);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log("error: ", error);
|
||||
});
|
||||
console.log("saving the post: ");
|
||||
await postRepository.persist(post);
|
||||
console.log("Post has been saved: ", post);
|
||||
|
||||
}, error => console.log("Cannot connect: ", error));
|
||||
console.log("now loading the post: ");
|
||||
const loadedPost = await postRepository.findOneById({ id: 1, type: "person" });
|
||||
console.log("loaded post: ", loadedPost);
|
||||
|
||||
}, error => console.log("Error: ", error));
|
||||
@ -330,11 +330,14 @@ export class EntityMetadataBuilder {
|
||||
entityMetadatas.forEach(metadata => {
|
||||
if (!metadata.table.isClosure)
|
||||
return;
|
||||
|
||||
if (metadata.primaryColumns.length > 1)
|
||||
throw new Error(`Cannot use given entity ${metadata.name} as a closure table, because it have multiple primary keys. Entities with multiple primary keys are not supported in closure tables.`);
|
||||
|
||||
const closureJunctionEntityMetadata = getFromContainer(ClosureJunctionEntityMetadataBuilder).build(lazyRelationsWrapper, {
|
||||
namingStrategy: namingStrategy,
|
||||
table: metadata.table,
|
||||
primaryColumn: metadata.primaryColumn,
|
||||
primaryColumn: metadata.firstPrimaryColumn,
|
||||
hasTreeLevelColumn: metadata.hasTreeLevelColumn
|
||||
});
|
||||
metadata.closureJunctionTable = closureJunctionEntityMetadata;
|
||||
|
||||
@ -31,7 +31,7 @@ export class EntityMetadataValidator {
|
||||
validate(entityMetadata: EntityMetadata) {
|
||||
|
||||
// check if table metadata has an id
|
||||
if (!entityMetadata.primaryColumn)
|
||||
if (!entityMetadata.primaryColumns.length)
|
||||
throw new MissingPrimaryColumnError(entityMetadata);
|
||||
|
||||
entityMetadata.relations.forEach(relation => {
|
||||
|
||||
@ -134,6 +134,13 @@ export class EntityMetadata {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity's table has multiple primary columns.
|
||||
*/
|
||||
get hasMultiplePrimaryKeys() {
|
||||
return this.primaryColumns.length > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the primary column.
|
||||
*
|
||||
@ -147,6 +154,35 @@ export class EntityMetadata {
|
||||
return primaryKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if table has generated column.
|
||||
*/
|
||||
get hasGeneratedColumn(): boolean {
|
||||
return !!this._columns.find(column => column.isGenerated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the column with generated flag.
|
||||
*/
|
||||
get generatedColumn(): ColumnMetadata {
|
||||
const generatedColumn = this._columns.find(column => column.isGenerated);
|
||||
if (!generatedColumn)
|
||||
throw new Error(`Generated column was not found`);
|
||||
|
||||
return generatedColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets first primary column. In the case if table contains multiple primary columns it
|
||||
* throws error.
|
||||
*/
|
||||
get firstPrimaryColumn(): ColumnMetadata {
|
||||
if (this.hasMultiplePrimaryKeys)
|
||||
throw new Error(`Entity ${this.name} has multiple primary keys. This operation is not supported on entities with multiple primary keys`);
|
||||
|
||||
return this.primaryColumns[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the primary columns.
|
||||
*/
|
||||
@ -365,11 +401,16 @@ export class EntityMetadata {
|
||||
return typeof nameOrFn === "string" ? nameOrFn : nameOrFn(this.createPropertiesMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns entity id of the given entity.
|
||||
*/
|
||||
getEntityId(entity: any) {
|
||||
return entity ? entity[this.primaryColumn.propertyName] : undefined;
|
||||
getEntityIdMap(entity: any): ObjectLiteral|undefined {
|
||||
if (!entity)
|
||||
return undefined;
|
||||
|
||||
const map: ObjectLiteral = {};
|
||||
this.primaryColumns.forEach(column => map[column.propertyName] = entity[column.propertyName]);
|
||||
const hasAllIds = this.primaryColumns.every(primaryColumn => {
|
||||
return map[primaryColumn.propertyName] !== undefined && map[primaryColumn.propertyName] !== null;
|
||||
});
|
||||
return hasAllIds ? map : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -464,5 +505,20 @@ export class EntityMetadata {
|
||||
return object.hasOwnProperty(primaryColumn.propertyName);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
compareEntities(firstEntity: any, secondEntity: any) {
|
||||
const firstEntityIds = this.getEntityIdMap(firstEntity);
|
||||
const secondEntityIds = this.getEntityIdMap(secondEntity);
|
||||
return this.compareIds(firstEntityIds, secondEntityIds);
|
||||
}
|
||||
|
||||
compareIds(firstIds: ObjectLiteral|undefined, secondIds: ObjectLiteral|undefined): boolean {
|
||||
if (!firstIds || !secondIds)
|
||||
return false;
|
||||
|
||||
return Object.keys(firstIds).every(key => {
|
||||
return firstIds[key] === secondIds[key];
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -62,7 +62,10 @@ export class JoinColumnMetadata extends PropertyMetadata {
|
||||
throw new Error(`Referenced column ${this.referencedColumnName} was not found in entity ${this.name}`);
|
||||
}
|
||||
|
||||
return this.relation.inverseEntityMetadata.primaryColumn;
|
||||
if (this.relation.inverseEntityMetadata.primaryColumns.length > 1)
|
||||
throw new Error(`Cannot automatically determine a referenced column of the "${this.relation.inverseEntityMetadata.name}", because it has multiple primary columns. Try to specify a referenced column explicitly.`);
|
||||
|
||||
return this.relation.inverseEntityMetadata.firstPrimaryColumn;
|
||||
}
|
||||
|
||||
}
|
||||
@ -140,7 +140,10 @@ export class JoinTableMetadata extends PropertyMetadata {
|
||||
return referencedColumn;
|
||||
}
|
||||
|
||||
return this.relation.entityMetadata.primaryColumn;
|
||||
if (this.relation.entityMetadata.primaryColumns.length > 1)
|
||||
throw new Error(`Cannot automatically determine a referenced column of the "${this.relation.entityMetadata.name}", because it has multiple primary columns. Try to specify a referenced column explicitly.`);
|
||||
|
||||
return this.relation.entityMetadata.firstPrimaryColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,7 +158,10 @@ export class JoinTableMetadata extends PropertyMetadata {
|
||||
return referencedColumn;
|
||||
}
|
||||
|
||||
return this.relation.inverseEntityMetadata.primaryColumn;
|
||||
if (this.relation.inverseEntityMetadata.primaryColumns.length > 1)
|
||||
throw new Error(`Cannot automatically determine inverse referenced column of the "${this.relation.inverseEntityMetadata.name}", because it has multiple primary columns. Try to specify a referenced column explicitly.`);
|
||||
|
||||
return this.relation.inverseEntityMetadata.firstPrimaryColumn;
|
||||
}
|
||||
|
||||
}
|
||||
@ -10,6 +10,7 @@ import {RemoveOperation} from "./operation/RemoveOperation";
|
||||
import {EntityMetadataCollection} from "../metadata-args/collection/EntityMetadataCollection";
|
||||
import {UpdateByInverseSideOperation} from "./operation/UpdateByInverseSideOperation";
|
||||
import {JunctionRemoveOperation} from "./operation/JunctionRemoveOperation";
|
||||
import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
|
||||
/**
|
||||
* 1. collect all exist objects from the db entity
|
||||
@ -115,7 +116,7 @@ export class EntityPersistOperationBuilder {
|
||||
operations: InsertOperation[] = []): InsertOperation[] {
|
||||
const newEntity = newEntityWithId.entity;
|
||||
const metadata = this.entityMetadatas.findByTarget(newEntityWithId.entityTarget);
|
||||
const isObjectNew = !this.findEntityWithId(dbEntities, metadata.target, newEntity[metadata.primaryColumn.propertyName]);
|
||||
const isObjectNew = !this.findEntityWithId(dbEntities, metadata.target, metadata.getEntityIdMap(newEntity)!);
|
||||
|
||||
// if object is new and should be inserted, we check if cascades are allowed before add it to operations list
|
||||
if (isObjectNew && fromRelation && !this.checkCascadesAllowed("insert", metadata, fromRelation)) {
|
||||
@ -132,19 +133,11 @@ export class EntityPersistOperationBuilder {
|
||||
|
||||
if (value instanceof Array) {
|
||||
value.forEach((subValue: any) => {
|
||||
const subValueWithId: EntityWithId = {
|
||||
id: inverseMetadata.getEntityId(subValue),
|
||||
entity: subValue,
|
||||
entityTarget: inverseMetadata.target
|
||||
};
|
||||
const subValueWithId = new EntityWithId(inverseMetadata, subValue);
|
||||
this.findCascadeInsertedEntities(subValueWithId, dbEntities, relation, operations);
|
||||
});
|
||||
} else {
|
||||
const valueWithId: EntityWithId = {
|
||||
id: inverseMetadata.getEntityId(value),
|
||||
entity: value,
|
||||
entityTarget: inverseMetadata.target
|
||||
};
|
||||
const valueWithId = new EntityWithId(inverseMetadata, value);
|
||||
this.findCascadeInsertedEntities(valueWithId, dbEntities, relation, operations);
|
||||
}
|
||||
});
|
||||
@ -171,14 +164,14 @@ export class EntityPersistOperationBuilder {
|
||||
return operations;
|
||||
|
||||
} else if (diffColumns.length || diffRelations.length) {
|
||||
const entityId = newEntity[metadata.primaryColumn.propertyName];
|
||||
const entityId = metadata.getEntityIdMap(newEntity);
|
||||
if (entityId)
|
||||
operations.push(new UpdateOperation(newEntityWithId.entityTarget, newEntity, entityId, diffColumns, diffRelations));
|
||||
}
|
||||
|
||||
metadata.relations.forEach(relation => {
|
||||
const relMetadata = relation.inverseEntityMetadata;
|
||||
const relationIdColumnName = relMetadata.primaryColumn.propertyName;
|
||||
const relationIdColumnName = relMetadata.firstPrimaryColumn.propertyName; // todo: join column metadata should be used here instead of primary column
|
||||
const value = this.getEntityRelationValue(relation, newEntity);
|
||||
const valueTarget = relation.target;
|
||||
const referencedColumnName = relation.isOwning ? relation.referencedColumnName : relation.inverseRelation.referencedColumnName;
|
||||
@ -196,16 +189,8 @@ export class EntityPersistOperationBuilder {
|
||||
return dbValue.entityTarget === valueTarget && dbValue.entity[referencedColumnName] === subEntity[relationIdColumnName];
|
||||
});
|
||||
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
|
||||
};
|
||||
const dbValueWithId = new EntityWithId(relMetadata, dbValue.entity);
|
||||
const subEntityWithId = new EntityWithId(relMetadata, subEntity);
|
||||
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValueWithId, subEntityWithId, dbEntities, relation, operations);
|
||||
}
|
||||
});
|
||||
@ -215,18 +200,8 @@ export class EntityPersistOperationBuilder {
|
||||
return dbValue.entityTarget === valueTarget && dbValue.entity[referencedColumnName] === value[relationIdColumnName];
|
||||
});
|
||||
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
|
||||
};
|
||||
|
||||
const dbValueWithId = new EntityWithId(relMetadata, dbValue.entity);
|
||||
const valueWithId = new EntityWithId(relMetadata, value);
|
||||
this.findCascadeUpdateEntities(updatesByRelations, relMetadata, dbValueWithId, valueWithId, dbEntities, relation, operations);
|
||||
}
|
||||
}
|
||||
@ -240,7 +215,7 @@ export class EntityPersistOperationBuilder {
|
||||
allPersistedEntities: EntityWithId[],
|
||||
fromRelation: RelationMetadata|undefined,
|
||||
fromMetadata: EntityMetadata|undefined,
|
||||
fromEntityId: any,
|
||||
fromEntityId: ObjectLiteral|undefined,
|
||||
parentAlreadyRemoved: boolean = false): RemoveOperation[] {
|
||||
const dbEntity = dbEntityWithId.entity;
|
||||
|
||||
@ -248,7 +223,7 @@ export class EntityPersistOperationBuilder {
|
||||
if (!dbEntity)
|
||||
return operations;
|
||||
|
||||
const entityId = dbEntity[metadata.primaryColumn.propertyName];
|
||||
const entityId = metadata.getEntityIdMap(dbEntity)!;
|
||||
const isObjectRemoved = parentAlreadyRemoved || !this.findEntityWithId(allPersistedEntities, metadata.target, entityId);
|
||||
|
||||
// if object is removed and should be removed, we check if cascades are allowed before add it to operations list
|
||||
@ -266,23 +241,15 @@ export class EntityPersistOperationBuilder {
|
||||
|
||||
if (dbValue instanceof Array) {
|
||||
dbValue.forEach((subDbEntity: any) => {
|
||||
const subDbEntityWithId: EntityWithId = {
|
||||
id: relMetadata.getEntityId(subDbEntity),
|
||||
entity: subDbEntity,
|
||||
entityTarget: relMetadata.target
|
||||
};
|
||||
const subDbEntityWithId = new EntityWithId(relMetadata, subDbEntity);
|
||||
|
||||
const relationOperations = this.findCascadeRemovedEntities(relMetadata, subDbEntityWithId, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
|
||||
const relationOperations = this.findCascadeRemovedEntities(relMetadata, subDbEntityWithId, allPersistedEntities, relation, metadata, metadata.getEntityIdMap(dbEntity), isObjectRemoved);
|
||||
relationOperations.forEach(o => operations.push(o));
|
||||
});
|
||||
} else {
|
||||
const dbValueWithId: EntityWithId = {
|
||||
id: relMetadata.getEntityId(dbValue),
|
||||
entity: dbValue,
|
||||
entityTarget: relMetadata.target
|
||||
};
|
||||
const dbValueWithId = new EntityWithId(relMetadata, dbValue);
|
||||
|
||||
const relationOperations = this.findCascadeRemovedEntities(relMetadata, dbValueWithId, allPersistedEntities, relation, metadata, dbEntity[metadata.primaryColumn.propertyName], isObjectRemoved);
|
||||
const relationOperations = this.findCascadeRemovedEntities(relMetadata, dbValueWithId, allPersistedEntities, relation, metadata, metadata.getEntityIdMap(dbEntity), isObjectRemoved);
|
||||
relationOperations.forEach(o => operations.push(o));
|
||||
}
|
||||
}, []);
|
||||
@ -309,7 +276,7 @@ export class EntityPersistOperationBuilder {
|
||||
return true;
|
||||
|
||||
return !dbEntity[relation.propertyName].find((dbSubEntity: any) => {
|
||||
return relation.inverseEntityMetadata.getEntityId(newSubEntity) === relation.inverseEntityMetadata.getEntityId(dbSubEntity);
|
||||
return relation.inverseEntityMetadata.compareEntities(newSubEntity, dbSubEntity);
|
||||
});
|
||||
}).forEach((subEntity: any) => {
|
||||
operations.push(new UpdateByInverseSideOperation(relationMetadata.target, newEntityWithId.entityTarget, "update", subEntity, newEntity, relation));
|
||||
@ -323,7 +290,7 @@ export class EntityPersistOperationBuilder {
|
||||
return true;
|
||||
|
||||
return !newEntity[relation.propertyName].find((newSubEntity: any) => {
|
||||
return relation.inverseEntityMetadata.getEntityId(dbSubEntity) === relation.inverseEntityMetadata.getEntityId(newSubEntity);
|
||||
return relation.inverseEntityMetadata.compareEntities(dbSubEntity, newSubEntity);
|
||||
});
|
||||
}).forEach((subEntity: any) => {
|
||||
operations.push(new UpdateByInverseSideOperation(relationMetadata.target, newEntityWithId.entityTarget, "remove", subEntity, newEntity, relation));
|
||||
@ -365,11 +332,7 @@ export class EntityPersistOperationBuilder {
|
||||
if (!relation.isManyToMany && sub === insertOperation.entity)
|
||||
operations.push(new UpdateByRelationOperation(entityToSearchInWithId.entityTarget, entityToSearchIn, insertOperation, relation));
|
||||
|
||||
const subWithId: EntityWithId = {
|
||||
id: inverseMetadata.getEntityId(sub),
|
||||
entity: sub,
|
||||
entityTarget: inverseMetadata.target
|
||||
};
|
||||
const subWithId = new EntityWithId(inverseMetadata, sub);
|
||||
const subOperations = this.findRelationsWithEntityInside(insertOperation, subWithId);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
});
|
||||
@ -380,11 +343,7 @@ export class EntityPersistOperationBuilder {
|
||||
operations.push(new UpdateByRelationOperation(entityToSearchInWithId.entityTarget, entityToSearchIn, insertOperation, relation));
|
||||
}
|
||||
|
||||
const valueWithId: EntityWithId = {
|
||||
id: inverseMetadata.getEntityId(value),
|
||||
entity: value,
|
||||
entityTarget: inverseMetadata.target
|
||||
};
|
||||
const valueWithId = new EntityWithId(inverseMetadata, value);
|
||||
const subOperations = this.findRelationsWithEntityInside(insertOperation, valueWithId);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
|
||||
@ -397,11 +356,11 @@ export class EntityPersistOperationBuilder {
|
||||
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;
|
||||
return dbEntity.compareId(metadata.getEntityIdMap(newEntity)!) && dbEntity.entityTarget === metadata.target;
|
||||
});
|
||||
return metadata.relations.reduce((operations, relation) => {
|
||||
const relationMetadata = relation.inverseEntityMetadata;
|
||||
const relationIdProperty = relationMetadata.primaryColumn.propertyName;
|
||||
const relationIdProperty = relationMetadata.firstPrimaryColumn.propertyName; // todo: join column metadata should be used instead of primaryColumn
|
||||
const value = this.getEntityRelationValue(relation, newEntity);
|
||||
if (value === null || value === undefined)
|
||||
return operations;
|
||||
@ -426,22 +385,14 @@ export class EntityPersistOperationBuilder {
|
||||
}
|
||||
|
||||
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
|
||||
const subEntityWithId: EntityWithId = {
|
||||
id: relationMetadata.getEntityId(subEntity),
|
||||
entity: subEntity,
|
||||
entityTarget: relationMetadata.target
|
||||
};
|
||||
const subEntityWithId = new EntityWithId(relationMetadata, subEntity);
|
||||
const subOperations = this.findJunctionInsertOperations(relationMetadata, subEntityWithId, dbEntities, false);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
|
||||
const valueWithId: EntityWithId = {
|
||||
id: relationMetadata.getEntityId(value),
|
||||
entity: value,
|
||||
entityTarget: relationMetadata.target
|
||||
};
|
||||
const valueWithId = new EntityWithId(relationMetadata, value);
|
||||
const subOperations = this.findJunctionInsertOperations(relationMetadata, valueWithId, dbEntities, false);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
}
|
||||
@ -457,13 +408,13 @@ export class EntityPersistOperationBuilder {
|
||||
return [];
|
||||
|
||||
const newEntity = newEntities.find(newEntity => {
|
||||
return newEntity.id === dbEntity[metadata.primaryColumn.propertyName] && newEntity.entityTarget === metadata.target;
|
||||
return newEntity.compareId(dbEntity) && newEntity.entityTarget === metadata.target;
|
||||
});
|
||||
return metadata.relations
|
||||
.filter(relation => dbEntity[relation.propertyName] !== null && dbEntity[relation.propertyName] !== undefined)
|
||||
.reduce((operations, relation) => {
|
||||
const relationMetadata = relation.inverseEntityMetadata;
|
||||
const relationIdProperty = relationMetadata.primaryColumn.propertyName;
|
||||
const relationIdProperty = relationMetadata.firstPrimaryColumn.propertyName; // todo: this should be got from join table metadata, not primaryColumn
|
||||
const value = newEntity ? this.getEntityRelationValue(relation, newEntity.entity) : null;
|
||||
const dbValue = this.getEntityRelationValue(relation, dbEntity);
|
||||
|
||||
@ -485,24 +436,14 @@ export class EntityPersistOperationBuilder {
|
||||
}
|
||||
|
||||
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
|
||||
const subEntityWithId: EntityWithId = {
|
||||
id: relationMetadata.getEntityId(subEntity),
|
||||
entity: subEntity,
|
||||
entityTarget: relationMetadata.target
|
||||
};
|
||||
|
||||
const subEntityWithId = new EntityWithId(relationMetadata, subEntity);
|
||||
const subOperations = this.findJunctionRemoveOperations(relationMetadata, subEntityWithId, newEntities, false);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (isRoot || this.checkCascadesAllowed("update", metadata, relation)) {
|
||||
const dbValueWithId: EntityWithId = {
|
||||
id: relationMetadata.getEntityId(dbValue),
|
||||
entity: dbValue,
|
||||
entityTarget: relationMetadata.target
|
||||
};
|
||||
|
||||
const dbValueWithId = new EntityWithId(relationMetadata, dbValue);
|
||||
const subOperations = this.findJunctionRemoveOperations(relationMetadata, dbValueWithId, newEntities, false);
|
||||
subOperations.forEach(o => operations.push(o));
|
||||
}
|
||||
@ -538,14 +479,13 @@ export class EntityPersistOperationBuilder {
|
||||
return true;
|
||||
const entityTarget = relation.target;
|
||||
|
||||
const newEntityRelationMetadata = this.entityMetadatas.findByTarget(entityTarget);
|
||||
const dbEntityRelationMetadata = this.entityMetadatas.findByTarget(entityTarget);
|
||||
return newEntityRelationMetadata.getEntityId(newEntity[relation.propertyName]) !== dbEntityRelationMetadata.getEntityId(dbEntity[relation.propertyName]);
|
||||
const relationMetadata = this.entityMetadatas.findByTarget(entityTarget);
|
||||
return !relationMetadata.compareEntities(newEntity[relation.propertyName], dbEntity[relation.propertyName]);
|
||||
});
|
||||
}
|
||||
|
||||
private findEntityWithId(entityWithIds: EntityWithId[], entityTarget: Function|string, id: any) {
|
||||
return entityWithIds.find(entityWithId => entityWithId.id === id && entityWithId.entityTarget === entityTarget);
|
||||
private findEntityWithId(entityWithIds: EntityWithId[], entityTarget: Function|string, id: ObjectLiteral) {
|
||||
return entityWithIds.find(entityWithId => entityWithId.compareId(id) && entityWithId.entityTarget === entityTarget);
|
||||
}
|
||||
|
||||
private checkCascadesAllowed(type: "insert"|"update"|"remove", metadata: EntityMetadata, relation: RelationMetadata) {
|
||||
|
||||
@ -37,7 +37,9 @@ export class PersistOperationExecutor {
|
||||
*/
|
||||
executePersistOperation(persistOperation: PersistOperation) {
|
||||
let isTransactionStartedByItself = false;
|
||||
|
||||
|
||||
// persistOperation.log();
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => this.broadcastBeforeEvents(persistOperation))
|
||||
.then(() => {
|
||||
@ -152,7 +154,10 @@ export class PersistOperationExecutor {
|
||||
private executeInsertOperations(persistOperation: PersistOperation) {
|
||||
return Promise.all(persistOperation.inserts.map(operation => {
|
||||
return this.insert(operation).then((insertId: any) => {
|
||||
operation.entityId = insertId;
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.target);
|
||||
if (insertId && metadata.hasGeneratedColumn) {
|
||||
operation.entityId = { [metadata.generatedColumn.propertyName]: insertId };
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
@ -258,7 +263,10 @@ export class PersistOperationExecutor {
|
||||
private updateIdsOfInsertedEntities(persistOperation: PersistOperation) {
|
||||
persistOperation.inserts.forEach(insertOperation => {
|
||||
const metadata = this.entityMetadatas.findByTarget(insertOperation.target);
|
||||
insertOperation.entity[metadata.primaryColumn.propertyName] = insertOperation.entityId;
|
||||
metadata.primaryColumns.forEach(primaryColumn => {
|
||||
if (insertOperation.entityId)
|
||||
insertOperation.entity[primaryColumn.propertyName] = insertOperation.entityId[primaryColumn.propertyName];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -301,28 +309,33 @@ export class PersistOperationExecutor {
|
||||
persistOperation.removes.forEach(removeOperation => {
|
||||
const metadata = this.entityMetadatas.findByTarget(removeOperation.target);
|
||||
const removedEntity = persistOperation.allPersistedEntities.find(allNewEntity => {
|
||||
return allNewEntity.entityTarget === removeOperation.target && allNewEntity.id === removeOperation.entity[metadata.primaryColumn.propertyName];
|
||||
return allNewEntity.entityTarget === removeOperation.target && allNewEntity.compareId(metadata.getEntityIdMap(removeOperation.entity)!);
|
||||
});
|
||||
if (removedEntity)
|
||||
removedEntity.entity[metadata.primaryColumn.propertyName] = undefined;
|
||||
if (removedEntity) {
|
||||
metadata.primaryColumns.forEach(primaryColumn => {
|
||||
removedEntity.entity[primaryColumn.propertyName] = undefined;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private findUpdateOperationForEntity(operations: UpdateByRelationOperation[], insertOperations: InsertOperation[], target: any): ObjectLiteral {
|
||||
// we are using firstPrimaryColumn here because this method is used only in executeInsertClosureTableOperations method
|
||||
// which means only for tree tables, but multiple primary keys are not supported in tree tables
|
||||
|
||||
let updateMap: ObjectLiteral = {};
|
||||
operations
|
||||
.forEach(operation => { // duplication with updateByRelation method
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.insertOperation.target);
|
||||
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.target);
|
||||
const idInInserts = relatedInsertOperation && relatedInsertOperation.entityId ? relatedInsertOperation.entityId[metadata.firstPrimaryColumn.propertyName] : null;
|
||||
if (operation.insertOperation.entity === target)
|
||||
updateMap[operation.updatedRelation.inverseRelation.propertyName] = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
|
||||
updateMap[operation.updatedRelation.inverseRelation.propertyName] = operation.targetEntity[metadata.firstPrimaryColumn.propertyName] || idInInserts;
|
||||
|
||||
} else {
|
||||
if (operation.targetEntity === target)
|
||||
updateMap[operation.updatedRelation.propertyName] = operation.insertOperation.entityId;
|
||||
if (operation.targetEntity === target && operation.insertOperation.entityId)
|
||||
updateMap[operation.updatedRelation.propertyName] = operation.insertOperation.entityId[metadata.firstPrimaryColumn.propertyName];
|
||||
}
|
||||
});
|
||||
|
||||
@ -330,26 +343,36 @@ export class PersistOperationExecutor {
|
||||
}
|
||||
|
||||
private updateByRelation(operation: UpdateByRelationOperation, insertOperations: InsertOperation[]) {
|
||||
let tableName: string, relationName: string, relationId: any, idColumn: string, id: any;
|
||||
|
||||
if (!operation.insertOperation.entityId)
|
||||
throw new Error(`insert operation does not have entity id`);
|
||||
|
||||
let tableName: string, relationName: string, relationId: ObjectLiteral, idColumn: string, id: any, updateMap: ObjectLiteral;
|
||||
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.target);
|
||||
const idInInserts = relatedInsertOperation && relatedInsertOperation.entityId ? relatedInsertOperation.entityId[metadata.firstPrimaryColumn.propertyName] : null;
|
||||
tableName = metadata.table.name;
|
||||
relationName = operation.updatedRelation.inverseRelation.name;
|
||||
relationId = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
|
||||
idColumn = metadata.primaryColumn.name;
|
||||
id = operation.insertOperation.entityId;
|
||||
relationId = operation.targetEntity[metadata.firstPrimaryColumn.propertyName] || idInInserts; // todo: make sure idInInserts is always a map
|
||||
// relationId = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
|
||||
// idColumn = metadata.primaryColumn.name;
|
||||
// id = operation.insertOperation.entityId;
|
||||
|
||||
updateMap = operation.insertOperation.entityId;
|
||||
|
||||
} else {
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.entityTarget);
|
||||
const idInInserts = relatedInsertOperation && relatedInsertOperation.entityId ? relatedInsertOperation.entityId[metadata.firstPrimaryColumn.propertyName] : null;
|
||||
tableName = metadata.table.name;
|
||||
relationName = operation.updatedRelation.name;
|
||||
relationId = operation.insertOperation.entityId;
|
||||
idColumn = metadata.primaryColumn.name;
|
||||
id = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
|
||||
relationId = operation.insertOperation.entityId[metadata.firstPrimaryColumn.propertyName]; // todo: make sure entityId is always a map
|
||||
// idColumn = metadata.primaryColumn.name;
|
||||
// id = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
|
||||
updateMap = metadata.getEntityIdMap(operation.targetEntity) || idInInserts; // todo: make sure idInInserts always object even when id is single!!!
|
||||
}
|
||||
return this.queryRunner.update(tableName, { [relationName]: relationId }, { [idColumn]: id });
|
||||
return this.queryRunner.update(tableName, { [relationName]: relationId }, updateMap);
|
||||
}
|
||||
|
||||
private updateInverseRelation(operation: UpdateByInverseSideOperation, insertOperations: InsertOperation[]) {
|
||||
@ -357,22 +380,22 @@ export class PersistOperationExecutor {
|
||||
const fromEntityMetadata = this.entityMetadatas.findByTarget(operation.fromEntityTarget);
|
||||
const tableName = targetEntityMetadata.table.name;
|
||||
const targetRelation = operation.fromRelation.inverseRelation;
|
||||
const idColumn = targetEntityMetadata.primaryColumn.name;
|
||||
const id = targetEntityMetadata.getEntityId(operation.targetEntity);
|
||||
const updateMap = targetEntityMetadata.getEntityIdMap(operation.targetEntity);
|
||||
if (!updateMap) return; // todo: is return correct here?
|
||||
|
||||
const fromEntityInsertOperation = insertOperations.find(o => o.entity === operation.fromEntity);
|
||||
let targetEntityId: any; // todo: better do it during insertion - pass UpdateByInverseSideOperation[] to insert and do it there
|
||||
if (operation.operationType === "remove") {
|
||||
targetEntityId = null;
|
||||
} else {
|
||||
if (fromEntityInsertOperation && targetRelation.joinColumn.referencedColumn === fromEntityMetadata.primaryColumn) {
|
||||
targetEntityId = fromEntityInsertOperation.entityId;
|
||||
if (fromEntityInsertOperation && fromEntityInsertOperation.entityId && targetRelation.joinColumn.referencedColumn === fromEntityMetadata.firstPrimaryColumn) {
|
||||
targetEntityId = fromEntityInsertOperation.entityId[fromEntityMetadata.firstPrimaryColumn.name];
|
||||
} else {
|
||||
targetEntityId = operation.fromEntity[targetRelation.joinColumn.referencedColumn.name];
|
||||
}
|
||||
}
|
||||
|
||||
return this.queryRunner.update(tableName, { [targetRelation.name]: targetEntityId }, { [idColumn]: id });
|
||||
return this.queryRunner.update(tableName, { [targetRelation.name]: targetEntityId }, updateMap);
|
||||
}
|
||||
|
||||
private update(updateOperation: UpdateOperation) {
|
||||
@ -386,7 +409,7 @@ export class PersistOperationExecutor {
|
||||
|
||||
updateOperation.relations.forEach(relation => {
|
||||
const value = this.getEntityRelationValue(relation, entity);
|
||||
values[relation.name] = value !== null && value !== undefined ? value[relation.inverseEntityMetadata.primaryColumn.propertyName] : null;
|
||||
values[relation.name] = value !== null && value !== undefined ? value[relation.inverseEntityMetadata.firstPrimaryColumn.propertyName] : null; // todo: should not have a call to primaryColumn, instead join column metadata should be used
|
||||
});
|
||||
|
||||
// if number of updated columns = 0 no need to update updated date and version columns
|
||||
@ -399,15 +422,18 @@ export class PersistOperationExecutor {
|
||||
if (metadata.hasVersionColumn)
|
||||
values[metadata.versionColumn.name] = this.driver.preparePersistentValue(entity[metadata.versionColumn.propertyName] + 1, metadata.versionColumn);
|
||||
|
||||
return this.queryRunner.update( metadata.table.name, values, { [metadata.primaryColumn.name]: metadata.getEntityId(entity) });
|
||||
return this.queryRunner.update(metadata.table.name, values, metadata.getEntityIdMap(entity)!);
|
||||
}
|
||||
|
||||
private updateDeletedRelations(removeOperation: RemoveOperation) { // todo: check if both many-to-one deletions work too
|
||||
if (!removeOperation.fromEntityId)
|
||||
throw new Error(`remove operation does not have entity id`);
|
||||
|
||||
if (removeOperation.relation) {
|
||||
return this.queryRunner.update(
|
||||
removeOperation.fromMetadata.table.name,
|
||||
{ [removeOperation.relation.name]: null },
|
||||
{ [removeOperation.fromMetadata.primaryColumn.name]: removeOperation.fromEntityId }
|
||||
removeOperation.fromEntityId
|
||||
);
|
||||
}
|
||||
|
||||
@ -416,7 +442,8 @@ export class PersistOperationExecutor {
|
||||
|
||||
private delete(target: Function|string, entity: any) {
|
||||
const metadata = this.entityMetadatas.findByTarget(target);
|
||||
return this.queryRunner.delete(metadata.table.name, { [metadata.primaryColumn.name]: entity[metadata.primaryColumn.propertyName] });
|
||||
console.log("getEntityIdOrIds: " , metadata.getEntityIdMap(entity));
|
||||
return this.queryRunner.delete(metadata.table.name, metadata.getEntityIdMap(entity)!);
|
||||
}
|
||||
|
||||
private insert(operation: InsertOperation) {
|
||||
@ -440,7 +467,7 @@ export class PersistOperationExecutor {
|
||||
.map(relation => {
|
||||
const value = this.getEntityRelationValue(relation, entity);
|
||||
if (value !== null && value !== undefined) // in the case if relation has null, which can be saved
|
||||
return value[relation.inverseEntityMetadata.primaryColumn.propertyName];
|
||||
return value[relation.inverseEntityMetadata.firstPrimaryColumn.propertyName]; // todo: it should be get by field set in join column in the relation metadata
|
||||
|
||||
return value;
|
||||
});
|
||||
@ -482,19 +509,24 @@ export class PersistOperationExecutor {
|
||||
}
|
||||
|
||||
private insertIntoClosureTable(operation: InsertOperation, updateMap: ObjectLiteral) {
|
||||
// here we can only support to work only with single primary key entities
|
||||
|
||||
const entity = operation.entity;
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.target);
|
||||
const parentEntity = entity[metadata.treeParentRelation.propertyName];
|
||||
|
||||
let parentEntityId: any = 0;
|
||||
if (parentEntity && parentEntity[metadata.primaryColumn.propertyName]) {
|
||||
parentEntityId = parentEntity[metadata.primaryColumn.propertyName];
|
||||
if (parentEntity && parentEntity[metadata.firstPrimaryColumn.propertyName]) {
|
||||
parentEntityId = parentEntity[metadata.firstPrimaryColumn.propertyName];
|
||||
|
||||
} else if (updateMap && updateMap[metadata.treeParentRelation.propertyName]) { // todo: name or propertyName: depend how update will be implemented. or even find relation of this treeParent and use its name?
|
||||
parentEntityId = updateMap[metadata.treeParentRelation.propertyName];
|
||||
}
|
||||
|
||||
return this.queryRunner.insertIntoClosureTable(metadata.closureJunctionTable.table.name, operation.entityId, parentEntityId, metadata.hasTreeLevelColumn)
|
||||
|
||||
if (!operation.entityId)
|
||||
throw new Error(`operation does not have entity id`);
|
||||
|
||||
return this.queryRunner.insertIntoClosureTable(metadata.closureJunctionTable.table.name, operation.entityId[metadata.firstPrimaryColumn.propertyName], parentEntityId, metadata.hasTreeLevelColumn)
|
||||
/*.then(() => {
|
||||
// we also need to update children count in parent
|
||||
if (parentEntity && parentEntityId) {
|
||||
@ -509,8 +541,11 @@ export class PersistOperationExecutor {
|
||||
const metadata = this.entityMetadatas.findByTarget(operation.target);
|
||||
|
||||
if (metadata.hasTreeLevelColumn && operation.treeLevel) {
|
||||
if (!operation.entityId)
|
||||
throw new Error(`remove operation does not have entity id`);
|
||||
|
||||
const values = { [metadata.treeLevelColumn.name]: operation.treeLevel };
|
||||
return this.queryRunner.update(metadata.table.name, values, { [metadata.primaryColumn.name]: operation.entityId });
|
||||
return this.queryRunner.update(metadata.table.name, values, operation.entityId);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
@ -518,6 +553,8 @@ export class PersistOperationExecutor {
|
||||
}
|
||||
|
||||
private insertJunctions(junctionOperation: JunctionInsertOperation, insertOperations: InsertOperation[]) {
|
||||
// I think here we can only support to work only with single primary key entities
|
||||
|
||||
const junctionMetadata = junctionOperation.metadata;
|
||||
const metadata1 = this.entityMetadatas.findByTarget(junctionOperation.entity1Target);
|
||||
const metadata2 = this.entityMetadatas.findByTarget(junctionOperation.entity2Target);
|
||||
@ -525,20 +562,20 @@ export class PersistOperationExecutor {
|
||||
const insertOperation1 = insertOperations.find(o => o.entity === junctionOperation.entity1);
|
||||
const insertOperation2 = insertOperations.find(o => o.entity === junctionOperation.entity2);
|
||||
|
||||
let id1 = junctionOperation.entity1[metadata1.primaryColumn.propertyName];
|
||||
let id2 = junctionOperation.entity2[metadata2.primaryColumn.propertyName];
|
||||
let id1 = junctionOperation.entity1[metadata1.firstPrimaryColumn.propertyName];
|
||||
let id2 = junctionOperation.entity2[metadata2.firstPrimaryColumn.propertyName];
|
||||
|
||||
if (!id1) {
|
||||
if (insertOperation1) {
|
||||
id1 = insertOperation1.entityId;
|
||||
if (insertOperation1 && insertOperation1.entityId) {
|
||||
id1 = insertOperation1.entityId[metadata1.firstPrimaryColumn.propertyName];
|
||||
} else {
|
||||
throw new Error(`Insert operation for ${junctionOperation.entity1} was not found.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!id2) {
|
||||
if (insertOperation2) {
|
||||
id2 = insertOperation2.entityId;
|
||||
if (insertOperation2 && insertOperation2.entityId) {
|
||||
id2 = insertOperation2.entityId[metadata2.firstPrimaryColumn.propertyName];
|
||||
} else {
|
||||
throw new Error(`Insert operation for ${junctionOperation.entity2} was not found.`);
|
||||
}
|
||||
@ -556,12 +593,13 @@ export class PersistOperationExecutor {
|
||||
}
|
||||
|
||||
private removeJunctions(junctionOperation: JunctionRemoveOperation) {
|
||||
// I think here we can only support to work only with single primary key entities
|
||||
const junctionMetadata = junctionOperation.metadata;
|
||||
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];
|
||||
const id1 = junctionOperation.entity1[metadata1.firstPrimaryColumn.propertyName];
|
||||
const id2 = junctionOperation.entity2[metadata2.firstPrimaryColumn.propertyName];
|
||||
return this.queryRunner.delete(junctionMetadata.table.name, { [columns[0]]: id1, [columns[1]]: id2 });
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import {ObjectLiteral} from "../../common/ObjectLiteral";
|
||||
/**
|
||||
*/
|
||||
export class InsertOperation {
|
||||
@ -6,7 +7,7 @@ export class InsertOperation {
|
||||
|
||||
constructor(public target: Function|string, // todo: probably should be metadata here
|
||||
public entity: any,
|
||||
public entityId?: number,
|
||||
public entityId?: ObjectLiteral|undefined, // entity ids it should be instead
|
||||
public date = new Date()) {
|
||||
}
|
||||
}
|
||||
@ -5,13 +5,28 @@ import {JunctionInsertOperation} from "./JunctionInsertOperation";
|
||||
import {JunctionRemoveOperation} from "./JunctionRemoveOperation";
|
||||
import {UpdateByRelationOperation} from "./UpdateByRelationOperation";
|
||||
import {UpdateByInverseSideOperation} from "./UpdateByInverseSideOperation";
|
||||
import {EntityMetadata} from "../../metadata/EntityMetadata";
|
||||
import {ObjectLiteral} from "../../common/ObjectLiteral";
|
||||
|
||||
/**
|
||||
*/
|
||||
export interface EntityWithId {
|
||||
id: any;
|
||||
export class EntityWithId { // todo: move entity with id creation into metadata?
|
||||
entityTarget: Function|string;
|
||||
entity: any;
|
||||
|
||||
constructor(public metadata: EntityMetadata, entity: ObjectLiteral) {
|
||||
// todo: check id usage
|
||||
this.entity = entity;
|
||||
this.entityTarget = metadata.target;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.metadata.getEntityIdMap(this.entity);
|
||||
}
|
||||
|
||||
compareId(id: ObjectLiteral): boolean { // todo: store metadata in this class and use compareIds of the metadata class instead of this duplication
|
||||
return this.metadata.compareIds(this.id, id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import {RelationMetadata} from "../../metadata/RelationMetadata";
|
||||
import {EntityMetadata} from "../../metadata/EntityMetadata";
|
||||
import {ObjectLiteral} from "../../common/ObjectLiteral";
|
||||
|
||||
/**
|
||||
*/
|
||||
export class RemoveOperation {
|
||||
constructor(public target: Function|string, // todo: probably should be metadata here
|
||||
public entity: any,
|
||||
public entityId: any,
|
||||
public entityId: ObjectLiteral,
|
||||
public fromMetadata: EntityMetadata, // todo: use relation.metadata instead?
|
||||
public relation: RelationMetadata|undefined,
|
||||
public fromEntityId: any) {
|
||||
public fromEntityId: ObjectLiteral|undefined) {
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,13 @@
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {RelationMetadata} from "../../metadata/RelationMetadata";
|
||||
import {ObjectLiteral} from "../../common/ObjectLiteral";
|
||||
|
||||
/**
|
||||
*/
|
||||
export class UpdateOperation {
|
||||
constructor(public target: Function|string,
|
||||
public entity: any,
|
||||
public entityId: any,
|
||||
public entityId: ObjectLiteral,
|
||||
public columns: ColumnMetadata[],
|
||||
public relations: RelationMetadata[],
|
||||
public date = new Date()) {
|
||||
|
||||
@ -482,9 +482,18 @@ export class QueryBuilder<Entity> {
|
||||
if (this.firstResult || this.maxResults) {
|
||||
const [sql, parameters] = this.getSqlWithParameters();
|
||||
|
||||
const distinctAlias = this.driver.escapeTableName("distinctAlias");
|
||||
const metadata = this.entityMetadatas.findByTarget(this.fromEntity.alias.target);
|
||||
let idsQuery = `SELECT DISTINCT(${this.driver.escapeTableName("distinctAlias")}.${this.driver.escapeAliasName(mainAliasName + "_" + metadata.primaryColumn.name)}) as ids`;
|
||||
idsQuery += ` FROM (${sql}) ${this.driver.escapeTableName("distinctAlias")}`; // TODO: WHAT TO DO WITH PARAMETERS HERE? DO THEY WORK?
|
||||
let idsQuery = `SELECT `;
|
||||
idsQuery += metadata.primaryColumns.map((primaryColumn, index) => {
|
||||
const propertyName = this.driver.escapeAliasName(mainAliasName + "_" + primaryColumn.name);
|
||||
if (index === 0) {
|
||||
return `DISTINCT(${distinctAlias}.${propertyName}) as ids_${primaryColumn.name}`;
|
||||
} else {
|
||||
return `${distinctAlias}.${propertyName}) as ids_${primaryColumn.name}`;
|
||||
}
|
||||
}).join(", ");
|
||||
idsQuery += ` FROM (${sql}) ${distinctAlias}`; // TODO: WHAT TO DO WITH PARAMETERS HERE? DO THEY WORK?
|
||||
if (this.maxResults)
|
||||
idsQuery += " LIMIT " + this.maxResults;
|
||||
if (this.firstResult)
|
||||
@ -493,13 +502,25 @@ export class QueryBuilder<Entity> {
|
||||
const results = await queryRunner.query(idsQuery, parameters)
|
||||
.then((results: any[]) => {
|
||||
scalarResults = results;
|
||||
const ids = results.map(result => result["ids"]);
|
||||
if (ids.length === 0)
|
||||
if (results.length === 0)
|
||||
return [];
|
||||
const queryWithIds = this.clone({ queryRunner: queryRunner })
|
||||
.andWhere(mainAliasName + "." + metadata.primaryColumn.propertyName + " IN (:ids)", { ids: ids });
|
||||
const [queryWithIdsSql, queryWithIdsParameters] = queryWithIds.getSqlWithParameters();
|
||||
|
||||
let condition = "";
|
||||
const parameters: ObjectLiteral = {};
|
||||
if (metadata.hasMultiplePrimaryKeys) {
|
||||
condition = results.map(result => {
|
||||
return metadata.primaryColumns.map(primaryColumn => {
|
||||
parameters["ids_" + primaryColumn.propertyName] = result["ids_" + primaryColumn.propertyName];
|
||||
return mainAliasName + "." + primaryColumn.propertyName + "=:ids_" + primaryColumn.propertyName;
|
||||
}).join(" AND ");
|
||||
}).join(" OR ");
|
||||
} else {
|
||||
parameters["ids"] = results.map(result => result["ids_" + metadata.firstPrimaryColumn.propertyName]);
|
||||
condition = mainAliasName + "." + metadata.firstPrimaryColumn.propertyName + " IN (:ids)";
|
||||
}
|
||||
const [queryWithIdsSql, queryWithIdsParameters] = this.clone({ queryRunner: queryRunner })
|
||||
.andWhere(condition, parameters)
|
||||
.getSqlWithParameters();
|
||||
return (queryRunner as QueryRunner).query(queryWithIdsSql, queryWithIdsParameters);
|
||||
})
|
||||
.then(results => {
|
||||
@ -551,6 +572,7 @@ export class QueryBuilder<Entity> {
|
||||
await queryRunner.release();
|
||||
}
|
||||
|
||||
// console.log("qb results : ", results);
|
||||
return results;
|
||||
}
|
||||
}
|
||||
@ -630,10 +652,12 @@ export class QueryBuilder<Entity> {
|
||||
// if (relationCountMeta.condition)
|
||||
// condition += relationCountMeta.condition;
|
||||
// relationCountMeta.alias.target;
|
||||
// todo: FIX primaryColumn usages
|
||||
|
||||
const ids = relationCountMeta.entities
|
||||
.map(entityWithMetadata => entityWithMetadata.entity[entityWithMetadata.metadata.primaryColumn.propertyName])
|
||||
.filter(id => id !== undefined);
|
||||
.map(entityWithMetadata => entityWithMetadata.metadata.getEntityIdMap(entityWithMetadata.entity))
|
||||
.filter(idMap => idMap !== undefined)
|
||||
.map(idMap => idMap![parentMetadata.primaryColumn.propertyName]);
|
||||
if (!ids || !ids.length)
|
||||
throw new Error(`No ids found to load relation counters`);
|
||||
|
||||
@ -688,8 +712,19 @@ export class QueryBuilder<Entity> {
|
||||
|
||||
const mainAlias = this.aliasMap.mainAlias.name;
|
||||
const metadata = this.entityMetadatas.findByTarget(this.fromEntity.alias.target);
|
||||
|
||||
const distinctAlias = this.driver.escapeAliasName(mainAlias);
|
||||
let countSql = `COUNT(` + metadata.primaryColumns.map((primaryColumn, index) => {
|
||||
const propertyName = this.driver.escapeColumnName(primaryColumn.name);
|
||||
if (index === 0) {
|
||||
return `DISTINCT(${distinctAlias}.${propertyName})`;
|
||||
} else {
|
||||
return `${distinctAlias}.${propertyName})`;
|
||||
}
|
||||
}).join(", ") + ") as cnt";
|
||||
|
||||
const countQuery = this.clone({ queryRunner: queryRunner, skipOrderBys: true })
|
||||
.select(`COUNT(DISTINCT(${this.driver.escapeAliasName(mainAlias)}.${this.driver.escapeColumnName(metadata.primaryColumn.name)})) as cnt`);
|
||||
.select(countSql);
|
||||
|
||||
const [countQuerySql, countQueryParameters] = countQuery.getSqlWithParameters();
|
||||
const results = await queryRunner.query(countQuerySql, countQueryParameters);
|
||||
@ -711,7 +746,7 @@ export class QueryBuilder<Entity> {
|
||||
]);
|
||||
}
|
||||
|
||||
clone(options?: { queryRunner?: QueryRunner, skipOrderBys?: boolean, skipLimit?: boolean, skipOffset?: boolean }) {
|
||||
clone(options?: { queryRunner?: QueryRunner, skipOrderBys?: boolean, skipLimit?: boolean, skipOffset?: boolean }): QueryBuilder<Entity> {
|
||||
const qb = new QueryBuilder(this.driver, this.entityMetadatas, this.broadcaster, options ? options.queryRunner : undefined);
|
||||
|
||||
switch (this.type) {
|
||||
|
||||
@ -33,7 +33,7 @@ export class PlainObjectToDatabaseEntityTransformer<Entity extends ObjectLiteral
|
||||
|
||||
metadata.primaryColumns.forEach(primaryColumn => {
|
||||
queryBuilder
|
||||
.where(alias + "." + primaryColumn.name + "=:" + primaryColumn.name)
|
||||
.andWhere(alias + "." + primaryColumn.name + "=:" + primaryColumn.name)
|
||||
.setParameter(primaryColumn.name, plainObject[primaryColumn.name]);
|
||||
});
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@ import {EntityMetadata} from "../../metadata/EntityMetadata";
|
||||
import {OrmUtils} from "../../util/OrmUtils";
|
||||
import {Driver} from "../../driver/Driver";
|
||||
import {JoinMapping, RelationCountMeta} from "../QueryBuilder";
|
||||
import {ColumnTypes} from "../../metadata/types/ColumnTypes";
|
||||
|
||||
/**
|
||||
* Transforms raw sql results returned from the database into entity object.
|
||||
@ -29,6 +28,7 @@ export class RawSqlResultsToEntityTransformer {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
transform(rawSqlResults: any[]): any[] {
|
||||
// console.log("rawSqlResults: ", rawSqlResults);
|
||||
return this.groupAndTransform(rawSqlResults, this.aliasMap.mainAlias);
|
||||
}
|
||||
|
||||
@ -48,9 +48,9 @@ export class RawSqlResultsToEntityTransformer {
|
||||
|
||||
const groupedResults = OrmUtils.groupBy(rawSqlResults, result => {
|
||||
if (!metadata) return;
|
||||
const columnName = metadata.primaryColumn.name;
|
||||
return result[alias.name + "_" + columnName];
|
||||
return metadata.primaryColumns.map(column => result[alias.name + "_" + column.name]).join("_"); // todo: check it
|
||||
});
|
||||
// console.log("groupedResults: ", groupedResults);
|
||||
return groupedResults
|
||||
.map(group => {
|
||||
if (!metadata) return;
|
||||
|
||||
@ -7,11 +7,7 @@ import {EntityPersistOperationBuilder} from "../persistment/EntityPersistOperati
|
||||
import {PersistOperationExecutor} from "../persistment/PersistOperationExecutor";
|
||||
import {EntityWithId} from "../persistment/operation/PersistOperation";
|
||||
import {FindOptions, FindOptionsUtils} from "./FindOptions";
|
||||
import {EntityMetadataCollection} from "../metadata-args/collection/EntityMetadataCollection";
|
||||
import {Broadcaster} from "../subscriber/Broadcaster";
|
||||
import {Driver} from "../driver/Driver";
|
||||
import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
import {DatabaseConnection} from "../driver/DatabaseConnection";
|
||||
import {QueryRunner} from "../driver/QueryRunner";
|
||||
|
||||
/**
|
||||
@ -53,14 +49,17 @@ export class Repository<Entity extends ObjectLiteral> {
|
||||
|
||||
/**
|
||||
* Checks if entity has an id.
|
||||
* If entity contains compose ids, then it checks them all.
|
||||
*/
|
||||
hasId(entity: Entity): boolean {
|
||||
const columnName = this.metadata.primaryColumn.propertyName;
|
||||
return !!entity &&
|
||||
entity.hasOwnProperty(columnName) &&
|
||||
entity[columnName] !== null &&
|
||||
entity[columnName] !== undefined &&
|
||||
entity[columnName] !== "";
|
||||
return this.metadata.primaryColumns.every(primaryColumn => {
|
||||
const columnName = primaryColumn.propertyName;
|
||||
return !!entity &&
|
||||
entity.hasOwnProperty(columnName) &&
|
||||
entity[columnName] !== null &&
|
||||
entity[columnName] !== undefined &&
|
||||
entity[columnName] !== "";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,16 +161,8 @@ export class Repository<Entity extends ObjectLiteral> {
|
||||
|
||||
// need to find db entities that were not loaded by initialize method
|
||||
const allDbEntities = await this.findNotLoadedIds(queryRunner, entityWithIds, allPersistedEntities);
|
||||
const persistedEntity: EntityWithId = {
|
||||
id: this.metadata.getEntityId(entityOrEntities),
|
||||
entityTarget: this.metadata.target,
|
||||
entity: entityOrEntities
|
||||
};
|
||||
const dbEntity: EntityWithId = {
|
||||
id: this.metadata.getEntityId(loadedDbEntity),
|
||||
entityTarget: this.metadata.target,
|
||||
entity: loadedDbEntity
|
||||
};
|
||||
const persistedEntity = new EntityWithId(this.metadata, entityOrEntities);
|
||||
const dbEntity = new EntityWithId(this.metadata, loadedDbEntity!); // todo: find if this can be executed if loadedDbEntity is empty
|
||||
const persistOperation = this.entityPersistOperationBuilder.buildFullPersistment(this.metadata, dbEntity, persistedEntity, allDbEntities, allPersistedEntities);
|
||||
|
||||
const persistOperationExecutor = new PersistOperationExecutor(this.connection.driver, this.connection.entityMetadatas, this.connection.broadcaster, queryRunner); // todo: better to pass connection?
|
||||
@ -204,21 +195,15 @@ export class Repository<Entity extends ObjectLiteral> {
|
||||
.from(this.metadata.target, this.metadata.table.name);
|
||||
const dbEntity = await this.plainObjectToDatabaseEntityTransformer.transform(entityOrEntities, this.metadata, queryBuilder);
|
||||
|
||||
(<any> entityOrEntities)[this.metadata.primaryColumn.name] = undefined;
|
||||
this.metadata.primaryColumns.forEach(primaryColumn => {
|
||||
(<any> entityOrEntities)[primaryColumn.name] = undefined;
|
||||
});
|
||||
const [dbEntities, allPersistedEntities] = await Promise.all([
|
||||
this.extractObjectsById(dbEntity, this.metadata),
|
||||
this.extractObjectsById(entityOrEntities, this.metadata)
|
||||
]);
|
||||
const entityWithId: EntityWithId = {
|
||||
id: this.metadata.getEntityId(entityOrEntities),
|
||||
entityTarget: this.metadata.target,
|
||||
entity: entityOrEntities
|
||||
};
|
||||
const dbEntityWithId: EntityWithId = {
|
||||
id: this.metadata.getEntityId(dbEntity),
|
||||
entityTarget: this.metadata.target,
|
||||
entity: dbEntity
|
||||
};
|
||||
const entityWithId = new EntityWithId(this.metadata, entityOrEntities);
|
||||
const dbEntityWithId = new EntityWithId(this.metadata, dbEntity);
|
||||
|
||||
const persistOperation = this.entityPersistOperationBuilder.buildOnlyRemovement(this.metadata, dbEntityWithId, entityWithId, dbEntities, allPersistedEntities);
|
||||
const persistOperationExecutor = new PersistOperationExecutor(this.connection.driver, this.connection.entityMetadatas, this.connection.broadcaster, queryRunner); // todo: better to pass connection?
|
||||
@ -315,7 +300,15 @@ export class Repository<Entity extends ObjectLiteral> {
|
||||
* Finds entity with given id.
|
||||
*/
|
||||
async findOneById(id: any, options?: FindOptions): Promise<Entity> {
|
||||
return this.createFindQueryBuilder({ [this.metadata.primaryColumn.name]: id }, options)
|
||||
const conditions: ObjectLiteral = {};
|
||||
if (this.metadata.hasMultiplePrimaryKeys) {
|
||||
this.metadata.primaryColumns.forEach(primaryColumn => {
|
||||
conditions[primaryColumn.name] = id[primaryColumn.name];
|
||||
});
|
||||
} else {
|
||||
conditions[this.metadata.firstPrimaryColumn.name] = id;
|
||||
}
|
||||
return this.createFindQueryBuilder(conditions, options)
|
||||
.getSingleResult();
|
||||
}
|
||||
|
||||
@ -395,25 +388,27 @@ export class Repository<Entity extends ObjectLiteral> {
|
||||
*/
|
||||
private findNotLoadedIds(queryRunner: QueryRunner, dbEntities: EntityWithId[], persistedEntities: EntityWithId[]): Promise<EntityWithId[]> {
|
||||
const missingDbEntitiesLoad = persistedEntities
|
||||
.filter(entityWithId => entityWithId.id !== null && entityWithId.id !== undefined)
|
||||
.filter(entityWithId => !dbEntities.find(dbEntity => dbEntity.entityTarget === entityWithId.entityTarget && dbEntity.id === entityWithId.id))
|
||||
.filter(entityWithId => entityWithId.id !== null && entityWithId.id !== undefined) // todo: not sure if this condition will work
|
||||
.filter(entityWithId => !dbEntities.find(dbEntity => dbEntity.entityTarget === entityWithId.entityTarget && dbEntity.compareId(entityWithId.id!)))
|
||||
.map(entityWithId => {
|
||||
const metadata = this.connection.entityMetadatas.findByTarget(entityWithId.entityTarget);
|
||||
const alias = (entityWithId.entityTarget as any).name;
|
||||
const qb = new QueryBuilder(this.connection.driver, this.connection.entityMetadatas, this.connection.broadcaster, queryRunner)
|
||||
.select(alias)
|
||||
.from(entityWithId.entityTarget, alias)
|
||||
.where(alias + "." + this.metadata.primaryColumn.propertyName + "=:id", { id: entityWithId.id })
|
||||
.getSingleResult();
|
||||
.from(entityWithId.entityTarget, alias);
|
||||
|
||||
const parameters: ObjectLiteral = {};
|
||||
const condition = this.metadata.primaryColumns.map(primaryColumn => {
|
||||
parameters[primaryColumn.propertyName] = entityWithId.id![primaryColumn.propertyName];
|
||||
return alias + "." + primaryColumn.propertyName + "=:" + primaryColumn.propertyName;
|
||||
}).join(" AND ");
|
||||
|
||||
const qbResult = qb.where(condition, parameters).getSingleResult();
|
||||
// const repository = this.connection.getRepository(entityWithId.entityTarget as any); // todo: fix type
|
||||
return qb.then(loadedEntity => {
|
||||
return qbResult.then(loadedEntity => {
|
||||
if (!loadedEntity) return undefined;
|
||||
|
||||
return <EntityWithId> {
|
||||
id: (<any> loadedEntity)[metadata.primaryColumn.name],
|
||||
entityTarget: metadata.target,
|
||||
entity: loadedEntity
|
||||
};
|
||||
return new EntityWithId(metadata, loadedEntity);
|
||||
});
|
||||
});
|
||||
|
||||
@ -446,11 +441,8 @@ export class Repository<Entity extends ObjectLiteral> {
|
||||
|
||||
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
|
||||
});
|
||||
const entityWithId = new EntityWithId(metadata, entity);
|
||||
entityWithIds.push(entityWithId);
|
||||
}
|
||||
|
||||
return entityWithIds;
|
||||
|
||||
@ -4,7 +4,6 @@ import {EntityWithId} from "../persistment/operation/PersistOperation";
|
||||
import {FindOptions, FindOptionsUtils} from "./FindOptions";
|
||||
import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
import {Repository} from "./Repository";
|
||||
import {DatabaseConnection} from "../driver/DatabaseConnection";
|
||||
import {QueryRunner} from "../driver/QueryRunner";
|
||||
|
||||
/**
|
||||
@ -264,9 +263,23 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
*/
|
||||
async removeById(id: any) {
|
||||
const alias = this.metadata.table.name;
|
||||
const parameters: ObjectLiteral = {};
|
||||
let condition = "";
|
||||
|
||||
if (this.metadata.hasMultiplePrimaryKeys) {
|
||||
condition = this.metadata.primaryColumns.map(primaryColumn => {
|
||||
parameters[primaryColumn.propertyName] = id[primaryColumn.propertyName];
|
||||
return alias + "." + primaryColumn.propertyName + "=:" + primaryColumn.propertyName;
|
||||
}).join(" AND ");
|
||||
|
||||
} else {
|
||||
condition = alias + "." + this.metadata.firstPrimaryColumn.propertyName + "=:id";
|
||||
parameters["id"] = id;
|
||||
}
|
||||
|
||||
await this.repository.createQueryBuilder(alias)
|
||||
.delete()
|
||||
.where(alias + "." + this.metadata.primaryColumn.propertyName + "=:id", { id: id })
|
||||
.where(condition, parameters)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@ -276,9 +289,24 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
*/
|
||||
async removeByIds(ids: any[]) {
|
||||
const alias = this.metadata.table.name;
|
||||
const parameters: ObjectLiteral = {};
|
||||
let condition = "";
|
||||
|
||||
if (this.metadata.hasMultiplePrimaryKeys) {
|
||||
condition = ids.map((id, idIndex) => {
|
||||
this.metadata.primaryColumns.map(primaryColumn => {
|
||||
parameters[primaryColumn.propertyName + "_" + idIndex] = id[primaryColumn.propertyName];
|
||||
return alias + "." + primaryColumn.propertyName + "=:" + primaryColumn.propertyName + "_" + idIndex;
|
||||
}).join(" AND ");
|
||||
}).join(" OR ");
|
||||
} else {
|
||||
condition = alias + "." + this.metadata.firstPrimaryColumn.propertyName + " IN (:ids)";
|
||||
parameters["ids"] = ids;
|
||||
}
|
||||
|
||||
await this.repository.createQueryBuilder(alias)
|
||||
.delete()
|
||||
.where(alias + "." + this.metadata.primaryColumn.propertyName + " IN (:ids)", { ids: ids })
|
||||
.where(condition, parameters)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@ -320,36 +348,6 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
return qb;
|
||||
}
|
||||
|
||||
/**
|
||||
* When ORM loads dbEntity it uses joins to load all entity dependencies. However when dbEntity is newly persisted
|
||||
* to the db, but uses already exist in the db relational entities, those entities cannot be loaded, and will
|
||||
* absent in dbEntities. To fix it, we need to go throw all persistedEntities we have, find out those which have
|
||||
* ids, check if we did not load them yet and try to load them. This algorithm will make sure that all dbEntities
|
||||
* are loaded. Further it will help insert operations to work correctly.
|
||||
*/
|
||||
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.entityTarget === entityWithId.entityTarget && dbEntity.id === entityWithId.id))
|
||||
.map(entityWithId => {
|
||||
const metadata = this.connection.getMetadata(entityWithId.entityTarget as any); // todo: fix type
|
||||
const repository = this.connection.getRepository(entityWithId.entityTarget as any); // todo: fix type
|
||||
return repository.findOneById(entityWithId.id).then(loadedEntity => {
|
||||
if (!loadedEntity) return undefined;
|
||||
|
||||
return <EntityWithId> {
|
||||
id: (<any> loadedEntity)[metadata.primaryColumn.name],
|
||||
entityTarget: metadata.target,
|
||||
entity: loadedEntity
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all<EntityWithId>(missingDbEntitiesLoad).then(missingDbEntities => {
|
||||
return dbEntities.concat(missingDbEntities.filter(dbEntity => !!dbEntity));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts unique objects from given entity and all its downside relations.
|
||||
*/
|
||||
@ -374,11 +372,8 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
|
||||
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
|
||||
});
|
||||
const entityWithId = new EntityWithId(metadata, entity);
|
||||
entityWithIds.push(entityWithId);
|
||||
}
|
||||
|
||||
return entityWithIds;
|
||||
|
||||
@ -27,10 +27,10 @@ export class TreeRepository<Entity> extends Repository<Entity> {
|
||||
* Creates a query builder used to get descendants of the entities in a tree.
|
||||
*/
|
||||
createDescendantsQueryBuilder(alias: string, closureTableAlias: string, entity: Entity): QueryBuilder<Entity> {
|
||||
const joinCondition = `${alias}.${this.metadata.primaryColumn.name}=${closureTableAlias}.descendant`;
|
||||
const joinCondition = `${alias}.${this.metadata.firstPrimaryColumn.name}=${closureTableAlias}.descendant`;
|
||||
return this.createQueryBuilder(alias)
|
||||
.innerJoin(this.metadata.closureJunctionTable.table.name, closureTableAlias, "ON", joinCondition)
|
||||
.where(`${closureTableAlias}.ancestor=${this.metadata.getEntityId(entity)}`);
|
||||
.where(`${closureTableAlias}.ancestor=${this.metadata.getEntityIdMap(entity)![this.metadata.firstPrimaryColumn.propertyName]}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,10 +70,10 @@ export class TreeRepository<Entity> extends Repository<Entity> {
|
||||
* Creates a query builder used to get ancestors of the entities in the tree.
|
||||
*/
|
||||
createAncestorsQueryBuilder(alias: string, closureTableAlias: string, entity: Entity): QueryBuilder<Entity> {
|
||||
const joinCondition = `${alias}.${this.metadata.primaryColumn.name}=${closureTableAlias}.ancestor`;
|
||||
const joinCondition = `${alias}.${this.metadata.firstPrimaryColumn.name}=${closureTableAlias}.ancestor`;
|
||||
return this.createQueryBuilder(alias)
|
||||
.innerJoin(this.metadata.closureJunctionTable.table.name, closureTableAlias, "ON", joinCondition)
|
||||
.where(`${closureTableAlias}.descendant=${this.metadata.getEntityId(entity)}`);
|
||||
.where(`${closureTableAlias}.descendant=${this.metadata.getEntityIdMap(entity)![this.metadata.firstPrimaryColumn.propertyName]}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -124,7 +124,7 @@ export class TreeRepository<Entity> extends Repository<Entity> {
|
||||
private createRelationMaps(alias: string, scalarResults: any[]): { id: any, parentId: any }[] {
|
||||
return scalarResults.map(scalarResult => {
|
||||
return {
|
||||
id: scalarResult[alias + "_" + this.metadata.primaryColumn.name],
|
||||
id: scalarResult[alias + "_" + this.metadata.firstPrimaryColumn.name],
|
||||
parentId: scalarResult[alias + "_" + this.metadata.treeParentRelation.name]
|
||||
};
|
||||
});
|
||||
@ -132,10 +132,10 @@ export class TreeRepository<Entity> extends Repository<Entity> {
|
||||
|
||||
private buildChildrenEntityTree(entity: any, entities: any[], relationMaps: { id: any, parentId: any }[]): void {
|
||||
const childProperty = this.metadata.treeChildrenRelation.propertyName;
|
||||
const parentEntityId = entity[this.metadata.primaryColumn.propertyName];
|
||||
const parentEntityId = entity[this.metadata.firstPrimaryColumn.propertyName];
|
||||
const childRelationMaps = relationMaps.filter(relationMap => relationMap.parentId === parentEntityId);
|
||||
const childIds = childRelationMaps.map(relationMap => relationMap.id);
|
||||
entity[childProperty] = entities.filter(entity => childIds.indexOf(entity[this.metadata.primaryColumn.propertyName]) !== -1);
|
||||
entity[childProperty] = entities.filter(entity => childIds.indexOf(entity[this.metadata.firstPrimaryColumn.propertyName]) !== -1);
|
||||
entity[childProperty].forEach((childEntity: any) => {
|
||||
this.buildChildrenEntityTree(childEntity, entities, relationMaps);
|
||||
});
|
||||
@ -143,13 +143,13 @@ export class TreeRepository<Entity> extends Repository<Entity> {
|
||||
|
||||
private buildParentEntityTree(entity: any, entities: any[], relationMaps: { id: any, parentId: any }[]): void {
|
||||
const parentProperty = this.metadata.treeParentRelation.propertyName;
|
||||
const entityId = entity[this.metadata.primaryColumn.propertyName];
|
||||
const entityId = entity[this.metadata.firstPrimaryColumn.propertyName];
|
||||
const parentRelationMap = relationMaps.find(relationMap => relationMap.id === entityId);
|
||||
const parentEntity = entities.find(entity => {
|
||||
if (!parentRelationMap)
|
||||
return false;
|
||||
|
||||
return entity[this.metadata.primaryColumn.propertyName] === parentRelationMap.parentId;
|
||||
return entity[this.metadata.firstPrimaryColumn.propertyName] === parentRelationMap.parentId;
|
||||
});
|
||||
if (parentEntity) {
|
||||
entity[parentProperty] = parentEntity;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user