UNPKG

typeorm

Version:

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

136 lines (134 loc) 6.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SubjectDatabaseEntityLoader = void 0; const OrmUtils_1 = require("../util/OrmUtils"); /** * Loads database entities for all operate subjects which do not have database entity set. * All entities that we load database entities for are marked as updated or inserted. * To understand which of them really needs to be inserted or updated we need to load * their original representations from the database. */ class SubjectDatabaseEntityLoader { // --------------------------------------------------------------------- // Constructor // --------------------------------------------------------------------- constructor(queryRunner, subjects) { this.queryRunner = queryRunner; this.subjects = subjects; } // --------------------------------------------------------------------- // Public Methods // --------------------------------------------------------------------- /** * Loads database entities for all subjects. * * loadAllRelations flag is used to load all relation ids of the object, no matter if they present in subject entity or not. * This option is used for deletion. */ async load(operationType) { // we are grouping subjects by target to perform more optimized queries using WHERE IN operator // go through the groups and perform loading of database entities of each subject in the group const promises = this.groupByEntityTargets().map(async (subjectGroup) => { // prepare entity ids of the subjects we need to load const allIds = []; const allSubjects = []; subjectGroup.subjects.forEach((subject) => { // we don't load if subject already has a database entity loaded if (subject.databaseEntity || !subject.identifier) return; allIds.push(subject.identifier); allSubjects.push(subject); }); // if there no ids found (means all entities are new and have generated ids) - then nothing to load there if (!allIds.length) return; const loadRelationPropertyPaths = []; // for the save, soft-remove and recover operation // extract all property paths of the relations we need to load relation ids for // this is for optimization purpose - this way we don't load relation ids for entities // whose relations are undefined, and since they are undefined its really pointless to // load something for them, since undefined properties are skipped by the orm if (operationType === "save" || operationType === "soft-remove" || operationType === "recover") { subjectGroup.subjects.forEach((subject) => { // gets all relation property paths that exist in the persisted entity. subject.metadata.relations.forEach((relation) => { const value = relation.getEntityValue(subject.entityWithFulfilledIds); if (value === undefined) return; if (loadRelationPropertyPaths.indexOf(relation.propertyPath) === -1) loadRelationPropertyPaths.push(relation.propertyPath); }); }); } else { // remove // for remove operation // we only need to load junction relation ids since only they are removed by cascades loadRelationPropertyPaths.push(...subjectGroup.subjects[0].metadata.manyToManyRelations.map((relation) => relation.propertyPath)); } const findOptions = { loadEagerRelations: false, loadRelationIds: { relations: loadRelationPropertyPaths, disableMixedMap: true, }, // the soft-deleted entities should be included in the loaded entities for recover operation withDeleted: true, }; // load database entities for all given ids let entities = []; if (this.queryRunner.connection.driver.options.type === "mongodb") { const mongoRepo = this.queryRunner.manager.getRepository(subjectGroup.target); entities = await mongoRepo.findByIds(allIds, findOptions); } else { entities = await this.queryRunner.manager .getRepository(subjectGroup.target) .createQueryBuilder() .setFindOptions(findOptions) .whereInIds(allIds) .getMany(); } // Now when we have entities we need to find subject of each entity // and insert that entity into database entity of the found subjects. // A single entity can be applied to many subjects as there might be duplicates. // This will likely result in the same row being updated multiple times during a transaction. entities.forEach((entity) => { const entityId = allSubjects[0].metadata.getEntityIdMap(entity); allSubjects.forEach((subject) => { if (subject.databaseEntity) return; if (OrmUtils_1.OrmUtils.compareIds(subject.identifier, entityId)) subject.databaseEntity = entity; }); }); // this way we tell what subjects we tried to load database entities of for (const subject of allSubjects) { subject.databaseEntityLoaded = true; } }); await Promise.all(promises); } // --------------------------------------------------------------------- // Protected Methods // --------------------------------------------------------------------- /** * Groups given Subject objects into groups separated by entity targets. */ groupByEntityTargets() { return this.subjects.reduce((groups, operatedEntity) => { let group = groups.find((group) => group.target === operatedEntity.metadata.target); if (!group) { group = { target: operatedEntity.metadata.target, subjects: [] }; groups.push(group); } group.subjects.push(operatedEntity); return groups; }, []); } } exports.SubjectDatabaseEntityLoader = SubjectDatabaseEntityLoader; //# sourceMappingURL=SubjectDatabaseEntityLoader.js.map