schangxiang@126.com
2025-09-19 0821aa23eabe557c0d9ef5dbe6989c68be35d1fe
1
{"version":3,"sources":["../browser/src/persistence/subject-builder/OneToOneInverseSideSubjectBuilder.ts"],"names":[],"mappings":"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","file":"OneToOneInverseSideSubjectBuilder.js","sourcesContent":["import {Subject} from \"../Subject\";\nimport {OrmUtils} from \"../../util/OrmUtils\";\nimport {ObjectLiteral} from \"../../common/ObjectLiteral\";\nimport {RelationMetadata} from \"../../metadata/RelationMetadata\";\n\n/**\n * Builds operations needs to be executed for one-to-one non-owner relations of the given subjects.\n *\n * by example: post contains one-to-one non-owner relation with category in the property called \"category\", e.g.\n *             @OneToOne(type => Category, category => category.post) category: Category\n *             If user sets a category into the post and saves post we need to bind them.\n *             This operation requires updation of category table since its owner of the relation and contains a join column.\n *\n * note: this class shares lot of things with OneToManyUpdateBuilder, so when you change this class\n *       make sure to reflect changes there as well.\n */\nexport class OneToOneInverseSideSubjectBuilder {\n\n    // ---------------------------------------------------------------------\n    // Constructor\n    // ---------------------------------------------------------------------\n\n    constructor(protected subjects: Subject[]) {\n    }\n\n    // ---------------------------------------------------------------------\n    // Public Methods\n    // ---------------------------------------------------------------------\n\n    /**\n     * Builds all required operations.\n     */\n    build(): void {\n        this.subjects.forEach(subject => {\n            subject.metadata.oneToOneRelations.forEach(relation => {\n\n                // we don't need owning relations, this operation is only for inverse side of one-to-one relations\n                // skip relations for which persistence is disabled\n                if (relation.isOwning || relation.persistenceEnabled === false)\n                    return;\n\n                this.buildForSubjectRelation(subject, relation);\n            });\n        });\n    }\n\n    // ---------------------------------------------------------------------\n    // Protected Methods\n    // ---------------------------------------------------------------------\n\n    /**\n     * Builds operations for a given subject and relation.\n     *\n     * by example: subject is \"post\" entity we are saving here and relation is \"category\" inside it here.\n     */\n    protected buildForSubjectRelation(subject: Subject, relation: RelationMetadata) {\n\n        // prepare objects (relation id map) for the database entity\n        // note: subject.databaseEntity contains relation with loaded relation id only (id map)\n        // by example: since subject is a post, we are expecting to get post's category saved in the database here,\n        //             particularly its relation id, e.g. category id stored in the database\n        let relatedEntityDatabaseRelationId: ObjectLiteral|undefined = undefined;\n        if (subject.databaseEntity) // related entity in the database can exist only if this entity (post) is saved\n            relatedEntityDatabaseRelationId = relation.getEntityValue(subject.databaseEntity);\n\n        // get related entities of persisted entity\n        // by example: get category from the passed to persist post entity\n        let relatedEntity: ObjectLiteral|null = relation.getEntityValue(subject.entity!); // by example: relatedEntity is a category here\n        if (relatedEntity === undefined) // if relation is undefined then nothing to update\n            return;\n\n        // if related entity is null then we need to check if there a bind in the database and unset it\n        // if there is no bind in the entity then we don't need to do anything\n        // by example: if post.category = null and category has this post in the database then we unset it\n        if (relatedEntity === null) {\n\n            // it makes sense to update database only there is a previously set value in the database\n            if (relatedEntityDatabaseRelationId) {\n                // todo: probably we can improve this in the future by finding entity with column those values,\n                // todo: maybe it was already in persistence process. This is possible due to unique requirements of join columns\n                // we create a new subject which operations will be executed in subject operation executor\n\n                const removedRelatedEntitySubject = new Subject({\n                    metadata: relation.inverseEntityMetadata,\n                    parentSubject: subject,\n                    canBeUpdated: true,\n                    identifier: relatedEntityDatabaseRelationId,\n                    changeMaps: [{\n                        relation: relation.inverseRelation!,\n                        value: null\n                    }]\n                });\n                this.subjects.push(removedRelatedEntitySubject);\n            }\n\n            return;\n        } // else means entity is bind in the database\n\n        // extract only relation id from the related entities, since we only need it for comparision\n        // by example: extract from category only relation id (category id, or let's say category title, depend on join column options)\n        let relationIdMap = relation.inverseEntityMetadata!.getEntityIdMap(relatedEntity); // by example: relationIdMap is category.id map here, e.g. { id: ... }\n\n        // try to find a subject of this related entity, maybe it was loaded or was marked for persistence\n        let relatedEntitySubject = this.subjects.find(operateSubject => {\n            return !!operateSubject.entity && operateSubject.entity === relatedEntity;\n        });\n\n        // if subject with entity was found take subject identifier as relation id map since it may contain extra properties resolved\n        if (relatedEntitySubject)\n            relationIdMap = relatedEntitySubject.identifier;\n\n        // if relationIdMap is undefined then it means user binds object which is not saved in the database yet\n        // by example: if post contains category which does not have id(s) yet (because its a new category)\n        //             it means its always newly inserted and relation update operation always must be created for it\n        //             it does not make sense to perform difference operation for it for both add and remove actions\n        if (!relationIdMap) {\n\n            // we decided to remove this error because it brings complications when saving object with non-saved entities\n            // if related entity does not have a subject then it means user tries to bind entity which wasn't saved\n            // in this persistence because he didn't pass this entity for save or he did not set cascades\n            // but without entity being inserted we cannot bind it in the relation operation, so we throw an exception here\n            // if (!relatedEntitySubject)\n            //     throw new Error(`One-to-one inverse relation \"${relation.entityMetadata.name}.${relation.propertyPath}\" contains ` +\n            //         `entity which does not exist in the database yet, thus cannot be bind in the database. ` +\n            //         `Please setup cascade insertion or save entity before binding it.`);\n            if (!relatedEntitySubject)\n                return;\n\n            // okay, so related subject exist and its marked for insertion, then add a new change map\n            // by example: this will tell category to insert into its post relation our post we are working with\n            //             relatedEntitySubject is newly inserted CategorySubject\n            //             relation.inverseRelation is OneToOne owner relation inside Category\n            //             subject is Post needs to be inserted into Category\n            relatedEntitySubject.changeMaps.push({\n                relation: relation.inverseRelation!,\n                value: subject\n            });\n        }\n\n        // check if this binding really exist in the database\n        // by example: find our post if its already bind to category in the database and its not equal to what user tries to set\n        const areRelatedIdEqualWithDatabase = relatedEntityDatabaseRelationId && OrmUtils.deepCompare(relationIdMap, relatedEntityDatabaseRelationId);\n\n        // if they aren't equal it means its a new relation and we need to \"bind\" them\n        // by example: this will tell category to insert into its post relation our post we are working with\n        //             relatedEntitySubject is newly inserted CategorySubject\n        //             relation.inverseRelation is ManyToOne relation inside Category\n        //             subject is Post needs to be inserted into Category\n        if (!areRelatedIdEqualWithDatabase) {\n\n            // if there is no relatedEntitySubject then it means \"category\" wasn't persisted,\n            // but since we are going to update \"category\" table (since its an owning side of relation with join column)\n            // we create a new subject here:\n            if (!relatedEntitySubject) {\n                relatedEntitySubject = new Subject({\n                    metadata: relation.inverseEntityMetadata,\n                    canBeUpdated: true,\n                    identifier: relationIdMap\n                });\n                this.subjects.push(relatedEntitySubject);\n            }\n\n            relatedEntitySubject.changeMaps.push({\n                relation: relation.inverseRelation!,\n                value: subject\n            });\n        }\n    }\n\n}"],"sourceRoot":"../.."}