typeorm
Version:
Data-Mapper ORM for TypeScript and ES2021+. Supports MySQL/MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, MongoDB databases.
839 lines (837 loc) • 48.1 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.EntityMetadataBuilder = void 0;
const EntityMetadata_1 = require("../metadata/EntityMetadata");
const ColumnMetadata_1 = require("../metadata/ColumnMetadata");
const IndexMetadata_1 = require("../metadata/IndexMetadata");
const RelationMetadata_1 = require("../metadata/RelationMetadata");
const EmbeddedMetadata_1 = require("../metadata/EmbeddedMetadata");
const RelationIdMetadata_1 = require("../metadata/RelationIdMetadata");
const RelationCountMetadata_1 = require("../metadata/RelationCountMetadata");
const EventListenerTypes_1 = require("../metadata/types/EventListenerTypes");
const MetadataUtils_1 = require("./MetadataUtils");
const JunctionEntityMetadataBuilder_1 = require("./JunctionEntityMetadataBuilder");
const ClosureJunctionEntityMetadataBuilder_1 = require("./ClosureJunctionEntityMetadataBuilder");
const RelationJoinColumnBuilder_1 = require("./RelationJoinColumnBuilder");
const EntityListenerMetadata_1 = require("../metadata/EntityListenerMetadata");
const UniqueMetadata_1 = require("../metadata/UniqueMetadata");
const CheckMetadata_1 = require("../metadata/CheckMetadata");
const ExclusionMetadata_1 = require("../metadata/ExclusionMetadata");
const error_1 = require("../error");
const DriverUtils_1 = require("../driver/DriverUtils");
const ForeignKeyMetadata_1 = require("../metadata/ForeignKeyMetadata");
const InstanceChecker_1 = require("../util/InstanceChecker");
/**
* Builds EntityMetadata objects and all its sub-metadatas.
*/
class EntityMetadataBuilder {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(connection, metadataArgsStorage) {
this.connection = connection;
this.metadataArgsStorage = metadataArgsStorage;
this.junctionEntityMetadataBuilder = new JunctionEntityMetadataBuilder_1.JunctionEntityMetadataBuilder(connection);
this.closureJunctionEntityMetadataBuilder =
new ClosureJunctionEntityMetadataBuilder_1.ClosureJunctionEntityMetadataBuilder(connection);
this.relationJoinColumnBuilder = new RelationJoinColumnBuilder_1.RelationJoinColumnBuilder(connection);
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
/**
* Builds a complete entity metadatas for the given entity classes.
*/
build(entityClasses) {
// 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;
// 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 === "entity-child" ||
table.type === "view");
// 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));
// compute parent entity metadatas for table inheritance
entityMetadatas.forEach((entityMetadata) => this.computeParentEntityMetadata(entityMetadatas, entityMetadata));
// after all metadatas created we set child entity metadatas for table inheritance
entityMetadatas.forEach((metadata) => {
metadata.childEntityMetadatas = entityMetadatas.filter((childMetadata) => {
return (typeof metadata.target === "function" &&
typeof childMetadata.target === "function" &&
MetadataUtils_1.MetadataUtils.isInherited(childMetadata.target, metadata.target));
});
});
// build entity metadata (step0), first for non-single-table-inherited entity metadatas (dependant)
entityMetadatas
.filter((entityMetadata) => entityMetadata.tableType !== "entity-child")
.forEach((entityMetadata) => entityMetadata.build());
// build entity metadata (step0), now for single-table-inherited entity metadatas (dependant)
entityMetadatas
.filter((entityMetadata) => entityMetadata.tableType === "entity-child")
.forEach((entityMetadata) => entityMetadata.build());
// compute entity metadata columns, relations, etc. first for the regular, non-single-table-inherited entity metadatas
entityMetadatas
.filter((entityMetadata) => entityMetadata.tableType !== "entity-child")
.forEach((entityMetadata) => this.computeEntityMetadataStep1(entityMetadatas, entityMetadata));
// then do it for single table inheritance children (since they are depend on their parents to be built)
entityMetadatas
.filter((entityMetadata) => entityMetadata.tableType === "entity-child")
.forEach((entityMetadata) => this.computeEntityMetadataStep1(entityMetadatas, entityMetadata));
// calculate entity metadata computed properties and all its sub-metadatas
entityMetadatas.forEach((entityMetadata) => this.computeEntityMetadataStep2(entityMetadata));
// 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
.filter((entityMetadata) => entityMetadata.tableType !== "entity-child")
.forEach((entityMetadata) => {
// 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 joinColumns = this.metadataArgsStorage.filterJoinColumns(relation.target, relation.propertyName);
const { foreignKey, columns, uniqueConstraint } = 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);
}
if (columns) {
relation.registerJoinColumns(columns);
}
if (uniqueConstraint) {
if (DriverUtils_1.DriverUtils.isMySQLFamily(this.connection.driver) ||
this.connection.driver.options.type ===
"aurora-mysql" ||
this.connection.driver.options.type ===
"mssql" ||
this.connection.driver.options.type === "sap" ||
this.connection.driver.options.type ===
"spanner") {
const index = new IndexMetadata_1.IndexMetadata({
entityMetadata: uniqueConstraint.entityMetadata,
columns: uniqueConstraint.columns,
args: {
target: uniqueConstraint.target,
name: uniqueConstraint.name,
unique: true,
synchronize: true,
},
});
if (this.connection.driver.options.type ===
"mssql") {
index.where = index.columns
.map((column) => {
return `${this.connection.driver.escape(column.databaseName)} IS NOT NULL`;
})
.join(" AND ");
}
if (this.connection.driver.options.type ===
"spanner") {
index.isNullFiltered = true;
}
if (relation.embeddedMetadata) {
relation.embeddedMetadata.indices.push(index);
}
else {
relation.entityMetadata.ownIndices.push(index);
}
this.computeEntityMetadataStep2(entityMetadata);
}
else {
if (relation.embeddedMetadata) {
relation.embeddedMetadata.uniques.push(uniqueConstraint);
}
else {
relation.entityMetadata.ownUniques.push(uniqueConstraint);
}
this.computeEntityMetadataStep2(entityMetadata);
}
}
if (foreignKey &&
this.connection.driver.options.type ===
"cockroachdb") {
const index = new IndexMetadata_1.IndexMetadata({
entityMetadata: relation.entityMetadata,
columns: foreignKey.columns,
args: {
target: relation.entityMetadata.target,
synchronize: true,
},
});
if (relation.embeddedMetadata) {
relation.embeddedMetadata.indices.push(index);
}
else {
relation.entityMetadata.ownIndices.push(index);
}
this.computeEntityMetadataStep2(entityMetadata);
}
});
// create junction entity metadatas for entity many-to-many relations
entityMetadata.relations
.filter((relation) => relation.isManyToMany)
.forEach((relation) => {
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)
// 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.registerJoinColumns(junctionEntityMetadata.ownIndices[0].columns, junctionEntityMetadata.ownIndices[1].columns);
relation.registerJunctionEntityMetadata(junctionEntityMetadata);
// compute new entity metadata properties and push it to entity metadatas pool
this.computeEntityMetadataStep2(junctionEntityMetadata);
this.computeInverseProperties(junctionEntityMetadata, entityMetadatas);
entityMetadatas.push(junctionEntityMetadata);
});
});
// update entity metadata depend properties
entityMetadatas.forEach((entityMetadata) => {
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.treeType === "closure-table")
.forEach((entityMetadata) => {
const closureJunctionEntityMetadata = this.closureJunctionEntityMetadataBuilder.build(entityMetadata);
entityMetadata.closureJunctionTable =
closureJunctionEntityMetadata;
this.computeEntityMetadataStep2(closureJunctionEntityMetadata);
this.computeInverseProperties(closureJunctionEntityMetadata, entityMetadatas);
entityMetadatas.push(closureJunctionEntityMetadata);
});
// generate keys for tables with single-table inheritance
entityMetadatas
.filter((metadata) => metadata.inheritancePattern === "STI" &&
metadata.discriminatorColumn)
.forEach((entityMetadata) => this.createKeysForTableInheritance(entityMetadata));
// build all indices (need to do it after relations and their join columns are built)
entityMetadatas.forEach((entityMetadata) => {
entityMetadata.indices.forEach((index) => index.build(this.connection.namingStrategy));
});
// build all unique constraints (need to do it after relations and their join columns are built)
entityMetadatas.forEach((entityMetadata) => {
entityMetadata.uniques.forEach((unique) => unique.build(this.connection.namingStrategy));
});
// build all check constraints
entityMetadatas.forEach((entityMetadata) => {
entityMetadata.checks.forEach((check) => check.build(this.connection.namingStrategy));
});
// build all exclusion constraints
entityMetadatas.forEach((entityMetadata) => {
entityMetadata.exclusions.forEach((exclusion) => exclusion.build(this.connection.namingStrategy));
});
// generate foreign keys for tables
entityMetadatas.forEach((entityMetadata) => this.createForeignKeys(entityMetadata, entityMetadatas));
// add lazy initializer for entity relations
entityMetadatas
.filter((metadata) => typeof metadata.target === "function")
.forEach((entityMetadata) => {
entityMetadata.relations
.filter((relation) => relation.isLazy)
.forEach((relation) => {
this.connection.relationLoader.enableLazyLoad(relation, entityMetadata.target.prototype);
});
});
entityMetadatas.forEach((entityMetadata) => {
entityMetadata.columns.forEach((column) => {
// const target = column.embeddedMetadata ? column.embeddedMetadata.type : column.target;
const generated = this.metadataArgsStorage.findGenerated(column.target, column.propertyName);
if (generated) {
column.isGenerated = true;
column.generationStrategy = generated.strategy;
if (generated.strategy === "uuid") {
column.type = "uuid";
}
else if (generated.strategy === "rowid") {
column.type = "int";
}
else {
column.type = column.type || Number;
}
column.build(this.connection);
this.computeEntityMetadataStep2(entityMetadata);
}
});
});
return entityMetadatas;
}
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
/**
* Creates entity metadata from the given table args.
* Creates column, relation, etc. metadatas for everything this entity metadata owns.
*/
createEntityMetadata(tableArgs) {
// 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 = typeof tableArgs.target === "function"
? MetadataUtils_1.MetadataUtils.getInheritanceTree(tableArgs.target)
: [tableArgs.target]; // todo: implement later here inheritance for string-targets
const tableInheritance = this.metadataArgsStorage.findInheritanceType(tableArgs.target);
const tableTree = this.metadataArgsStorage.findTree(tableArgs.target);
// if single table inheritance used, we need to copy all children columns in to parent table
let singleTableChildrenTargets;
if ((tableInheritance && tableInheritance.pattern === "STI") ||
tableArgs.type === "entity-child") {
singleTableChildrenTargets = this.metadataArgsStorage
.filterSingleTableChildren(tableArgs.target)
.map((args) => args.target)
.filter((target) => typeof target === "function");
inheritanceTree.push(...singleTableChildrenTargets);
}
return new EntityMetadata_1.EntityMetadata({
connection: this.connection,
args: tableArgs,
inheritanceTree: inheritanceTree,
tableTree: tableTree,
inheritancePattern: tableInheritance
? tableInheritance.pattern
: undefined,
});
}
computeParentEntityMetadata(allEntityMetadatas, entityMetadata) {
// after all metadatas created we set parent entity metadata for table inheritance
if (entityMetadata.tableType === "entity-child") {
entityMetadata.parentEntityMetadata = allEntityMetadatas.find((allEntityMetadata) => {
return (allEntityMetadata.inheritanceTree.indexOf(entityMetadata.target) !== -1 &&
allEntityMetadata.inheritancePattern === "STI");
});
}
}
computeEntityMetadataStep1(allEntityMetadatas, entityMetadata) {
const entityInheritance = this.metadataArgsStorage.findInheritanceType(entityMetadata.target);
const discriminatorValue = this.metadataArgsStorage.findDiscriminatorValue(entityMetadata.target);
if (typeof discriminatorValue !== "undefined") {
entityMetadata.discriminatorValue = discriminatorValue.value;
}
else {
entityMetadata.discriminatorValue = entityMetadata.target.name;
}
// if single table inheritance is used, we need to mark all embedded columns as nullable
entityMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(entityMetadata.inheritanceTree)).map((embedded) => {
if (entityMetadata.inheritancePattern === "STI") {
embedded.columns = embedded.columns.map((column) => {
column.isNullable = true;
return column;
});
}
return embedded;
});
entityMetadata.ownColumns = this.metadataArgsStorage
.filterColumns(entityMetadata.inheritanceTree)
.map((args) => {
// for single table children we reuse columns created for their parents
if (entityMetadata.tableType === "entity-child")
return entityMetadata.parentEntityMetadata.ownColumns.find((column) => column.propertyName === args.propertyName);
// for multiple table inheritance we can override default column values
if (entityMetadata.tableType === "regular" &&
args.target !== entityMetadata.target) {
const childArgs = this.metadataArgsStorage.columns.find((c) => c.propertyName === args.propertyName &&
c.target === entityMetadata.target);
if (childArgs && childArgs.options.default) {
args.options.default = childArgs.options.default;
}
}
const column = new ColumnMetadata_1.ColumnMetadata({
connection: this.connection,
entityMetadata,
args,
});
// if single table inheritance used, we need to mark all inherit table columns as nullable
const columnInSingleTableInheritedChild = allEntityMetadatas.find((otherEntityMetadata) => otherEntityMetadata.tableType === "entity-child" &&
otherEntityMetadata.target === args.target);
if (columnInSingleTableInheritedChild)
column.isNullable = true;
return column;
});
// for table inheritance we need to add a discriminator column
//
if (entityInheritance && entityInheritance.column) {
const discriminatorColumnName = entityInheritance.column && entityInheritance.column.name
? entityInheritance.column.name
: "type";
let discriminatorColumn = entityMetadata.ownColumns.find((column) => column.propertyName === discriminatorColumnName);
if (!discriminatorColumn) {
discriminatorColumn = new ColumnMetadata_1.ColumnMetadata({
connection: this.connection,
entityMetadata: entityMetadata,
args: {
target: entityMetadata.target,
mode: "virtual",
propertyName: discriminatorColumnName,
options: entityInheritance.column || {
name: discriminatorColumnName,
type: "varchar",
nullable: false,
},
},
});
discriminatorColumn.isVirtual = true;
discriminatorColumn.isDiscriminator = true;
entityMetadata.ownColumns.push(discriminatorColumn);
}
else {
discriminatorColumn.isDiscriminator = true;
}
}
// add discriminator column to the child entity metadatas
// discriminator column will not be there automatically since we are creating it in the code above
if (entityMetadata.tableType === "entity-child") {
const discriminatorColumn = entityMetadata.parentEntityMetadata.ownColumns.find((column) => column.isDiscriminator);
if (discriminatorColumn &&
!entityMetadata.ownColumns.find((column) => column === discriminatorColumn)) {
entityMetadata.ownColumns.push(discriminatorColumn);
}
// also copy the inheritance pattern & tree metadata
// this comes in handy when inheritance and trees are used together
entityMetadata.inheritancePattern =
entityMetadata.parentEntityMetadata.inheritancePattern;
if (!entityMetadata.treeType &&
!!entityMetadata.parentEntityMetadata.treeType) {
entityMetadata.treeType =
entityMetadata.parentEntityMetadata.treeType;
entityMetadata.treeOptions =
entityMetadata.parentEntityMetadata.treeOptions;
entityMetadata.treeParentRelation =
entityMetadata.parentEntityMetadata.treeParentRelation;
entityMetadata.treeLevelColumn =
entityMetadata.parentEntityMetadata.treeLevelColumn;
}
}
const { namingStrategy } = this.connection;
// check if tree is used then we need to add extra columns for specific tree types
if (entityMetadata.treeType === "materialized-path") {
entityMetadata.ownColumns.push(new ColumnMetadata_1.ColumnMetadata({
connection: this.connection,
entityMetadata: entityMetadata,
materializedPath: true,
args: {
target: entityMetadata.target,
mode: "virtual",
propertyName: "mpath",
options: /*tree.column || */ {
name: namingStrategy.materializedPathColumnName,
type: String,
nullable: true,
default: "",
},
},
}));
}
else if (entityMetadata.treeType === "nested-set") {
const { left, right } = namingStrategy.nestedSetColumnNames;
entityMetadata.ownColumns.push(new ColumnMetadata_1.ColumnMetadata({
connection: this.connection,
entityMetadata: entityMetadata,
nestedSetLeft: true,
args: {
target: entityMetadata.target,
mode: "virtual",
propertyName: left,
options: /*tree.column || */ {
name: left,
type: Number,
nullable: false,
default: 1,
},
},
}));
entityMetadata.ownColumns.push(new ColumnMetadata_1.ColumnMetadata({
connection: this.connection,
entityMetadata: entityMetadata,
nestedSetRight: true,
args: {
target: entityMetadata.target,
mode: "virtual",
propertyName: right,
options: /*tree.column || */ {
name: right,
type: Number,
nullable: false,
default: 2,
},
},
}));
}
entityMetadata.ownRelations = this.metadataArgsStorage
.filterRelations(entityMetadata.inheritanceTree)
.map((args) => {
// for single table children we reuse relations created for their parents
if (entityMetadata.tableType === "entity-child") {
const parentRelation = entityMetadata.parentEntityMetadata.ownRelations.find((relation) => relation.propertyName === args.propertyName);
const type = typeof args.type === "function"
? args.type()
: args.type;
if (parentRelation.type !== type) {
const clone = Object.create(parentRelation);
clone.type = type;
return clone;
}
return parentRelation;
}
return new RelationMetadata_1.RelationMetadata({ entityMetadata, args });
});
entityMetadata.relationIds = this.metadataArgsStorage
.filterRelationIds(entityMetadata.inheritanceTree)
.map((args) => {
// for single table children we reuse relation ids created for their parents
if (entityMetadata.tableType === "entity-child")
return entityMetadata.parentEntityMetadata.relationIds.find((relationId) => relationId.propertyName === args.propertyName);
return new RelationIdMetadata_1.RelationIdMetadata({ entityMetadata, args });
});
entityMetadata.relationCounts = this.metadataArgsStorage
.filterRelationCounts(entityMetadata.inheritanceTree)
.map((args) => {
// for single table children we reuse relation counts created for their parents
if (entityMetadata.tableType === "entity-child")
return entityMetadata.parentEntityMetadata.relationCounts.find((relationCount) => relationCount.propertyName === args.propertyName);
return new RelationCountMetadata_1.RelationCountMetadata({ entityMetadata, args });
});
entityMetadata.ownListeners = this.metadataArgsStorage
.filterListeners(entityMetadata.inheritanceTree)
.map((args) => {
return new EntityListenerMetadata_1.EntityListenerMetadata({
entityMetadata: entityMetadata,
args: args,
});
});
entityMetadata.checks = this.metadataArgsStorage
.filterChecks(entityMetadata.inheritanceTree)
.map((args) => {
return new CheckMetadata_1.CheckMetadata({ entityMetadata, args });
});
// Only PostgreSQL supports exclusion constraints.
if (this.connection.driver.options.type === "postgres") {
entityMetadata.exclusions = this.metadataArgsStorage
.filterExclusions(entityMetadata.inheritanceTree)
.map((args) => {
return new ExclusionMetadata_1.ExclusionMetadata({ entityMetadata, args });
});
}
if (this.connection.driver.options.type === "cockroachdb") {
entityMetadata.ownIndices = this.metadataArgsStorage
.filterIndices(entityMetadata.inheritanceTree)
.filter((args) => !args.unique)
.map((args) => {
return new IndexMetadata_1.IndexMetadata({ entityMetadata, args });
});
const uniques = this.metadataArgsStorage
.filterIndices(entityMetadata.inheritanceTree)
.filter((args) => args.unique)
.map((args) => {
return new UniqueMetadata_1.UniqueMetadata({
entityMetadata: entityMetadata,
args: {
target: args.target,
name: args.name,
columns: args.columns,
},
});
});
entityMetadata.ownUniques.push(...uniques);
}
else {
entityMetadata.ownIndices = this.metadataArgsStorage
.filterIndices(entityMetadata.inheritanceTree)
.map((args) => {
return new IndexMetadata_1.IndexMetadata({ entityMetadata, args });
});
}
// This drivers stores unique constraints as unique indices.
if (DriverUtils_1.DriverUtils.isMySQLFamily(this.connection.driver) ||
this.connection.driver.options.type === "aurora-mysql" ||
this.connection.driver.options.type === "sap" ||
this.connection.driver.options.type === "spanner") {
const indices = this.metadataArgsStorage
.filterUniques(entityMetadata.inheritanceTree)
.map((args) => {
return new IndexMetadata_1.IndexMetadata({
entityMetadata: entityMetadata,
args: {
target: args.target,
name: args.name,
columns: args.columns,
unique: true,
synchronize: true,
},
});
});
entityMetadata.ownIndices.push(...indices);
}
else {
const uniques = this.metadataArgsStorage
.filterUniques(entityMetadata.inheritanceTree)
.map((args) => {
return new UniqueMetadata_1.UniqueMetadata({ entityMetadata, args });
});
entityMetadata.ownUniques.push(...uniques);
}
}
/**
* 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).
*/
createEmbeddedsRecursively(entityMetadata, embeddedArgs) {
return embeddedArgs.map((embeddedArgs) => {
const embeddedMetadata = new EmbeddedMetadata_1.EmbeddedMetadata({
entityMetadata: entityMetadata,
args: embeddedArgs,
});
const targets = typeof embeddedMetadata.type === "function"
? MetadataUtils_1.MetadataUtils.getInheritanceTree(embeddedMetadata.type)
: [embeddedMetadata.type]; // todo: implement later here inheritance for string-targets
embeddedMetadata.columns = this.metadataArgsStorage
.filterColumns(targets)
.map((args) => {
return new ColumnMetadata_1.ColumnMetadata({
connection: this.connection,
entityMetadata,
embeddedMetadata,
args,
});
});
embeddedMetadata.relations = this.metadataArgsStorage
.filterRelations(targets)
.map((args) => {
return new RelationMetadata_1.RelationMetadata({
entityMetadata,
embeddedMetadata,
args,
});
});
embeddedMetadata.listeners = this.metadataArgsStorage
.filterListeners(targets)
.map((args) => {
return new EntityListenerMetadata_1.EntityListenerMetadata({
entityMetadata,
embeddedMetadata,
args,
});
});
embeddedMetadata.indices = this.metadataArgsStorage
.filterIndices(targets)
.map((args) => {
return new IndexMetadata_1.IndexMetadata({
entityMetadata,
embeddedMetadata,
args,
});
});
embeddedMetadata.uniques = this.metadataArgsStorage
.filterUniques(targets)
.map((args) => {
return new UniqueMetadata_1.UniqueMetadata({
entityMetadata,
embeddedMetadata,
args,
});
});
embeddedMetadata.relationIds = this.metadataArgsStorage
.filterRelationIds(targets)
.map((args) => {
return new RelationIdMetadata_1.RelationIdMetadata({ entityMetadata, args });
});
embeddedMetadata.relationCounts = this.metadataArgsStorage
.filterRelationCounts(targets)
.map((args) => {
return new RelationCountMetadata_1.RelationCountMetadata({ entityMetadata, args });
});
embeddedMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(targets));
embeddedMetadata.embeddeds.forEach((subEmbedded) => (subEmbedded.parentEmbeddedMetadata = embeddedMetadata));
entityMetadata.allEmbeddeds.push(embeddedMetadata);
return embeddedMetadata;
});
}
/**
* Computes all entity metadata's computed properties, and all its sub-metadatas (relations, columns, embeds, etc).
*/
computeEntityMetadataStep2(entityMetadata) {
entityMetadata.embeddeds.forEach((embedded) => embedded.build(this.connection));
entityMetadata.embeddeds.forEach((embedded) => {
embedded.columnsFromTree.forEach((column) => column.build(this.connection));
embedded.relationsFromTree.forEach((relation) => relation.build());
});
entityMetadata.ownColumns.forEach((column) => column.build(this.connection));
entityMetadata.ownRelations.forEach((relation) => relation.build());
entityMetadata.relations = entityMetadata.embeddeds.reduce((relations, embedded) => relations.concat(embedded.relationsFromTree), entityMetadata.ownRelations);
entityMetadata.eagerRelations = entityMetadata.relations.filter((relation) => relation.isEager);
entityMetadata.lazyRelations = entityMetadata.relations.filter((relation) => relation.isLazy);
entityMetadata.oneToOneRelations = entityMetadata.relations.filter((relation) => relation.isOneToOne);
entityMetadata.oneToManyRelations = entityMetadata.relations.filter((relation) => relation.isOneToMany);
entityMetadata.manyToOneRelations = entityMetadata.relations.filter((relation) => relation.isManyToOne);
entityMetadata.manyToManyRelations = entityMetadata.relations.filter((relation) => relation.isManyToMany);
entityMetadata.ownerOneToOneRelations = entityMetadata.relations.filter((relation) => relation.isOneToOneOwner);
entityMetadata.ownerManyToManyRelations =
entityMetadata.relations.filter((relation) => relation.isManyToManyOwner);
entityMetadata.treeParentRelation = entityMetadata.relations.find((relation) => relation.isTreeParent);
entityMetadata.treeChildrenRelation = entityMetadata.relations.find((relation) => relation.isTreeChildren);
entityMetadata.columns = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.columnsFromTree), entityMetadata.ownColumns);
entityMetadata.listeners = entityMetadata.embeddeds.reduce((listeners, embedded) => listeners.concat(embedded.listenersFromTree), entityMetadata.ownListeners);
entityMetadata.afterLoadListeners = entityMetadata.listeners.filter((listener) => listener.type === EventListenerTypes_1.EventListenerTypes.AFTER_LOAD);
entityMetadata.afterInsertListeners = entityMetadata.listeners.filter((listener) => listener.type === EventListenerTypes_1.EventListenerTypes.AFTER_INSERT);
entityMetadata.afterUpdateListeners = entityMetadata.listeners.filter((listener) => listener.type === EventListenerTypes_1.EventListenerTypes.AFTER_UPDATE);
entityMetadata.afterRemoveListeners = entityMetadata.listeners.filter((listener) => listener.type === EventListenerTypes_1.EventListenerTypes.AFTER_REMOVE);
entityMetadata.afterSoftRemoveListeners =
entityMetadata.listeners.filter((listener) => listener.type === EventListenerTypes_1.EventListenerTypes.AFTER_SOFT_REMOVE);
entityMetadata.afterRecoverListeners = entityMetadata.listeners.filter((listener) => listener.type === EventListenerTypes_1.EventListenerTypes.AFTER_RECOVER);
entityMetadata.beforeInsertListeners = entityMetadata.listeners.filter((listener) => listener.type === EventListenerTypes_1.EventListenerTypes.BEFORE_INSERT);
entityMetadata.beforeUpdateListeners = entityMetadata.listeners.filter((listener) => listener.type === EventListenerTypes_1.EventListenerTypes.BEFORE_UPDATE);
entityMetadata.beforeRemoveListeners = entityMetadata.listeners.filter((listener) => listener.type === EventListenerTypes_1.EventListenerTypes.BEFORE_REMOVE);
entityMetadata.beforeSoftRemoveListeners =
entityMetadata.listeners.filter((listener) => listener.type === EventListenerTypes_1.EventListenerTypes.BEFORE_SOFT_REMOVE);
entityMetadata.beforeRecoverListeners = entityMetadata.listeners.filter((listener) => listener.type === EventListenerTypes_1.EventListenerTypes.BEFORE_RECOVER);
entityMetadata.indices = entityMetadata.embeddeds.reduce((indices, embedded) => indices.concat(embedded.indicesFromTree), entityMetadata.ownIndices);
entityMetadata.uniques = entityMetadata.embeddeds.reduce((uniques, embedded) => uniques.concat(embedded.uniquesFromTree), entityMetadata.ownUniques);
entityMetadata.primaryColumns = entityMetadata.columns.filter((column) => column.isPrimary);
entityMetadata.nonVirtualColumns = entityMetadata.columns.filter((column) => !column.isVirtual);
entityMetadata.ancestorColumns = entityMetadata.columns.filter((column) => column.closureType === "ancestor");
entityMetadata.descendantColumns = entityMetadata.columns.filter((column) => column.closureType === "descendant");
entityMetadata.hasMultiplePrimaryKeys =
entityMetadata.primaryColumns.length > 1;
entityMetadata.generatedColumns = entityMetadata.columns.filter((column) => column.isGenerated || column.isObjectId);
entityMetadata.hasUUIDGeneratedColumns =
entityMetadata.columns.filter((column) => column.isGenerated || column.generationStrategy === "uuid").length > 0;
entityMetadata.createDateColumn = entityMetadata.columns.find((column) => column.isCreateDate);
entityMetadata.updateDateColumn = entityMetadata.columns.find((column) => column.isUpdateDate);
entityMetadata.deleteDateColumn = entityMetadata.columns.find((column) => column.isDeleteDate);
entityMetadata.versionColumn = entityMetadata.columns.find((column) => column.isVersion);
entityMetadata.discriminatorColumn = entityMetadata.columns.find((column) => column.isDiscriminator);
entityMetadata.treeLevelColumn = entityMetadata.columns.find((column) => column.isTreeLevel);
entityMetadata.nestedSetLeftColumn = entityMetadata.columns.find((column) => column.isNestedSetLeft);
entityMetadata.nestedSetRightColumn = entityMetadata.columns.find((column) => column.isNestedSetRight);
entityMetadata.materializedPathColumn = entityMetadata.columns.find((column) => column.isMaterializedPath);
entityMetadata.objectIdColumn = entityMetadata.columns.find((column) => column.isObjectId);
entityMetadata.foreignKeys.forEach((foreignKey) => foreignKey.build(this.connection.namingStrategy));
entityMetadata.propertiesMap = entityMetadata.createPropertiesMap();
entityMetadata.relationIds.forEach((relationId) => relationId.build());
entityMetadata.relationCounts.forEach((relationCount) => relationCount.build());
entityMetadata.embeddeds.forEach((embedded) => {
embedded.relationIdsFromTree.forEach((relationId) => relationId.build());
embedded.relationCountsFromTree.forEach((relationCount) => relationCount.build());
});
}
/**
* Computes entity metadata's relations inverse side properties.
*/
computeInverseProperties(entityMetadata, entityMetadatas) {
entityMetadata.relations.forEach((relation) => {
// compute inverse side (related) entity metadatas for all relation metadatas
const inverseEntityMetadata = entityMetadatas.find((m) => m.target === relation.type ||
(typeof relation.type === "string" &&
(m.targetName === relation.type ||
m.givenTableName === relation.type)));
if (!inverseEntityMetadata)
throw new error_1.TypeORMError("Entity metadata for " +
entityMetadata.name +
"#" +
relation.propertyPath +
" was not found. Check if you specified a correct entity object and if it's connected in the connection options.");
relation.inverseEntityMetadata = inverseEntityMetadata;
relation.inverseSidePropertyPath =
relation.buildInverseSidePropertyPath();
// and compute inverse relation and mark if it has such
relation.inverseRelation = inverseEntityMetadata.relations.find((foundRelation) => foundRelation.propertyPath ===
relation.inverseSidePropertyPath);
});
}
/**
* Creates indices for the table of single table inheritance.
*/
createKeysForTableInheritance(entityMetadata) {
const isDiscriminatorColumnAlreadyIndexed = entityMetadata.indices.some(({ givenColumnNames }) => !!givenColumnNames &&
Array.isArray(givenColumnNames) &&
givenColumnNames.length === 1 &&
givenColumnNames[0] ===
entityMetadata.discriminatorColumn?.databaseName);
// If the discriminator column is already indexed, there is no need to
// add another index on top of it.
if (isDiscriminatorColumnAlreadyIndexed) {
return;
}
entityMetadata.indices.push(new IndexMetadata_1.IndexMetadata({
entityMetadata: entityMetadata,
columns: [entityMetadata.discriminatorColumn],
args: {
target: entityMetadata.target,
unique: false,
},
}));
}
/**
* Creates from the given foreign key metadata args real foreign key metadatas.
*/
createForeignKeys(entityMetadata, entityMetadatas) {
this.metadataArgsStorage
.filterForeignKeys(entityMetadata.inheritanceTree)
.forEach((foreignKeyArgs) => {
const foreignKeyType = typeof foreignKeyArgs.type === "function"
? foreignKeyArgs.type()
: foreignKeyArgs.type;
const referencedEntityMetadata = entityMetadatas.find((m) => typeof foreignKeyType === "string"
? m.targetName === foreignKeyType ||
m.givenTableName === foreignKeyType
: InstanceChecker_1.InstanceChecker.isEntitySchema(foreignKeyType)
? m.target === foreignKeyType.options.name ||
m.target === foreignKeyType.options.target
: m.target === foreignKeyType);
if (!referencedEntityMetadata) {
throw new error_1.TypeORMError("Entity metadata for " +
entityMetadata.name +
(foreignKeyArgs.propertyName
? "#" + foreignKeyArgs.propertyName
: "") +
" was not found. Check if you specified a correct entity object and if it's connected in the connection options.");
}
const columnNames = foreignKeyArgs.columnNames ?? [];
const referencedColumnNames = foreignKeyArgs.referencedColumnNames ?? [];
const columns = [];
const referencedColumns = [];
if (foreignKeyArgs.propertyName) {
columnNames.push(foreignKeyArgs.propertyName);
if (foreignKeyArgs.inverseSide) {
if (typeof foreignKeyArgs.inverseSide === "function") {
referencedColumnNames.push(foreignKeyArgs.inverseSide(referencedEntityMetadata.propertiesMap));
}
else {
referencedColumnNames.push(foreignKeyArgs.inverseSide);
}
}
}
if (!referencedColumnNames.length) {
referencedColumns.push(...referencedEntityMetadata.primaryColumns);
}
const columnNameToColumn = (columnName, entityMetadata) => {
const column = entityMetadata.columns.find((column) => column.propertyName === columnName ||
column.databaseName === columnName);
if (column)
return column;
const foreignKeyName = foreignKeyArgs.name
? '"' + foreignKeyArgs.name + '" '
: "";
const entityName = entityMetadata.targetName;
throw new error_1.TypeORMError(`Foreign key constraint ${foreignKeyName}contains column that is missing in the entity (${entityName}): ${columnName}`);
};
columns.push(...columnNames.map((columnName) => columnNameToColumn(columnName, entityMetadata)));
referencedColumns.push(...referencedColumnNames.map((columnName) => columnNameToColumn(columnName, referencedEntityMetadata)));
entityMetadata.foreignKeys.push(new ForeignKeyMetadata_1.ForeignKeyMetadata({
entityMetadata,
referencedEntityMetadata,
namingStrategy: this.connection.namingStrategy,
columns,
referencedColumns,
...foreignKeyArgs,
}));
});
}
}
exports.EntityMetadataBuilder = EntityMetadataBuilder;
//# sourceMappingURL=EntityMetadataBuilder.js.map
;