removed join table metadata

This commit is contained in:
Zotov Dmitry 2017-04-28 19:12:07 +05:00
parent 569dcf28c5
commit 77b177e0ab
19 changed files with 226 additions and 310 deletions

View File

@ -108,14 +108,14 @@ export class LazyRelationsWrapper {
AND post_categories.categoryId = category.id
*/
joinColumnConditions = relation.joinTable.joinColumns.map(joinColumn => {
return `${joinAlias}.${joinColumn.name} = :${joinColumn.name}`;
joinColumnConditions = relation.joinColumns.map(joinColumn => {
return `${joinAlias}.${joinColumn.propertyName} = :${joinColumn.propertyName}`;
});
inverseJoinColumnConditions = relation.joinTable.inverseJoinColumns.map(inverseJoinColumn => {
return `${joinAlias}.${inverseJoinColumn.name}=${mainAlias}.${inverseJoinColumn.referencedColumn.fullName}`;
inverseJoinColumnConditions = relation.inverseJoinColumns.map(inverseJoinColumn => {
return `${joinAlias}.${inverseJoinColumn.propertyName}=${mainAlias}.${inverseJoinColumn.referencedColumn.propertyName}`;
});
parameters = relation.joinTable.joinColumns.reduce((parameters, joinColumn) => {
parameters[joinColumn.name] = this[joinColumn.referencedColumn.propertyName];
parameters = relation.joinColumns.reduce((parameters, joinColumn) => {
parameters[joinColumn.propertyName] = this[joinColumn.referencedColumn.propertyName];
return parameters;
}, {} as ObjectLiteral);
@ -128,14 +128,14 @@ export class LazyRelationsWrapper {
AND post_categories.categoryId = post_categories.categoryId
*/
joinColumnConditions = relation.inverseRelation.joinTable.joinColumns.map(joinColumn => {
return `${joinAlias}.${joinColumn.name} = ${mainAlias}.${joinColumn.referencedColumn.fullName}`;
joinColumnConditions = relation.inverseRelation.joinColumns.map(joinColumn => {
return `${joinAlias}.${joinColumn.propertyName} = ${mainAlias}.${joinColumn.referencedColumn.propertyName}`;
});
inverseJoinColumnConditions = relation.inverseRelation.joinTable.inverseJoinColumns.map(inverseJoinColumn => {
return `${joinAlias}.${inverseJoinColumn.name} = :${inverseJoinColumn.name}`;
inverseJoinColumnConditions = relation.inverseRelation.inverseJoinColumns.map(inverseJoinColumn => {
return `${joinAlias}.${inverseJoinColumn.propertyName} = :${inverseJoinColumn.propertyName}`;
});
parameters = relation.inverseRelation.joinTable.inverseJoinColumns.reduce((parameters, joinColumn) => {
parameters[joinColumn.name] = this[joinColumn.referencedColumn.propertyName];
parameters = relation.inverseRelation.inverseJoinColumns.reduce((parameters, joinColumn) => {
parameters[joinColumn.propertyName] = this[joinColumn.referencedColumn.propertyName];
return parameters;
}, {} as ObjectLiteral);
}

View File

@ -6,15 +6,11 @@ import {ColumnOptions} from "../decorator/options/ColumnOptions";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {EntityMetadataValidator} from "./EntityMetadataValidator";
import {IndexMetadata} from "../metadata/IndexMetadata";
import {JoinColumnMetadata} from "../metadata/JoinColumnMetadata";
import {TableMetadata} from "../metadata/TableMetadata";
import {RelationMetadata} from "../metadata/RelationMetadata";
import {JoinTableMetadata} from "../metadata/JoinTableMetadata";
import {JunctionEntityMetadataBuilder} from "./JunctionEntityMetadataBuilder";
import {ClosureJunctionEntityMetadataBuilder} from "./ClosureJunctionEntityMetadataBuilder";
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
import {MetadataArgsStorage} from "../metadata-args/MetadataArgsStorage";
import {JoinColumnMetadataArgs} from "../metadata-args/JoinColumnMetadataArgs";
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
import {Driver} from "../driver/Driver";
import {EmbeddedMetadataArgs} from "../metadata-args/EmbeddedMetadataArgs";
@ -174,6 +170,7 @@ export class EntityMetadataBuilder {
"CASCADE"
);
foreignKey.entityMetadata = metadata;
// parentRelationColumn.foreignKey =
metadata.foreignKeys.push(foreignKey);
});
@ -285,36 +282,150 @@ export class EntityMetadataBuilder {
const joinTableMetadataArgs = mergedArgs.joinTables.findByProperty(relation.propertyName);
if (!joinTableMetadataArgs) return;
if (joinTableMetadataArgs) {
const joinTable = new JoinTableMetadata();
joinTable.target = joinTableMetadataArgs.target;
joinTable.propertyName = joinTableMetadataArgs.propertyName;
joinTable.joinColumns = this.createJoinColumns(
joinTableMetadataArgs.joinColumns || [],
relation.entityMetadata.primaryColumnsWithParentIdColumns,
relation.entityMetadata.allColumns,
relation,
(columnName => namingStrategy.joinTableColumnName(relation.entityMetadata.table.nameWithoutPrefix, columnName))
);
joinTable.inverseJoinColumns = this.createJoinColumns(
joinTableMetadataArgs.inverseJoinColumns || [],
relation.inverseEntityMetadata.primaryColumnsWithParentIdColumns,
relation.inverseEntityMetadata.allColumns,
relation,
(columnName => namingStrategy.joinTableColumnName(relation.inverseEntityMetadata.table.nameWithoutPrefix, columnName))
);
joinTable.name = joinTableMetadataArgs.name || relation.entityMetadata.namingStrategy.joinTableName(
const joinTableName = joinTableMetadataArgs.name || relation.entityMetadata.namingStrategy.joinTableName(
relation.entityMetadata.table.nameWithoutPrefix,
relation.inverseEntityMetadata.table.nameWithoutPrefix,
relation.propertyName,
relation.hasInverseSide ? relation.inverseRelation.propertyName : "",
joinTable.joinColumns.map(joinColumn => joinColumn.referencedColumn.name)
relation.hasInverseSide ? relation.inverseRelation.propertyName : ""
);
relation.joinTable = joinTable;
joinTable.relation = relation;
let referencedColumns: ColumnMetadata[];
const hasJoinColumns = !!joinTableMetadataArgs.joinColumns;
const hasAnyReferencedColumnName = hasJoinColumns ? joinTableMetadataArgs.joinColumns!.find(joinColumn => !!joinColumn.referencedColumnName) : false;
if (!hasJoinColumns || (hasJoinColumns && !hasAnyReferencedColumnName)) {
referencedColumns = relation.entityMetadata.primaryColumnsWithParentIdColumns;
} else {
referencedColumns = joinTableMetadataArgs.joinColumns!.map(joinColumn => {
const referencedColumn = relation.entityMetadata.columns.find(column => column.fullName === joinColumn.referencedColumnName);
if (!referencedColumn)
throw new Error(`Referenced column ${joinColumn.referencedColumnName} was not found in entity ${relation.entityMetadata.name}`);
return referencedColumn;
});
}
let inverseReferencedColumns: ColumnMetadata[];
const hasInverseJoinColumns = !!joinTableMetadataArgs.inverseJoinColumns;
const hasAnyInverseReferencedColumnName = hasInverseJoinColumns ? joinTableMetadataArgs.inverseJoinColumns!.find(joinColumn => !!joinColumn.referencedColumnName) : false;
if (!hasInverseJoinColumns || (hasInverseJoinColumns && !hasAnyInverseReferencedColumnName)) {
inverseReferencedColumns = relation.inverseEntityMetadata.primaryColumnsWithParentIdColumns;
} else {
inverseReferencedColumns = joinTableMetadataArgs.inverseJoinColumns!.map(joinColumn => {
const referencedColumn = relation.inverseEntityMetadata.columns.find(column => column.fullName === joinColumn.referencedColumnName);
if (!referencedColumn)
throw new Error(`Referenced column ${joinColumn.referencedColumnName} was not found in entity ${relation.inverseEntityMetadata.name}`);
return referencedColumn;
});
}
// return joinColumnArgsArray.map(joinColumnMetadataArgs => {
// const joinColumn = new JoinColumnMetadata();
// joinColumn.relation = relation;
// joinColumn.target = joinColumnMetadataArgs.target;
// joinColumn.propertyName = joinColumnMetadataArgs.propertyName;
// const referencedColumn = columnMetadatas.find(column => {
// return column.propertyName === joinColumnMetadataArgs.referencedColumnName;
// });
// if (!referencedColumn)
// throw new Error(`Referenced column ${joinColumnMetadataArgs.referencedColumnName} was not found in entity ${relation.inverseEntityMetadata.name}`); // todo: fix ${relation.inverseEntityMetadata.name}
//
// joinColumn.referencedColumn = referencedColumn;
// joinColumn.name = joinColumnMetadataArgs.name || columnNameFactory(joinColumn.referencedColumn.propertyName);
// return joinColumn;
// });
//
// const columns = this.createJoinColumns(
// joinTableMetadataArgs.joinColumns || [],
// relation.entityMetadata.primaryColumnsWithParentIdColumns,
// relation.entityMetadata.allColumns,
// relation,
// (columnName => namingStrategy.joinTableColumnName(relation.entityMetadata.table.nameWithoutPrefix, columnName))
// );
// const inverseJoinColumns = this.createJoinColumns(
// joinTableMetadataArgs.inverseJoinColumns || [],
// relation.inverseEntityMetadata.primaryColumnsWithParentIdColumns,
// relation.inverseEntityMetadata.allColumns,
// relation,
// (columnName => namingStrategy.joinTableColumnName(relation.inverseEntityMetadata.table.nameWithoutPrefix, columnName))
// );
const tableMetadata = new TableMetadata({
target: "",
name: joinTableName,
type: "junction"
});
const junctionColumns = referencedColumns.map(referencedColumn => {
const joinColumn = joinTableMetadataArgs.joinColumns ? joinTableMetadataArgs.joinColumns.find(joinColumnArgs => {
return (!joinColumnArgs.referencedColumnName || joinColumnArgs.referencedColumnName === referencedColumn.propertyName) &&
!!joinColumnArgs.name;
}) : undefined;
const columnName = joinColumn && joinColumn.name ? joinColumn.name : namingStrategy.joinTableColumnName(relation.entityMetadata.table.nameWithoutPrefix, referencedColumn.fullName);
return new ColumnMetadata({
target: "__virtual__",
propertyName: columnName,
mode: "virtual",
options: <ColumnOptions> {
length: referencedColumn.length,
type: referencedColumn.type,
name: columnName,
nullable: false,
primary: true
}
});
});
const inverseJunctionColumns = inverseReferencedColumns.map(inverseReferencedColumn => {
const joinColumn = joinTableMetadataArgs.inverseJoinColumns ? joinTableMetadataArgs.inverseJoinColumns.find(joinColumnArgs => {
return (!joinColumnArgs.referencedColumnName || joinColumnArgs.referencedColumnName === inverseReferencedColumn.propertyName) &&
!!joinColumnArgs.name;
}) : undefined;
const columnName = joinColumn && joinColumn.name ? joinColumn.name : namingStrategy.joinTableColumnName(relation.inverseEntityMetadata.table.nameWithoutPrefix, inverseReferencedColumn.fullName);
return new ColumnMetadata({
target: "__virtual__",
propertyName: columnName,
mode: "virtual",
options: <ColumnOptions> {
length: inverseReferencedColumn.length,
type: inverseReferencedColumn.type,
name: columnName,
nullable: false,
primary: true
}
});
});
const foreignKeys = [
new ForeignKeyMetadata(junctionColumns, relation.entityMetadata.table, referencedColumns),
new ForeignKeyMetadata(inverseJunctionColumns, relation.inverseEntityMetadata.table, inverseReferencedColumns)
];
junctionColumns.concat(inverseJunctionColumns).forEach(column => column.relationMetadata = relation);
const junctionEntityMetadata = new EntityMetadata({
junction: true,
target: "__virtual__",
tablesPrefix: driver.options.tablesPrefix,
namingStrategy: namingStrategy,
tableMetadata: tableMetadata,
columnMetadatas: junctionColumns.concat(inverseJunctionColumns),
foreignKeyMetadatas: foreignKeys,
indexMetadatas: [ // todo: shall we remove indices?
new IndexMetadata({ columns: junctionColumns.map(column => column.fullName), unique: false }),
new IndexMetadata({ columns: inverseJunctionColumns.map(column => column.fullName), unique: false })
]
}, lazyRelationsWrapper);
junctionEntityMetadata.columns.forEach(column => column.entityMetadata = entityMetadata);
relation.foreignKeys = foreignKeys;
relation.junctionEntityMetadata = junctionEntityMetadata;
if (relation.hasInverseSide)
relation.inverseRelation.junctionEntityMetadata = junctionEntityMetadata;
entityMetadatas.push(junctionEntityMetadata);
});
});
@ -404,23 +515,6 @@ export class EntityMetadataBuilder {
entityMetadatas.push(closureJunctionEntityMetadata);
});
// generate junction tables for all many-to-many tables
entityMetadatas.forEach(metadata => {
metadata.ownerManyToManyRelations.forEach(relation => {
const junctionEntityMetadata = getFromContainer(JunctionEntityMetadataBuilder).build(driver, lazyRelationsWrapper, {
namingStrategy: namingStrategy,
firstTable: metadata.table,
secondTable: relation.inverseEntityMetadata.table,
joinTable: relation.joinTable
});
relation.junctionEntityMetadata = junctionEntityMetadata;
if (relation.hasInverseSide)
relation.inverseRelation.junctionEntityMetadata = junctionEntityMetadata;
entityMetadatas.push(junctionEntityMetadata);
});
});
// check for errors in a built metadata schema (we need to check after relationEntityMetadata is set)
getFromContainer(EntityMetadataValidator).validateMany(entityMetadatas);
@ -431,11 +525,11 @@ export class EntityMetadataBuilder {
// Private Methods
// -------------------------------------------------------------------------
private createJoinColumns(joinColumnArgsArray: JoinColumnMetadataArgs[],
/*private createJoinColumns(joinColumnArgsArray: JoinColumnMetadataArgs[],
primaryColumns: ColumnMetadata[],
columnMetadatas: ColumnMetadata[],
relation: RelationMetadata,
columnNameFactory: (columnName: string) => string): JoinColumnMetadata[] {
columnNameFactory: (columnName: string) => string): ColumnMetadata[] {
const hasAnyReferencedColumnName = joinColumnArgsArray.find(joinColumnArgs => !!joinColumnArgs.referencedColumnName);
const createColumns = (joinColumnArgsArray.length === 0 && relation.isManyToOne) ||
@ -472,6 +566,6 @@ export class EntityMetadataBuilder {
joinColumn.name = joinColumnMetadataArgs.name || columnNameFactory(joinColumn.referencedColumn.propertyName);
return joinColumn;
});
}
}*/
}

View File

@ -1,6 +1,3 @@
import {UsingJoinTableIsNotAllowedError} from "./error/UsingJoinTableIsNotAllowedError";
import {UsingJoinTableOnlyOnOneSideAllowedError} from "./error/UsingJoinTableOnlyOnOneSideAllowedError";
import {MissingJoinTableError} from "./error/MissingJoinTableError";
import {EntityMetadata} from "../metadata/EntityMetadata";
import {MissingPrimaryColumnError} from "./error/MissingPrimaryColumnError";
import {CircularRelationsError} from "./error/CircularRelationsError";
@ -61,14 +58,15 @@ export class EntityMetadataValidator {
// check join tables:
// using JoinTable is possible only on one side of the many-to-many relation
if (relation.joinTable) {
if (!relation.isManyToMany)
throw new UsingJoinTableIsNotAllowedError(entityMetadata, relation);
// todo(dima): fix
// if (relation.joinTable) {
// if (!relation.isManyToMany)
// throw new UsingJoinTableIsNotAllowedError(entityMetadata, relation);
// if there is inverse side of the relation, then check if it does not have join table too
if (relation.hasInverseSide && relation.inverseRelation.joinTable)
throw new UsingJoinTableOnlyOnOneSideAllowedError(entityMetadata, relation);
}
// // if there is inverse side of the relation, then check if it does not have join table too
// if (relation.hasInverseSide && relation.inverseRelation.joinTable)
// throw new UsingJoinTableOnlyOnOneSideAllowedError(entityMetadata, relation);
// }
// check join columns:
// using JoinColumn is possible only on one side of the relation and on one-to-one, many-to-one relation types
@ -97,8 +95,9 @@ export class EntityMetadataValidator {
// 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
if (!relation.joinTable && relation.isManyToMany && (!relation.hasInverseSide || !relation.inverseRelation.joinTable))
throw new MissingJoinTableError(entityMetadata, relation);
// todo(dima): fix it
// if (!relation.joinTable && relation.isManyToMany && (!relation.hasInverseSide || !relation.inverseRelation.joinTable))
// throw new MissingJoinTableError(entityMetadata, relation);
// todo: validate if its one-to-one and side which does not have join column MUST have inverse side

View File

@ -1,93 +0,0 @@
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {ColumnOptions} from "../decorator/options/ColumnOptions";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {TableMetadata} from "../metadata/TableMetadata";
import {JoinTableMetadata} from "../metadata/JoinTableMetadata";
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
import {IndexMetadata} from "../metadata/IndexMetadata";
import {EntityMetadata} from "../metadata/EntityMetadata";
import {Driver} from "../driver/Driver";
/**
* Helps to create EntityMetadatas for junction tables.
*/
export interface JunctionEntityMetadataBuilderArgs {
namingStrategy: NamingStrategyInterface;
firstTable: TableMetadata;
secondTable: TableMetadata;
joinTable: JoinTableMetadata;
}
/**
* Helps to create EntityMetadatas for junction tables.
*/
export class JunctionEntityMetadataBuilder {
build(driver: Driver, lazyRelationsWrapper: LazyRelationsWrapper, args: JunctionEntityMetadataBuilderArgs) {
const tableMetadata = new TableMetadata({
target: "",
name: args.joinTable.name,
type: "junction"
});
const junctionColumns = args.joinTable.joinColumns.map(joinColumn => {
return new ColumnMetadata({
target: "__virtual__",
propertyName: joinColumn.name,
mode: "virtual",
options: <ColumnOptions> {
length: joinColumn.referencedColumn.length,
type: joinColumn.referencedColumn.type,
name: joinColumn.name,
nullable: false,
primary: true
}
});
});
const inverseJunctionColumns = args.joinTable.inverseJoinColumns.map(joinColumn => {
return new ColumnMetadata({
target: "__virtual__",
propertyName: joinColumn.name,
mode: "virtual",
options: <ColumnOptions> {
length: joinColumn.referencedColumn.length,
type: joinColumn.referencedColumn.type,
name: joinColumn.name,
nullable: false,
primary: true
}
});
});
const junctionReferencedColumns = args.joinTable.joinColumns.map(joinColumn => joinColumn.referencedColumn);
const inverseJunctionReferencedColumns = args.joinTable.inverseJoinColumns.map(joinColumn => joinColumn.referencedColumn);
const junctionColumnNames = args.joinTable.joinColumns.map(joinColumn => joinColumn.name);
const inverseJunctionColumnNames = args.joinTable.inverseJoinColumns.map(joinColumn => joinColumn.name);
const entityMetadata = new EntityMetadata({
junction: true,
target: "__virtual__",
tablesPrefix: driver.options.tablesPrefix,
namingStrategy: args.namingStrategy,
tableMetadata: tableMetadata,
columnMetadatas: junctionColumns.concat(inverseJunctionColumns),
foreignKeyMetadatas: [
new ForeignKeyMetadata(junctionColumns, args.firstTable, junctionReferencedColumns),
new ForeignKeyMetadata(inverseJunctionColumns, args.secondTable, inverseJunctionReferencedColumns)
],
indexMetadatas: [
new IndexMetadata({ columns: junctionColumnNames, unique: false }),
new IndexMetadata({ columns: inverseJunctionColumnNames, unique: false })
]
}, lazyRelationsWrapper);
entityMetadata.columns.forEach(column => column.entityMetadata = entityMetadata);
return entityMetadata;
}
}

View File

@ -227,7 +227,8 @@ export class ColumnMetadata {
if (this.entityMetadata)
return this.entityMetadata.namingStrategy.columnName(this.propertyName, this._name);
throw new Error(`Column ${this._name ? this._name + " " : ""}is not attached to any entity or embedded.`);
return this._name;
// throw new Error(`Column ${this._name ? this._name + " " : ""}is not attached to any entity or embedded.`);
}
/**
@ -301,7 +302,8 @@ export class ColumnMetadata {
* and this property will contain reference to this column.
*/
get referencedColumn(): ColumnMetadata/*|undefined*/ {
const foreignKey = this.entityMetadata.foreignKeys.find(foreignKey => foreignKey.columns.indexOf(this) !== -1);
const foreignKeys = this.relationMetadata ? this.relationMetadata.foreignKeys : this.entityMetadata.foreignKeys;
const foreignKey = foreignKeys.find(foreignKey => foreignKey.columns.indexOf(this) !== -1);
if (foreignKey) {
const columnIndex = foreignKey.columns.indexOf(this);
return foreignKey.referencedColumns[columnIndex];

View File

@ -1,38 +0,0 @@
import {RelationMetadata} from "./RelationMetadata";
import {ColumnMetadata} from "./ColumnMetadata";
/**
* JoinColumnMetadata contains all information about relation's join column.
*/
export class JoinColumnMetadata {
/**
* Relation - owner of this join column metadata.
*/
relation: RelationMetadata;
/**
* 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 in the database.
*/
name: string;
/**
* Join column referenced column.
*/
referencedColumn: ColumnMetadata;
}

View File

@ -1,43 +0,0 @@
import {RelationMetadata} from "./RelationMetadata";
import {JoinColumnMetadata} from "./JoinColumnMetadata";
/**
* JoinTableMetadata contains all information about relation's join table.
*/
export class JoinTableMetadata {
/**
* Relation - owner of this join table metadata.
*/
relation: RelationMetadata;
/**
* 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 table name.
*/
name: string;
/**
* Join table columns.
*/
joinColumns: JoinColumnMetadata[];
/**
* Inverse side join table columns.
*/
inverseJoinColumns: JoinColumnMetadata[];
}

View File

@ -1,7 +1,6 @@
import {RelationTypes, RelationType} from "./types/RelationTypes";
import {EntityMetadata} from "./EntityMetadata";
import {OnDeleteType, ForeignKeyMetadata} from "./ForeignKeyMetadata";
import {JoinTableMetadata} from "./JoinTableMetadata";
import {RelationMetadataArgs} from "../metadata-args/RelationMetadataArgs";
import {ObjectLiteral} from "../common/ObjectLiteral";
import {ColumnMetadata} from "./ColumnMetadata";
@ -42,11 +41,6 @@ export class RelationMetadata {
*/
junctionEntityMetadata: EntityMetadata;
/**
* Join table metadata.
*/
joinTable: JoinTableMetadata;
foreignKeys: ForeignKeyMetadata[] = [];
// ---------------------------------------------------------------------
@ -194,15 +188,15 @@ export class RelationMetadata {
// if (!this.isOwning || this.isManyToMany)
if (this.isOwning) {
if (this.joinTable) {
return this.joinTable.joinColumns[0].name;
if (this.isManyToMany) {
return this.joinColumns[0].name;
} else if (this.foreignKeys[0] && this.foreignKeys[0].columns) {
return this.foreignKeys[0].columns[0].name;
}
} else if (this.hasInverseSide) {
if (this.inverseRelation.joinTable) {
return this.inverseRelation.joinTable.inverseJoinColumns[0].name;
if (this.inverseRelation.isManyToMany) {
return this.inverseRelation.inverseJoinColumns[0].name;
} else if (this.inverseRelation.foreignKeys[0] && this.inverseRelation.foreignKeys[0].columns && this.inverseRelation.foreignKeys[0].columns[0].referencedColumn) {
return this.inverseRelation.foreignKeys[0].columns[0].referencedColumn.fullName; // todo: [0] is temporary!!
}
@ -225,6 +219,8 @@ export class RelationMetadata {
get joinColumns(): ColumnMetadata[] {
if (!this.isOwning)
throw new Error(`Inverse join columns are only supported from owning side`);
// if (!this.foreignKeys[0])
// return [];
return this.foreignKeys[0].columns;
}
@ -309,7 +305,7 @@ export class RelationMetadata {
*/
get isOwning() {
return !!(this.isManyToOne ||
(this.isManyToMany && this.joinTable) ||
(this.isManyToMany && this.foreignKeys.length > 0) ||
(this.isOneToOne && this.foreignKeys.length > 0));
}

View File

@ -41,9 +41,8 @@ export class DefaultNamingStrategy implements NamingStrategyInterface {
joinTableName(firstTableName: string,
secondTableName: string,
firstPropertyName: string,
secondPropertyName: string,
columns: string[]): string {
return snakeCase(firstTableName + "_" + firstPropertyName + "_" + secondTableName + "_" + columns.join("_"));
secondPropertyName: string): string {
return snakeCase(firstTableName + "_" + firstPropertyName + "_" + secondTableName);
}
joinTableColumnDuplicationPrefix(columnName: string, index: number): string {

View File

@ -45,8 +45,7 @@ export interface NamingStrategyInterface {
joinTableName(firstTableName: string,
secondTableName: string,
firstPropertyName: string,
secondPropertyName: string,
columns: string[]): string;
secondPropertyName: string): string;
/**
* Columns in join tables can have duplicate names in case of self-referencing.

View File

@ -197,6 +197,7 @@ export class Subject {
/**
* Gets entity from the database (e.g. original entity).
* THIS IS NOT RAW ENTITY DATA.
* Throws error if database entity was not set.
*/
get databaseEntity(): ObjectLiteral {

View File

@ -537,18 +537,18 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
const joinAlias = ea("persistenceJoinedRelation");
const joinColumnConditions = relation.joinTable.joinColumns.map(joinColumn => {
return `${joinAlias}.${joinColumn.name} = :${joinColumn.name}`;
const joinColumnConditions = relation.joinColumns.map(joinColumn => {
return `${joinAlias}.${joinColumn.propertyName} = :${joinColumn.propertyName}`;
});
const inverseJoinColumnConditions = relation.joinTable.inverseJoinColumns.map(inverseJoinColumn => {
return `${joinAlias}.${inverseJoinColumn.name} = ${ea(qbAlias)}.${ec(inverseJoinColumn.referencedColumn.fullName)}`;
const inverseJoinColumnConditions = relation.inverseJoinColumns.map(inverseJoinColumn => {
return `${joinAlias}.${inverseJoinColumn.propertyName} = ${ea(qbAlias)}.${ec(inverseJoinColumn.referencedColumn.propertyName)}`;
});
const conditions = joinColumnConditions.concat(inverseJoinColumnConditions).join(" AND ");
// (example) returns us referenced column (detail's id)
const parameters = relation.joinTable.joinColumns.reduce((parameters, joinColumn) => {
parameters[joinColumn.name] = subject.databaseEntity[joinColumn.referencedColumn.propertyName];
const parameters = relation.joinColumns.reduce((parameters, joinColumn) => {
parameters[joinColumn.propertyName] = subject.databaseEntity[joinColumn.referencedColumn.propertyName];
return parameters;
}, {} as ObjectLiteral);
@ -568,18 +568,18 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
const joinAlias = ea("persistenceJoinedRelation");
const joinColumnConditions = relation.joinTable.joinColumns.map(joinColumn => {
return `${joinAlias}.${joinColumn.name} = ${ea(qbAlias)}.${ec(joinColumn.referencedColumn.fullName)}`;
const joinColumnConditions = relation.joinColumns.map(joinColumn => {
return `${joinAlias}.${joinColumn.propertyName} = ${ea(qbAlias)}.${ec(joinColumn.referencedColumn.propertyName)}`;
});
const inverseJoinColumnConditions = relation.joinTable.inverseJoinColumns.map(inverseJoinColumn => {
return `${joinAlias}.${inverseJoinColumn.name} = :${inverseJoinColumn.name}`;
const inverseJoinColumnConditions = relation.inverseJoinColumns.map(inverseJoinColumn => {
return `${joinAlias}.${inverseJoinColumn.propertyName} = :${inverseJoinColumn.propertyName}`;
});
const conditions = joinColumnConditions.concat(inverseJoinColumnConditions).join(" AND ");
// (example) returns us referenced column (detail's id)
const parameters = relation.inverseRelation.joinTable.inverseJoinColumns.reduce((parameters, joinColumn) => {
parameters[joinColumn.name] = subject.databaseEntity[joinColumn.referencedColumn.propertyName];
const parameters = relation.inverseRelation.inverseJoinColumns.reduce((parameters, joinColumn) => {
parameters[joinColumn.propertyName] = subject.databaseEntity[joinColumn.referencedColumn.propertyName];
return parameters;
}, {} as ObjectLiteral);
@ -775,7 +775,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
// get all inverse entities relation ids that are "bind" to the currently persisted entity
const changedInverseEntityRelationIds = relatedValue
.map(subRelationValue => {
const joinColumns = relation.isOwning ? relation.joinTable.inverseJoinColumns : relation.inverseRelation.joinTable.joinColumns;
const joinColumns = relation.isOwning ? relation.inverseJoinColumns : relation.inverseRelation.joinColumns;
return joinColumns.reduce((ids, joinColumn) => {
ids[joinColumn.referencedColumn.propertyName] = subRelationValue[joinColumn.referencedColumn.propertyName];
return ids;
@ -795,7 +795,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
// now from all entities in the persisted entity find only those which aren't found in the db
const newJunctionEntities = relatedValue.filter(subRelatedValue => {
const joinColumns = relation.isOwning ? relation.joinTable.inverseJoinColumns : relation.inverseRelation.joinTable.joinColumns;
const joinColumns = relation.isOwning ? relation.inverseJoinColumns : relation.inverseRelation.joinColumns;
const ids = joinColumns.reduce((ids, joinColumn) => {
ids[joinColumn.referencedColumn.propertyName] = subRelatedValue[joinColumn.referencedColumn.propertyName];
return ids;

View File

@ -9,7 +9,7 @@ import {EntityManager} from "../entity-manager/EntityManager";
import {PromiseUtils} from "../util/PromiseUtils";
import {MongoDriver} from "../driver/mongodb/MongoDriver";
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
import {JoinColumnMetadata} from "../metadata/JoinColumnMetadata";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
/**
* Executes all database operations (inserts, updated, deletes) that must be executed
@ -946,7 +946,7 @@ export class SubjectOperationExecutor {
private async insertJunctions(subject: Subject, junctionInsert: JunctionInsert): Promise<void> {
// I think here we can only support to work only with single primary key entities
const getRelationId = (entity: ObjectLiteral, joinColumns: JoinColumnMetadata[]): any[] => {
const getRelationId = (entity: ObjectLiteral, joinColumns: ColumnMetadata[]): any[] => {
return joinColumns.map(joinColumn => {
const id = entity[joinColumn.referencedColumn.propertyName];
if (!id && joinColumn.referencedColumn.isGenerated) {
@ -966,7 +966,7 @@ export class SubjectOperationExecutor {
};
const relation = junctionInsert.relation;
const joinColumns = relation.isManyToManyOwner ? relation.joinTable.joinColumns : relation.inverseRelation.joinTable.inverseJoinColumns;
const joinColumns = relation.isManyToManyOwner ? relation.joinColumns : relation.inverseRelation.inverseJoinColumns;
const ownId = getRelationId(subject.entity, joinColumns);
if (!ownId.length)
@ -975,7 +975,7 @@ export class SubjectOperationExecutor {
const promises = junctionInsert.junctionEntities.map(newBindEntity => {
// get relation id from the newly bind entity
const joinColumns = relation.isManyToManyOwner ? relation.joinTable.inverseJoinColumns : relation.inverseRelation.joinTable.joinColumns;
const joinColumns = relation.isManyToManyOwner ? relation.inverseJoinColumns : relation.inverseRelation.joinColumns;
const relationId = getRelationId(newBindEntity, joinColumns);
// if relation id still does not exist - we arise an error
@ -1016,18 +1016,18 @@ export class SubjectOperationExecutor {
const junctionMetadata = junctionRemove.relation.junctionEntityMetadata;
const entity = subject.hasEntity ? subject.entity : subject.databaseEntity;
const firstJoinColumns = junctionRemove.relation.isOwning ? junctionRemove.relation.joinTable.joinColumns : junctionRemove.relation.joinTable.inverseJoinColumns;
const secondJoinColumns = junctionRemove.relation.isOwning ? junctionRemove.relation.joinTable.inverseJoinColumns : junctionRemove.relation.joinTable.joinColumns;
const firstJoinColumns = junctionRemove.relation.isOwning ? junctionRemove.relation.joinColumns : junctionRemove.relation.inverseJoinColumns;
const secondJoinColumns = junctionRemove.relation.isOwning ? junctionRemove.relation.inverseJoinColumns : junctionRemove.relation.joinColumns;
let conditions: ObjectLiteral = {};
firstJoinColumns.forEach(joinColumn => {
conditions[joinColumn.name] = entity[joinColumn.referencedColumn.propertyName];
conditions[joinColumn.fullName] = entity[joinColumn.referencedColumn.propertyName];
});
const removePromises = junctionRemove.junctionRelationIds.map(relationIds => {
let inverseConditions: ObjectLiteral = {};
Object.keys(relationIds).forEach(key => {
const joinColumn = secondJoinColumns.find(column => column.referencedColumn.propertyName === key);
inverseConditions[joinColumn!.name] = entity[joinColumn!.referencedColumn.propertyName];
inverseConditions[joinColumn!.fullName] = entity[joinColumn!.referencedColumn.propertyName];
});
return this.queryRunner.delete(junctionMetadata.table.name, Object.assign({}, inverseConditions, conditions));
});

View File

@ -1477,25 +1477,25 @@ export class QueryBuilder<Entity> {
if (relation.isOwning) {
junctionCondition = relation.joinTable.joinColumns.map(joinColumn => {
junctionCondition = relation.joinColumns.map(joinColumn => {
// `post_category`.`postId` = `post`.`id`
return ea(junctionAlias) + "." + ec(joinColumn.name) + "=" + ea(parentAlias) + "." + ec(joinColumn.referencedColumn.fullName);
return ea(junctionAlias) + "." + ec(joinColumn.propertyName) + "=" + ea(parentAlias) + "." + ec(joinColumn.referencedColumn.fullName);
}).join(" AND ");
destinationCondition = relation.joinTable.inverseJoinColumns.map(joinColumn => {
destinationCondition = relation.inverseJoinColumns.map(joinColumn => {
// `category`.`id` = `post_category`.`categoryId`
return ea(destinationTableAlias) + "." + ec(joinColumn.referencedColumn.fullName) + "=" + ea(junctionAlias) + "." + ec(joinColumn.name);
return ea(destinationTableAlias) + "." + ec(joinColumn.referencedColumn.fullName) + "=" + ea(junctionAlias) + "." + ec(joinColumn.propertyName);
}).join(" AND ");
} else {
junctionCondition = relation.inverseRelation.joinTable.inverseJoinColumns.map(joinColumn => {
junctionCondition = relation.inverseRelation.inverseJoinColumns.map(joinColumn => {
// `post_category`.`categoryId` = `category`.`id`
return ea(junctionAlias) + "." + ec(joinColumn.name) + "=" + ea(parentAlias) + "." + ec(joinColumn.referencedColumn.fullName);
return ea(junctionAlias) + "." + ec(joinColumn.propertyName) + "=" + ea(parentAlias) + "." + ec(joinColumn.referencedColumn.fullName);
}).join(" AND ");
destinationCondition = relation.inverseRelation.joinTable.joinColumns.map(joinColumn => {
destinationCondition = relation.inverseRelation.joinColumns.map(joinColumn => {
// `post`.`id` = `post_category`.`postId`
return ea(destinationTableAlias) + "." + ec(joinColumn.referencedColumn.fullName) + "=" + ea(junctionAlias) + "." + ec(joinColumn.name);
return ea(destinationTableAlias) + "." + ec(joinColumn.referencedColumn.fullName) + "=" + ea(junctionAlias) + "." + ec(joinColumn.propertyName);
}).join(" AND ");
}

View File

@ -78,14 +78,14 @@ export class RelationCountLoader {
let secondJunctionColumn: ColumnMetadata;
if (relationCountAttr.relation.isOwning) { // todo fix joinColumns[0] and inverseJoinColumns[0].
joinTableColumnName = relationCountAttr.relation.joinTable.joinColumns[0].referencedColumn.fullName;
inverseJoinColumnName = relationCountAttr.relation.joinTable.inverseJoinColumns[0].referencedColumn.fullName;
joinTableColumnName = relationCountAttr.relation.joinColumns[0].referencedColumn.fullName;
inverseJoinColumnName = relationCountAttr.relation.inverseJoinColumns[0].referencedColumn.fullName;
firstJunctionColumn = relationCountAttr.relation.junctionEntityMetadata.columnsWithoutEmbeddeds[0];
secondJunctionColumn = relationCountAttr.relation.junctionEntityMetadata.columnsWithoutEmbeddeds[1];
} else {
joinTableColumnName = relationCountAttr.relation.inverseRelation.joinTable.inverseJoinColumns[0].referencedColumn.fullName;
inverseJoinColumnName = relationCountAttr.relation.inverseRelation.joinTable.joinColumns[0].referencedColumn.fullName;
joinTableColumnName = relationCountAttr.relation.inverseRelation.inverseJoinColumns[0].referencedColumn.fullName;
inverseJoinColumnName = relationCountAttr.relation.inverseRelation.joinColumns[0].referencedColumn.fullName;
firstJunctionColumn = relationCountAttr.relation.junctionEntityMetadata.columnsWithoutEmbeddeds[1];
secondJunctionColumn = relationCountAttr.relation.junctionEntityMetadata.columnsWithoutEmbeddeds[0];
}

View File

@ -140,14 +140,14 @@ export class RelationIdLoader {
let secondJunctionColumn: ColumnMetadata;
if (relationIdAttr.relation.isOwning) { // todo fix joinColumns[0]
joinTableColumnName = relationIdAttr.relation.joinTable.joinColumns[0].referencedColumn.fullName;
inverseJoinColumnName = relationIdAttr.relation.joinTable.joinColumns[0].referencedColumn.fullName;
joinTableColumnName = relationIdAttr.relation.joinColumns[0].referencedColumn.fullName;
inverseJoinColumnName = relationIdAttr.relation.joinColumns[0].referencedColumn.fullName;
firstJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columnsWithoutEmbeddeds[0];
secondJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columnsWithoutEmbeddeds[1];
} else {
joinTableColumnName = relationIdAttr.relation.inverseRelation.joinTable.joinColumns[0].referencedColumn.fullName;
inverseJoinColumnName = relationIdAttr.relation.inverseRelation.joinTable.joinColumns[0].referencedColumn.fullName;
joinTableColumnName = relationIdAttr.relation.inverseRelation.joinColumns[0].referencedColumn.fullName;
inverseJoinColumnName = relationIdAttr.relation.inverseRelation.joinColumns[0].referencedColumn.fullName;
firstJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columnsWithoutEmbeddeds[1];
secondJunctionColumn = relationIdAttr.relation.junctionEntityMetadata.columnsWithoutEmbeddeds[0];
}

View File

@ -173,7 +173,7 @@ export class RawSqlResultsToEntityTransformer {
if (relation.isOneToMany || relation.isOneToOneNotOwner) { // todo: fix joinColumns[0]
referenceColumnName = relation.inverseRelation.joinColumns[0].referencedColumn.fullName;
} else {
referenceColumnName = relation.isOwning ? relation.joinTable.joinColumns[0].referencedColumn.fullName : relation.inverseRelation.joinTable.joinColumns[0].referencedColumn.fullName;
referenceColumnName = relation.isOwning ? relation.joinColumns[0].referencedColumn.fullName : relation.inverseRelation.joinColumns[0].referencedColumn.fullName;
}
referenceColumnValue = rawSqlResults[0][alias.name + "_" + referenceColumnName];
if (referenceColumnValue === undefined || referenceColumnValue === null)
@ -205,7 +205,7 @@ export class RawSqlResultsToEntityTransformer {
referenceColumnName = relation.inverseRelation.joinColumns[0].referencedColumn.fullName; // todo: fix joinColumns[0]
} else {
referenceColumnName = relation.isOwning ? relation.joinTable.joinColumns[0].referencedColumn.fullName : relation.inverseRelation.joinTable.joinColumns[0].referencedColumn.fullName;
referenceColumnName = relation.isOwning ? relation.joinColumns[0].referencedColumn.fullName : relation.inverseRelation.joinColumns[0].referencedColumn.fullName;
}
const referenceColumnValue = rawSqlResults[0][alias.name + "_" + referenceColumnName]; // we use zero index since its grouped data // todo: selection with alias for entity columns wont work

View File

@ -439,10 +439,10 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
const relation = this.convertMixedRelationToMetadata(relationOrName);
if (!(entityOrEntities instanceof Array)) entityOrEntities = [entityOrEntities];
// todo fix joinColumns[0]
const entityReferencedColumns = relation.isOwning ? relation.joinTable.joinColumns.map(joinColumn => joinColumn.referencedColumn) : relation.inverseRelation.joinTable.inverseJoinColumns.map(joinColumn => joinColumn.referencedColumn);
const ownerEntityColumns = relation.isOwning ? relation.joinTable.joinColumns : relation.inverseRelation.joinTable.inverseJoinColumns;
const inverseEntityColumns = relation.isOwning ? relation.joinTable.inverseJoinColumns : relation.inverseRelation.joinTable.joinColumns;
const inverseEntityColumnNames = relation.isOwning ? relation.joinTable.inverseJoinColumns.map(joinColumn => joinColumn.name) : relation.inverseRelation.joinTable.joinColumns.map(joinColumn => joinColumn.name);
const entityReferencedColumns = relation.isOwning ? relation.joinColumns.map(joinColumn => joinColumn.referencedColumn) : relation.inverseRelation.inverseJoinColumns.map(joinColumn => joinColumn.referencedColumn);
const ownerEntityColumns = relation.isOwning ? relation.joinColumns : relation.inverseRelation.inverseJoinColumns;
const inverseEntityColumns = relation.isOwning ? relation.inverseJoinColumns : relation.inverseRelation.joinColumns;
const inverseEntityColumnNames = relation.isOwning ? relation.inverseJoinColumns.map(joinColumn => joinColumn.name) : relation.inverseRelation.joinColumns.map(joinColumn => joinColumn.name);
let entityIds = this.convertEntityOrEntitiesToIdOrIds(entityReferencedColumns, entityOrEntities);
if (!(entityIds instanceof Array)) entityIds = [entityIds];

View File

@ -177,7 +177,7 @@ describe("query builder > joins", () => {
const loadedRawPost = await connection.entityManager
.createQueryBuilder(Post, "post")
.leftJoinAndSelect("post_categories_category_id", "categoriesJunction", "categoriesJunction.postId = post.id")
.leftJoinAndSelect("post_categories_category", "categoriesJunction", "categoriesJunction.postId = post.id")
.leftJoinAndSelect(Category, "categories", "categories.id = categoriesJunction.categoryId")
.where("post.id = :id", { id: post.id })
.getRawOne();