refactoring entity metadata stuff

This commit is contained in:
Umed Khudoiberdiev 2017-05-15 16:35:45 +05:00
parent f0e47cb17f
commit 311773b942
11 changed files with 530 additions and 415 deletions

View File

@ -16,7 +16,7 @@ import {InheritanceMetadataArgs} from "./InheritanceMetadataArgs";
import {DiscriminatorValueMetadataArgs} from "./DiscriminatorValueMetadataArgs";
import {EntityRepositoryMetadataArgs} from "./EntityRepositoryMetadataArgs";
import {TransactionEntityMetadataArgs} from "./TransactionEntityMetadataArgs";
import {MetadataArgsUtils} from "./MetadataArgsUtils";
import {MetadataUtils} from "./MetadataUtils";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
/**

View File

@ -1,7 +1,7 @@
/**
* Metadata args utility functions.
*/
export class MetadataArgsUtils {
export class MetadataUtils {
/**
* Gets given's entity all inherited classes.

View File

@ -1,7 +1,6 @@
import {EntityMetadata} from "../metadata/EntityMetadata";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {IndexMetadata} from "../metadata/IndexMetadata";
import {RelationMetadata} from "../metadata/RelationMetadata";
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
@ -11,8 +10,11 @@ import {Driver} from "../driver/Driver";
import {EmbeddedMetadataArgs} from "../metadata-args/EmbeddedMetadataArgs";
import {RelationIdMetadata} from "../metadata/RelationIdMetadata";
import {RelationCountMetadata} from "../metadata/RelationCountMetadata";
import {ColumnTypes} from "../metadata/types/ColumnTypes";
import {MetadataArgsUtils} from "../metadata-args/MetadataArgsUtils";
import {MetadataUtils} from "../metadata-args/MetadataUtils";
import {TableMetadataArgs} from "../metadata-args/TableMetadataArgs";
import {JunctionEntityMetadataBuilder} from "./JunctionEntityMetadataBuilder";
import {ClosureJunctionEntityMetadataBuilder} from "./ClosureJunctionEntityMetadataBuilder";
import {RelationJoinColumnBuilder} from "./RelationJoinColumnBuilder";
/**
* Aggregates all metadata: table, column, relation into one collection grouped by tables for a given set of classes.
@ -26,10 +28,25 @@ export class AllEntityMetadataBuilder {
// todo: tree decorators can be used only on closure table (validation)
// todo: throw error if parent tree metadata was not specified in a closure table
// -------------------------------------------------------------------------
// Protected Properties
// -------------------------------------------------------------------------
protected junctionEntityMetadataBuilder: JunctionEntityMetadataBuilder;
protected closureJunctionEntityMetadataBuilder: ClosureJunctionEntityMetadataBuilder;
protected relationJoinColumnBuilder: RelationJoinColumnBuilder;
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(private driver: Driver,
private lazyRelationsWrapper: LazyRelationsWrapper,
private metadataArgsStorage: MetadataArgsStorage,
private namingStrategy: NamingStrategyInterface) {
this.junctionEntityMetadataBuilder = new JunctionEntityMetadataBuilder(driver, lazyRelationsWrapper, namingStrategy);
this.closureJunctionEntityMetadataBuilder = new ClosureJunctionEntityMetadataBuilder(driver, lazyRelationsWrapper, namingStrategy);
this.relationJoinColumnBuilder = new RelationJoinColumnBuilder(namingStrategy);
}
// -------------------------------------------------------------------------
@ -37,101 +54,77 @@ export class AllEntityMetadataBuilder {
// -------------------------------------------------------------------------
/**
* Builds a complete metadata aggregations for the given entity classes.
* Builds a complete entity metadatas for the given entity classes.
*/
build(entityClasses?: Function[]): EntityMetadata[] {
// if entity classes to filter entities by are given then do filtering, otherwise use all
const allTables = entityClasses ? this.metadataArgsStorage.filterTables(entityClasses) : this.metadataArgsStorage.tables.toArray();
// filter out table metadata args for those we really create entity metadatas and tables in the db
const realTables = allTables.filter(table => table.type === "regular" || table.type === "closure" || table.type === "class-table-child");
const entityMetadatas: EntityMetadata[] = realTables.map(tableArgs => {
const inheritanceTree = tableArgs.target instanceof Function ? MetadataArgsUtils.getInheritanceTree(tableArgs.target) : [tableArgs.target]; // todo: implement later here inheritance for string-targets
// collect all table embeddeds
const entityMetadata = new EntityMetadata({
namingStrategy: this.namingStrategy,
lazyRelationsWrapper: this.lazyRelationsWrapper,
tablesPrefix: this.driver.options.tablesPrefix,
args: tableArgs
});
entityMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(inheritanceTree));
entityMetadata.ownColumns = this.metadataArgsStorage.filterColumns(inheritanceTree).map(args => {
return new ColumnMetadata({ entityMetadata, args });
});
entityMetadata.ownRelations = this.metadataArgsStorage.filterRelations(inheritanceTree).map(args => {
return new RelationMetadata({ entityMetadata, args });
});
entityMetadata.relationIds = this.metadataArgsStorage.filterRelationIds(inheritanceTree).map(args => {
return new RelationIdMetadata({ entityMetadata, args });
});
entityMetadata.relationCounts = this.metadataArgsStorage.filterRelationCounts(inheritanceTree).map(args => {
return new RelationCountMetadata({ entityMetadata, args });
});
entityMetadata.indices = this.metadataArgsStorage.filterIndices(inheritanceTree).map(args => {
return new IndexMetadata({ entityMetadata, args });
});
return entityMetadata;
});
// create entity metadatas for a user defined entities (marked with @Entity decorator or loaded from entity schemas)
const entityMetadatas = realTables.map(tableArgs => this.createEntityMetadata(tableArgs));
// calculate entity metadata computed properties and all its sub-metadatas
entityMetadatas.forEach(entityMetadata => this.buildEntityMetadata(entityMetadata));
entityMetadatas.forEach(entityMetadata => this.computeEntityMetadata(entityMetadata));
// calculate entity metadata computed properties and all its sub-metadatas
entityMetadatas.forEach(entityMetadata => this.buildInverseProperties(entityMetadata, entityMetadatas));
// calculate entity metadata's inverse properties
entityMetadatas.forEach(entityMetadata => this.computeInverseProperties(entityMetadata, entityMetadatas));
// go through all entity metadatas and create foreign keys / junction entity metadatas for their relations
entityMetadatas.forEach(entityMetadata => {
// create entity's relations join columns
// create entity's relations join columns (for many-to-one and one-to-one owner)
entityMetadata.relations.filter(relation => relation.isOneToOne || relation.isManyToOne).forEach(relation => {
const foreignKey = this.createJoinColumnRelationForeignKey(relation);
if (!foreignKey) return; // this case is possible only for one-to-one non owning side
foreignKey.build(this.namingStrategy);
relation.foreignKeys.push(foreignKey);
entityMetadata.foreignKeys.push(foreignKey);
relation.buildOnForeignKeysChange();
const joinColumns = this.metadataArgsStorage.filterJoinColumns(relation.target, relation.propertyName);
const foreignKey = this.relationJoinColumnBuilder.build(joinColumns, relation); // create a foreign key based on its metadata args
if (foreignKey) {
relation.registerForeignKeys(foreignKey); // push it to the relation and thus register there a join column
entityMetadata.foreignKeys.push(foreignKey);
}
});
// create junction entity metadatas for entity many-to-many relations
entityMetadata.relations.filter(relation => relation.isManyToMany).forEach(relation => {
const joinTableMetadataArgs = this.metadataArgsStorage.findJoinTable(relation.target, relation.propertyName);
if (!joinTableMetadataArgs) return; // no join table set - no need to do anything
const joinTable = this.metadataArgsStorage.findJoinTable(relation.target, relation.propertyName);
if (!joinTable) return; // no join table set - no need to do anything (it means this is many-to-many inverse side)
const referencedColumns = this.collectRelationReferencedColumns(relation);
const inverseReferencedColumns = this.collectRelationInverseReferencedColumns(relation);
const junctionEntityMetadata = this.createJunctionEntityMetadata(relation, referencedColumns, inverseReferencedColumns);
relation.foreignKeys = junctionEntityMetadata.foreignKeys;
relation.buildOnForeignKeysChange();
// here we create a junction entity metadata for a new junction table of many-to-many relation
const junctionEntityMetadata = this.junctionEntityMetadataBuilder.build(relation, joinTable);
relation.registerForeignKeys(...junctionEntityMetadata.foreignKeys);
relation.junctionEntityMetadata = junctionEntityMetadata;
if (relation.hasInverseSide)
if (relation.inverseRelation)
relation.inverseRelation.junctionEntityMetadata = junctionEntityMetadata;
// compute new entity metadata properties and push it to entity metadatas pool
this.computeEntityMetadata(junctionEntityMetadata);
this.computeInverseProperties(junctionEntityMetadata, entityMetadatas);
entityMetadatas.push(junctionEntityMetadata);
this.buildEntityMetadata(junctionEntityMetadata);
this.buildInverseProperties(junctionEntityMetadata, entityMetadatas);
});
// update entity metadata depend properties
entityMetadata.relationsWithJoinColumns = entityMetadata.relations.filter(relation => relation.isWithJoinColumn);
entityMetadata.hasNonNullableRelations = entityMetadata.relationsWithJoinColumns.some(relation => !relation.isNullable || relation.isPrimary);
});
// generate closure junction tables for all closure tables
entityMetadatas
.filter(metadata => metadata.isClosure)
.forEach(entityMetadata => {
const closureJunctionEntityMetadata = this.closureJunctionEntityMetadataBuilder.build(entityMetadata);
entityMetadata.closureJunctionTable = closureJunctionEntityMetadata;
this.computeEntityMetadata(closureJunctionEntityMetadata);
this.computeInverseProperties(closureJunctionEntityMetadata, entityMetadatas);
entityMetadatas.push(closureJunctionEntityMetadata);
});
// generate keys for tables with single-table inheritance
entityMetadatas
.filter(metadata => metadata.inheritanceType === "single-table")
.forEach(entityMetadata => this.createKeysForTableInheritance(entityMetadata));
// generate junction tables for all closure tables
entityMetadatas
.filter(metadata => metadata.isClosure)
.forEach(entityMetadata => {
const closureJunctionEntityMetadata = this.createClosureJunctionEntityMetadata(entityMetadata);
entityMetadata.closureJunctionTable = closureJunctionEntityMetadata;
this.buildEntityMetadata(closureJunctionEntityMetadata);
this.buildInverseProperties(closureJunctionEntityMetadata, entityMetadatas);
entityMetadatas.push(closureJunctionEntityMetadata);
});
// add lazy initializer for entity relations
entityMetadatas
.filter(metadata => metadata.target instanceof Function)
@ -143,11 +136,6 @@ export class AllEntityMetadataBuilder {
});
});
entityMetadatas.forEach(metadata => {
metadata.relations.forEach(relation => relation.buildOnForeignKeysChange());
metadata.buildOnRelationsChange();
});
return entityMetadatas;
}
@ -155,188 +143,49 @@ export class AllEntityMetadataBuilder {
// Protected Methods
// -------------------------------------------------------------------------
protected createJunctionEntityMetadata(relation: RelationMetadata, referencedColumns: ColumnMetadata[], inverseReferencedColumns: ColumnMetadata[]) {
const joinTableMetadataArgs = this.metadataArgsStorage.findJoinTable(relation.target, relation.propertyName)!;
/**
* Creates entity metadata from the given table args.
* Creates column, relation, etc. metadatas for everything this entity metadata owns.
*/
protected createEntityMetadata(tableArgs: TableMetadataArgs): EntityMetadata {
const joinTableName = joinTableMetadataArgs.name || this.namingStrategy.joinTableName(
relation.entityMetadata.tableNameWithoutPrefix,
relation.inverseEntityMetadata.tableNameWithoutPrefix,
relation.propertyPath,
relation.hasInverseSide ? relation.inverseRelation.propertyName : ""
);
// we take all "inheritance tree" from a target entity to collect all stored metadata args
// (by decorators or inside entity schemas). For example for target Post < ContentModel < Unit
// it will be an array of [Post, ContentModel, Unit] and we can then get all metadata args of those classes
const inheritanceTree = tableArgs.target instanceof Function
? MetadataUtils.getInheritanceTree(tableArgs.target)
: [tableArgs.target]; // todo: implement later here inheritance for string-targets
const junctionEntityMetadata = new EntityMetadata({
lazyRelationsWrapper: this.lazyRelationsWrapper,
namingStrategy: this.namingStrategy,
tablesPrefix: this.driver.options.tablesPrefix,
args: {
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 : this.namingStrategy.joinTableColumnName(relation.entityMetadata.tableNameWithoutPrefix, referencedColumn.propertyName, referencedColumn.givenDatabaseName);
return new ColumnMetadata({
entityMetadata: junctionEntityMetadata,
referencedColumn: referencedColumn,
args: {
target: "",
mode: "virtual",
propertyName: columnName,
options: {
name: columnName,
length: referencedColumn.length,
type: referencedColumn.type,
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 : this.namingStrategy.joinTableColumnName(relation.inverseEntityMetadata.tableNameWithoutPrefix, inverseReferencedColumn.propertyName, inverseReferencedColumn.givenDatabaseName);
return new ColumnMetadata({
entityMetadata: junctionEntityMetadata,
referencedColumn: inverseReferencedColumn,
args: {
target: "",
mode: "virtual",
propertyName: columnName,
options: {
length: inverseReferencedColumn.length,
type: inverseReferencedColumn.type,
name: columnName,
nullable: false,
primary: true,
}
}
});
});
junctionEntityMetadata.foreignKeys = [
new ForeignKeyMetadata({
entityMetadata: junctionEntityMetadata,
referencedEntityMetadata: relation.entityMetadata,
columns: junctionColumns,
referencedColumns: referencedColumns
}),
new ForeignKeyMetadata({
entityMetadata: junctionEntityMetadata,
referencedEntityMetadata: relation.inverseEntityMetadata,
columns: inverseJunctionColumns,
referencedColumns: inverseReferencedColumns
}),
];
junctionColumns.concat(inverseJunctionColumns).forEach(column => column.relationMetadata = relation);
junctionEntityMetadata.ownColumns = junctionColumns.concat(inverseJunctionColumns);
junctionEntityMetadata.indices = [
new IndexMetadata({
entityMetadata: junctionEntityMetadata,
columns: junctionColumns,
args: {
target: "",
unique: false
}
}),
new IndexMetadata({
entityMetadata: junctionEntityMetadata,
columns: inverseJunctionColumns,
args: {
target: "",
unique: false
}
})
];
return junctionEntityMetadata;
}
protected createClosureJunctionEntityMetadata(parentClosureEntityMetadata: EntityMetadata): EntityMetadata {
const entityMetadata = new EntityMetadata({
parentClosureEntityMetadata: parentClosureEntityMetadata,
lazyRelationsWrapper: this.lazyRelationsWrapper,
namingStrategy: this.namingStrategy,
lazyRelationsWrapper: this.lazyRelationsWrapper,
tablesPrefix: this.driver.options.tablesPrefix,
args: {
target: "",
name: parentClosureEntityMetadata.tableNameWithoutPrefix,
type: "closure-junction"
}
args: tableArgs
});
entityMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(inheritanceTree));
parentClosureEntityMetadata.primaryColumns.forEach(primaryColumn => {
entityMetadata.ownColumns.push(new ColumnMetadata({
entityMetadata: entityMetadata,
args: {
target: "",
mode: "virtual",
propertyName: "ancestor_" + primaryColumn.databaseName, // todo: naming strategy
options: {
length: primaryColumn.length,
type: primaryColumn.type,
}
}
}));
entityMetadata.ownColumns.push(new ColumnMetadata({
entityMetadata: entityMetadata,
args: {
target: "",
mode: "virtual",
propertyName: "descendant_" + primaryColumn.databaseName,
options: {
length: primaryColumn.length,
type: primaryColumn.type,
}
}
}));
entityMetadata.ownColumns = this.metadataArgsStorage.filterColumns(inheritanceTree).map(args => {
return new ColumnMetadata({ entityMetadata, args });
});
entityMetadata.ownRelations = this.metadataArgsStorage.filterRelations(inheritanceTree).map(args => {
return new RelationMetadata({ entityMetadata, args });
});
entityMetadata.relationIds = this.metadataArgsStorage.filterRelationIds(inheritanceTree).map(args => {
return new RelationIdMetadata({ entityMetadata, args });
});
entityMetadata.relationCounts = this.metadataArgsStorage.filterRelationCounts(inheritanceTree).map(args => {
return new RelationCountMetadata({ entityMetadata, args });
});
entityMetadata.indices = this.metadataArgsStorage.filterIndices(inheritanceTree).map(args => {
return new IndexMetadata({ entityMetadata, args });
});
if (parentClosureEntityMetadata.treeLevelColumn) {
entityMetadata.ownColumns.push(new ColumnMetadata({
entityMetadata: entityMetadata,
args: {
target: "",
mode: "virtual",
propertyName: "level",
options: {
type: ColumnTypes.INTEGER,
}
}
}));
}
entityMetadata.foreignKeys = [
new ForeignKeyMetadata({
entityMetadata: entityMetadata,
referencedEntityMetadata: parentClosureEntityMetadata,
columns: [entityMetadata.ownColumns[0]],
referencedColumns: parentClosureEntityMetadata.primaryColumns
}),
new ForeignKeyMetadata({
entityMetadata: entityMetadata,
referencedEntityMetadata: parentClosureEntityMetadata,
columns: [entityMetadata.ownColumns[1]],
referencedColumns: parentClosureEntityMetadata.primaryColumns
}),
];
return entityMetadata;
}
/**
* Creates from the given embedded metadata args real embedded metadatas with its columns and relations,
* and does the same for all its sub-embeddeds (goes recursively).
*/
protected createEmbeddedsRecursively(entityMetadata: EntityMetadata, embeddedArgs: EmbeddedMetadataArgs[]): EmbeddedMetadata[] {
return embeddedArgs.map(embeddedArgs => {
const embeddedMetadata = new EmbeddedMetadata({ entityMetadata: entityMetadata, args: embeddedArgs });
@ -352,24 +201,10 @@ export class AllEntityMetadataBuilder {
});
}
protected createJoinColumnRelationForeignKey(relation: RelationMetadata): ForeignKeyMetadata|undefined {
const referencedColumns = this.collectRelationReferencedColumns(relation);
if (!referencedColumns.length)
return undefined; // this case is possible only for one-to-one non owning side
const columns = this.collectRelationColumns(relation, referencedColumns);
return new ForeignKeyMetadata({
entityMetadata: relation.entityMetadata,
referencedEntityMetadata: relation.inverseEntityMetadata,
columns: columns,
referencedColumns: referencedColumns,
onDelete: relation.onDelete,
});
}
protected buildEntityMetadata(entityMetadata: EntityMetadata) {
// first build all embeddeds, because all columns and relations including owning by embedded depend on embeds
/**
* Computes all entity metadata's computed properties, and all its sub-metadatas (relations, columns, embeds, etc).
*/
protected computeEntityMetadata(entityMetadata: EntityMetadata) {
entityMetadata.embeddeds.forEach(embedded => embedded.build(this.namingStrategy));
entityMetadata.embeddeds.forEach(embedded => {
embedded.columnsFromTree.forEach(column => column.build(this.namingStrategy));
@ -377,9 +212,6 @@ export class AllEntityMetadataBuilder {
});
entityMetadata.ownColumns.forEach(column => column.build(this.namingStrategy));
entityMetadata.ownRelations.forEach(relation => relation.build(this.namingStrategy));
entityMetadata.buildOnRelationsChange();
entityMetadata.relations = entityMetadata.embeddeds.reduce((relations, embedded) => relations.concat(embedded.relationsFromTree), entityMetadata.ownRelations);
entityMetadata.oneToOneRelations = entityMetadata.relations.filter(relation => relation.isOneToOne);
entityMetadata.oneToManyRelations = entityMetadata.relations.filter(relation => relation.isOneToMany);
@ -389,8 +221,9 @@ export class AllEntityMetadataBuilder {
entityMetadata.ownerManyToManyRelations = entityMetadata.relations.filter(relation => relation.isManyToManyOwner);
entityMetadata.treeParentRelation = entityMetadata.relations.find(relation => relation.isTreeParent)!; // todo: fix ! later
entityMetadata.treeChildrenRelation = entityMetadata.relations.find(relation => relation.isTreeChildren)!; // todo: fix ! later
entityMetadata.buildOnColumnsChange();
entityMetadata.columns = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.columnsFromTree), entityMetadata.ownColumns);
entityMetadata.primaryColumns = entityMetadata.columns.filter(column => column.isPrimary);
entityMetadata.hasMultiplePrimaryKeys = entityMetadata.primaryColumns.length > 1;
entityMetadata.generatedColumn = entityMetadata.columns.find(column => column.isGenerated)!; // todo: fix ! later
entityMetadata.createDateColumn = entityMetadata.columns.find(column => column.mode === "createDate")!; // todo: fix ! later
entityMetadata.updateDateColumn = entityMetadata.columns.find(column => column.mode === "updateDate")!; // todo: fix ! later
@ -399,12 +232,15 @@ export class AllEntityMetadataBuilder {
entityMetadata.treeLevelColumn = entityMetadata.columns.find(column => column.mode === "treeLevel")!; // todo: fix ! later
entityMetadata.parentIdColumns = entityMetadata.columns.filter(column => column.mode === "parentId")!; // todo: fix ! later
entityMetadata.objectIdColumn = entityMetadata.columns.find(column => column.mode === "objectId")!; // todo: fix ! later
entityMetadata.foreignKeys.forEach(foreignKey => foreignKey.build(this.namingStrategy));
entityMetadata.indices.forEach(index => index.build(this.namingStrategy));
entityMetadata.propertiesMap = entityMetadata.createPropertiesMap();
}
protected buildInverseProperties(entityMetadata: EntityMetadata, entityMetadatas: EntityMetadata[]) {
/**
* Computes entity metadata's relations inverse side properties.
*/
protected computeInverseProperties(entityMetadata: EntityMetadata, entityMetadatas: EntityMetadata[]) {
entityMetadata.relations.forEach(relation => {
// compute inverse side (related) entity metadatas for all relation metadatas
@ -443,119 +279,6 @@ export class AllEntityMetadataBuilder {
entityMetadata.indices.push(indexForKeyWithPrimary);
}
// cases it should cover:
// 1. when join column is set with custom name and without referenced column name
// we need automatically set referenced column name - primary ids by default
// @JoinColumn({ name: "custom_name" })
// 2. when join column is set with only referenced column name
// we need automatically set join column name - relation name + referenced column name
// @JoinColumn({ referencedColumnName: "title" })
// 3. when join column is set without both referenced column name and join column name
// we need to automatically set both of them
// @JoinColumn()
// 4. when join column is not set at all (as in case of @ManyToOne relation)
// we need to create join column for it with proper referenced column name and join column name
// 5. when multiple join columns set none of referencedColumnName and name can be optional
// both options are required
// @JoinColumn([
// { name: "category_title", referencedColumnName: "type" },
// { name: "category_title", referencedColumnName: "name" },
// ])
// since for many-to-one relations having JoinColumn decorator is not required,
// we need to go thought each many-to-one relation without join column decorator set
// and create join column metadata args for them
protected collectRelationInverseReferencedColumns(relation: RelationMetadata) {
const joinTableMetadataArgs = this.metadataArgsStorage.findJoinTable(relation.target, relation.propertyName)!;
const hasInverseJoinColumns = !!joinTableMetadataArgs.inverseJoinColumns;
const hasAnyInverseReferencedColumnName = hasInverseJoinColumns ? joinTableMetadataArgs.inverseJoinColumns!.find(joinColumn => !!joinColumn.referencedColumnName) : false;
if (!hasInverseJoinColumns || (hasInverseJoinColumns && !hasAnyInverseReferencedColumnName)) {
return relation.inverseEntityMetadata.primaryColumns;
} else {
return joinTableMetadataArgs.inverseJoinColumns!.map(joinColumn => {
const referencedColumn = relation.inverseEntityMetadata.ownColumns.find(column => column.propertyName === joinColumn.referencedColumnName);
if (!referencedColumn)
throw new Error(`Referenced column ${joinColumn.referencedColumnName} was not found in entity ${relation.inverseEntityMetadata.name}`);
return referencedColumn;
});
}
}
protected collectRelationReferencedColumns(relation: RelationMetadata) {
if (relation.isManyToMany) {
const joinTableMetadataArgs = this.metadataArgsStorage.findJoinTable(relation.target, relation.propertyName)!;
const hasAnyReferencedColumnName = joinTableMetadataArgs.joinColumns ? joinTableMetadataArgs.joinColumns.find(joinColumn => !!joinColumn.referencedColumnName) : false;
if (!joinTableMetadataArgs.joinColumns || (joinTableMetadataArgs.joinColumns && !hasAnyReferencedColumnName)) {
return relation.entityMetadata.ownColumns.filter(column => column.isPrimary);
} else {
return joinTableMetadataArgs.joinColumns.map(joinColumn => {
const referencedColumn = relation.entityMetadata.ownColumns.find(column => column.propertyName === joinColumn.referencedColumnName);
if (!referencedColumn)
throw new Error(`Referenced column ${joinColumn.referencedColumnName} was not found in entity ${relation.entityMetadata.name}`);
return referencedColumn;
});
}
} else {
const joinColumnArgsArray = this.metadataArgsStorage.filterJoinColumns(relation.target, relation.propertyName);
const hasAnyReferencedColumnName = joinColumnArgsArray.find(joinColumnArgs => !!joinColumnArgs.referencedColumnName);
const manyToOneWithoutJoinColumn = joinColumnArgsArray.length === 0 && relation.isManyToOne;
const hasJoinColumnWithoutAnyReferencedColumnName = joinColumnArgsArray.length > 0 && !hasAnyReferencedColumnName;
if (manyToOneWithoutJoinColumn || hasJoinColumnWithoutAnyReferencedColumnName) { // covers case3 and case1
return relation.inverseEntityMetadata.primaryColumns;
} else { // cases with referenced columns defined
return joinColumnArgsArray.map(joinColumnArgs => {
const referencedColumn = relation.inverseEntityMetadata.ownColumns.find(column => column.propertyName === joinColumnArgs.referencedColumnName); // todo: can we also search in relations?
if (!referencedColumn)
throw new Error(`Referenced column ${joinColumnArgs.referencedColumnName} was not found in entity ${relation.inverseEntityMetadata.name}`);
return referencedColumn;
});
}
}
}
private collectRelationColumns(relation: RelationMetadata, referencedColumns: ColumnMetadata[]) {
const joinColumnArgsArray = this.metadataArgsStorage.filterJoinColumns(relation.target, relation.propertyName);
return referencedColumns.map(referencedColumn => {
// in the case if relation has join column with only name set we need this check
const joinColumnMetadataArg = joinColumnArgsArray.find(joinColumnArgs => {
return (!joinColumnArgs.referencedColumnName || joinColumnArgs.referencedColumnName === referencedColumn.propertyName) &&
!!joinColumnArgs.name;
});
const joinColumnName = joinColumnMetadataArg ? joinColumnMetadataArg.name : this.namingStrategy.joinColumnName(relation.propertyName, referencedColumn.propertyName);
let relationalColumn = relation.entityMetadata.ownColumns.find(column => column.databaseName === joinColumnName);
if (!relationalColumn) {
relationalColumn = new ColumnMetadata({
entityMetadata: relation.entityMetadata,
args: {
target: "",
mode: "virtual",
propertyName: joinColumnName!,
options: {
name: joinColumnName,
type: referencedColumn.type,
primary: relation.isPrimary,
nullable: relation.isNullable,
}
}
});
relationalColumn.build(this.namingStrategy);
relation.entityMetadata.ownColumns.push(relationalColumn);
relation.entityMetadata.buildOnColumnsChange();
}
relationalColumn.referencedColumn = referencedColumn; // its important to set it here because we need to set referenced column for user defined join column
relationalColumn.type = referencedColumn.type; // also since types of relational column and join column must be equal we override user defined column type
relationalColumn.relationMetadata = relation;
return relationalColumn;
});
}
}
// generate virtual column with foreign key for class-table inheritance

View File

@ -0,0 +1,96 @@
import {EntityMetadata} from "../metadata/EntityMetadata";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
import {Driver} from "../driver/Driver";
import {ColumnTypes} from "../metadata/types/ColumnTypes";
export class ClosureJunctionEntityMetadataBuilder {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(private driver: Driver,
private lazyRelationsWrapper: LazyRelationsWrapper,
private namingStrategy: NamingStrategyInterface) {
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
build(parentClosureEntityMetadata: EntityMetadata) {
const entityMetadata = new EntityMetadata({
parentClosureEntityMetadata: parentClosureEntityMetadata,
lazyRelationsWrapper: this.lazyRelationsWrapper,
namingStrategy: this.namingStrategy,
tablesPrefix: this.driver.options.tablesPrefix,
args: {
target: "",
name: parentClosureEntityMetadata.tableNameWithoutPrefix,
type: "closure-junction"
}
});
parentClosureEntityMetadata.primaryColumns.forEach(primaryColumn => {
entityMetadata.ownColumns.push(new ColumnMetadata({
entityMetadata: entityMetadata,
args: {
target: "",
mode: "virtual",
propertyName: "ancestor_" + primaryColumn.databaseName, // todo: naming strategy
options: {
length: primaryColumn.length,
type: primaryColumn.type,
}
}
}));
entityMetadata.ownColumns.push(new ColumnMetadata({
entityMetadata: entityMetadata,
args: {
target: "",
mode: "virtual",
propertyName: "descendant_" + primaryColumn.databaseName,
options: {
length: primaryColumn.length,
type: primaryColumn.type,
}
}
}));
});
if (parentClosureEntityMetadata.treeLevelColumn) {
entityMetadata.ownColumns.push(new ColumnMetadata({
entityMetadata: entityMetadata,
args: {
target: "",
mode: "virtual",
propertyName: "level",
options: {
type: ColumnTypes.INTEGER,
}
}
}));
}
entityMetadata.foreignKeys = [
new ForeignKeyMetadata({
entityMetadata: entityMetadata,
referencedEntityMetadata: parentClosureEntityMetadata,
columns: [entityMetadata.ownColumns[0]],
referencedColumns: parentClosureEntityMetadata.primaryColumns
}),
new ForeignKeyMetadata({
entityMetadata: entityMetadata,
referencedEntityMetadata: parentClosureEntityMetadata,
columns: [entityMetadata.ownColumns[1]],
referencedColumns: parentClosureEntityMetadata.primaryColumns
}),
];
return entityMetadata;
}
}

View File

@ -0,0 +1,172 @@
import {EntityMetadata} from "../metadata/EntityMetadata";
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {IndexMetadata} from "../metadata/IndexMetadata";
import {RelationMetadata} from "../metadata/RelationMetadata";
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
import {Driver} from "../driver/Driver";
import {JoinTableMetadataArgs} from "../metadata-args/JoinTableMetadataArgs";
export class JunctionEntityMetadataBuilder {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(private driver: Driver,
private lazyRelationsWrapper: LazyRelationsWrapper,
private namingStrategy: NamingStrategyInterface) {
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
build(relation: RelationMetadata, joinTable: JoinTableMetadataArgs) {
const referencedColumns = this.collectRelationReferencedColumns(relation, joinTable);
const inverseReferencedColumns = this.collectRelationInverseReferencedColumns(relation, joinTable);
const joinTableName = joinTable.name || this.namingStrategy.joinTableName(
relation.entityMetadata.tableNameWithoutPrefix,
relation.inverseEntityMetadata.tableNameWithoutPrefix,
relation.propertyPath,
relation.hasInverseSide ? relation.inverseRelation.propertyName : ""
);
const entityMetadata = new EntityMetadata({
lazyRelationsWrapper: this.lazyRelationsWrapper,
namingStrategy: this.namingStrategy,
tablesPrefix: this.driver.options.tablesPrefix,
args: {
target: "",
name: joinTableName,
type: "junction"
}
});
const junctionColumns = referencedColumns.map(referencedColumn => {
const joinColumn = joinTable.joinColumns ? joinTable.joinColumns.find(joinColumnArgs => {
return (!joinColumnArgs.referencedColumnName || joinColumnArgs.referencedColumnName === referencedColumn.propertyName) &&
!!joinColumnArgs.name;
}) : undefined;
const columnName = joinColumn && joinColumn.name ? joinColumn.name : this.namingStrategy.joinTableColumnName(relation.entityMetadata.tableNameWithoutPrefix, referencedColumn.propertyName, referencedColumn.givenDatabaseName);
return new ColumnMetadata({
entityMetadata: entityMetadata,
referencedColumn: referencedColumn,
args: {
target: "",
mode: "virtual",
propertyName: columnName,
options: {
name: columnName,
length: referencedColumn.length,
type: referencedColumn.type,
nullable: false,
primary: true,
}
}
});
});
const inverseJunctionColumns = inverseReferencedColumns.map(inverseReferencedColumn => {
const joinColumn = joinTable.inverseJoinColumns ? joinTable.inverseJoinColumns.find(joinColumnArgs => {
return (!joinColumnArgs.referencedColumnName || joinColumnArgs.referencedColumnName === inverseReferencedColumn.propertyName) &&
!!joinColumnArgs.name;
}) : undefined;
const columnName = joinColumn && joinColumn.name ? joinColumn.name : this.namingStrategy.joinTableColumnName(relation.inverseEntityMetadata.tableNameWithoutPrefix, inverseReferencedColumn.propertyName, inverseReferencedColumn.givenDatabaseName);
return new ColumnMetadata({
entityMetadata: entityMetadata,
referencedColumn: inverseReferencedColumn,
args: {
target: "",
mode: "virtual",
propertyName: columnName,
options: {
length: inverseReferencedColumn.length,
type: inverseReferencedColumn.type,
name: columnName,
nullable: false,
primary: true,
}
}
});
});
entityMetadata.foreignKeys = [
new ForeignKeyMetadata({
entityMetadata: entityMetadata,
referencedEntityMetadata: relation.entityMetadata,
columns: junctionColumns,
referencedColumns: referencedColumns
}),
new ForeignKeyMetadata({
entityMetadata: entityMetadata,
referencedEntityMetadata: relation.inverseEntityMetadata,
columns: inverseJunctionColumns,
referencedColumns: inverseReferencedColumns
}),
];
junctionColumns.concat(inverseJunctionColumns).forEach(column => column.relationMetadata = relation);
entityMetadata.ownColumns = junctionColumns.concat(inverseJunctionColumns);
entityMetadata.indices = [
new IndexMetadata({
entityMetadata: entityMetadata,
columns: junctionColumns,
args: {
target: "",
unique: false
}
}),
new IndexMetadata({
entityMetadata: entityMetadata,
columns: inverseJunctionColumns,
args: {
target: "",
unique: false
}
})
];
return entityMetadata;
}
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
protected collectRelationReferencedColumns(relation: RelationMetadata, joinTable: JoinTableMetadataArgs) {
const hasAnyReferencedColumnName = joinTable.joinColumns ? joinTable.joinColumns.find(joinColumn => !!joinColumn.referencedColumnName) : false;
if (!joinTable.joinColumns || (joinTable.joinColumns && !hasAnyReferencedColumnName)) {
return relation.entityMetadata.ownColumns.filter(column => column.isPrimary);
} else {
return joinTable.joinColumns.map(joinColumn => {
const referencedColumn = relation.entityMetadata.ownColumns.find(column => column.propertyName === joinColumn.referencedColumnName);
if (!referencedColumn)
throw new Error(`Referenced column ${joinColumn.referencedColumnName} was not found in entity ${relation.entityMetadata.name}`);
return referencedColumn;
});
}
}
protected collectRelationInverseReferencedColumns(relation: RelationMetadata, joinTable: JoinTableMetadataArgs) {
const hasInverseJoinColumns = !!joinTable.inverseJoinColumns;
const hasAnyInverseReferencedColumnName = hasInverseJoinColumns ? joinTable.inverseJoinColumns!.find(joinColumn => !!joinColumn.referencedColumnName) : false;
if (!hasInverseJoinColumns || (hasInverseJoinColumns && !hasAnyInverseReferencedColumnName)) {
return relation.inverseEntityMetadata.primaryColumns;
} else {
return joinTable.inverseJoinColumns!.map(joinColumn => {
const referencedColumn = relation.inverseEntityMetadata.ownColumns.find(column => column.propertyName === joinColumn.referencedColumnName);
if (!referencedColumn)
throw new Error(`Referenced column ${joinColumn.referencedColumnName} was not found in entity ${relation.inverseEntityMetadata.name}`);
return referencedColumn;
});
}
}
}

View File

@ -0,0 +1,118 @@
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {RelationMetadata} from "../metadata/RelationMetadata";
import {MetadataArgsStorage} from "../metadata-args/MetadataArgsStorage";
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
import {Driver} from "../driver/Driver";
import {JoinColumnMetadataArgs} from "../metadata-args/JoinColumnMetadataArgs";
// cases it should cover:
// 1. when join column is set with custom name and without referenced column name
// we need automatically set referenced column name - primary ids by default
// @JoinColumn({ name: "custom_name" })
// 2. when join column is set with only referenced column name
// we need automatically set join column name - relation name + referenced column name
// @JoinColumn({ referencedColumnName: "title" })
// 3. when join column is set without both referenced column name and join column name
// we need to automatically set both of them
// @JoinColumn()
// 4. when join column is not set at all (as in case of @ManyToOne relation)
// we need to create join column for it with proper referenced column name and join column name
// 5. when multiple join columns set none of referencedColumnName and name can be optional
// both options are required
// @JoinColumn([
// { name: "category_title", referencedColumnName: "type" },
// { name: "category_title", referencedColumnName: "name" },
// ])
// since for many-to-one relations having JoinColumn decorator is not required,
// we need to go thought each many-to-one relation without join column decorator set
// and create join column metadata args for them
export class RelationJoinColumnBuilder {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(private namingStrategy: NamingStrategyInterface) {
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
build(joinColumns: JoinColumnMetadataArgs[], relation: RelationMetadata): ForeignKeyMetadata|undefined {
const referencedColumns = this.collectRelationReferencedColumns(joinColumns, relation);
if (!referencedColumns.length)
return undefined; // this case is possible only for one-to-one non owning side
const columns = this.collectRelationColumns(joinColumns, relation, referencedColumns);
return new ForeignKeyMetadata({
entityMetadata: relation.entityMetadata,
referencedEntityMetadata: relation.inverseEntityMetadata,
namingStrategy: this.namingStrategy,
columns: columns,
referencedColumns: referencedColumns,
onDelete: relation.onDelete,
});
}
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
protected collectRelationReferencedColumns(joinColumns: JoinColumnMetadataArgs[], relation: RelationMetadata): ColumnMetadata[] {
const hasAnyReferencedColumnName = joinColumns.find(joinColumnArgs => !!joinColumnArgs.referencedColumnName);
const manyToOneWithoutJoinColumn = joinColumns.length === 0 && relation.isManyToOne;
const hasJoinColumnWithoutAnyReferencedColumnName = joinColumns.length > 0 && !hasAnyReferencedColumnName;
if (manyToOneWithoutJoinColumn || hasJoinColumnWithoutAnyReferencedColumnName) { // covers case3 and case1
return relation.inverseEntityMetadata.primaryColumns;
} else { // cases with referenced columns defined
return joinColumns.map(joinColumn => {
const referencedColumn = relation.inverseEntityMetadata.ownColumns.find(column => column.propertyName === joinColumn.referencedColumnName); // todo: can we also search in relations?
if (!referencedColumn)
throw new Error(`Referenced column ${joinColumn.referencedColumnName} was not found in entity ${relation.inverseEntityMetadata.name}`);
return referencedColumn;
});
}
}
private collectRelationColumns(joinColumns: JoinColumnMetadataArgs[], relation: RelationMetadata, referencedColumns: ColumnMetadata[]): ColumnMetadata[] {
return referencedColumns.map(referencedColumn => {
// in the case if relation has join column with only name set we need this check
const joinColumnMetadataArg = joinColumns.find(joinColumn => {
return (!joinColumn.referencedColumnName || joinColumn.referencedColumnName === referencedColumn.propertyName) &&
!!joinColumn.name;
});
const joinColumnName = joinColumnMetadataArg ? joinColumnMetadataArg.name : this.namingStrategy.joinColumnName(relation.propertyName, referencedColumn.propertyName);
let relationalColumn = relation.entityMetadata.ownColumns.find(column => column.databaseName === joinColumnName);
if (!relationalColumn) {
relationalColumn = new ColumnMetadata({
entityMetadata: relation.entityMetadata,
args: {
target: "",
mode: "virtual",
propertyName: joinColumnName!,
options: {
name: joinColumnName,
type: referencedColumn.type,
primary: relation.isPrimary,
nullable: relation.isNullable,
}
}
});
relationalColumn.build(this.namingStrategy);
relation.entityMetadata.registerColumn(relationalColumn);
}
relationalColumn.referencedColumn = referencedColumn; // its important to set it here because we need to set referenced column for user defined join column
relationalColumn.type = referencedColumn.type; // also since types of relational column and join column must be equal we override user defined column type
relationalColumn.relationMetadata = relation;
return relationalColumn;
});
}
}

View File

@ -331,12 +331,10 @@ export class RelationMetadataBuilder {
metadata.isOneToOne = this.isOneToOne;
metadata.isOneToOneOwner = this.isOneToOneOwner;
metadata.isWithJoinColumn = this.isWithJoinColumn;
metadata.isOneToOneNotOwner = this.isOneToOneNotOwner;
metadata.isOneToMany = this.isOneToMany;
metadata.isManyToOne = this.isManyToOne;
metadata.isManyToMany = this.isManyToMany;
metadata.isManyToManyOwner = this.isManyToManyOwner;
metadata.isManyToManyNotOwner = this.isManyToManyNotOwner;
metadata.hasInverseSide = this.hasInverseSide;
metadata.propertyPath = this.propertyPath;
metadata.inverseSidePropertyPath = this.inverseSidePropertyPath;

View File

@ -583,30 +583,38 @@ export class EntityMetadata {
}
// ---------------------------------------------------------------------
// Builder Methods
// Public Builder Methods
// ---------------------------------------------------------------------
buildOnRelationsChange() {
this.relationsWithJoinColumns = this.relations.filter(relation => relation.isWithJoinColumn);
this.hasNonNullableRelations = this.relationsWithJoinColumns.some(relation => !relation.isNullable || relation.isPrimary);
}
buildOnColumnsChange() {
registerColumn(column: ColumnMetadata) {
this.ownColumns.push(column);
this.columns = this.embeddeds.reduce((columns, embedded) => columns.concat(embedded.columnsFromTree), this.ownColumns);
this.primaryColumns = this.columns.filter(column => column.isPrimary);
this.hasMultiplePrimaryKeys = this.primaryColumns.length > 1;
this.propertiesMap = this.createPropertiesMap();
}
// ---------------------------------------------------------------------
// Protected Methods
// ---------------------------------------------------------------------
protected createPropertiesMap(): { [name: string]: string|any } {
createPropertiesMap(): { [name: string]: string|any } {
const map: { [name: string]: string|any } = {};
this.columns.forEach(column => OrmUtils.mergeDeep(map, column.createValueMap(column.propertyPath)));
this.relations.forEach(relation => OrmUtils.mergeDeep(map, relation.createValueMap(relation.propertyPath)));
return map;
}
// buildOnRelationsChange() {
// this.relationsWithJoinColumns = this.relations.filter(relation => relation.isWithJoinColumn);
// this.hasNonNullableRelations = this.relationsWithJoinColumns.some(relation => !relation.isNullable || relation.isPrimary);
// }
// buildOnColumnsChange() {
// this.columns = this.embeddeds.reduce((columns, embedded) => columns.concat(embedded.columnsFromTree), this.ownColumns);
// this.primaryColumns = this.columns.filter(column => column.isPrimary);
// this.hasMultiplePrimaryKeys = this.primaryColumns.length > 1;
// this.propertiesMap = this.createPropertiesMap();
// }
// ---------------------------------------------------------------------
// Protected Methods
// ---------------------------------------------------------------------
}

View File

@ -82,19 +82,18 @@ export class ForeignKeyMetadata {
constructor(options: {
entityMetadata: EntityMetadata,
referencedEntityMetadata: EntityMetadata,
namingStrategy?: NamingStrategyInterface,
columns: ColumnMetadata[],
referencedColumns: ColumnMetadata[],
onDelete?: OnDeleteType
}) {
this.entityMetadata = options.entityMetadata;
this.referencedEntityMetadata = options.referencedEntityMetadata;
// this.tableName = options.entityMetadata.tableName;
// this.referencedTableName = options.referencedEntityMetadata.tableName;
this.columns = options.columns;
// this.columnNames = options.columns.map(column => column.databaseName);
this.referencedColumns = options.referencedColumns;
// this.referencedColumnNames = options.referencedColumns.map(column => column.databaseName);
this.onDelete = options.onDelete;
if (options.namingStrategy)
this.build(options.namingStrategy);
}
build(namingStrategy: NamingStrategyInterface) {

View File

@ -145,7 +145,7 @@ export class RelationMetadata {
* If this relation is a many-to-one/one-to-one then it takes join columns from the current entity.
* If this relation is many-to-many then it takes all owner join columns from the junction entity.
*/
joinColumns: ColumnMetadata[];
joinColumns: ColumnMetadata[] = [];
/**
* Inverse join table columns.
@ -153,7 +153,7 @@ export class RelationMetadata {
* and can be obtained only from owner side of the relation.
* From non-owner side of the relation join columns will be undefined.
*/
inverseJoinColumns: ColumnMetadata[];
inverseJoinColumns: ColumnMetadata[] = [];
/**
* Gets the property's type to which this relation is applied.
@ -270,6 +270,8 @@ export class RelationMetadata {
this.isOneToMany = this.relationType === "one-to-many";
this.isManyToOne = this.relationType === "many-to-one";
this.isManyToMany = this.relationType === "many-to-many";
this.isOneToOneNotOwner = this.isOneToOne ? true : false;
this.isManyToManyNotOwner = this.isManyToMany ? true : false;
}
// ---------------------------------------------------------------------
@ -384,15 +386,14 @@ export class RelationMetadata {
this.propertyPath = this.buildPropertyPath();
}
buildOnForeignKeysChange() {
this.isOwning = this.isManyToOne || ((this.isManyToMany || this.isOneToOne) && this.foreignKeys.length > 0);
this.isOneToOneOwner = this.isOneToOne && this.isOwning;
this.isOneToOneNotOwner = this.isOneToOne && !this.isOwning;
this.isManyToManyOwner = this.isManyToMany && this.isOwning;
this.isManyToManyNotOwner = this.isManyToMany && !this.isOwning;
this.isWithJoinColumn = this.isManyToOne || this.isOneToOneOwner;
registerForeignKeys(...foreignKeys: ForeignKeyMetadata[]) {
this.foreignKeys.push(...foreignKeys);
this.joinColumns = this.foreignKeys[0] ? this.foreignKeys[0].columns : [];
this.inverseJoinColumns = this.foreignKeys[1] ? this.foreignKeys[1].columns : [];
this.isOwning = this.isManyToOne || ((this.isManyToMany || this.isOneToOne) && this.joinColumns.length > 0);
this.isOneToOneOwner = this.isOneToOne && this.isOwning;
this.isManyToManyOwner = this.isManyToMany && this.isOwning;
this.isWithJoinColumn = this.isManyToOne || this.isOneToOneOwner;
}
/**

View File

@ -2,12 +2,12 @@ import "reflect-metadata";
import {Post} from "./entity/Post";
import {ContentModule} from "./entity/ContentModule";
import {Unit} from "./entity/Unit";
import {MetadataArgsUtils} from "../../../../src/metadata-args/MetadataArgsUtils";
import {MetadataUtils} from "../../../../src/metadata-args/MetadataUtils";
describe("metadata builder > MetadataArgsUtils", () => {
it("getInheritanceTree", () => {
const inheritanceTree = MetadataArgsUtils.getInheritanceTree(Post);
const inheritanceTree = MetadataUtils.getInheritanceTree(Post);
inheritanceTree.should.be.eql([
Post,
ContentModule,
@ -16,7 +16,7 @@ describe("metadata builder > MetadataArgsUtils", () => {
});
it("filterByTargetClasses", () => {
MetadataArgsUtils.filterByTarget([
MetadataUtils.filterByTarget([
{ },
{ target: undefined },
{ target: null },
@ -30,7 +30,7 @@ describe("metadata builder > MetadataArgsUtils", () => {
{ target: Unit },
]);
MetadataArgsUtils.filterByTarget([
MetadataUtils.filterByTarget([
{ },
{ target: undefined },
{ target: null },
@ -42,7 +42,7 @@ describe("metadata builder > MetadataArgsUtils", () => {
{ target: Unit },
]);
MetadataArgsUtils.filterByTarget([
MetadataUtils.filterByTarget([
{ },
{ target: undefined },
{ target: null },
@ -57,11 +57,11 @@ describe("metadata builder > MetadataArgsUtils", () => {
{ target: Unit },
]);
MetadataArgsUtils.filterByTarget([
MetadataUtils.filterByTarget([
], [Post, Unit, ContentModule]).should.be.eql([
]);
MetadataArgsUtils.filterByTarget([
MetadataUtils.filterByTarget([
{ },
{ target: undefined },
{ target: null },