implemented multiple join columns support and proper multiple primary keys relations

This commit is contained in:
Zotov Dmitry 2017-04-21 18:31:56 +05:00
parent 84c7c93c50
commit 94a6ef4c13
18 changed files with 381 additions and 295 deletions

View File

@ -7,16 +7,39 @@ import {JoinColumnMetadataArgs} from "../../metadata-args/JoinColumnMetadataArgs
* It also can be used on both one-to-one and many-to-one relations to specify custom column name
* or custom referenced column.
*/
export function JoinColumn(options?: JoinColumnOptions): Function {
export function JoinColumn(): Function;
/**
* JoinColumn decorator used on one-to-one relations to specify owner side of relationship.
* It also can be used on both one-to-one and many-to-one relations to specify custom column name
* or custom referenced column.
*/
export function JoinColumn(options: JoinColumnOptions): Function;
/**
* JoinColumn decorator used on one-to-one relations to specify owner side of relationship.
* It also can be used on both one-to-one and many-to-one relations to specify custom column name
* or custom referenced column.
*/
export function JoinColumn(options: JoinColumnOptions[]): Function;
/**
* JoinColumn decorator used on one-to-one relations to specify owner side of relationship.
* It also can be used on both one-to-one and many-to-one relations to specify custom column name
* or custom referenced column.
*/
export function JoinColumn(optionsOrOptionsArray?: JoinColumnOptions|JoinColumnOptions[]): Function {
return function (object: Object, propertyName: string) {
options = options || {} as JoinColumnOptions;
const args: JoinColumnMetadataArgs = {
target: object.constructor,
propertyName: propertyName,
name: options.name,
referencedColumnName: options.referencedColumnName
};
getMetadataArgsStorage().joinColumns.add(args);
const options = optionsOrOptionsArray instanceof Array ? optionsOrOptionsArray : [optionsOrOptionsArray || {}];
options.forEach(options => {
const args: JoinColumnMetadataArgs = {
target: object.constructor,
propertyName: propertyName,
name: options.name,
referencedColumnName: options.referencedColumnName
};
getMetadataArgsStorage().joinColumns.add(args);
});
};
}

View File

@ -39,7 +39,7 @@ export class LazyRelationsWrapper {
.innerJoin(relation.junctionEntityMetadata.table.name, relation.junctionEntityMetadata.table.name,
`${relation.junctionEntityMetadata.table.name}.${relation.joinTable.joinColumnName}=:${relation.propertyName}Id AND ` +
`${relation.junctionEntityMetadata.table.name}.${relation.joinTable.inverseJoinColumnName}=${relation.propertyName}.${relation.joinTable.referencedColumn.propertyName}`)
.setParameter(relation.propertyName + "Id", this[relation.referencedColumn.propertyName]);
.setParameter(relation.propertyName + "Id", this[relation.joinTable.referencedColumn.propertyName]);
} else { // non-owner
qb.select(relation.propertyName)
@ -47,7 +47,7 @@ export class LazyRelationsWrapper {
.innerJoin(relation.junctionEntityMetadata.table.name, relation.junctionEntityMetadata.table.name,
`${relation.junctionEntityMetadata.table.name}.${relation.inverseRelation.joinTable.inverseJoinColumnName}=:${relation.propertyName}Id AND ` +
`${relation.junctionEntityMetadata.table.name}.${relation.inverseRelation.joinTable.joinColumnName}=${relation.propertyName}.${relation.inverseRelation.joinTable.referencedColumn.propertyName}`)
.setParameter(relation.propertyName + "Id", this[relation.inverseRelation.referencedColumn.propertyName]);
.setParameter(relation.propertyName + "Id", this[relation.inverseRelation.joinTable.referencedColumn.propertyName]);
}
this[promiseIndex] = qb.getMany().then(results => {
@ -78,13 +78,13 @@ export class LazyRelationsWrapper {
});
return this[promiseIndex];
} else {
} else { // todo: fix issues with joinColumn[0]
if (relation.hasInverseSide) {
qb.select(relation.propertyName)
.from(relation.inverseRelation.entityMetadata.target, relation.propertyName)
.innerJoin(`${relation.propertyName}.${relation.inverseRelation.propertyName}`, relation.entityMetadata.targetName)
.where(relation.entityMetadata.targetName + "." + relation.joinColumn.referencedColumn.fullName + "=:id", { id: relation.entityMetadata.getEntityIdMixedMap(this) }); // is referenced column usage is correct here?
.where(relation.entityMetadata.targetName + "." + relation.joinColumns[0].referencedColumn.fullName + "=:id", { id: relation.entityMetadata.getEntityIdMixedMap(this) }); // is referenced column usage is correct here?
} else {
// (ow) post.category<=>category.post
@ -94,8 +94,8 @@ export class LazyRelationsWrapper {
qb.select(relation.propertyName) // category
.from(relation.type, relation.propertyName) // Category, category
.innerJoin(relation.entityMetadata.target as Function, relation.entityMetadata.name,
`${relation.entityMetadata.name}.${relation.propertyName}=${relation.propertyName}.${relation.referencedColumn.propertyName}`)
.where(relation.entityMetadata.name + "." + relation.joinColumn.referencedColumn.fullName + "=:id", { id: relation.entityMetadata.getEntityIdMixedMap(this) }); // is referenced column usage is correct here?
`${relation.entityMetadata.name}.${relation.propertyName}=${relation.propertyName}.${relation.isOwning ? relation.joinColumns[0].referencedColumn.propertyName : relation.inverseRelation.joinColumns[0].referencedColumn.propertyName }`)
.where(relation.entityMetadata.name + "." + relation.joinColumns[0].referencedColumn.fullName + "=:id", { id: relation.entityMetadata.getEntityIdMixedMap(this) }); // is referenced column usage is correct here?
}
this[promiseIndex] = qb.getOne().then(result => {

View File

@ -394,7 +394,7 @@ export class EntityMetadataBuilder {
joinColumn.referencedColumn = referencedColumn;
joinColumn.name = joinColumnMetadataArgs.name || namingStrategy.joinColumnInverseSideName(relation.propertyName, joinColumn.referencedColumn.propertyName);
relation.joinColumn = joinColumn;
relation.joinColumns.push(joinColumn);
});
});
@ -426,31 +426,34 @@ export class EntityMetadataBuilder {
entityMetadatas.forEach(metadata => {
metadata.relationsWithJoinColumns.forEach(relation => {
// find relational column and if it does not exist - add it
const inverseSideColumn = relation.joinColumn.referencedColumn;
let relationalColumn = metadata.columns.find(column => column.fullName === relation.name);
if (!relationalColumn) {
relationalColumn = new ColumnMetadata({
target: metadata.target,
propertyName: relation.name,
// propertyType: inverseSideColumn.propertyType,
mode: "virtual",
options: <ColumnOptions> {
name: relation.name,
type: inverseSideColumn.type,
nullable: relation.isNullable,
primary: relation.isPrimary
}
});
relationalColumn.relationMetadata = relation;
metadata.addColumn(relationalColumn);
}
const columns = relation.joinColumns.map(joinColumn => {
// find relational column and if it does not exist - add it
let relationalColumn = metadata.columns.find(column => column.fullName === joinColumn.name);
if (!relationalColumn) {
relationalColumn = new ColumnMetadata({
target: metadata.target,
propertyName: joinColumn.name,
mode: "virtual",
options: {
name: joinColumn.name,
type: joinColumn.referencedColumn.type,
nullable: relation.isNullable,
primary: relation.isPrimary
}
});
relationalColumn.relationMetadata = relation;
metadata.addColumn(relationalColumn);
}
return relationalColumn;
});
// create and add foreign key
const inverseSideColumns = relation.joinColumns.map(joinColumn => joinColumn.referencedColumn);
const foreignKey = new ForeignKeyMetadata(
[relationalColumn],
columns,
relation.inverseEntityMetadata.table,
[inverseSideColumn],
inverseSideColumns,
relation.onDelete
);
foreignKey.entityMetadata = metadata;

View File

@ -1,8 +1,5 @@
import {UsingJoinTableIsNotAllowedError} from "./error/UsingJoinTableIsNotAllowedError";
import {UsingJoinTableOnlyOnOneSideAllowedError} from "./error/UsingJoinTableOnlyOnOneSideAllowedError";
import {UsingJoinColumnIsNotAllowedError} from "./error/UsingJoinColumnIsNotAllowedError";
import {UsingJoinColumnOnlyOnOneSideAllowedError} from "./error/UsingJoinColumnOnlyOnOneSideAllowedError";
import {MissingJoinColumnError} from "./error/MissingJoinColumnError";
import {MissingJoinTableError} from "./error/MissingJoinTableError";
import {EntityMetadata} from "../metadata/EntityMetadata";
import {MissingPrimaryColumnError} from "./error/MissingPrimaryColumnError";
@ -76,7 +73,8 @@ export class EntityMetadataValidator {
// check join columns:
// using JoinColumn is possible only on one side of the relation and on one-to-one, many-to-one relation types
// first check if relation is one-to-one or many-to-one
if (relation.joinColumn) {
// todo(dima): fix
/*if (relation.joinColumn) {
// join column can be applied only on one-to-one and many-to-one relations
if (!relation.isOneToOne && !relation.isManyToOne)
@ -95,7 +93,7 @@ export class EntityMetadataValidator {
// if its a one-to-one relation and JoinColumn is missing on both sides of the relation
// or its one-side relation without JoinColumn we should give an error
if (!relation.joinColumn && relation.isOneToOne && (!relation.hasInverseSide || !relation.inverseRelation.joinColumn))
throw new MissingJoinColumnError(entityMetadata, relation);
throw new MissingJoinColumnError(entityMetadata, relation);*/
// if its a many-to-many relation and JoinTable is missing on both sides of the relation
// or its one-side relation without JoinTable we should give an error

View File

@ -603,10 +603,14 @@ export class EntityMetadata {
// if entity id is a relation, then extract referenced column from that relation
const columnRelation = this.relations.find(relation => relation.propertyName === column.propertyName);
if (columnRelation && columnRelation.joinColumn) {
map[column.propertyName] = entityValue[columnRelation.joinColumn.referencedColumn.propertyName];
} else if (columnRelation && columnRelation.inverseRelation.joinColumn) {
map[column.propertyName] = entityValue[columnRelation.inverseRelation.joinColumn.referencedColumn.propertyName];
if (columnRelation && columnRelation.joinColumns.length) {
const ids = columnRelation.joinColumns.map(joinColumn => entityValue[joinColumn.referencedColumn.propertyName]);
map[column.propertyName] = ids.length === 1 ? ids[0] : ids;
} else if (columnRelation && columnRelation.inverseRelation.joinColumns.length) {
const ids = columnRelation.inverseRelation.joinColumns.map(joinColumn => entityValue[joinColumn.referencedColumn.propertyName]);
map[column.propertyName] = ids.length === 1 ? ids[0] : ids;
} else {
map[column.propertyName] = entityValue;
}
@ -618,13 +622,18 @@ export class EntityMetadata {
if (entityValue === null || entityValue === undefined)
return;
// this case is real when your entity primary keys are relations
// if entity id is a relation, then extract referenced column from that relation
const columnRelation = this.relations.find(relation => relation.propertyName === column.propertyName);
if (columnRelation && columnRelation.joinColumn) {
map[column.propertyName] = entityValue[columnRelation.joinColumn.referencedColumn.propertyName];
} else if (columnRelation && columnRelation.inverseRelation.joinColumn) {
map[column.propertyName] = entityValue[columnRelation.inverseRelation.joinColumn.referencedColumn.propertyName];
if (columnRelation && columnRelation.joinColumns.length) {
const ids = columnRelation.joinColumns.map(joinColumn => entityValue[joinColumn.referencedColumn.propertyName]);
map[column.propertyName] = ids.length === 1 ? ids[0] : ids;
} else if (columnRelation && columnRelation.inverseRelation.joinColumns.length) {
const ids = columnRelation.inverseRelation.joinColumns.map(joinColumn => entityValue[joinColumn.referencedColumn.propertyName]);
map[column.propertyName] = ids.length === 1 ? ids[0] : ids;
} else {
map[column.propertyName] = entityValue;
}
@ -647,10 +656,14 @@ export class EntityMetadata {
// if entity id is a relation, then extract referenced column from that relation
const columnRelation = this.relations.find(relation => relation.propertyName === column.propertyName);
if (columnRelation && columnRelation.joinColumn) {
map[column.fullName] = entityValue[columnRelation.joinColumn.referencedColumn.propertyName];
} else if (columnRelation && columnRelation.inverseRelation.joinColumn) {
map[column.fullName] = entityValue[columnRelation.inverseRelation.joinColumn.referencedColumn.propertyName];
if (columnRelation && columnRelation.joinColumns.length) {
const ids = columnRelation.joinColumns.map(joinColumn => entityValue[joinColumn.referencedColumn.propertyName]);
map[column.fullName] = ids.length === 1 ? ids[0] : ids;
} else if (columnRelation && columnRelation.inverseRelation.joinColumns.length) {
const ids = columnRelation.inverseRelation.joinColumns.map(joinColumn => entityValue[joinColumn.referencedColumn.propertyName]);
map[column.fullName] = ids.length === 1 ? ids[0] : ids;
} else {
map[column.fullName] = entityValue;
}
@ -665,10 +678,14 @@ export class EntityMetadata {
// if entity id is a relation, then extract referenced column from that relation
const columnRelation = this.relations.find(relation => relation.propertyName === column.propertyName);
if (columnRelation && columnRelation.joinColumn) {
map[column.fullName] = entityValue[columnRelation.joinColumn.referencedColumn.propertyName];
} else if (columnRelation && columnRelation.inverseRelation.joinColumn) {
map[column.fullName] = entityValue[columnRelation.inverseRelation.joinColumn.referencedColumn.propertyName];
if (columnRelation && columnRelation.joinColumns.length) {
const ids = columnRelation.joinColumns.map(joinColumn => entityValue[joinColumn.referencedColumn.propertyName]);
map[column.fullName] = ids.length === 1 ? ids[0] : ids;
} else if (columnRelation && columnRelation.inverseRelation.joinColumns.length) {
const ids = columnRelation.inverseRelation.joinColumns.map(joinColumn => entityValue[joinColumn.referencedColumn.propertyName]);
map[column.fullName] = ids.length === 1 ? ids[0] : ids;
} else {
map[column.fullName] = entityValue;
}
@ -863,8 +880,15 @@ export class EntityMetadata {
return false;
return Object.keys(firstId).every(key => {
if (firstId[key] instanceof Object && secondId[key] instanceof Object)
// ids can be arrays in the case if they are mapped into multiple primary keys
if (firstId[key] instanceof Array && secondId[key] instanceof Array) {
return firstId[key].length === secondId[key].length &&
firstId[key].every((key: any, index: number) => firstId[key][index] === secondId[key][index]);
} else if (firstId[key] instanceof Object && secondId[key] instanceof Object) { // ids can be objects
return firstId[key].equals(secondId[key]);
}
return firstId[key] === secondId[key];
});

View File

@ -13,16 +13,20 @@ export class JoinColumnMetadata {
/**
* Target class to which metadata is applied.
*
* @deprecated
*/
target: Function|string;
/**
* Target's property name to which this metadata is applied.
*
* @deprecated
*/
propertyName: string;
/**
* Join column name.
* Join column name in the database.
*/
name: string;

View File

@ -4,7 +4,6 @@ import {OnDeleteType} from "./ForeignKeyMetadata";
import {JoinTableMetadata} from "./JoinTableMetadata";
import {JoinColumnMetadata} from "./JoinColumnMetadata";
import {RelationMetadataArgs} from "../metadata-args/RelationMetadataArgs";
import {ColumnMetadata} from "./ColumnMetadata";
import {ObjectLiteral} from "../common/ObjectLiteral";
/**
@ -51,8 +50,7 @@ export class RelationMetadata {
/**
* Relation join columns metadata.
*/
joinColumn: JoinColumnMetadata;
// joinColumns: JoinColumnMetadata[] = [];
joinColumns: JoinColumnMetadata[] = [];
// ---------------------------------------------------------------------
// Readonly Properties
@ -201,15 +199,15 @@ export class RelationMetadata {
if (this.isOwning) {
if (this.joinTable) {
return this.joinTable.joinColumnName;
} else if (this.joinColumn) {
return this.joinColumn.name;
} else if (this.joinColumns) {
return this.joinColumns[0].name;
}
} else if (this.hasInverseSide) {
if (this.inverseRelation.joinTable) {
return this.inverseRelation.joinTable.inverseJoinColumnName;
} else if (this.inverseRelation.joinColumn && this.inverseRelation.joinColumn.referencedColumn) {
return this.inverseRelation.joinColumn.referencedColumn.fullName;
} else if (this.inverseRelation.joinColumns && this.inverseRelation.joinColumns[0].referencedColumn) {
return this.inverseRelation.joinColumns[0].referencedColumn.fullName; // todo: [0] is temporary!!
}
}
@ -222,23 +220,8 @@ export class RelationMetadata {
* //Also only owning sides of the relations have this property.
*
* @deprecated Use joinTable or joinColumn directly where needed, because this method it too much confusing
*/
get referencedColumnName(): string {
// if (!this.isOwning)
// throw new Error(`Only owning side of the relations can have information about referenced column names.`);
// for many-to-one and owner one-to-one relations we get referenced column from join column
/*if (this.joinColumn && this.joinColumn.referencedColumn && this.joinColumn.referencedColumn.name)
return this.joinColumn.referencedColumn.name;
// for many-to-many relation we give referenced column depend of owner side
if (this.joinTable) { // need to check if this algorithm works correctly
if (this.isOwning) {
return this.joinTable.referencedColumn.name;
} else {
return this.joinTable.inverseReferencedColumn.name;
}
}*/
if (this.isOwning) {
if (this.joinTable) {
@ -258,13 +241,13 @@ export class RelationMetadata {
// this should not be possible, but anyway throw error
throw new Error(`Cannot get referenced column name of the relation ${this.entityMetadata.name}#${this.name}`);
}
} */
/**
* Gets the column to which this relation is referenced.
*
* @deprecated Use joinTable or joinColumn directly where needed, because this method it too much confusing
*/
get referencedColumn(): ColumnMetadata {
if (this.isOwning) {
if (this.joinTable) {
@ -284,7 +267,7 @@ export class RelationMetadata {
// this should not be possible, but anyway throw error
throw new Error(`Cannot get referenced column of the relation ${this.entityMetadata.name}#${this.name}`);
}
} */
/**
* Gets the property's type to which this relation is applied.
@ -299,7 +282,7 @@ export class RelationMetadata {
get isOwning() {
return !!(this.isManyToOne ||
(this.isManyToMany && this.joinTable) ||
(this.isOneToOne && this.joinColumn));
(this.isOneToOne && this.joinColumns.length > 0));
}
/**
@ -443,7 +426,9 @@ export class RelationMetadata {
*
* if from Post relation we are passing Category here,
* it should return a post.category
*/
*
* @deprecated
getOwnEntityRelationId(ownEntity: ObjectLiteral): any {
if (this.isManyToManyOwner) {
return ownEntity[this.joinTable.referencedColumn.propertyName];
@ -457,7 +442,7 @@ export class RelationMetadata {
} else if (this.isOneToOneNotOwner || this.isOneToMany) {
return ownEntity[this.inverseRelation.joinColumn.referencedColumn.propertyName];
}
}
}*/
/**
*
@ -473,7 +458,7 @@ export class RelationMetadata {
* it should return a category.id
*
* @deprecated Looks like this method does not make sence and does same as getOwnEntityRelationId ?
*/
getInverseEntityRelationId(inverseEntity: ObjectLiteral): any {
if (this.isManyToManyOwner) {
return inverseEntity[this.joinTable.inverseReferencedColumn.propertyName];
@ -487,7 +472,7 @@ export class RelationMetadata {
} else if (this.isOneToOneNotOwner || this.isOneToMany) {
return inverseEntity[this.inverseRelation.joinColumn.propertyName];
}
}
}*/
// ---------------------------------------------------------------------
// Private Methods

View File

@ -334,10 +334,11 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
// (example) "relation" - is a relation in post with details.
// (example) "valueMetadata" - is an entity metadata of the Details object.
// (example) "persistValue" - is a detailsId from the persisted entity
// todo: fix joinColumns[0] usages
// note that if databaseEntity has relation, it can only be a relation id,
// because of query builder option "RELATION_ID_VALUES" we used
const relationIdInDatabaseEntity = subject.databaseEntity[relation.joinColumn.propertyName]; // (example) returns post.detailsId
const relationIdInDatabaseEntity = subject.databaseEntity[relation.propertyName]; // (example) returns post.detailsId
// if database relation id does not exist in the database object then nothing to remove
if (relationIdInDatabaseEntity === null || relationIdInDatabaseEntity === undefined)
@ -348,7 +349,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
if (subject.hasEntity) {
const persistValue = relation.getEntityValue(subject.entity);
if (persistValue === null) persistValueRelationId = null;
if (persistValue) persistValueRelationId = persistValue[relation.joinColumn.referencedColumn.propertyName];
if (persistValue) persistValueRelationId = persistValue[relation.joinColumns[0].referencedColumn.propertyName];
if (persistValueRelationId === undefined) return; // skip undefined properties
}
@ -366,7 +367,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
// (example) here we seek a Details loaded from the database in the subjects
// (example) here relatedSubject.databaseEntity is a Details
// (example) and we need to compare details.id === post.detailsId
return relatedSubject.databaseEntity[relation.joinColumn.referencedColumn.propertyName] === relationIdInDatabaseEntity;
return relatedSubject.databaseEntity[relation.joinColumns[0].referencedColumn.propertyName] === relationIdInDatabaseEntity;
});
// if not loaded yet then load it from the database
@ -376,7 +377,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
const databaseEntity = await this.connection
.getRepository<ObjectLiteral>(valueMetadata.target)
.createQueryBuilder(qbAlias, this.queryRunnerProvider) // todo: this wont work for mongodb. implement this in some method and call it here instead?
.where(qbAlias + "." + relation.joinColumn.referencedColumn.propertyName + "=:id")
.where(qbAlias + "." + relation.joinColumns[0].referencedColumn.propertyName + "=:id")
.setParameter("id", relationIdInDatabaseEntity) // (example) subject.entity is a post here
.enableAutoRelationIdsLoad()
.getOne();
@ -422,12 +423,12 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
let persistValueRelationId: any = undefined;
if (subject.hasEntity && !subject.mustBeRemoved) {
const persistValue = relation.getEntityValue(subject.entity);
if (persistValue) persistValueRelationId = persistValue[relation.inverseRelation.joinColumn.propertyName];
if (persistValue) persistValueRelationId = persistValue[relation.inverseRelation.propertyName];
if (persistValueRelationId === undefined) return; // skip undefined properties
}
// (example) returns us referenced column (detail's id)
const relationIdInDatabaseEntity = subject.databaseEntity[relation.inverseRelation.joinColumn.referencedColumn.propertyName];
const relationIdInDatabaseEntity = subject.databaseEntity[relation.inverseRelation.joinColumns[0].referencedColumn.propertyName];
// if database relation id does not exist then nothing to remove (but can this be possible?)
if (relationIdInDatabaseEntity === null || relationIdInDatabaseEntity === undefined)
@ -443,7 +444,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
// (example) here we seek a Post loaded from the database in the subjects
// (example) here relatedSubject.databaseEntity is a Post
// (example) and we need to compare post.detailsId === details.id
return relatedSubject.databaseEntity[relation.inverseRelation.joinColumn.propertyName] === relationIdInDatabaseEntity;
return relatedSubject.databaseEntity[relation.inverseRelation.propertyName] === relationIdInDatabaseEntity;
});
// if not loaded yet then load it from the database
@ -472,7 +473,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
// (example) persistValue is a postFromPersistedDetails here
// (example) alreadyLoadedRelatedDatabaseSubject.databaseEntity is a postFromDatabaseDetails here
// (example) postFromPersistedDetails.id === postFromDatabaseDetails - means nothing changed
const inverseEntityRelationId = alreadyLoadedRelatedDatabaseSubject.databaseEntity[relation.inverseRelation.joinColumn.propertyName];
const inverseEntityRelationId = alreadyLoadedRelatedDatabaseSubject.databaseEntity[relation.inverseRelation.propertyName];
if (persistValueRelationId && persistValueRelationId === inverseEntityRelationId)
return;
@ -569,7 +570,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
} else { // this case can only be a oneToMany relation
// (example) returns us referenced column (detail's id)
const relationIdInDatabaseEntity = subject.databaseEntity[relation.inverseRelation.joinColumn.referencedColumn.propertyName];
const relationIdInDatabaseEntity = subject.databaseEntity[relation.inverseRelation.joinColumns[0].referencedColumn.propertyName];
// in this case we need inverse entities not only because of cascade removes
// because we also need inverse entities to be able to perform update of entities

View File

@ -205,65 +205,67 @@ export class SubjectOperationExecutor {
// first update relations with join columns (one-to-one owner and many-to-one relations)
const updateOptions: ObjectLiteral = {};
subject.metadata.relationsWithJoinColumns.forEach(relation => {
const referencedColumn = relation.joinColumn.referencedColumn;
const relatedEntity = relation.getEntityValue(subject.entity);
relation.joinColumns.forEach(joinColumn => {
const referencedColumn = joinColumn.referencedColumn;
const relatedEntity = relation.getEntityValue(subject.entity);
// if relation value is not set then nothing to do here
if (!relatedEntity)
return;
// if relation value is not set then nothing to do here
if (!relatedEntity)
return;
// check if relation reference column is a relation
let relationId: any;
const columnRelation = relation.inverseEntityMetadata.relations.find(rel => rel.propertyName === relation.joinColumn.referencedColumn.propertyName);
if (columnRelation) { // if referenced column is a relation
const insertSubject = this.insertSubjects.find(insertedSubject => insertedSubject.entity === relatedEntity[referencedColumn.propertyName]);
// check if relation reference column is a relation
let relationId: any;
const columnRelation = relation.inverseEntityMetadata.relations.find(rel => rel.propertyName === joinColumn.referencedColumn.propertyName);
if (columnRelation) { // if referenced column is a relation
const insertSubject = this.insertSubjects.find(insertedSubject => insertedSubject.entity === relatedEntity[referencedColumn.propertyName]);
// if this relation was just inserted
if (insertSubject) {
// if this relation was just inserted
if (insertSubject) {
// check if we have this relation id already
relationId = relatedEntity[referencedColumn.propertyName][columnRelation.propertyName];
if (!relationId) {
// check if we have this relation id already
relationId = relatedEntity[referencedColumn.propertyName][columnRelation.propertyName];
if (!relationId) {
// if we don't have relation id then use special values
if (referencedColumn.isGenerated) {
relationId = insertSubject.newlyGeneratedId;
// if we don't have relation id then use special values
if (referencedColumn.isGenerated) {
relationId = insertSubject.newlyGeneratedId;
} else if (referencedColumn.isObjectId) {
relationId = insertSubject.generatedObjectId;
} else if (referencedColumn.isObjectId) {
relationId = insertSubject.generatedObjectId;
}
// todo: handle other special types too
}
// todo: handle other special types too
}
} else { // if referenced column is a simple non relational column
const insertSubject = this.insertSubjects.find(insertedSubject => insertedSubject.entity === relatedEntity);
// if this relation was just inserted
if (insertSubject) {
// check if we have this relation id already
relationId = relatedEntity[referencedColumn.propertyName];
if (!relationId) {
// if we don't have relation id then use special values
if (referencedColumn.isGenerated) {
relationId = insertSubject.newlyGeneratedId;
} else if (referencedColumn.isObjectId) {
relationId = insertSubject.generatedObjectId;
}
// todo: handle other special types too
}
}
}
} else { // if referenced column is a simple non relational column
const insertSubject = this.insertSubjects.find(insertedSubject => insertedSubject.entity === relatedEntity);
// if this relation was just inserted
if (insertSubject) {
// check if we have this relation id already
relationId = relatedEntity[referencedColumn.propertyName];
if (!relationId) {
// if we don't have relation id then use special values
if (referencedColumn.isGenerated) {
relationId = insertSubject.newlyGeneratedId;
} else if (referencedColumn.isObjectId) {
relationId = insertSubject.generatedObjectId;
}
// todo: handle other special types too
}
if (relationId) {
updateOptions[joinColumn.name] = relationId;
}
}
if (relationId) {
updateOptions[relation.name] = relationId;
}
});
});
// if we found relations which we can update - then update them
@ -279,23 +281,25 @@ export class SubjectOperationExecutor {
// if entity id is a relation, then extract referenced column from that relation
const columnRelation = subject.metadata.relations.find(relation => relation.propertyName === column.propertyName);
if (entityValue && columnRelation && columnRelation.joinColumn) { // not sure if we need handle join column from inverse side
let relationIdOfEntityValue = entityValue[columnRelation.joinColumn.referencedColumn.propertyName];
if (!relationIdOfEntityValue) {
const entityValueInsertSubject = this.insertSubjects.find(subject => subject.entity === entityValue);
if (entityValueInsertSubject) {
if (columnRelation.joinColumn.referencedColumn.isGenerated) {
relationIdOfEntityValue = entityValueInsertSubject.newlyGeneratedId;
if (entityValue && columnRelation) { // not sure if we need handle join column from inverse side
columnRelation.joinColumns.forEach(joinColumn => {
let relationIdOfEntityValue = entityValue[joinColumn.referencedColumn.propertyName];
if (!relationIdOfEntityValue) {
const entityValueInsertSubject = this.insertSubjects.find(subject => subject.entity === entityValue);
if (entityValueInsertSubject) {
if (joinColumn.referencedColumn.isGenerated) {
relationIdOfEntityValue = entityValueInsertSubject.newlyGeneratedId;
} else if (columnRelation.joinColumn.referencedColumn.isObjectId) {
relationIdOfEntityValue = entityValueInsertSubject.generatedObjectId;
} else if (joinColumn.referencedColumn.isObjectId) {
relationIdOfEntityValue = entityValueInsertSubject.generatedObjectId;
}
}
}
}
if (relationIdOfEntityValue) {
conditions[column.fullName] = relationIdOfEntityValue;
}
if (relationIdOfEntityValue) {
conditions[column.fullName] = relationIdOfEntityValue;
}
});
} else {
if (entityValue) {
@ -322,73 +326,80 @@ export class SubjectOperationExecutor {
const oneToManyAndOneToOneNonOwnerRelations = subject.metadata.oneToManyRelations.concat(subject.metadata.oneToOneRelations.filter(relation => !relation.isOwning));
subject.metadata.extractRelationValuesFromEntity(subject.entity, oneToManyAndOneToOneNonOwnerRelations)
.forEach(([relation, subRelatedEntity, inverseEntityMetadata]) => {
const referencedColumn = relation.inverseRelation.joinColumn.referencedColumn;
const columns = inverseEntityMetadata.parentEntityMetadata ? inverseEntityMetadata.primaryColumnsWithParentIdColumns : inverseEntityMetadata.primaryColumns;
const conditions: ObjectLiteral = {};
relation.inverseRelation.joinColumns.forEach(joinColumn => {
columns.forEach(column => {
const entityValue = subRelatedEntity[column.propertyName];
const referencedColumn = joinColumn.referencedColumn;
const columns = inverseEntityMetadata.parentEntityMetadata ? inverseEntityMetadata.primaryColumnsWithParentIdColumns : inverseEntityMetadata.primaryColumns;
const conditions: ObjectLiteral = {};
// if entity id is a relation, then extract referenced column from that relation
const columnRelation = inverseEntityMetadata.relations.find(relation => relation.propertyName === column.propertyName);
columns.forEach(column => {
const entityValue = subRelatedEntity[column.propertyName];
if (entityValue && columnRelation && columnRelation.joinColumn) { // not sure if we need handle join column from inverse side
let relationIdOfEntityValue = entityValue[columnRelation.joinColumn.referencedColumn.propertyName];
if (!relationIdOfEntityValue) {
const entityValueInsertSubject = this.insertSubjects.find(subject => subject.entity === entityValue);
if (entityValueInsertSubject) {
if (columnRelation.joinColumn.referencedColumn.isGenerated) {
relationIdOfEntityValue = entityValueInsertSubject.newlyGeneratedId;
// if entity id is a relation, then extract referenced column from that relation
const columnRelation = inverseEntityMetadata.relations.find(relation => relation.propertyName === column.propertyName);
if (entityValue && columnRelation) { // not sure if we need handle join column from inverse side
columnRelation.joinColumns.forEach(columnRelationJoinColumn => {
let relationIdOfEntityValue = entityValue[columnRelationJoinColumn.referencedColumn.propertyName];
if (!relationIdOfEntityValue) {
const entityValueInsertSubject = this.insertSubjects.find(subject => subject.entity === entityValue);
if (entityValueInsertSubject) {
if (columnRelationJoinColumn.referencedColumn.isGenerated) {
relationIdOfEntityValue = entityValueInsertSubject.newlyGeneratedId;
} else if (columnRelationJoinColumn.referencedColumn.isObjectId) {
relationIdOfEntityValue = entityValueInsertSubject.generatedObjectId;
}
}
}
if (relationIdOfEntityValue) {
conditions[column.fullName] = relationIdOfEntityValue;
}
});
} else {
const entityValueInsertSubject = this.insertSubjects.find(subject => subject.entity === subRelatedEntity);
if (entityValue) {
conditions[column.fullName] = entityValue;
} else {
if (entityValueInsertSubject && entityValueInsertSubject.newlyGeneratedId) {
conditions[column.fullName] = entityValueInsertSubject.newlyGeneratedId;
} else if (entityValueInsertSubject && entityValueInsertSubject.generatedObjectId) {
conditions[column.fullName] = entityValueInsertSubject.generatedObjectId;
} else if (columnRelation.joinColumn.referencedColumn.isObjectId) {
relationIdOfEntityValue = entityValueInsertSubject.generatedObjectId;
}
}
}
if (relationIdOfEntityValue) {
conditions[column.fullName] = relationIdOfEntityValue;
}
});
if (!Object.keys(conditions).length)
return;
const updateOptions: ObjectLiteral = {};
const columnRelation = relation.inverseEntityMetadata.relations.find(rel => rel.propertyName === referencedColumn.propertyName);
if (columnRelation) {
let id = subject.entity[referencedColumn.propertyName][columnRelation.propertyName];
if (!id) {
const insertSubject = this.insertSubjects.find(subject => subject.entity === subject.entity[referencedColumn.propertyName]);
if (insertSubject) {
if (insertSubject.newlyGeneratedId) {
id = insertSubject.newlyGeneratedId;
} else if (insertSubject.generatedObjectId) {
id = insertSubject.generatedObjectId;
}
}
}
updateOptions[joinColumn.name] = id;
} else {
const entityValueInsertSubject = this.insertSubjects.find(subject => subject.entity === subRelatedEntity);
if (entityValue) {
conditions[column.fullName] = entityValue;
} else {
if (entityValueInsertSubject && entityValueInsertSubject.newlyGeneratedId) {
conditions[column.fullName] = entityValueInsertSubject.newlyGeneratedId;
} else if (entityValueInsertSubject && entityValueInsertSubject.generatedObjectId) {
conditions[column.fullName] = entityValueInsertSubject.generatedObjectId;
}
}
updateOptions[joinColumn.name] = subject.entity[referencedColumn.propertyName] || subject.newlyGeneratedId || subRelatedEntity.generatedObjectId;
}
const updatePromise = this.queryRunner.update(relation.inverseEntityMetadata.table.name, updateOptions, conditions);
updatePromises.push(updatePromise);
});
if (!Object.keys(conditions).length)
return;
const updateOptions: ObjectLiteral = {};
const columnRelation = relation.inverseEntityMetadata.relations.find(rel => rel.propertyName === referencedColumn.propertyName);
if (columnRelation) {
let id = subject.entity[referencedColumn.propertyName][columnRelation.propertyName];
if (!id) {
const insertSubject = this.insertSubjects.find(subject => subject.entity === subject.entity[referencedColumn.propertyName]);
if (insertSubject) {
if (insertSubject.newlyGeneratedId) {
id = insertSubject.newlyGeneratedId;
} else if (insertSubject.generatedObjectId) {
id = insertSubject.generatedObjectId;
}
}
}
updateOptions[relation.inverseRelation.joinColumn.name] = id;
} else {
updateOptions[relation.inverseRelation.joinColumn.name] = subject.entity[referencedColumn.propertyName] || subject.newlyGeneratedId || subRelatedEntity.generatedObjectId;
}
const updatePromise = this.queryRunner.update(relation.inverseEntityMetadata.table.name, updateOptions, conditions);
updatePromises.push(updatePromise);
});
});
@ -495,67 +506,70 @@ export class SubjectOperationExecutor {
collectFromEmbeddeds(entity, columnsAndValuesMap, metadata.embeddeds);
metadata.relationsWithJoinColumns.forEach(relation => {
relation.joinColumns.forEach(joinColumn => {
let relationValue: any;
const value = relation.getEntityValue(entity);
let relationValue: any;
const value = relation.getEntityValue(entity);
if (value) {
// if relation value is stored in the entity itself then use it from there
const relationId = relation.getInverseEntityRelationId(value); // todo: check it
if (relationId) {
relationValue = relationId;
}
// otherwise try to find relational value from just inserted subjects
const alreadyInsertedSubject = alreadyInsertedSubjects.find(insertedSubject => {
return insertedSubject.entity === value;
});
if (alreadyInsertedSubject) {
const referencedColumn = relation.joinColumn.referencedColumn;
// if join column references to the primary generated column then seek in the newEntityId of the insertedSubject
if (referencedColumn.referencedColumn && referencedColumn.referencedColumn.isGenerated) {
if (referencedColumn.isParentId) {
relationValue = alreadyInsertedSubject.parentGeneratedId;
}
// todo: what if reference column is not generated?
// todo: what if reference column is not related to table inheritance?
if (value) {
// if relation value is stored in the entity itself then use it from there
const relationId = value[joinColumn.referencedColumn.propertyName]; // relation.getInverseEntityRelationId(value); // todo: check it
if (relationId) {
relationValue = relationId;
}
if (referencedColumn.isGenerated)
relationValue = alreadyInsertedSubject.newlyGeneratedId;
if (referencedColumn.isObjectId)
relationValue = alreadyInsertedSubject.generatedObjectId;
// if it references to create or update date columns
if (referencedColumn.isCreateDate || referencedColumn.isUpdateDate)
relationValue = this.connection.driver.preparePersistentValue(alreadyInsertedSubject.date, referencedColumn);
// if it references to version column
if (referencedColumn.isVersion)
relationValue = this.connection.driver.preparePersistentValue(1, referencedColumn);
}
} else if (relation.hasInverseSide) {
const inverseSubject = this.allSubjects.find(subject => {
if (!subject.hasEntity || subject.entityTarget !== relation.inverseRelation.target)
return false;
const inverseRelationValue = subject.entity[relation.inverseRelation.propertyName];
if (inverseRelationValue) {
if (inverseRelationValue instanceof Array) {
return inverseRelationValue.find(subValue => subValue === subValue);
} else {
return inverseRelationValue === entity;
// otherwise try to find relational value from just inserted subjects
const alreadyInsertedSubject = alreadyInsertedSubjects.find(insertedSubject => {
return insertedSubject.entity === value;
});
if (alreadyInsertedSubject) {
const referencedColumn = joinColumn.referencedColumn;
// if join column references to the primary generated column then seek in the newEntityId of the insertedSubject
if (referencedColumn.referencedColumn && referencedColumn.referencedColumn.isGenerated) {
if (referencedColumn.isParentId) {
relationValue = alreadyInsertedSubject.parentGeneratedId;
}
// todo: what if reference column is not generated?
// todo: what if reference column is not related to table inheritance?
}
}
});
if (inverseSubject && inverseSubject.entity[relation.joinColumn.referencedColumn.propertyName]) {
relationValue = inverseSubject.entity[relation.joinColumn.referencedColumn.propertyName];
}
}
if (relationValue) {
columnNames.push(relation.name);
columnValues.push(relationValue);
columnsAndValuesMap[relation.propertyName] = entity[relation.name];
}
if (referencedColumn.isGenerated)
relationValue = alreadyInsertedSubject.newlyGeneratedId;
if (referencedColumn.isObjectId)
relationValue = alreadyInsertedSubject.generatedObjectId;
// if it references to create or update date columns
if (referencedColumn.isCreateDate || referencedColumn.isUpdateDate)
relationValue = this.connection.driver.preparePersistentValue(alreadyInsertedSubject.date, referencedColumn);
// if it references to version column
if (referencedColumn.isVersion)
relationValue = this.connection.driver.preparePersistentValue(1, referencedColumn);
}
} else if (relation.hasInverseSide) {
const inverseSubject = this.allSubjects.find(subject => {
if (!subject.hasEntity || subject.entityTarget !== relation.inverseRelation.target)
return false;
const inverseRelationValue = subject.entity[relation.inverseRelation.propertyName];
if (inverseRelationValue) {
if (inverseRelationValue instanceof Array) {
return inverseRelationValue.find(subValue => subValue === subValue);
} else {
return inverseRelationValue === entity;
}
}
});
if (inverseSubject && inverseSubject.entity[joinColumn.referencedColumn.propertyName]) {
relationValue = inverseSubject.entity[joinColumn.referencedColumn.propertyName];
}
}
if (relationValue) {
columnNames.push(joinColumn.name);
columnValues.push(relationValue);
columnsAndValuesMap[relation.propertyName] = entity[relation.name]; // todo: check most probably wont work
}
});
});
// add special column and value - date of creation
@ -638,7 +652,8 @@ export class SubjectOperationExecutor {
// todo: since closure tables do not support compose primary keys - throw an exception?
// todo: what if parent entity or parentEntityId is empty?!
const tableName = subject.metadata.closureJunctionTable.table.name;
const referencedColumn = subject.metadata.treeParentRelation.joinColumn.referencedColumn; // todo: check if joinColumn works
const referencedColumn = subject.metadata.treeParentRelation.joinColumns[0].referencedColumn; // todo: check if joinColumn works
// todo: fix joinColumns[0] usage
let newEntityId = subject.entity[referencedColumn.propertyName];
if (!newEntityId && referencedColumn.isGenerated) {
@ -776,7 +791,9 @@ export class SubjectOperationExecutor {
}
const value = relation.getEntityValue(entity);
valueMap.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
relation.joinColumns.forEach(joinColumn => {
valueMap!.values[joinColumn.name] = value !== null && value !== undefined ? value[joinColumn.referencedColumn.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
@ -859,8 +876,10 @@ export class SubjectOperationExecutor {
private async updateRelations(subject: Subject) {
const values: ObjectLiteral = {};
subject.relationUpdates.forEach(setRelation => {
const value = setRelation.value ? setRelation.value[setRelation.relation.joinColumn.referencedColumn.propertyName] : null;
values[setRelation.relation.name] = value; // todo: || fromInsertedSubjects ??
setRelation.relation.joinColumns.forEach(joinColumn => {
const value = setRelation.value ? setRelation.value[joinColumn.referencedColumn.propertyName] : null;
values[joinColumn.name] = value; // todo: || fromInsertedSubjects ??
});
});
const idMap = subject.metadata.getDatabaseEntityIdMap(subject.databaseEntity);
@ -931,7 +950,14 @@ export class SubjectOperationExecutor {
const firstColumn = relation.isOwning ? joinTable.referencedColumn : joinTable.inverseReferencedColumn;
const secondColumn = relation.isOwning ? joinTable.inverseReferencedColumn : joinTable.referencedColumn;
let ownId = relation.getOwnEntityRelationId(subject.entity);
let ownId: any;
if (relation.isManyToManyOwner) {
ownId = subject.entity[relation.joinTable.referencedColumn.propertyName];
} else if (relation.isManyToManyNotOwner) {
ownId = subject.entity[relation.inverseRelation.joinTable.inverseReferencedColumn.propertyName];
}
if (!ownId) {
if (firstColumn.isGenerated) {
ownId = subject.newlyGeneratedId;
@ -1010,7 +1036,15 @@ export class SubjectOperationExecutor {
private async removeJunctions(subject: Subject, junctionRemove: JunctionRemove) {
const junctionMetadata = junctionRemove.relation.junctionEntityMetadata;
const entity = subject.hasEntity ? subject.entity : subject.databaseEntity;
const ownId = junctionRemove.relation.getOwnEntityRelationId(entity);
let ownId: any;
if (junctionRemove.relation.isManyToManyOwner) {
ownId = entity[junctionRemove.relation.joinTable.referencedColumn.propertyName];
} else if (junctionRemove.relation.isManyToManyNotOwner) {
ownId = entity[junctionRemove.relation.inverseRelation.joinTable.inverseReferencedColumn.propertyName];
}
const ownColumn = junctionRemove.relation.isOwning ? junctionMetadata.columns[0] : junctionMetadata.columns[1];
const relateColumn = junctionRemove.relation.isOwning ? junctionMetadata.columns[1] : junctionMetadata.columns[0];
const removePromises = junctionRemove.junctionRelationIds.map(relationId => {

View File

@ -1452,17 +1452,21 @@ export class QueryBuilder<Entity> {
// if real entity relation is involved
if (relation.isManyToOne || relation.isOneToOneOwner) {
const destinationTableReferencedColumn = relation.joinColumn.referencedColumn;
// JOIN `category` `category` ON `category`.`id` = `post`.`categoryId`
const condition = ea(destinationTableAlias) + "." + ec(destinationTableReferencedColumn.fullName) + "=" + ea(parentAlias) + "." + ec(relation.name);
const condition = relation.joinColumns.map(joinColumn => {
return ea(destinationTableAlias) + "." + ec(joinColumn.referencedColumn.fullName) + "=" + ea(parentAlias) + "." + ec(joinColumn.name);
}).join(" AND ");
return " " + joinAttr.direction + " JOIN " + et(destinationTableName) + " " + ea(destinationTableAlias) + " ON " + condition + appendedCondition;
} else if (relation.isOneToMany || relation.isOneToOneNotOwner) {
const relationOwnerReferencedColumn = relation.inverseRelation.joinColumn.referencedColumn;
// JOIN `post` `post` ON `post`.`categoryId` = `category`.`id`
const condition = ea(destinationTableAlias!) + "." + ec(relation.inverseRelation.name) + "=" + ea(parentAlias) + "." + ec(relationOwnerReferencedColumn.fullName);
const condition = relation.inverseRelation.joinColumns.map(joinColumn => {
return ea(destinationTableAlias!) + "." + ec(joinColumn.name) + "=" + ea(parentAlias) + "." + ec(joinColumn.referencedColumn.fullName);
}).join(" AND ");
return " " + joinAttr.direction + " JOIN " + et(destinationTableName) + " " + ea(destinationTableAlias) + " ON " + condition + appendedCondition;
} else { // means many-to-many

View File

@ -29,9 +29,10 @@ export class RelationCountLoader {
// loadRelationCountAndMap("post.categoryCount", "post.categories")
// we expect it to load array of post ids
// todo(dima): fix issues wit multiple primary keys and remove joinColumns[0]
const relation = relationCountAttr.relation; // "category.posts"
const inverseRelation = relation.inverseRelation; // "post.category"
const referenceColumnName = inverseRelation.joinColumn.referencedColumn.propertyName; // post id
const referenceColumnName = inverseRelation.joinColumns[0].referencedColumn.propertyName; // post id
const inverseSideTable = relation.inverseEntityMetadata.table.target; // Post
const inverseSideTableName = relation.inverseEntityMetadata.table.name; // post
const inverseSideTableAlias = relationCountAttr.alias || inverseSideTableName; // if condition (custom query builder factory) is set then relationIdAttr.alias defined

View File

@ -99,7 +99,7 @@ export class RelationIdAttribute {
return parentAlias + "_" + relationProperty + "_relation_id";
}
get referenceColumnName(): string {
/*get referenceColumnName(): string {
if (this.relation.isManyToOne || this.relation.isOneToOneOwner) {
return this.relation.joinColumn.referencedColumn.fullName;
@ -109,7 +109,7 @@ export class RelationIdAttribute {
} else {
return this.relation.isOwning ? this.relation.joinTable.referencedColumn.fullName : this.relation.inverseRelation.joinTable.referencedColumn.fullName;
}
}
}*/
/**
* Metadata of the joined entity.

View File

@ -55,7 +55,7 @@ export class RelationIdLoader {
const relation = relationIdAttr.relation; // "category.posts"
const inverseRelation = relation.inverseRelation; // "post.category"
const referenceColumnName = inverseRelation.joinColumn.referencedColumn.propertyName; // post id
const referenceColumnName = inverseRelation.joinColumns[0].referencedColumn.propertyName; // post id
const inverseSideTable = relation.inverseEntityMetadata.table.target; // Post
const inverseSideTableName = relation.inverseEntityMetadata.table.name; // post
const inverseSideTableAlias = relationIdAttr.alias || inverseSideTableName; // if condition (custom query builder factory) is set then relationIdAttr.alias defined

View File

@ -46,7 +46,9 @@ export class PlainObjectToNewEntityTransformer {
// todo: support custom initial fields here
if (entity[relation.propertyName] instanceof Array) {
const existRelation = entity[relation.propertyName].find((subEntity: any) => {
return subEntity[relation.referencedColumnName] === subObject[relation.referencedColumnName];
return relation.joinColumns.every(joinColumn => {
return subEntity[joinColumn.name] === subObject[joinColumn.name];
});
});
if (existRelation)
this.groupAndTransform(subEntity, existRelation, relationMetadata);

View File

@ -170,8 +170,8 @@ export class RawSqlResultsToEntityTransformer {
} else {
let referenceColumnName: string;
if (relation.isOneToMany || relation.isOneToOneNotOwner) {
referenceColumnName = relation.inverseRelation.joinColumn.referencedColumn.fullName;
if (relation.isOneToMany || relation.isOneToOneNotOwner) { // todo: fix joinColumns[0]
referenceColumnName = relation.inverseRelation.joinColumns[0].referencedColumn.fullName;
} else {
referenceColumnName = relation.isOwning ? relation.joinTable.referencedColumn.fullName : relation.inverseRelation.joinTable.referencedColumn.fullName;
}
@ -202,7 +202,7 @@ export class RawSqlResultsToEntityTransformer {
let referenceColumnName: string;
if (relation.isOneToMany) {
referenceColumnName = relation.inverseRelation.joinColumn.referencedColumn.fullName;
referenceColumnName = relation.inverseRelation.joinColumns[0].referencedColumn.fullName; // todo: fix joinColumns[0]
} else {
referenceColumnName = relation.isOwning ? relation.joinTable.referencedColumn.fullName : relation.inverseRelation.joinTable.referencedColumn.fullName;

View File

@ -9,6 +9,8 @@ import {QueryBuilder} from "../query-builder/QueryBuilder";
/**
* Repository for more specific operations.
*
* @deprecated Don't use it yet
*/
export class SpecificRepository<Entity extends ObjectLiteral> {
@ -55,15 +57,17 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
if (relation.isManyToMany)
throw new Error(`Many-to-many relation is not supported for this operation. Use #addToRelation method for many-to-many relations.`);
// todo: fix issues with joinColumns[0]
let table: string, values: any = {}, conditions: any = {};
if (relation.isOwning) {
table = relation.entityMetadata.table.name;
values[relation.name] = relatedEntityId;
conditions[relation.joinColumn.referencedColumn.fullName] = entityId;
conditions[relation.joinColumns[0].referencedColumn.fullName] = entityId;
} else {
table = relation.inverseEntityMetadata.table.name;
values[relation.inverseRelation.name] = relatedEntityId;
conditions[relation.inverseRelation.joinColumn.referencedColumn.fullName] = entityId;
conditions[relation.inverseRelation.joinColumns[0].referencedColumn.fullName] = entityId;
}
@ -98,6 +102,8 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
if (!this.metadata.hasRelationWithPropertyName(propertyName))
throw new Error(`Relation ${propertyName} was not found in the ${this.metadata.name} entity.`);
// todo: fix issues with joinColumns[0]
const relation = this.metadata.findRelationWithPropertyName(propertyName);
// if (relation.isManyToMany || relation.isOneToMany || relation.isOneToOneNotOwner)
// throw new Error(`Only many-to-one and one-to-one with join column are supported for this operation. ${this.metadata.name}#${propertyName} relation type is ${relation.relationType}`);
@ -108,11 +114,11 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
if (relation.isOwning) {
table = relation.inverseEntityMetadata.table.name;
values[relation.inverseRelation.name] = relatedEntityId;
conditions[relation.inverseRelation.joinColumn.referencedColumn.fullName] = entityId;
conditions[relation.inverseRelation.joinColumns[0].referencedColumn.fullName] = entityId;
} else {
table = relation.entityMetadata.table.name;
values[relation.name] = relatedEntityId;
conditions[relation.joinColumn.referencedColumn.fullName] = entityId;
conditions[relation.joinColumns[0].referencedColumn.fullName] = entityId;
}
const queryRunnerProvider = this.queryRunnerProvider ? this.queryRunnerProvider : new QueryRunnerProvider(this.connection.driver);

View File

@ -9,7 +9,7 @@ import {Tag} from "./entity/Tag";
const should = chai.should();
describe.only("relations > custom-referenced-column-name", () => {
describe("relations > custom-referenced-column-name", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({

View File

@ -8,7 +8,7 @@ import {Category} from "./entity/Category";
const should = chai.should();
describe.skip("relations > multiple-primary-keys", () => {
describe.only("relations > multiple-primary-keys", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
@ -28,7 +28,7 @@ describe.skip("relations > multiple-primary-keys", () => {
const category2 = new Category();
category2.name = "airplanes";
category1.type = "common-category";
category2.type = "common-category";
await connection.entityManager.persist(category2);
const post1 = new Post();
@ -53,6 +53,7 @@ describe.skip("relations > multiple-primary-keys", () => {
let loadedPost = await connection.entityManager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post.category", "category")
.where("post.id = :id", { id: 1 })
.getOne();