@mikro-orm/core 7.0.0-dev.30 → 7.0.0-dev.300
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EntityManager.d.ts +68 -60
- package/EntityManager.js +290 -259
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +109 -142
- package/README.md +2 -0
- package/cache/FileCacheAdapter.d.ts +1 -2
- package/cache/FileCacheAdapter.js +18 -11
- package/cache/GeneratedCacheAdapter.d.ts +0 -1
- package/cache/GeneratedCacheAdapter.js +0 -2
- package/cache/index.d.ts +0 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +12 -5
- package/connections/Connection.js +21 -12
- package/drivers/DatabaseDriver.d.ts +25 -16
- package/drivers/DatabaseDriver.js +119 -36
- package/drivers/IDatabaseDriver.d.ts +118 -23
- package/entity/BaseEntity.d.ts +63 -4
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +101 -29
- package/entity/Collection.js +436 -104
- package/entity/EntityAssigner.js +17 -17
- package/entity/EntityFactory.d.ts +7 -1
- package/entity/EntityFactory.js +87 -55
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +57 -19
- package/entity/EntityLoader.d.ts +11 -10
- package/entity/EntityLoader.js +213 -82
- package/entity/EntityRepository.d.ts +28 -8
- package/entity/EntityRepository.js +8 -2
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/Reference.d.ts +1 -5
- package/entity/Reference.js +15 -11
- package/entity/WrappedEntity.d.ts +3 -8
- package/entity/WrappedEntity.js +2 -7
- package/entity/defineEntity.d.ts +526 -310
- package/entity/defineEntity.js +134 -290
- package/entity/index.d.ts +2 -2
- package/entity/index.js +2 -2
- package/entity/utils.d.ts +6 -1
- package/entity/utils.js +34 -1
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +65 -0
- package/enums.d.ts +8 -6
- package/enums.js +2 -1
- package/errors.d.ts +20 -10
- package/errors.js +55 -23
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +19 -11
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +79 -34
- package/index.d.ts +2 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/DefaultLogger.js +1 -0
- package/logging/SimpleLogger.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +7 -6
- package/logging/index.d.ts +1 -0
- package/logging/index.js +1 -0
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +11 -0
- package/metadata/EntitySchema.d.ts +47 -23
- package/metadata/EntitySchema.js +92 -33
- package/metadata/MetadataDiscovery.d.ts +64 -9
- package/metadata/MetadataDiscovery.js +782 -325
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +66 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +72 -39
- package/metadata/MetadataValidator.d.ts +32 -9
- package/metadata/MetadataValidator.js +196 -41
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +40 -0
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +1 -1
- package/metadata/types.d.ts +577 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
- package/naming-strategy/AbstractNamingStrategy.js +20 -2
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
- package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
- package/naming-strategy/MongoNamingStrategy.js +6 -6
- package/naming-strategy/NamingStrategy.d.ts +28 -4
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/not-supported.d.ts +2 -0
- package/not-supported.js +4 -0
- package/package.json +22 -11
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +11 -15
- package/platforms/Platform.js +24 -44
- package/serialization/EntitySerializer.d.ts +6 -3
- package/serialization/EntitySerializer.js +46 -26
- package/serialization/EntityTransformer.js +33 -21
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +3 -3
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.js +1 -1
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +1 -0
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.js +2 -2
- package/types/DoubleType.js +1 -1
- package/types/EnumArrayType.js +1 -2
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/TinyIntType.js +1 -1
- package/types/Type.d.ts +2 -4
- package/types/Type.js +3 -3
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +1 -4
- package/types/index.d.ts +1 -1
- package/typings.d.ts +412 -155
- package/typings.js +99 -44
- package/unit-of-work/ChangeSet.d.ts +4 -6
- package/unit-of-work/ChangeSet.js +4 -5
- package/unit-of-work/ChangeSetComputer.d.ts +3 -8
- package/unit-of-work/ChangeSetComputer.js +41 -20
- package/unit-of-work/ChangeSetPersister.d.ts +13 -12
- package/unit-of-work/ChangeSetPersister.js +94 -36
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +13 -13
- package/unit-of-work/IdentityMap.d.ts +12 -0
- package/unit-of-work/IdentityMap.js +39 -1
- package/unit-of-work/UnitOfWork.d.ts +27 -3
- package/unit-of-work/UnitOfWork.js +248 -90
- package/utils/AbstractMigrator.d.ts +101 -0
- package/utils/AbstractMigrator.js +305 -0
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +28 -17
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +795 -211
- package/utils/Configuration.js +153 -194
- package/utils/ConfigurationLoader.d.ts +1 -52
- package/utils/ConfigurationLoader.js +1 -330
- package/utils/Cursor.d.ts +0 -3
- package/utils/Cursor.js +24 -11
- package/utils/DataloaderUtils.d.ts +10 -5
- package/utils/DataloaderUtils.js +29 -12
- package/utils/EntityComparator.d.ts +16 -9
- package/utils/EntityComparator.js +154 -56
- package/utils/QueryHelper.d.ts +18 -6
- package/utils/QueryHelper.js +76 -23
- package/utils/RawQueryFragment.d.ts +28 -34
- package/utils/RawQueryFragment.js +35 -71
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +9 -6
- package/utils/Utils.d.ts +15 -126
- package/utils/Utils.js +80 -382
- package/utils/clone.js +8 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +97 -0
- package/utils/fs-utils.d.ts +34 -0
- package/utils/fs-utils.js +196 -0
- package/utils/index.d.ts +1 -3
- package/utils/index.js +1 -3
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +46 -3
- package/decorators/Check.d.ts +0 -3
- package/decorators/Check.js +0 -13
- package/decorators/CreateRequestContext.d.ts +0 -3
- package/decorators/CreateRequestContext.js +0 -32
- package/decorators/Embeddable.d.ts +0 -8
- package/decorators/Embeddable.js +0 -11
- package/decorators/Embedded.d.ts +0 -12
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -33
- package/decorators/Entity.js +0 -12
- package/decorators/Enum.d.ts +0 -9
- package/decorators/Enum.js +0 -16
- package/decorators/Filter.d.ts +0 -2
- package/decorators/Filter.js +0 -8
- package/decorators/Formula.d.ts +0 -4
- package/decorators/Formula.js +0 -15
- package/decorators/Indexed.d.ts +0 -19
- package/decorators/Indexed.js +0 -20
- package/decorators/ManyToMany.d.ts +0 -42
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -34
- package/decorators/ManyToOne.js +0 -14
- package/decorators/OneToMany.d.ts +0 -28
- package/decorators/OneToMany.js +0 -17
- package/decorators/OneToOne.d.ts +0 -28
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -8
- package/decorators/PrimaryKey.js +0 -20
- package/decorators/Property.d.ts +0 -250
- package/decorators/Property.js +0 -32
- package/decorators/Transactional.d.ts +0 -14
- package/decorators/Transactional.js +0 -28
- package/decorators/hooks.d.ts +0 -16
- package/decorators/hooks.js +0 -47
- package/decorators/index.d.ts +0 -17
- package/decorators/index.js +0 -17
- package/entity/ArrayCollection.d.ts +0 -118
- package/entity/ArrayCollection.js +0 -407
- package/entity/EntityValidator.d.ts +0 -19
- package/entity/EntityValidator.js +0 -150
- package/metadata/ReflectMetadataProvider.d.ts +0 -8
- package/metadata/ReflectMetadataProvider.js +0 -44
- package/utils/resolveContextProvider.d.ts +0 -10
- package/utils/resolveContextProvider.js +0 -28
package/entity/EntityAssigner.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { inspect } from 'node:util';
|
|
2
1
|
import { Collection } from './Collection.js';
|
|
3
2
|
import { Utils } from '../utils/Utils.js';
|
|
4
3
|
import { Reference } from './Reference.js';
|
|
5
4
|
import { ReferenceKind, SCALAR_TYPES } from '../enums.js';
|
|
6
|
-
import {
|
|
5
|
+
import { validateProperty } from './validators.js';
|
|
7
6
|
import { helper, wrap } from './wrap.js';
|
|
8
7
|
import { EntityHelper } from './EntityHelper.js';
|
|
9
|
-
|
|
8
|
+
import { ValidationError } from '../errors.js';
|
|
10
9
|
export class EntityAssigner {
|
|
11
10
|
static assign(entity, data, options = {}) {
|
|
12
11
|
let opts = options;
|
|
@@ -78,7 +77,7 @@ export class EntityAssigner {
|
|
|
78
77
|
if (options.updateByPrimaryKey) {
|
|
79
78
|
const pk = Utils.extractPK(value, prop.targetMeta);
|
|
80
79
|
if (pk) {
|
|
81
|
-
const ref = options.em.getReference(prop.
|
|
80
|
+
const ref = options.em.getReference(prop.targetMeta.class, pk, options);
|
|
82
81
|
// if the PK differs, we want to change the target entity, not update it
|
|
83
82
|
const wrappedChild = helper(ref);
|
|
84
83
|
const sameTarget = wrappedChild.getSerializedPrimaryKey() === wrapped.getSerializedPrimaryKey();
|
|
@@ -94,8 +93,9 @@ export class EntityAssigner {
|
|
|
94
93
|
}
|
|
95
94
|
return EntityAssigner.assignReference(entity, value, prop, options.em, options);
|
|
96
95
|
}
|
|
97
|
-
if (prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.
|
|
98
|
-
|
|
96
|
+
if (prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && (prop.setter || !prop.getter)) {
|
|
97
|
+
validateProperty(prop, value, entity);
|
|
98
|
+
return entity[prop.name] = value;
|
|
99
99
|
}
|
|
100
100
|
if (prop.kind === ReferenceKind.EMBEDDED && EntityAssigner.validateEM(options.em)) {
|
|
101
101
|
return EntityAssigner.assignEmbeddable(entity, value, prop, options.em, options);
|
|
@@ -120,7 +120,7 @@ export class EntityAssigner {
|
|
|
120
120
|
}
|
|
121
121
|
const meta2 = helper(ref).__meta;
|
|
122
122
|
const prop2 = meta2.properties[prop.inversedBy || prop.mappedBy];
|
|
123
|
-
/* v8 ignore next
|
|
123
|
+
/* v8 ignore next */
|
|
124
124
|
if (prop2 && !ref[prop2.name]) {
|
|
125
125
|
if (Reference.isReference(ref)) {
|
|
126
126
|
ref.unwrap()[prop2.name] = Reference.wrapReference(entity, prop2);
|
|
@@ -141,13 +141,13 @@ export class EntityAssigner {
|
|
|
141
141
|
entity[prop.name] = Reference.wrapReference(value, prop);
|
|
142
142
|
}
|
|
143
143
|
else if (Utils.isPrimaryKey(value, true) && EntityAssigner.validateEM(em)) {
|
|
144
|
-
entity[prop.name] = prop.mapToPk ? value : Reference.wrapReference(em.getReference(prop.
|
|
144
|
+
entity[prop.name] = prop.mapToPk ? value : Reference.wrapReference(em.getReference(prop.targetMeta.class, value, options), prop);
|
|
145
145
|
}
|
|
146
146
|
else if (Utils.isPlainObject(value) && options.merge && EntityAssigner.validateEM(em)) {
|
|
147
|
-
entity[prop.name] = Reference.wrapReference(em.merge(prop.
|
|
147
|
+
entity[prop.name] = Reference.wrapReference(em.merge(prop.targetMeta.class, value, options), prop);
|
|
148
148
|
}
|
|
149
149
|
else if (Utils.isPlainObject(value) && EntityAssigner.validateEM(em)) {
|
|
150
|
-
entity[prop.name] = Reference.wrapReference(em.create(prop.
|
|
150
|
+
entity[prop.name] = Reference.wrapReference(em.create(prop.targetMeta.class, value, options), prop);
|
|
151
151
|
}
|
|
152
152
|
else {
|
|
153
153
|
const name = entity.constructor.name;
|
|
@@ -166,14 +166,14 @@ export class EntityAssigner {
|
|
|
166
166
|
if (options.updateNestedEntities && options.updateByPrimaryKey && Utils.isPlainObject(item)) {
|
|
167
167
|
const pk = Utils.extractPK(item, prop.targetMeta);
|
|
168
168
|
if (pk && EntityAssigner.validateEM(em)) {
|
|
169
|
-
const ref = em.getUnitOfWork().getById(prop.
|
|
169
|
+
const ref = em.getUnitOfWork().getById(prop.targetMeta.class, pk, options.schema);
|
|
170
170
|
if (ref) {
|
|
171
171
|
return EntityAssigner.assign(ref, item, options);
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
return this.createCollectionItem(item, em, prop, invalid, options);
|
|
175
175
|
}
|
|
176
|
-
/* v8 ignore next
|
|
176
|
+
/* v8 ignore next */
|
|
177
177
|
if (options.updateNestedEntities && !options.updateByPrimaryKey && collection[idx] && helper(collection[idx])?.isInitialized()) {
|
|
178
178
|
return EntityAssigner.assign(collection[idx], item, options);
|
|
179
179
|
}
|
|
@@ -181,7 +181,7 @@ export class EntityAssigner {
|
|
|
181
181
|
});
|
|
182
182
|
if (invalid.length > 0) {
|
|
183
183
|
const name = entity.constructor.name;
|
|
184
|
-
throw
|
|
184
|
+
throw ValidationError.invalidCollectionValues(name, prop.name, invalid);
|
|
185
185
|
}
|
|
186
186
|
if (Array.isArray(value)) {
|
|
187
187
|
collection.set(items);
|
|
@@ -207,7 +207,7 @@ export class EntityAssigner {
|
|
|
207
207
|
entity[propName].push(...Object.values(tmp));
|
|
208
208
|
});
|
|
209
209
|
}
|
|
210
|
-
const create = () => EntityAssigner.validateEM(em) && em.getEntityFactory().createEmbeddable(prop.
|
|
210
|
+
const create = () => EntityAssigner.validateEM(em) && em.getEntityFactory().createEmbeddable(prop.targetMeta.class, value, {
|
|
211
211
|
convertCustomTypes: options.convertCustomTypes,
|
|
212
212
|
newEntity: options.mergeEmbeddedProperties ? !('propName' in entity) : true,
|
|
213
213
|
});
|
|
@@ -221,13 +221,13 @@ export class EntityAssigner {
|
|
|
221
221
|
return item;
|
|
222
222
|
}
|
|
223
223
|
if (Utils.isPrimaryKey(item) && EntityAssigner.validateEM(em)) {
|
|
224
|
-
return em.getReference(prop.
|
|
224
|
+
return em.getReference(prop.targetMeta.class, item, options);
|
|
225
225
|
}
|
|
226
226
|
if (Utils.isPlainObject(item) && options.merge && EntityAssigner.validateEM(em)) {
|
|
227
|
-
return em.merge(prop.
|
|
227
|
+
return em.merge(prop.targetMeta.class, item, options);
|
|
228
228
|
}
|
|
229
229
|
if (Utils.isPlainObject(item) && EntityAssigner.validateEM(em)) {
|
|
230
|
-
return em.create(prop.
|
|
230
|
+
return em.create(prop.targetMeta.class, item, options);
|
|
231
231
|
}
|
|
232
232
|
invalid.push(item);
|
|
233
233
|
return item;
|
|
@@ -15,6 +15,12 @@ export interface FactoryOptions {
|
|
|
15
15
|
recomputeSnapshot?: boolean;
|
|
16
16
|
schema?: string;
|
|
17
17
|
parentSchema?: string;
|
|
18
|
+
normalizeAccessors?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Property name to use for identity map lookup instead of the primary key.
|
|
21
|
+
* This is useful for creating references by unique non-PK properties.
|
|
22
|
+
*/
|
|
23
|
+
key?: string;
|
|
18
24
|
}
|
|
19
25
|
export declare class EntityFactory {
|
|
20
26
|
private readonly em;
|
|
@@ -28,7 +34,7 @@ export declare class EntityFactory {
|
|
|
28
34
|
constructor(em: EntityManager);
|
|
29
35
|
create<T extends object, P extends string = string>(entityName: EntityName<T>, data: EntityData<T>, options?: FactoryOptions): New<T, P>;
|
|
30
36
|
mergeData<T extends object>(meta: EntityMetadata<T>, entity: T, data: EntityData<T>, options?: FactoryOptions): void;
|
|
31
|
-
createReference<T extends object>(entityName: EntityName<T>, id: Primary<T> | Primary<T>[] | Record<string, Primary<T>>, options?: Pick<FactoryOptions, 'merge' | 'convertCustomTypes' | 'schema'>): T;
|
|
37
|
+
createReference<T extends object>(entityName: EntityName<T>, id: Primary<T> | Primary<T>[] | Record<string, Primary<T>>, options?: Pick<FactoryOptions, 'merge' | 'convertCustomTypes' | 'schema' | 'key'>): T;
|
|
32
38
|
createEmbeddable<T extends object>(entityName: EntityName<T>, data: EntityData<T>, options?: Pick<FactoryOptions, 'newEntity' | 'convertCustomTypes'>): T;
|
|
33
39
|
getComparator(): EntityComparator;
|
|
34
40
|
private createEntity;
|
package/entity/EntityFactory.js
CHANGED
|
@@ -4,6 +4,7 @@ import { EventType, ReferenceKind } from '../enums.js';
|
|
|
4
4
|
import { Reference } from './Reference.js';
|
|
5
5
|
import { helper } from './wrap.js';
|
|
6
6
|
import { EntityHelper } from './EntityHelper.js';
|
|
7
|
+
import { JsonType } from '../types/JsonType.js';
|
|
7
8
|
export class EntityFactory {
|
|
8
9
|
em;
|
|
9
10
|
driver;
|
|
@@ -29,7 +30,6 @@ export class EntityFactory {
|
|
|
29
30
|
if (data.__entity) {
|
|
30
31
|
return data;
|
|
31
32
|
}
|
|
32
|
-
entityName = Utils.className(entityName);
|
|
33
33
|
const meta = this.metadata.get(entityName);
|
|
34
34
|
if (meta.virtual) {
|
|
35
35
|
data = { ...data };
|
|
@@ -37,8 +37,8 @@ export class EntityFactory {
|
|
|
37
37
|
this.hydrate(entity, meta, data, options);
|
|
38
38
|
return entity;
|
|
39
39
|
}
|
|
40
|
-
if (
|
|
41
|
-
|
|
40
|
+
if (meta.serializedPrimaryKey) {
|
|
41
|
+
this.denormalizePrimaryKey(meta, data);
|
|
42
42
|
}
|
|
43
43
|
const meta2 = this.processDiscriminatorColumn(meta, data);
|
|
44
44
|
const exists = this.findEntity(data, meta2, options);
|
|
@@ -59,7 +59,7 @@ export class EntityFactory {
|
|
|
59
59
|
wrapped.__initialized = options.initialized;
|
|
60
60
|
if (options.newEntity || meta.forceConstructor || meta.virtual) {
|
|
61
61
|
const tmp = { ...data };
|
|
62
|
-
meta.constructorParams
|
|
62
|
+
meta.constructorParams?.forEach(prop => delete tmp[prop]);
|
|
63
63
|
this.hydrate(entity, meta2, tmp, options);
|
|
64
64
|
// since we now process only a copy of the `data` via hydrator, but later we register the state with the full snapshot,
|
|
65
65
|
// we need to go through all props with custom types that have `ensureComparable: true` and ensure they are comparable
|
|
@@ -73,7 +73,9 @@ export class EntityFactory {
|
|
|
73
73
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
|
|
74
74
|
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
if (prop.customType instanceof JsonType && this.platform.convertsJsonAutomatically()) {
|
|
77
|
+
data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.platform, { key: prop.name, mode: 'hydration' });
|
|
78
|
+
}
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
}
|
|
@@ -81,8 +83,7 @@ export class EntityFactory {
|
|
|
81
83
|
else {
|
|
82
84
|
this.hydrate(entity, meta2, data, options);
|
|
83
85
|
}
|
|
84
|
-
|
|
85
|
-
if (exists && meta.discriminatorColumn && !(entity instanceof meta2.class)) {
|
|
86
|
+
if (exists && meta.root.inheritanceType && !(entity instanceof meta2.class)) {
|
|
86
87
|
Object.setPrototypeOf(entity, meta2.prototype);
|
|
87
88
|
}
|
|
88
89
|
if (options.merge && wrapped.hasPrimaryKey()) {
|
|
@@ -108,12 +109,12 @@ export class EntityFactory {
|
|
|
108
109
|
data = QueryHelper.processParams(data);
|
|
109
110
|
const existsData = this.comparator.prepareEntity(entity);
|
|
110
111
|
const originalEntityData = helper(entity).__originalEntityData ?? {};
|
|
111
|
-
const diff = this.comparator.diffEntities(meta.
|
|
112
|
+
const diff = this.comparator.diffEntities(meta.class, originalEntityData, existsData);
|
|
112
113
|
// version properties are not part of entity snapshots
|
|
113
114
|
if (meta.versionProperty && data[meta.versionProperty] && data[meta.versionProperty] !== originalEntityData[meta.versionProperty]) {
|
|
114
115
|
diff[meta.versionProperty] = data[meta.versionProperty];
|
|
115
116
|
}
|
|
116
|
-
const diff2 = this.comparator.diffEntities(meta.
|
|
117
|
+
const diff2 = this.comparator.diffEntities(meta.class, existsData, data, { includeInverseSides: true });
|
|
117
118
|
// do not override values changed by user
|
|
118
119
|
Utils.keys(diff).forEach(key => delete diff2[key]);
|
|
119
120
|
Utils.keys(diff2).filter(key => {
|
|
@@ -128,7 +129,10 @@ export class EntityFactory {
|
|
|
128
129
|
.filter(key => meta.properties[key]?.formula || [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind))
|
|
129
130
|
.forEach(key => diff2[key] = data[key]);
|
|
130
131
|
// rehydrated with the new values, skip those changed by user
|
|
131
|
-
|
|
132
|
+
// use full hydration if the entity is already initialized, even if the caller used `initialized: false`
|
|
133
|
+
// (e.g. from createReference), otherwise scalar properties in diff2 won't be applied
|
|
134
|
+
const initialized = options.initialized || helper(entity).__initialized;
|
|
135
|
+
this.hydrate(entity, meta, diff2, initialized ? { ...options, initialized } : options);
|
|
132
136
|
// we need to update the entity data only with keys that were not present before
|
|
133
137
|
const nullVal = this.config.get('forceUndefined') ? undefined : null;
|
|
134
138
|
Utils.keys(diff2).forEach(key => {
|
|
@@ -136,6 +140,10 @@ export class EntityFactory {
|
|
|
136
140
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
|
|
137
141
|
diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
|
|
138
142
|
}
|
|
143
|
+
if (!options.convertCustomTypes && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) && prop.customType?.ensureComparable(meta, prop) && diff2[key] != null) {
|
|
144
|
+
const converted = prop.customType.convertToJSValue(diff2[key], this.platform, { force: true });
|
|
145
|
+
diff2[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { fromQuery: true });
|
|
146
|
+
}
|
|
139
147
|
originalEntityData[key] = diff2[key] === null ? nullVal : diff2[key];
|
|
140
148
|
helper(entity).__loadedProperties.add(key);
|
|
141
149
|
});
|
|
@@ -146,20 +154,31 @@ export class EntityFactory {
|
|
|
146
154
|
// we just create the entity from scratch, which will automatically pick the right one from the identity map and call `mergeData` on it
|
|
147
155
|
data[prop.name]
|
|
148
156
|
.filter(child => Utils.isPlainObject(child)) // objects with prototype can be PKs (e.g. `ObjectId`)
|
|
149
|
-
.forEach(child => this.create(prop.
|
|
157
|
+
.forEach(child => this.create(prop.targetMeta.class, child, options)); // we can ignore the value, we just care about the `mergeData` call
|
|
150
158
|
return;
|
|
151
159
|
}
|
|
152
160
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name]) && entity[prop.name] && helper(entity[prop.name]).__initialized) {
|
|
153
|
-
this.create(prop.
|
|
161
|
+
this.create(prop.targetMeta.class, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
|
|
154
162
|
}
|
|
155
163
|
});
|
|
156
|
-
|
|
164
|
+
this.unitOfWork.normalizeEntityData(meta, originalEntityData);
|
|
157
165
|
}
|
|
158
166
|
createReference(entityName, id, options = {}) {
|
|
159
167
|
options.convertCustomTypes ??= true;
|
|
160
|
-
entityName = Utils.className(entityName);
|
|
161
168
|
const meta = this.metadata.get(entityName);
|
|
162
169
|
const schema = this.driver.getSchemaName(meta, options);
|
|
170
|
+
// Handle alternate key lookup
|
|
171
|
+
if (options.key) {
|
|
172
|
+
const value = '' + (Array.isArray(id) ? id[0] : Utils.isPlainObject(id) ? id[options.key] : id);
|
|
173
|
+
const exists = this.unitOfWork.getByKey(entityName, options.key, value, schema, options.convertCustomTypes);
|
|
174
|
+
if (exists) {
|
|
175
|
+
return exists;
|
|
176
|
+
}
|
|
177
|
+
// Create entity stub - storeByKey will set the alternate key property and store in identity map
|
|
178
|
+
const entity = this.create(entityName, {}, { ...options, initialized: false });
|
|
179
|
+
this.unitOfWork.storeByKey(entity, options.key, value, schema, options.convertCustomTypes);
|
|
180
|
+
return entity;
|
|
181
|
+
}
|
|
163
182
|
if (meta.simplePK) {
|
|
164
183
|
const exists = this.unitOfWork.getById(entityName, id, schema);
|
|
165
184
|
if (exists) {
|
|
@@ -182,7 +201,6 @@ export class EntityFactory {
|
|
|
182
201
|
return this.create(entityName, id, { ...options, initialized: false });
|
|
183
202
|
}
|
|
184
203
|
createEmbeddable(entityName, data, options = {}) {
|
|
185
|
-
entityName = Utils.className(entityName);
|
|
186
204
|
data = { ...data };
|
|
187
205
|
const meta = this.metadata.get(entityName);
|
|
188
206
|
const meta2 = this.processDiscriminatorColumn(meta, data);
|
|
@@ -194,7 +212,7 @@ export class EntityFactory {
|
|
|
194
212
|
createEntity(data, meta, options) {
|
|
195
213
|
const schema = this.driver.getSchemaName(meta, options);
|
|
196
214
|
if (options.newEntity || meta.forceConstructor || meta.virtual) {
|
|
197
|
-
if (
|
|
215
|
+
if (meta.polymorphs) {
|
|
198
216
|
throw new Error(`Cannot create entity ${meta.className}, class prototype is unknown`);
|
|
199
217
|
}
|
|
200
218
|
const params = this.extractConstructorParams(meta, data, options);
|
|
@@ -240,10 +258,10 @@ export class EntityFactory {
|
|
|
240
258
|
}
|
|
241
259
|
hydrate(entity, meta, data, options) {
|
|
242
260
|
if (options.initialized) {
|
|
243
|
-
this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
|
|
261
|
+
this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options), options.normalizeAccessors);
|
|
244
262
|
}
|
|
245
263
|
else {
|
|
246
|
-
this.hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
|
|
264
|
+
this.hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options), options.normalizeAccessors);
|
|
247
265
|
}
|
|
248
266
|
Utils.keys(data).forEach(key => {
|
|
249
267
|
helper(entity)?.__loadedProperties.add(key);
|
|
@@ -257,75 +275,89 @@ export class EntityFactory {
|
|
|
257
275
|
findEntity(data, meta, options) {
|
|
258
276
|
const schema = this.driver.getSchemaName(meta, options);
|
|
259
277
|
if (meta.simplePK) {
|
|
260
|
-
return this.unitOfWork.getById(meta.
|
|
278
|
+
return this.unitOfWork.getById(meta.class, data[meta.primaryKeys[0]], schema);
|
|
261
279
|
}
|
|
262
280
|
if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
|
|
263
281
|
return undefined;
|
|
264
282
|
}
|
|
265
283
|
const pks = Utils.getOrderedPrimaryKeys(data, meta, this.platform, options.convertCustomTypes);
|
|
266
|
-
return this.unitOfWork.getById(meta.
|
|
284
|
+
return this.unitOfWork.getById(meta.class, pks, schema);
|
|
267
285
|
}
|
|
268
286
|
processDiscriminatorColumn(meta, data) {
|
|
269
|
-
|
|
287
|
+
// Handle STI discriminator (persisted column)
|
|
288
|
+
if (meta.root.inheritanceType === 'sti') {
|
|
289
|
+
const prop = meta.properties[meta.root.discriminatorColumn];
|
|
290
|
+
const value = data[prop.name];
|
|
291
|
+
const type = meta.root.discriminatorMap[value];
|
|
292
|
+
meta = type ? this.metadata.get(type) : meta;
|
|
270
293
|
return meta;
|
|
271
294
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
295
|
+
// Handle TPT discriminator (computed at query time)
|
|
296
|
+
if (meta.root.inheritanceType === 'tpt' && meta.root.discriminatorMap) {
|
|
297
|
+
const value = data[meta.root.tptDiscriminatorColumn];
|
|
298
|
+
if (value) {
|
|
299
|
+
const type = meta.root.discriminatorMap[value];
|
|
300
|
+
meta = type ? this.metadata.get(type) : meta;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
276
303
|
return meta;
|
|
277
304
|
}
|
|
278
305
|
/**
|
|
279
306
|
* denormalize PK to value required by driver (e.g. ObjectId)
|
|
280
307
|
*/
|
|
281
|
-
denormalizePrimaryKey(
|
|
282
|
-
const pk =
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
data[
|
|
308
|
+
denormalizePrimaryKey(meta, data) {
|
|
309
|
+
const pk = meta.getPrimaryProp();
|
|
310
|
+
const spk = meta.properties[meta.serializedPrimaryKey];
|
|
311
|
+
if (!spk?.serializedPrimaryKey) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (pk.type === 'ObjectId' && (data[pk.name] != null || data[spk.name] != null)) {
|
|
315
|
+
data[pk.name] = this.platform.denormalizePrimaryKey((data[spk.name] || data[pk.name]));
|
|
316
|
+
delete data[spk.name];
|
|
290
317
|
}
|
|
291
318
|
}
|
|
292
319
|
/**
|
|
293
320
|
* returns parameters for entity constructor, creating references from plain ids
|
|
294
321
|
*/
|
|
295
322
|
extractConstructorParams(meta, data, options) {
|
|
323
|
+
if (!meta.constructorParams) {
|
|
324
|
+
return [data];
|
|
325
|
+
}
|
|
296
326
|
return meta.constructorParams.map(k => {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
327
|
+
const prop = meta.properties[k];
|
|
328
|
+
const value = data[k];
|
|
329
|
+
if (prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && value) {
|
|
330
|
+
const pk = Reference.unwrapReference(value);
|
|
331
|
+
const entity = this.unitOfWork.getById(prop.targetMeta.class, pk, options.schema, true);
|
|
300
332
|
if (entity) {
|
|
301
333
|
return entity;
|
|
302
334
|
}
|
|
303
|
-
if (Utils.isEntity(
|
|
304
|
-
return
|
|
335
|
+
if (Utils.isEntity(value)) {
|
|
336
|
+
return value;
|
|
305
337
|
}
|
|
306
|
-
const nakedPk = Utils.extractPK(
|
|
307
|
-
if (Utils.isObject(
|
|
308
|
-
return this.create(
|
|
338
|
+
const nakedPk = Utils.extractPK(value, prop.targetMeta, true);
|
|
339
|
+
if (Utils.isObject(value) && !nakedPk) {
|
|
340
|
+
return this.create(prop.targetMeta.class, value, options);
|
|
309
341
|
}
|
|
310
342
|
const { newEntity, initialized, ...rest } = options;
|
|
311
|
-
const target = this.createReference(
|
|
312
|
-
return Reference.wrapReference(target,
|
|
343
|
+
const target = this.createReference(prop.targetMeta.class, nakedPk, rest);
|
|
344
|
+
return Reference.wrapReference(target, prop);
|
|
313
345
|
}
|
|
314
|
-
if (
|
|
315
|
-
/* v8 ignore next
|
|
316
|
-
if (Utils.isEntity(
|
|
317
|
-
return
|
|
346
|
+
if (prop?.kind === ReferenceKind.EMBEDDED && value) {
|
|
347
|
+
/* v8 ignore next */
|
|
348
|
+
if (Utils.isEntity(value)) {
|
|
349
|
+
return value;
|
|
318
350
|
}
|
|
319
|
-
return this.createEmbeddable(
|
|
351
|
+
return this.createEmbeddable(prop.targetMeta.class, value, options);
|
|
320
352
|
}
|
|
321
|
-
if (!
|
|
353
|
+
if (!prop) {
|
|
322
354
|
const tmp = { ...data };
|
|
323
355
|
for (const prop of meta.props) {
|
|
324
356
|
if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
|
|
325
357
|
continue;
|
|
326
358
|
}
|
|
327
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(tmp[prop.name]) && !Utils.extractPK(tmp[prop.name],
|
|
328
|
-
tmp[prop.name] = Reference.wrapReference(this.create(
|
|
359
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(tmp[prop.name]) && !Utils.extractPK(tmp[prop.name], prop.targetMeta, true)) {
|
|
360
|
+
tmp[prop.name] = Reference.wrapReference(this.create(prop.targetMeta.class, tmp[prop.name], options), prop);
|
|
329
361
|
}
|
|
330
362
|
else if (prop.kind === ReferenceKind.SCALAR) {
|
|
331
363
|
tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.platform);
|
|
@@ -333,10 +365,10 @@ export class EntityFactory {
|
|
|
333
365
|
}
|
|
334
366
|
return tmp;
|
|
335
367
|
}
|
|
336
|
-
if (options.convertCustomTypes &&
|
|
337
|
-
return
|
|
368
|
+
if (options.convertCustomTypes && prop.customType && value != null) {
|
|
369
|
+
return prop.customType.convertToJSValue(value, this.platform);
|
|
338
370
|
}
|
|
339
|
-
return
|
|
371
|
+
return value;
|
|
340
372
|
});
|
|
341
373
|
}
|
|
342
374
|
get unitOfWork() {
|
package/entity/EntityHelper.d.ts
CHANGED
|
@@ -6,9 +6,9 @@ import { type EntityMetadata, type EntityProperty, type IHydrator } from '../typ
|
|
|
6
6
|
export declare class EntityHelper {
|
|
7
7
|
static decorate<T extends object>(meta: EntityMetadata<T>, em: EntityManager): void;
|
|
8
8
|
/**
|
|
9
|
-
* As a performance optimization, we create entity state methods
|
|
9
|
+
* As a performance optimization, we create entity state methods lazily. We first add
|
|
10
10
|
* the `null` value to the prototype to reserve space in memory. Then we define a setter on the
|
|
11
|
-
* prototype
|
|
11
|
+
* prototype that will be executed exactly once per entity instance. There we redefine the given
|
|
12
12
|
* property on the entity instance, so shadowing the prototype setter.
|
|
13
13
|
*/
|
|
14
14
|
private static defineBaseProperties;
|
package/entity/EntityHelper.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { EagerProps, EntityRepositoryType, HiddenProps, OptionalProps, PrimaryKeyProp, } from '../typings.js';
|
|
1
|
+
import { EagerProps, EntityName, EntityRepositoryType, HiddenProps, OptionalProps, PrimaryKeyProp, } from '../typings.js';
|
|
3
2
|
import { EntityTransformer } from '../serialization/EntityTransformer.js';
|
|
4
3
|
import { Reference } from './Reference.js';
|
|
5
4
|
import { Utils } from '../utils/Utils.js';
|
|
6
5
|
import { WrappedEntity } from './WrappedEntity.js';
|
|
7
6
|
import { ReferenceKind } from '../enums.js';
|
|
8
7
|
import { helper } from './wrap.js';
|
|
8
|
+
import { inspect } from '../logging/inspect.js';
|
|
9
|
+
import { getEnv } from '../utils/env-vars.js';
|
|
9
10
|
/**
|
|
10
11
|
* @internal
|
|
11
12
|
*/
|
|
@@ -31,15 +32,25 @@ export class EntityHelper {
|
|
|
31
32
|
}
|
|
32
33
|
const prototype = meta.prototype;
|
|
33
34
|
if (!prototype.toJSON) { // toJSON can be overridden
|
|
34
|
-
prototype
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
Object.defineProperty(prototype, 'toJSON', {
|
|
36
|
+
value: function (...args) {
|
|
37
|
+
// Guard against being called on the prototype itself (e.g. by serializers
|
|
38
|
+
// walking the object graph and calling toJSON on prototype objects)
|
|
39
|
+
if (this === prototype) {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
return EntityTransformer.toObject(this, ...args);
|
|
43
|
+
},
|
|
44
|
+
writable: true,
|
|
45
|
+
configurable: true,
|
|
46
|
+
enumerable: false,
|
|
47
|
+
});
|
|
37
48
|
}
|
|
38
49
|
}
|
|
39
50
|
/**
|
|
40
|
-
* As a performance optimization, we create entity state methods
|
|
51
|
+
* As a performance optimization, we create entity state methods lazily. We first add
|
|
41
52
|
* the `null` value to the prototype to reserve space in memory. Then we define a setter on the
|
|
42
|
-
* prototype
|
|
53
|
+
* prototype that will be executed exactly once per entity instance. There we redefine the given
|
|
43
54
|
* property on the entity instance, so shadowing the prototype setter.
|
|
44
55
|
*/
|
|
45
56
|
static defineBaseProperties(meta, prototype, em) {
|
|
@@ -87,7 +98,7 @@ export class EntityHelper {
|
|
|
87
98
|
});
|
|
88
99
|
return;
|
|
89
100
|
}
|
|
90
|
-
if (prop.inherited || prop.primary || prop.
|
|
101
|
+
if (prop.inherited || prop.primary || prop.accessor || prop.persist === false || prop.embedded || isCollection) {
|
|
91
102
|
return;
|
|
92
103
|
}
|
|
93
104
|
Object.defineProperty(meta.prototype, prop.name, {
|
|
@@ -98,13 +109,11 @@ export class EntityHelper {
|
|
|
98
109
|
},
|
|
99
110
|
set(val) {
|
|
100
111
|
this.__helper.__data[prop.name] = val;
|
|
101
|
-
this.__helper.__touched = !this.__helper.hydrator.isRunning();
|
|
102
112
|
},
|
|
103
113
|
enumerable: true,
|
|
104
114
|
configurable: true,
|
|
105
115
|
});
|
|
106
116
|
this.__helper.__data[prop.name] = val;
|
|
107
|
-
this.__helper.__touched = !this.__helper.hydrator.isRunning();
|
|
108
117
|
},
|
|
109
118
|
configurable: true,
|
|
110
119
|
});
|
|
@@ -112,16 +121,27 @@ export class EntityHelper {
|
|
|
112
121
|
}
|
|
113
122
|
static defineCustomInspect(meta) {
|
|
114
123
|
// @ts-ignore
|
|
115
|
-
meta.prototype[inspect.custom] ??= function (depth = 2) {
|
|
116
|
-
const object = {
|
|
124
|
+
meta.prototype[Symbol.for('nodejs.util.inspect.custom')] ??= function (depth = 2) {
|
|
125
|
+
const object = {};
|
|
126
|
+
const keys = new Set(Utils.keys(this));
|
|
127
|
+
for (const prop of meta.props) {
|
|
128
|
+
if (keys.has(prop.name) || (prop.getter && prop.accessor === prop.name)) {
|
|
129
|
+
object[prop.name] = this[prop.name];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
for (const key of keys) {
|
|
133
|
+
if (!meta.properties[key]) {
|
|
134
|
+
object[key] = this[key];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
117
137
|
// ensure we dont have internal symbols in the POJO
|
|
118
|
-
[OptionalProps, EntityRepositoryType, PrimaryKeyProp, EagerProps, HiddenProps].forEach(sym => delete object[sym]);
|
|
138
|
+
[OptionalProps, EntityRepositoryType, PrimaryKeyProp, EagerProps, HiddenProps, EntityName].forEach(sym => delete object[sym]);
|
|
119
139
|
meta.props
|
|
120
140
|
.filter(prop => object[prop.name] === undefined)
|
|
121
141
|
.forEach(prop => delete object[prop.name]);
|
|
122
142
|
const ret = inspect(object, { depth });
|
|
123
|
-
let name =
|
|
124
|
-
const showEM = ['true', 't', '1'].includes(
|
|
143
|
+
let name = this.constructor.name;
|
|
144
|
+
const showEM = ['true', 't', '1'].includes(getEnv('MIKRO_ORM_LOG_EM_ID')?.toLowerCase() ?? '');
|
|
125
145
|
if (showEM) {
|
|
126
146
|
if (helper(this).__em) {
|
|
127
147
|
name += ` [managed by ${helper(this).__em.id}]`;
|
|
@@ -154,9 +174,6 @@ export class EntityHelper {
|
|
|
154
174
|
if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
|
|
155
175
|
wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(wrapped.__data[prop.name], prop.targetMeta, true);
|
|
156
176
|
}
|
|
157
|
-
else {
|
|
158
|
-
wrapped.__touched = !hydrator.isRunning();
|
|
159
|
-
}
|
|
160
177
|
EntityHelper.propagate(meta, entity, this, prop, Reference.unwrapReference(val), old);
|
|
161
178
|
},
|
|
162
179
|
enumerable: true,
|
|
@@ -164,7 +181,19 @@ export class EntityHelper {
|
|
|
164
181
|
});
|
|
165
182
|
}
|
|
166
183
|
static propagate(meta, entity, owner, prop, value, old) {
|
|
167
|
-
|
|
184
|
+
// For polymorphic relations, get bidirectional relations from the actual entity's metadata
|
|
185
|
+
let bidirectionalRelations;
|
|
186
|
+
if (prop.polymorphic && prop.polymorphTargets?.length) {
|
|
187
|
+
// For polymorphic relations, we need to get the bidirectional relations from the actual value's metadata
|
|
188
|
+
if (!value) {
|
|
189
|
+
return; // No value means no propagation needed
|
|
190
|
+
}
|
|
191
|
+
bidirectionalRelations = helper(value).__meta.bidirectionalRelations;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
bidirectionalRelations = prop.targetMeta.bidirectionalRelations;
|
|
195
|
+
}
|
|
196
|
+
for (const prop2 of bidirectionalRelations) {
|
|
168
197
|
if ((prop2.inversedBy || prop2.mappedBy) !== prop.name) {
|
|
169
198
|
continue;
|
|
170
199
|
}
|
|
@@ -172,6 +201,10 @@ export class EntityHelper {
|
|
|
172
201
|
continue;
|
|
173
202
|
}
|
|
174
203
|
const inverse = value?.[prop2.name];
|
|
204
|
+
if (prop.ref && owner[prop.name]) {
|
|
205
|
+
// eslint-disable-next-line dot-notation
|
|
206
|
+
owner[prop.name]['property'] = prop;
|
|
207
|
+
}
|
|
175
208
|
if (Utils.isCollection(inverse) && inverse.isPartial()) {
|
|
176
209
|
continue;
|
|
177
210
|
}
|
|
@@ -202,6 +235,11 @@ export class EntityHelper {
|
|
|
202
235
|
helper(other).__em?.getUnitOfWork().scheduleOrphanRemoval(other);
|
|
203
236
|
}
|
|
204
237
|
}
|
|
238
|
+
// Skip setting the inverse side to null if it's a primary key - the entity will be removed via orphan removal
|
|
239
|
+
// Setting a primary key to null would corrupt the entity and cause validation errors
|
|
240
|
+
if (value == null && prop.orphanRemoval && prop2.primary) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
205
243
|
if (value == null) {
|
|
206
244
|
entity[prop2.name] = value;
|
|
207
245
|
}
|