UNPKG

typeorm

Version:

Data-Mapper ORM for TypeScript and ES2023+. Supports MySQL/MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, MongoDB databases.

303 lines • 15.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JunctionEntityMetadataBuilder = void 0; const ColumnMetadata_1 = require("../metadata/ColumnMetadata"); const EntityMetadata_1 = require("../metadata/EntityMetadata"); const ForeignKeyMetadata_1 = require("../metadata/ForeignKeyMetadata"); const IndexMetadata_1 = require("../metadata/IndexMetadata"); const error_1 = require("../error"); const DriverUtils_1 = require("../driver/DriverUtils"); /** * Creates EntityMetadata for junction tables. * Junction tables are tables generated by many-to-many relations. */ class JunctionEntityMetadataBuilder { // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- constructor(dataSource) { this.dataSource = dataSource; } // ------------------------------------------------------------------------- // Public Methods // ------------------------------------------------------------------------- /** * Builds EntityMetadata for the junction of the given many-to-many relation. * * @param relation * @param joinTable */ build(relation, joinTable) { const referencedColumns = this.collectReferencedColumns(relation, joinTable); const inverseReferencedColumns = this.collectInverseReferencedColumns(relation, joinTable); const joinTableName = joinTable.name ?? this.dataSource.namingStrategy.joinTableName(relation.entityMetadata.tableNameWithoutPrefix, relation.inverseEntityMetadata.tableNameWithoutPrefix, relation.propertyPath, relation.inverseRelation ? relation.inverseRelation.propertyName : ""); const entityMetadata = new EntityMetadata_1.EntityMetadata({ dataSource: this.dataSource, args: { target: "", name: joinTableName, type: "junction", database: joinTable.database ?? relation.entityMetadata.database, schema: joinTable.schema ?? relation.entityMetadata.schema, synchronize: joinTable.synchronize, }, }); entityMetadata.build(); // 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) && !!joinColumnArgs.name); }) : undefined; const columnName = joinColumn?.name ?? this.dataSource.namingStrategy.joinTableColumnName(relation.entityMetadata.tableNameWithoutPrefix, referencedColumn.propertyName, referencedColumn.databaseName); return new ColumnMetadata_1.ColumnMetadata({ entityMetadata: entityMetadata, referencedColumn: referencedColumn, args: { target: "", mode: "virtual", propertyName: columnName, options: { name: columnName, length: !referencedColumn.length && (DriverUtils_1.DriverUtils.isMySQLFamily(this.dataSource.driver) || this.dataSource.driver.options.type === "aurora-mysql") && // some versions of mariadb support the column type and should not try to provide the length property this.dataSource.driver.normalizeType(referencedColumn) !== "uuid" && (referencedColumn.generationStrategy === "uuid" || referencedColumn.type === "uuid") ? "36" : referencedColumn.length, // fix https://github.com/typeorm/typeorm/issues/3604 type: referencedColumn.type, precision: referencedColumn.precision, scale: referencedColumn.scale, charset: referencedColumn.charset, collation: referencedColumn.collation, unsigned: referencedColumn.unsigned, enum: referencedColumn.enum, enumName: referencedColumn.enumName, foreignKeyConstraintName: joinColumn?.foreignKeyConstraintName, nullable: false, primary: true, }, }, }); }); // 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) && !!joinColumnArgs.name); }) : undefined; const columnName = joinColumn?.name ?? this.dataSource.namingStrategy.joinTableInverseColumnName(relation.inverseEntityMetadata.tableNameWithoutPrefix, inverseReferencedColumn.propertyName, inverseReferencedColumn.databaseName); return new ColumnMetadata_1.ColumnMetadata({ entityMetadata, referencedColumn: inverseReferencedColumn, args: { target: "", mode: "virtual", propertyName: columnName, options: { length: !inverseReferencedColumn.length && (DriverUtils_1.DriverUtils.isMySQLFamily(this.dataSource.driver) || this.dataSource.driver.options.type === "aurora-mysql") && // some versions of mariadb support the column type and should not try to provide the length property this.dataSource.driver.normalizeType(inverseReferencedColumn) !== "uuid" && (inverseReferencedColumn.generationStrategy === "uuid" || inverseReferencedColumn.type === "uuid") ? "36" : inverseReferencedColumn.length, // fix https://github.com/typeorm/typeorm/issues/3604 type: inverseReferencedColumn.type, precision: inverseReferencedColumn.precision, scale: inverseReferencedColumn.scale, charset: inverseReferencedColumn.charset, collation: inverseReferencedColumn.collation, unsigned: inverseReferencedColumn.unsigned, enum: inverseReferencedColumn.enum, enumName: inverseReferencedColumn.enumName, foreignKeyConstraintName: joinColumn?.foreignKeyConstraintName, name: columnName, nullable: false, primary: true, }, }, }); }); this.changeDuplicatedColumnNames(junctionColumns, inverseJunctionColumns); // set junction table columns entityMetadata.ownerColumns = junctionColumns; entityMetadata.inverseColumns = inverseJunctionColumns; entityMetadata.ownColumns = [ ...junctionColumns, ...inverseJunctionColumns, ]; entityMetadata.ownColumns.forEach((column) => { column.relationMetadata = relation; }); // create junction table foreign keys // Note: UPDATE CASCADE clause is not supported in Oracle. // Note: UPDATE/DELETE CASCADE clauses are not supported in Spanner. entityMetadata.foreignKeys = relation.createForeignKeyConstraints ? [ new ForeignKeyMetadata_1.ForeignKeyMetadata({ entityMetadata: entityMetadata, referencedEntityMetadata: relation.entityMetadata, columns: junctionColumns, referencedColumns: referencedColumns, name: junctionColumns[0]?.foreignKeyConstraintName, onDelete: this.dataSource.driver.options.type === "spanner" ? "NO ACTION" : (relation.onDelete ?? "CASCADE"), onUpdate: this.dataSource.driver.options.type === "oracle" || this.dataSource.driver.options.type === "spanner" ? "NO ACTION" : (relation.onUpdate ?? "CASCADE"), deferrable: relation.deferrable, }), new ForeignKeyMetadata_1.ForeignKeyMetadata({ entityMetadata: entityMetadata, referencedEntityMetadata: relation.inverseEntityMetadata, columns: inverseJunctionColumns, referencedColumns: inverseReferencedColumns, name: inverseJunctionColumns[0]?.foreignKeyConstraintName, onDelete: this.dataSource.driver.options.type === "spanner" ? "NO ACTION" : relation.inverseRelation ? relation.inverseRelation.onDelete : "CASCADE", onUpdate: this.dataSource.driver.options.type === "oracle" || this.dataSource.driver.options.type === "spanner" ? "NO ACTION" : relation.inverseRelation ? relation.inverseRelation.onUpdate : "CASCADE", deferrable: relation.inverseRelation ? relation.inverseRelation.deferrable : relation.deferrable, }), ] : []; // create junction table indices entityMetadata.ownIndices = [ new IndexMetadata_1.IndexMetadata({ entityMetadata: entityMetadata, columns: junctionColumns, args: { target: entityMetadata.target, synchronize: true, }, }), new IndexMetadata_1.IndexMetadata({ entityMetadata: entityMetadata, columns: inverseJunctionColumns, args: { target: entityMetadata.target, synchronize: true, }, }), ]; // finally return entity metadata return entityMetadata; } // ------------------------------------------------------------------------- // Protected Methods // ------------------------------------------------------------------------- /** * Collects referenced columns from the given join column args. * * @param relation * @param joinTable */ collectReferencedColumns(relation, joinTable) { const hasAnyReferencedColumnName = joinTable.joinColumns ? joinTable.joinColumns.find((joinColumn) => !!joinColumn.referencedColumnName) : false; if (!joinTable.joinColumns || (joinTable.joinColumns && !hasAnyReferencedColumnName)) { return relation.entityMetadata.columns.filter((column) => column.isPrimary); } else { const referencedColumns = joinTable.joinColumns.map((joinColumn) => { const referencedColumn = relation.entityMetadata.columns.find((column) => column.propertyName === joinColumn.referencedColumnName); if (!referencedColumn) throw new error_1.TypeORMError(`Referenced column ${joinColumn.referencedColumnName} was not found in entity ${relation.entityMetadata.name}`); return referencedColumn; }); if (referencedColumns.length > 1) { const pkColumns = relation.entityMetadata.primaryColumns; const orderMap = new Map(pkColumns.map((col, idx) => [col, idx])); return [...referencedColumns].sort((a, b) => (orderMap.get(a) ?? Infinity) - (orderMap.get(b) ?? Infinity)); } return referencedColumns; } } /** * Collects inverse referenced columns from the given join column args. * * @param relation * @param joinTable */ collectInverseReferencedColumns(relation, joinTable) { const inverseJoinColumns = joinTable.inverseJoinColumns; const hasAnyInverseReferencedColumnName = inverseJoinColumns ? inverseJoinColumns.find((joinColumn) => !!joinColumn.referencedColumnName) : false; if (!inverseJoinColumns || (inverseJoinColumns && !hasAnyInverseReferencedColumnName)) { return relation.inverseEntityMetadata.primaryColumns; } else { const referencedColumns = inverseJoinColumns.map((joinColumn) => { const referencedColumn = relation.inverseEntityMetadata.ownColumns.find((column) => column.propertyName === joinColumn.referencedColumnName); if (!referencedColumn) throw new error_1.TypeORMError(`Referenced column ${joinColumn.referencedColumnName} was not found in entity ${relation.inverseEntityMetadata.name}`); return referencedColumn; }); if (referencedColumns.length > 1) { const pkColumns = relation.inverseEntityMetadata.primaryColumns; const orderMap = new Map(pkColumns.map((col, idx) => [col, idx])); return [...referencedColumns].sort((a, b) => (orderMap.get(a) ?? Infinity) - (orderMap.get(b) ?? Infinity)); } return referencedColumns; } } changeDuplicatedColumnNames(junctionColumns, inverseJunctionColumns) { junctionColumns.forEach((junctionColumn) => { inverseJunctionColumns.forEach((inverseJunctionColumn) => { if (junctionColumn.givenDatabaseName === inverseJunctionColumn.givenDatabaseName) { const junctionColumnName = this.dataSource.namingStrategy.joinTableColumnDuplicationPrefix(junctionColumn.propertyName, 1); junctionColumn.propertyName = junctionColumnName; junctionColumn.givenDatabaseName = junctionColumnName; const inverseJunctionColumnName = this.dataSource.namingStrategy.joinTableColumnDuplicationPrefix(inverseJunctionColumn.propertyName, 2); inverseJunctionColumn.propertyName = inverseJunctionColumnName; inverseJunctionColumn.givenDatabaseName = inverseJunctionColumnName; } }); }); } } exports.JunctionEntityMetadataBuilder = JunctionEntityMetadataBuilder; //# sourceMappingURL=JunctionEntityMetadataBuilder.js.map