refactoring entity metadata builder stuff

This commit is contained in:
Umed Khudoiberdiev 2017-05-16 11:25:50 +05:00
parent 39c6ef7eb7
commit 32505c6f47
4 changed files with 121 additions and 58 deletions

View File

@ -4,6 +4,10 @@ import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
import {ColumnTypes} from "../metadata/types/ColumnTypes";
import {Connection} from "../connection/Connection";
/**
* Creates EntityMetadata for junction tables of the closure entities.
* Closure junction tables are tables generated by closure entities.
*/
export class ClosureJunctionEntityMetadataBuilder {
// -------------------------------------------------------------------------
@ -17,7 +21,12 @@ export class ClosureJunctionEntityMetadataBuilder {
// Public Methods
// -------------------------------------------------------------------------
/**
* Builds EntityMetadata for the closure junction of the given closure entity.
*/
build(parentClosureEntityMetadata: EntityMetadata) {
// create entity metadata itself
const entityMetadata = new EntityMetadata({
parentClosureEntityMetadata: parentClosureEntityMetadata,
connection: this.connection,
@ -28,6 +37,7 @@ export class ClosureJunctionEntityMetadataBuilder {
}
});
// create ancestor and descendant columns for new closure junction table
parentClosureEntityMetadata.primaryColumns.forEach(primaryColumn => {
entityMetadata.ownColumns.push(new ColumnMetadata({
entityMetadata: entityMetadata,
@ -55,6 +65,7 @@ export class ClosureJunctionEntityMetadataBuilder {
}));
});
// if tree level column was defined by a closure entity then add it to the junction columns as well
if (parentClosureEntityMetadata.treeLevelColumn) {
entityMetadata.ownColumns.push(new ColumnMetadata({
entityMetadata: entityMetadata,
@ -69,6 +80,7 @@ export class ClosureJunctionEntityMetadataBuilder {
}));
}
// create junction table foreign keys
entityMetadata.foreignKeys = [
new ForeignKeyMetadata({
entityMetadata: entityMetadata,

View File

@ -15,7 +15,7 @@ import {RelationJoinColumnBuilder} from "./RelationJoinColumnBuilder";
import {Connection} from "../connection/Connection";
/**
* Aggregates all metadata: table, column, relation into one collection grouped by tables for a given set of classes.
* Builds EntityMetadata objects and all its sub-metadatas.
*/
export class EntityMetadataBuilder {
@ -23,8 +23,19 @@ export class EntityMetadataBuilder {
// Protected Properties
// -------------------------------------------------------------------------
/**
* Used to build entity metadatas of the junction entities.
*/
protected junctionEntityMetadataBuilder: JunctionEntityMetadataBuilder;
/**
* Used to build entity metadatas of the closure junction entities.
*/
protected closureJunctionEntityMetadataBuilder: ClosureJunctionEntityMetadataBuilder;
/**
* Used to build join columns of the relations.
*/
protected relationJoinColumnBuilder: RelationJoinColumnBuilder;
// -------------------------------------------------------------------------
@ -239,26 +250,28 @@ export class EntityMetadataBuilder {
});
}
/**
* Creates indices for the table of single table inheritance.
*/
protected createKeysForTableInheritance(entityMetadata: EntityMetadata) {
const indexForKey = new IndexMetadata({
entityMetadata: entityMetadata,
columns: [entityMetadata.discriminatorColumn],
args: {
target: entityMetadata.target,
unique: false
}
});
entityMetadata.indices.push(indexForKey);
const indexForKeyWithPrimary = new IndexMetadata({
entityMetadata: entityMetadata,
columns: [entityMetadata.primaryColumns[0], entityMetadata.discriminatorColumn],
args: {
target: entityMetadata.target,
unique: false
}
});
entityMetadata.indices.push(indexForKeyWithPrimary);
entityMetadata.indices.push(
new IndexMetadata({
entityMetadata: entityMetadata,
columns: [entityMetadata.discriminatorColumn],
args: {
target: entityMetadata.target,
unique: false
}
}),
new IndexMetadata({
entityMetadata: entityMetadata,
columns: [...entityMetadata.primaryColumns, entityMetadata.discriminatorColumn],
args: {
target: entityMetadata.target,
unique: false
}
})
);
}
}

View File

@ -6,6 +6,10 @@ import {RelationMetadata} from "../metadata/RelationMetadata";
import {JoinTableMetadataArgs} from "../metadata-args/JoinTableMetadataArgs";
import {Connection} from "../connection/Connection";
/**
* Creates EntityMetadata for junction tables.
* Junction tables are tables generated by many-to-many relations.
*/
export class JunctionEntityMetadataBuilder {
// -------------------------------------------------------------------------
@ -19,16 +23,19 @@ export class JunctionEntityMetadataBuilder {
// Public Methods
// -------------------------------------------------------------------------
build(relation: RelationMetadata, joinTable: JoinTableMetadataArgs) {
const referencedColumns = this.collectRelationReferencedColumns(relation, joinTable);
const inverseReferencedColumns = this.collectRelationInverseReferencedColumns(relation, joinTable);
/**
* Builds EntityMetadata for the junction of the given many-to-many relation.
*/
build(relation: RelationMetadata, joinTable: JoinTableMetadataArgs): EntityMetadata {
const referencedColumns = this.collectReferencedColumns(relation, joinTable);
const inverseReferencedColumns = this.collectInverseReferencedColumns(relation, joinTable);
const joinTableName = joinTable.name || this.connection.driver.namingStrategy.joinTableName(
relation.entityMetadata.tableNameWithoutPrefix,
relation.inverseEntityMetadata.tableNameWithoutPrefix,
relation.propertyPath,
relation.hasInverseSide ? relation.inverseRelation.propertyName : ""
);
relation.entityMetadata.tableNameWithoutPrefix,
relation.inverseEntityMetadata.tableNameWithoutPrefix,
relation.propertyPath,
relation.hasInverseSide ? relation.inverseRelation.propertyName : ""
);
const entityMetadata = new EntityMetadata({
connection: this.connection,
@ -38,6 +45,8 @@ export class JunctionEntityMetadataBuilder {
type: "junction"
}
});
// create original side junction columns
const junctionColumns = referencedColumns.map(referencedColumn => {
const joinColumn = joinTable.joinColumns ? joinTable.joinColumns.find(joinColumnArgs => {
return (!joinColumnArgs.referencedColumnName || joinColumnArgs.referencedColumnName === referencedColumn.propertyName) &&
@ -63,6 +72,7 @@ export class JunctionEntityMetadataBuilder {
});
});
// create inverse side junction columns
const inverseJunctionColumns = inverseReferencedColumns.map(inverseReferencedColumn => {
const joinColumn = joinTable.inverseJoinColumns ? joinTable.inverseJoinColumns.find(joinColumnArgs => {
return (!joinColumnArgs.referencedColumnName || joinColumnArgs.referencedColumnName === inverseReferencedColumn.propertyName) &&
@ -88,6 +98,11 @@ export class JunctionEntityMetadataBuilder {
});
});
// set junction table columns
entityMetadata.ownColumns = [...junctionColumns, ...inverseJunctionColumns];
entityMetadata.ownColumns.forEach(column => column.relationMetadata = relation);
// create junction table foreign keys
entityMetadata.foreignKeys = [
new ForeignKeyMetadata({
entityMetadata: entityMetadata,
@ -103,8 +118,7 @@ export class JunctionEntityMetadataBuilder {
}),
];
junctionColumns.concat(inverseJunctionColumns).forEach(column => column.relationMetadata = relation);
entityMetadata.ownColumns = junctionColumns.concat(inverseJunctionColumns);
// create junction table indices
entityMetadata.indices = [
new IndexMetadata({
entityMetadata: entityMetadata,
@ -125,6 +139,7 @@ export class JunctionEntityMetadataBuilder {
})
];
// finally return entity metadata
return entityMetadata;
}
@ -132,7 +147,10 @@ export class JunctionEntityMetadataBuilder {
// Protected Methods
// -------------------------------------------------------------------------
protected collectRelationReferencedColumns(relation: RelationMetadata, joinTable: JoinTableMetadataArgs) {
/**
* Collects referenced columns from the given join column args.
*/
protected collectReferencedColumns(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);
@ -147,7 +165,10 @@ export class JunctionEntityMetadataBuilder {
}
}
protected collectRelationInverseReferencedColumns(relation: RelationMetadata, joinTable: JoinTableMetadataArgs) {
/**
* Collects inverse referenced columns from the given join column args.
*/
protected collectInverseReferencedColumns(relation: RelationMetadata, joinTable: JoinTableMetadataArgs) {
const hasInverseJoinColumns = !!joinTable.inverseJoinColumns;
const hasAnyInverseReferencedColumnName = hasInverseJoinColumns ? joinTable.inverseJoinColumns!.find(joinColumn => !!joinColumn.referencedColumnName) : false;
if (!hasInverseJoinColumns || (hasInverseJoinColumns && !hasAnyInverseReferencedColumnName)) {

View File

@ -4,28 +4,36 @@ import {RelationMetadata} from "../metadata/RelationMetadata";
import {JoinColumnMetadataArgs} from "../metadata-args/JoinColumnMetadataArgs";
import {Connection} from "../connection/Connection";
// 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
/**
* Builds join column for the many-to-one and one-to-one owner relations.
*
* 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 {
// -------------------------------------------------------------------------
@ -39,12 +47,15 @@ export class RelationJoinColumnBuilder {
// Public Methods
// -------------------------------------------------------------------------
/**
* Builds a foreign key of the many-to-one or one-to-one owner relations.
*/
build(joinColumns: JoinColumnMetadataArgs[], relation: RelationMetadata): ForeignKeyMetadata|undefined {
const referencedColumns = this.collectRelationReferencedColumns(joinColumns, relation);
const referencedColumns = this.collectReferencedColumns(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);
const columns = this.collectColumns(joinColumns, relation, referencedColumns);
return new ForeignKeyMetadata({
entityMetadata: relation.entityMetadata,
referencedEntityMetadata: relation.inverseEntityMetadata,
@ -58,7 +69,10 @@ export class RelationJoinColumnBuilder {
// Protected Methods
// -------------------------------------------------------------------------
protected collectRelationReferencedColumns(joinColumns: JoinColumnMetadataArgs[], relation: RelationMetadata): ColumnMetadata[] {
/**
* Collects referenced columns from the given join column args.
*/
protected collectReferencedColumns(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;
@ -77,7 +91,10 @@ export class RelationJoinColumnBuilder {
}
}
private collectRelationColumns(joinColumns: JoinColumnMetadataArgs[], relation: RelationMetadata, referencedColumns: ColumnMetadata[]): ColumnMetadata[] {
/**
* Collects columns from the given join column args.
*/
private collectColumns(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