@mikro-orm/core 7.0.15-dev.8 → 7.0.15
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 +885 -584
- package/EntityManager.js +1939 -1911
- package/MikroORM.d.ts +103 -74
- package/MikroORM.js +177 -178
- package/README.md +1 -1
- package/cache/CacheAdapter.d.ts +36 -36
- package/cache/FileCacheAdapter.d.ts +30 -24
- package/cache/FileCacheAdapter.js +80 -78
- package/cache/GeneratedCacheAdapter.d.ts +18 -20
- package/cache/GeneratedCacheAdapter.js +30 -30
- package/cache/MemoryCacheAdapter.d.ts +18 -20
- package/cache/MemoryCacheAdapter.js +35 -36
- package/cache/NullCacheAdapter.d.ts +16 -16
- package/cache/NullCacheAdapter.js +24 -24
- package/connections/Connection.d.ts +95 -84
- package/connections/Connection.js +165 -168
- package/drivers/DatabaseDriver.d.ts +186 -80
- package/drivers/DatabaseDriver.js +450 -443
- package/drivers/IDatabaseDriver.d.ts +444 -305
- package/entity/BaseEntity.d.ts +120 -83
- package/entity/BaseEntity.js +43 -43
- package/entity/Collection.d.ts +215 -181
- package/entity/Collection.js +730 -724
- package/entity/EntityAssigner.d.ts +88 -77
- package/entity/EntityAssigner.js +231 -230
- package/entity/EntityFactory.d.ts +67 -55
- package/entity/EntityFactory.js +454 -413
- package/entity/EntityHelper.d.ts +35 -23
- package/entity/EntityHelper.js +302 -290
- package/entity/EntityIdentifier.d.ts +4 -4
- package/entity/EntityIdentifier.js +10 -10
- package/entity/EntityLoader.d.ts +98 -72
- package/entity/EntityLoader.js +815 -786
- package/entity/EntityRepository.d.ts +316 -201
- package/entity/EntityRepository.js +213 -213
- package/entity/PolymorphicRef.d.ts +5 -5
- package/entity/PolymorphicRef.js +10 -10
- package/entity/Reference.d.ts +127 -83
- package/entity/Reference.js +281 -277
- package/entity/WrappedEntity.d.ts +115 -72
- package/entity/WrappedEntity.js +168 -166
- package/entity/defineEntity.d.ts +1412 -659
- package/entity/defineEntity.js +527 -518
- package/entity/utils.d.ts +13 -3
- package/entity/utils.js +71 -73
- package/entity/validators.js +43 -43
- package/entity/wrap.js +8 -8
- package/enums.d.ts +258 -253
- package/enums.js +251 -252
- package/errors.d.ts +114 -72
- package/errors.js +350 -253
- package/events/EventManager.d.ts +26 -14
- package/events/EventManager.js +79 -77
- package/events/EventSubscriber.d.ts +29 -29
- package/events/TransactionEventBroadcaster.d.ts +15 -8
- package/events/TransactionEventBroadcaster.js +14 -14
- package/exceptions.d.ts +23 -40
- package/exceptions.js +35 -52
- package/hydration/Hydrator.d.ts +42 -17
- package/hydration/Hydrator.js +43 -43
- package/hydration/ObjectHydrator.d.ts +50 -17
- package/hydration/ObjectHydrator.js +483 -418
- package/index.d.ts +118 -2
- package/index.js +10 -1
- package/logging/DefaultLogger.d.ts +34 -32
- package/logging/DefaultLogger.js +86 -86
- package/logging/Logger.d.ts +41 -41
- package/logging/SimpleLogger.d.ts +13 -11
- package/logging/SimpleLogger.js +22 -22
- package/logging/colors.d.ts +6 -6
- package/logging/colors.js +11 -10
- package/logging/inspect.js +7 -7
- package/metadata/EntitySchema.d.ts +214 -130
- package/metadata/EntitySchema.js +411 -412
- package/metadata/MetadataDiscovery.d.ts +115 -115
- package/metadata/MetadataDiscovery.js +2013 -1931
- package/metadata/MetadataProvider.d.ts +29 -26
- package/metadata/MetadataProvider.js +95 -97
- package/metadata/MetadataStorage.d.ts +38 -32
- package/metadata/MetadataStorage.js +118 -118
- package/metadata/MetadataValidator.d.ts +39 -39
- package/metadata/MetadataValidator.js +385 -340
- package/metadata/discover-entities.d.ts +5 -2
- package/metadata/discover-entities.js +35 -37
- package/metadata/types.d.ts +615 -531
- package/naming-strategy/AbstractNamingStrategy.d.ts +54 -39
- package/naming-strategy/AbstractNamingStrategy.js +90 -85
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +6 -6
- package/naming-strategy/EntityCaseNamingStrategy.js +22 -22
- package/naming-strategy/MongoNamingStrategy.d.ts +6 -6
- package/naming-strategy/MongoNamingStrategy.js +18 -18
- package/naming-strategy/NamingStrategy.d.ts +109 -99
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +7 -7
- package/naming-strategy/UnderscoreNamingStrategy.js +21 -21
- package/not-supported.js +7 -4
- package/package.json +1 -1
- package/platforms/ExceptionConverter.d.ts +1 -1
- package/platforms/ExceptionConverter.js +4 -4
- package/platforms/Platform.d.ts +312 -303
- package/platforms/Platform.js +715 -695
- package/serialization/EntitySerializer.d.ts +49 -26
- package/serialization/EntitySerializer.js +224 -218
- package/serialization/EntityTransformer.d.ts +10 -6
- package/serialization/EntityTransformer.js +219 -217
- package/serialization/SerializationContext.d.ts +27 -23
- package/serialization/SerializationContext.js +105 -105
- package/types/ArrayType.d.ts +8 -8
- package/types/ArrayType.js +33 -33
- package/types/BigIntType.d.ts +17 -10
- package/types/BigIntType.js +37 -37
- package/types/BlobType.d.ts +3 -3
- package/types/BlobType.js +13 -13
- package/types/BooleanType.d.ts +4 -4
- package/types/BooleanType.js +12 -12
- package/types/CharacterType.d.ts +2 -2
- package/types/CharacterType.js +6 -6
- package/types/DateTimeType.d.ts +5 -5
- package/types/DateTimeType.js +15 -15
- package/types/DateType.d.ts +5 -5
- package/types/DateType.js +15 -15
- package/types/DecimalType.d.ts +7 -7
- package/types/DecimalType.js +26 -26
- package/types/DoubleType.d.ts +3 -3
- package/types/DoubleType.js +12 -12
- package/types/EnumArrayType.d.ts +5 -5
- package/types/EnumArrayType.js +24 -24
- package/types/EnumType.d.ts +3 -3
- package/types/EnumType.js +11 -11
- package/types/FloatType.d.ts +3 -3
- package/types/FloatType.js +9 -9
- package/types/IntegerType.d.ts +3 -3
- package/types/IntegerType.js +9 -9
- package/types/IntervalType.d.ts +4 -4
- package/types/IntervalType.js +12 -12
- package/types/JsonType.d.ts +8 -8
- package/types/JsonType.js +32 -32
- package/types/MediumIntType.d.ts +1 -1
- package/types/MediumIntType.js +3 -3
- package/types/SmallIntType.d.ts +3 -3
- package/types/SmallIntType.js +9 -9
- package/types/StringType.d.ts +4 -4
- package/types/StringType.js +12 -12
- package/types/TextType.d.ts +3 -3
- package/types/TextType.js +9 -9
- package/types/TimeType.d.ts +5 -5
- package/types/TimeType.js +17 -17
- package/types/TinyIntType.d.ts +3 -3
- package/types/TinyIntType.js +10 -10
- package/types/Type.d.ts +83 -79
- package/types/Type.js +82 -82
- package/types/Uint8ArrayType.d.ts +4 -4
- package/types/Uint8ArrayType.js +21 -21
- package/types/UnknownType.d.ts +4 -4
- package/types/UnknownType.js +12 -12
- package/types/UuidType.d.ts +5 -5
- package/types/UuidType.js +19 -19
- package/types/index.d.ts +75 -49
- package/types/index.js +52 -26
- package/typings.d.ts +1268 -746
- package/typings.js +246 -233
- package/unit-of-work/ChangeSet.d.ts +26 -26
- package/unit-of-work/ChangeSet.js +56 -56
- package/unit-of-work/ChangeSetComputer.d.ts +12 -12
- package/unit-of-work/ChangeSetComputer.js +187 -179
- package/unit-of-work/ChangeSetPersister.d.ts +69 -50
- package/unit-of-work/ChangeSetPersister.js +469 -446
- package/unit-of-work/CommitOrderCalculator.d.ts +40 -40
- package/unit-of-work/CommitOrderCalculator.js +89 -88
- package/unit-of-work/IdentityMap.d.ts +31 -31
- package/unit-of-work/IdentityMap.js +105 -105
- package/unit-of-work/UnitOfWork.d.ts +181 -141
- package/unit-of-work/UnitOfWork.js +1240 -1226
- package/utils/AbstractMigrator.d.ts +112 -92
- package/utils/AbstractMigrator.js +286 -288
- package/utils/AbstractSchemaGenerator.d.ts +43 -34
- package/utils/AbstractSchemaGenerator.js +121 -122
- package/utils/AsyncContext.d.ts +3 -3
- package/utils/AsyncContext.js +34 -35
- package/utils/Configuration.d.ts +852 -808
- package/utils/Configuration.js +359 -344
- package/utils/Cursor.d.ts +40 -22
- package/utils/Cursor.js +135 -127
- package/utils/DataloaderUtils.d.ts +58 -43
- package/utils/DataloaderUtils.js +203 -198
- package/utils/EntityComparator.d.ts +99 -82
- package/utils/EntityComparator.js +836 -744
- package/utils/NullHighlighter.d.ts +1 -1
- package/utils/NullHighlighter.js +3 -3
- package/utils/QueryHelper.d.ts +85 -57
- package/utils/QueryHelper.js +404 -392
- package/utils/RawQueryFragment.d.ts +50 -34
- package/utils/RawQueryFragment.js +122 -121
- package/utils/RequestContext.d.ts +32 -32
- package/utils/RequestContext.js +52 -53
- package/utils/TransactionContext.d.ts +16 -16
- package/utils/TransactionContext.js +27 -27
- package/utils/TransactionManager.d.ts +58 -58
- package/utils/TransactionManager.js +202 -200
- package/utils/Utils.d.ts +204 -145
- package/utils/Utils.js +826 -826
- package/utils/clone.js +105 -114
- package/utils/env-vars.js +90 -88
- package/utils/fs-utils.d.ts +15 -15
- package/utils/fs-utils.js +181 -182
- package/utils/upsert-utils.d.ts +20 -5
- package/utils/upsert-utils.js +114 -116
package/entity/EntityFactory.js
CHANGED
|
@@ -8,441 +8,482 @@ import { JsonType } from '../types/JsonType.js';
|
|
|
8
8
|
import { isRaw } from '../utils/RawQueryFragment.js';
|
|
9
9
|
/** @internal Factory responsible for creating, merging, and hydrating entity instances. */
|
|
10
10
|
export class EntityFactory {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
11
|
+
#driver;
|
|
12
|
+
#platform;
|
|
13
|
+
#config;
|
|
14
|
+
#metadata;
|
|
15
|
+
#hydrator;
|
|
16
|
+
#eventManager;
|
|
17
|
+
#comparator;
|
|
18
|
+
#em;
|
|
19
|
+
constructor(em) {
|
|
20
|
+
this.#em = em;
|
|
21
|
+
this.#driver = this.#em.getDriver();
|
|
22
|
+
this.#platform = this.#driver.getPlatform();
|
|
23
|
+
this.#config = this.#em.config;
|
|
24
|
+
this.#metadata = this.#em.getMetadata();
|
|
25
|
+
this.#hydrator = this.#config.getHydrator(this.#metadata);
|
|
26
|
+
this.#eventManager = this.#em.getEventManager();
|
|
27
|
+
this.#comparator = this.#em.getComparator();
|
|
28
|
+
}
|
|
29
|
+
/** Creates a new entity instance or returns an existing one from the identity map, hydrating it with the provided data. */
|
|
30
|
+
create(entityName, data, options = {}) {
|
|
31
|
+
data = Reference.unwrapReference(data);
|
|
32
|
+
options.initialized ??= true;
|
|
33
|
+
if (EntityHelper.isEntity(data)) {
|
|
34
|
+
return data;
|
|
28
35
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// ensure all data keys are tracked as loaded for shouldRefresh checks,
|
|
64
|
-
// but don't overwrite __originalEntityData — mergeData already set it
|
|
65
|
-
// with DB values for non-user-modified keys, leaving user changes detectable
|
|
66
|
-
for (const key of Utils.keys(data)) {
|
|
67
|
-
if (meta2.properties[key]) {
|
|
68
|
-
wrapped.__loadedProperties.add(key);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (this.#eventManager.hasListeners(EventType.onInit, meta2)) {
|
|
73
|
-
this.#eventManager.dispatchEvent(EventType.onInit, { entity: exists, meta: meta2, em: this.#em });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return exists;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
data = { ...data };
|
|
80
|
-
const entity = exists ?? this.createEntity(data, meta2, options);
|
|
81
|
-
wrapped = helper(entity);
|
|
82
|
-
wrapped.__processing = true;
|
|
83
|
-
wrapped.__initialized = options.initialized;
|
|
84
|
-
if (options.newEntity || meta.forceConstructor || meta.virtual) {
|
|
85
|
-
const tmp = { ...data };
|
|
86
|
-
meta.constructorParams?.forEach(prop => delete tmp[prop]);
|
|
87
|
-
this.hydrate(entity, meta2, tmp, options);
|
|
88
|
-
// since we now process only a copy of the `data` via hydrator, but later we register the state with the full snapshot,
|
|
89
|
-
// we need to go through all props with custom types that have `ensureComparable: true` and ensure they are comparable
|
|
90
|
-
// even if they are not part of constructor parameters (as this is otherwise normalized during hydration, here only in `tmp`)
|
|
91
|
-
if (options.convertCustomTypes) {
|
|
92
|
-
for (const prop of meta.props) {
|
|
93
|
-
if (prop.customType?.ensureComparable(meta, prop) && data[prop.name]) {
|
|
94
|
-
if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
98
|
-
Utils.isPlainObject(data[prop.name])) {
|
|
99
|
-
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
100
|
-
}
|
|
101
|
-
if (prop.customType instanceof JsonType && this.#platform.convertsJsonAutomatically()) {
|
|
102
|
-
data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.#platform, {
|
|
103
|
-
key: prop.name,
|
|
104
|
-
mode: 'hydration',
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
this.hydrate(entity, meta2, data, options);
|
|
113
|
-
}
|
|
114
|
-
if (exists && meta.root.inheritanceType && !(entity instanceof meta2.class)) {
|
|
115
|
-
Object.setPrototypeOf(entity, meta2.prototype);
|
|
116
|
-
}
|
|
117
|
-
if (options.merge && wrapped.hasPrimaryKey()) {
|
|
118
|
-
this.unitOfWork.register(entity, data, {
|
|
119
|
-
// Always refresh to ensure the payload is in correct shape for joined strategy. When loading nested relations,
|
|
120
|
-
// they will be created early without `Type.ensureComparable` being properly handled, resulting in extra updates.
|
|
121
|
-
refresh: options.initialized,
|
|
122
|
-
newEntity: options.newEntity,
|
|
123
|
-
loaded: options.initialized,
|
|
124
|
-
});
|
|
125
|
-
if (options.recomputeSnapshot) {
|
|
126
|
-
wrapped.__originalEntityData = this.#comparator.prepareEntity(entity);
|
|
36
|
+
const meta = this.#metadata.get(entityName);
|
|
37
|
+
if (meta.virtual) {
|
|
38
|
+
data = { ...data };
|
|
39
|
+
const entity = this.createEntity(data, meta, options);
|
|
40
|
+
this.hydrate(entity, meta, data, options);
|
|
41
|
+
return entity;
|
|
42
|
+
}
|
|
43
|
+
if (meta.serializedPrimaryKey) {
|
|
44
|
+
this.denormalizePrimaryKey(meta, data);
|
|
45
|
+
}
|
|
46
|
+
const meta2 = this.processDiscriminatorColumn(meta, data);
|
|
47
|
+
const exists = this.findEntity(data, meta2, options);
|
|
48
|
+
let wrapped = exists && helper(exists);
|
|
49
|
+
if (wrapped && !options.refresh) {
|
|
50
|
+
const wasInitialized = wrapped.isInitialized();
|
|
51
|
+
wrapped.__processing = true;
|
|
52
|
+
Utils.dropUndefinedProperties(data);
|
|
53
|
+
this.mergeData(meta2, exists, data, options);
|
|
54
|
+
wrapped.__processing = false;
|
|
55
|
+
wrapped.__initialized ||= !!options.initialized;
|
|
56
|
+
if (wrapped.isInitialized()) {
|
|
57
|
+
if (!wasInitialized) {
|
|
58
|
+
if (meta.root.inheritanceType && !(exists instanceof meta2.class)) {
|
|
59
|
+
Object.setPrototypeOf(exists, meta2.prototype);
|
|
60
|
+
}
|
|
61
|
+
if (options.merge && wrapped.hasPrimaryKey()) {
|
|
62
|
+
this.unitOfWork.register(exists, data, { loaded: options.initialized });
|
|
63
|
+
// ensure all data keys are tracked as loaded for shouldRefresh checks,
|
|
64
|
+
// but don't overwrite __originalEntityData — mergeData already set it
|
|
65
|
+
// with DB values for non-user-modified keys, leaving user changes detectable
|
|
66
|
+
for (const key of Utils.keys(data)) {
|
|
67
|
+
if (meta2.properties[key]) {
|
|
68
|
+
wrapped.__loadedProperties.add(key);
|
|
69
|
+
}
|
|
127
70
|
}
|
|
71
|
+
}
|
|
72
|
+
if (this.#eventManager.hasListeners(EventType.onInit, meta2)) {
|
|
73
|
+
this.#eventManager.dispatchEvent(EventType.onInit, { entity: exists, meta: meta2, em: this.#em });
|
|
74
|
+
}
|
|
128
75
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
wrapped.__processing = false;
|
|
133
|
-
return entity;
|
|
76
|
+
return exists;
|
|
77
|
+
}
|
|
134
78
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
Utils.keys(diff2)
|
|
156
|
-
.filter(key => {
|
|
157
|
-
// ignore null values if there is already present non-null value
|
|
158
|
-
if (existsData[key] != null) {
|
|
159
|
-
return diff2[key] == null;
|
|
160
|
-
}
|
|
161
|
-
return diff2[key] === undefined;
|
|
162
|
-
})
|
|
163
|
-
.forEach(key => delete diff2[key]);
|
|
164
|
-
// but always add collection properties, formulas, generated columns, and version properties if they
|
|
165
|
-
// are part of the `data`, as these are excluded from `comparableProps` and won't appear in the diff
|
|
166
|
-
Utils.keys(data)
|
|
167
|
-
.filter(key => meta.properties[key]?.formula ||
|
|
168
|
-
meta.properties[key]?.version ||
|
|
169
|
-
(meta.properties[key]?.generated && !meta.properties[key]?.primary) ||
|
|
170
|
-
[ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind))
|
|
171
|
-
.forEach(key => (diff2[key] = data[key]));
|
|
172
|
-
// rehydrated with the new values, skip those changed by user
|
|
173
|
-
// use full hydration if the entity is already initialized, even if the caller used `initialized: false`
|
|
174
|
-
// (e.g. from createReference), otherwise scalar properties in diff2 won't be applied
|
|
175
|
-
const initialized = options.initialized || helper(entity).__initialized;
|
|
176
|
-
this.hydrate(entity, meta, diff2, initialized ? { ...options, initialized } : options);
|
|
177
|
-
// we need to update the entity data only with keys that were not present before
|
|
178
|
-
const nullVal = this.#config.get('forceUndefined') ? undefined : null;
|
|
179
|
-
Utils.keys(diff2).forEach(key => {
|
|
180
|
-
const prop = meta.properties[key];
|
|
181
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
182
|
-
Utils.isPlainObject(data[prop.name])) {
|
|
183
|
-
// oxfmt-ignore
|
|
184
|
-
diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
|
|
185
|
-
}
|
|
186
|
-
if (!options.convertCustomTypes &&
|
|
187
|
-
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) &&
|
|
188
|
-
prop.customType?.ensureComparable(meta, prop) &&
|
|
189
|
-
diff2[key] != null) {
|
|
190
|
-
const converted = prop.customType.convertToJSValue(diff2[key], this.#platform, { force: true });
|
|
191
|
-
diff2[key] = prop.customType.convertToDatabaseValue(converted, this.#platform, { fromQuery: true });
|
|
192
|
-
}
|
|
193
|
-
originalEntityData[key] = diff2[key] === null ? nullVal : diff2[key];
|
|
194
|
-
helper(entity).__loadedProperties.add(key);
|
|
195
|
-
});
|
|
196
|
-
// in case of joined loading strategy, we need to cascade the merging to possibly loaded relations manually
|
|
197
|
-
meta.relations.forEach(prop => {
|
|
198
|
-
if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
|
|
199
|
-
Array.isArray(data[prop.name])) {
|
|
200
|
-
// instead of trying to match the collection items (which could easily fail if the collection was loaded with different ordering),
|
|
201
|
-
// we just create the entity from scratch, which will automatically pick the right one from the identity map and call `mergeData` on it
|
|
202
|
-
data[prop.name]
|
|
203
|
-
.filter(child => Utils.isPlainObject(child)) // objects with prototype can be PKs (e.g. `ObjectId`)
|
|
204
|
-
.forEach(child => this.create(prop.targetMeta.class, child, options)); // we can ignore the value, we just care about the `mergeData` call
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
208
|
-
Utils.isPlainObject(data[prop.name]) &&
|
|
209
|
-
entity[prop.name] &&
|
|
210
|
-
helper(entity[prop.name]).__initialized) {
|
|
211
|
-
this.create(prop.targetMeta.class, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
|
|
79
|
+
data = { ...data };
|
|
80
|
+
const entity = exists ?? this.createEntity(data, meta2, options);
|
|
81
|
+
wrapped = helper(entity);
|
|
82
|
+
wrapped.__processing = true;
|
|
83
|
+
wrapped.__initialized = options.initialized;
|
|
84
|
+
if (options.newEntity || meta.forceConstructor || meta.virtual) {
|
|
85
|
+
const tmp = { ...data };
|
|
86
|
+
meta.constructorParams?.forEach(prop => delete tmp[prop]);
|
|
87
|
+
this.hydrate(entity, meta2, tmp, options);
|
|
88
|
+
// since we now process only a copy of the `data` via hydrator, but later we register the state with the full snapshot,
|
|
89
|
+
// we need to go through all props with custom types that have `ensureComparable: true` and ensure they are comparable
|
|
90
|
+
// even if they are not part of constructor parameters (as this is otherwise normalized during hydration, here only in `tmp`)
|
|
91
|
+
if (options.convertCustomTypes) {
|
|
92
|
+
for (const prop of meta.props) {
|
|
93
|
+
if (prop.customType?.ensureComparable(meta, prop) && data[prop.name]) {
|
|
94
|
+
if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
95
|
+
continue;
|
|
212
96
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
options.convertCustomTypes ??= true;
|
|
219
|
-
const meta = this.#metadata.get(entityName);
|
|
220
|
-
const schema = this.#driver.getSchemaName(meta, options);
|
|
221
|
-
// Handle alternate key lookup
|
|
222
|
-
if (options.key) {
|
|
223
|
-
const value = '' + (Array.isArray(id) ? id[0] : Utils.isPlainObject(id) ? id[options.key] : id);
|
|
224
|
-
const exists = this.unitOfWork.getByKey(entityName, options.key, value, schema, options.convertCustomTypes);
|
|
225
|
-
if (exists) {
|
|
226
|
-
return exists;
|
|
97
|
+
if (
|
|
98
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
99
|
+
Utils.isPlainObject(data[prop.name])
|
|
100
|
+
) {
|
|
101
|
+
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
227
102
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (meta.simplePK) {
|
|
234
|
-
const exists = this.unitOfWork.getById(entityName, id, schema);
|
|
235
|
-
if (exists) {
|
|
236
|
-
return exists;
|
|
103
|
+
if (prop.customType instanceof JsonType && this.#platform.convertsJsonAutomatically()) {
|
|
104
|
+
data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.#platform, {
|
|
105
|
+
key: prop.name,
|
|
106
|
+
mode: 'hydration',
|
|
107
|
+
});
|
|
237
108
|
}
|
|
238
|
-
|
|
239
|
-
return this.create(entityName, data, { ...options, initialized: false });
|
|
240
|
-
}
|
|
241
|
-
if (Array.isArray(id)) {
|
|
242
|
-
id = Utils.getPrimaryKeyCondFromArray(id, meta);
|
|
243
|
-
}
|
|
244
|
-
const pks = Utils.getOrderedPrimaryKeys(id, meta, this.#platform);
|
|
245
|
-
const exists = this.unitOfWork.getById(entityName, pks, schema, options.convertCustomTypes);
|
|
246
|
-
if (exists) {
|
|
247
|
-
return exists;
|
|
248
|
-
}
|
|
249
|
-
if (Utils.isPrimaryKey(id)) {
|
|
250
|
-
id = { [meta.primaryKeys[0]]: id };
|
|
109
|
+
}
|
|
251
110
|
}
|
|
252
|
-
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
this.hydrate(entity, meta2, data, options);
|
|
253
114
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
data = { ...data };
|
|
257
|
-
const meta = this.#metadata.get(entityName);
|
|
258
|
-
const meta2 = this.processDiscriminatorColumn(meta, data);
|
|
259
|
-
return this.createEntity(data, meta2, options);
|
|
115
|
+
if (exists && meta.root.inheritanceType && !(entity instanceof meta2.class)) {
|
|
116
|
+
Object.setPrototypeOf(entity, meta2.prototype);
|
|
260
117
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
118
|
+
if (options.merge && wrapped.hasPrimaryKey()) {
|
|
119
|
+
this.unitOfWork.register(entity, data, {
|
|
120
|
+
// Always refresh to ensure the payload is in correct shape for joined strategy. When loading nested relations,
|
|
121
|
+
// they will be created early without `Type.ensureComparable` being properly handled, resulting in extra updates.
|
|
122
|
+
refresh: options.initialized,
|
|
123
|
+
newEntity: options.newEntity,
|
|
124
|
+
loaded: options.initialized,
|
|
125
|
+
});
|
|
126
|
+
if (options.recomputeSnapshot) {
|
|
127
|
+
wrapped.__originalEntityData = this.#comparator.prepareEntity(entity);
|
|
128
|
+
}
|
|
264
129
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
EntityHelper.ensurePropagation(entity);
|
|
288
|
-
}
|
|
289
|
-
return entity;
|
|
290
|
-
}
|
|
291
|
-
// creates new entity instance, bypassing constructor call as its already persisted entity
|
|
292
|
-
const entity = Object.create(meta.class.prototype);
|
|
293
|
-
helper(entity).__managed = true;
|
|
294
|
-
helper(entity).__processing = !meta.embeddable && !meta.virtual;
|
|
295
|
-
helper(entity).__schema = schema;
|
|
296
|
-
if (options.merge && !options.newEntity) {
|
|
297
|
-
this.#hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, options.parentSchema);
|
|
298
|
-
this.unitOfWork.register(entity);
|
|
130
|
+
if (this.#eventManager.hasListeners(EventType.onInit, meta2)) {
|
|
131
|
+
this.#eventManager.dispatchEvent(EventType.onInit, { entity, meta: meta2, em: this.#em });
|
|
132
|
+
}
|
|
133
|
+
wrapped.__processing = false;
|
|
134
|
+
return entity;
|
|
135
|
+
}
|
|
136
|
+
/** Merges new data into an existing entity, preserving user-modified properties. */
|
|
137
|
+
mergeData(meta, entity, data, options = {}) {
|
|
138
|
+
// merge unchanged properties automatically
|
|
139
|
+
data = QueryHelper.processParams(data);
|
|
140
|
+
const existsData = this.#comparator.prepareEntity(entity);
|
|
141
|
+
const originalEntityData = helper(entity).__originalEntityData ?? {};
|
|
142
|
+
const diff = this.#comparator.diffEntities(meta.class, originalEntityData, existsData);
|
|
143
|
+
const diff2 = this.#comparator.diffEntities(meta.class, existsData, data, { includeInverseSides: true });
|
|
144
|
+
// do not override values changed by user; for uninitialized entities,
|
|
145
|
+
// the diff may include stale snapshot entries (value is undefined) — skip those
|
|
146
|
+
if (helper(entity).__initialized) {
|
|
147
|
+
Utils.keys(diff).forEach(key => delete diff2[key]);
|
|
148
|
+
} else {
|
|
149
|
+
Utils.keys(diff).forEach(key => {
|
|
150
|
+
if (diff[key] !== undefined) {
|
|
151
|
+
delete diff2[key];
|
|
299
152
|
}
|
|
300
|
-
|
|
301
|
-
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
Utils.keys(diff2)
|
|
156
|
+
.filter(key => {
|
|
157
|
+
// ignore null values if there is already present non-null value
|
|
158
|
+
if (existsData[key] != null) {
|
|
159
|
+
return diff2[key] == null;
|
|
302
160
|
}
|
|
161
|
+
return diff2[key] === undefined;
|
|
162
|
+
})
|
|
163
|
+
.forEach(key => delete diff2[key]);
|
|
164
|
+
// but always add collection properties, formulas, generated columns, and version properties if they
|
|
165
|
+
// are part of the `data`, as these are excluded from `comparableProps` and won't appear in the diff
|
|
166
|
+
Utils.keys(data)
|
|
167
|
+
.filter(
|
|
168
|
+
key =>
|
|
169
|
+
meta.properties[key]?.formula ||
|
|
170
|
+
meta.properties[key]?.version ||
|
|
171
|
+
(meta.properties[key]?.generated && !meta.properties[key]?.primary) ||
|
|
172
|
+
[ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind),
|
|
173
|
+
)
|
|
174
|
+
.forEach(key => (diff2[key] = data[key]));
|
|
175
|
+
// rehydrated with the new values, skip those changed by user
|
|
176
|
+
// use full hydration if the entity is already initialized, even if the caller used `initialized: false`
|
|
177
|
+
// (e.g. from createReference), otherwise scalar properties in diff2 won't be applied
|
|
178
|
+
const initialized = options.initialized || helper(entity).__initialized;
|
|
179
|
+
this.hydrate(entity, meta, diff2, initialized ? { ...options, initialized } : options);
|
|
180
|
+
// we need to update the entity data only with keys that were not present before
|
|
181
|
+
const nullVal = this.#config.get('forceUndefined') ? undefined : null;
|
|
182
|
+
Utils.keys(diff2).forEach(key => {
|
|
183
|
+
const prop = meta.properties[key];
|
|
184
|
+
if (
|
|
185
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
186
|
+
Utils.isPlainObject(data[prop.name])
|
|
187
|
+
) {
|
|
188
|
+
// oxfmt-ignore
|
|
189
|
+
diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
|
|
190
|
+
}
|
|
191
|
+
if (
|
|
192
|
+
!options.convertCustomTypes &&
|
|
193
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) &&
|
|
194
|
+
prop.customType?.ensureComparable(meta, prop) &&
|
|
195
|
+
diff2[key] != null
|
|
196
|
+
) {
|
|
197
|
+
const converted = prop.customType.convertToJSValue(diff2[key], this.#platform, { force: true });
|
|
198
|
+
diff2[key] = prop.customType.convertToDatabaseValue(converted, this.#platform, { fromQuery: true });
|
|
199
|
+
}
|
|
200
|
+
originalEntityData[key] = diff2[key] === null ? nullVal : diff2[key];
|
|
201
|
+
helper(entity).__loadedProperties.add(key);
|
|
202
|
+
});
|
|
203
|
+
// in case of joined loading strategy, we need to cascade the merging to possibly loaded relations manually
|
|
204
|
+
meta.relations.forEach(prop => {
|
|
205
|
+
if (
|
|
206
|
+
[ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
|
|
207
|
+
Array.isArray(data[prop.name])
|
|
208
|
+
) {
|
|
209
|
+
// instead of trying to match the collection items (which could easily fail if the collection was loaded with different ordering),
|
|
210
|
+
// we just create the entity from scratch, which will automatically pick the right one from the identity map and call `mergeData` on it
|
|
211
|
+
data[prop.name]
|
|
212
|
+
.filter(child => Utils.isPlainObject(child)) // objects with prototype can be PKs (e.g. `ObjectId`)
|
|
213
|
+
.forEach(child => this.create(prop.targetMeta.class, child, options)); // we can ignore the value, we just care about the `mergeData` call
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (
|
|
217
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
218
|
+
Utils.isPlainObject(data[prop.name]) &&
|
|
219
|
+
entity[prop.name] &&
|
|
220
|
+
helper(entity[prop.name]).__initialized
|
|
221
|
+
) {
|
|
222
|
+
this.create(prop.targetMeta.class, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
this.unitOfWork.normalizeEntityData(meta, originalEntityData);
|
|
226
|
+
}
|
|
227
|
+
/** Creates or retrieves an uninitialized entity reference by its primary key or alternate key. */
|
|
228
|
+
createReference(entityName, id, options = {}) {
|
|
229
|
+
options.convertCustomTypes ??= true;
|
|
230
|
+
const meta = this.#metadata.get(entityName);
|
|
231
|
+
const schema = this.#driver.getSchemaName(meta, options);
|
|
232
|
+
// Handle alternate key lookup
|
|
233
|
+
if (options.key) {
|
|
234
|
+
const value = '' + (Array.isArray(id) ? id[0] : Utils.isPlainObject(id) ? id[options.key] : id);
|
|
235
|
+
const exists = this.unitOfWork.getByKey(entityName, options.key, value, schema, options.convertCustomTypes);
|
|
236
|
+
if (exists) {
|
|
237
|
+
return exists;
|
|
238
|
+
}
|
|
239
|
+
// Create entity stub - storeByKey will set the alternate key property and store in identity map
|
|
240
|
+
const entity = this.create(entityName, {}, { ...options, initialized: false });
|
|
241
|
+
this.unitOfWork.storeByKey(entity, options.key, value, schema, options.convertCustomTypes);
|
|
242
|
+
return entity;
|
|
243
|
+
}
|
|
244
|
+
if (meta.simplePK) {
|
|
245
|
+
const exists = this.unitOfWork.getById(entityName, id, schema);
|
|
246
|
+
if (exists) {
|
|
247
|
+
return exists;
|
|
248
|
+
}
|
|
249
|
+
const data = Utils.isPlainObject(id) ? id : { [meta.primaryKeys[0]]: Array.isArray(id) ? id[0] : id };
|
|
250
|
+
return this.create(entityName, data, { ...options, initialized: false });
|
|
251
|
+
}
|
|
252
|
+
if (Array.isArray(id)) {
|
|
253
|
+
id = Utils.getPrimaryKeyCondFromArray(id, meta);
|
|
254
|
+
}
|
|
255
|
+
const pks = Utils.getOrderedPrimaryKeys(id, meta, this.#platform);
|
|
256
|
+
const exists = this.unitOfWork.getById(entityName, pks, schema, options.convertCustomTypes);
|
|
257
|
+
if (exists) {
|
|
258
|
+
return exists;
|
|
259
|
+
}
|
|
260
|
+
if (Utils.isPrimaryKey(id)) {
|
|
261
|
+
id = { [meta.primaryKeys[0]]: id };
|
|
262
|
+
}
|
|
263
|
+
return this.create(entityName, id, { ...options, initialized: false });
|
|
264
|
+
}
|
|
265
|
+
/** Creates an embeddable entity instance from the provided data. */
|
|
266
|
+
createEmbeddable(entityName, data, options = {}) {
|
|
267
|
+
data = { ...data };
|
|
268
|
+
const meta = this.#metadata.get(entityName);
|
|
269
|
+
const meta2 = this.processDiscriminatorColumn(meta, data);
|
|
270
|
+
return this.createEntity(data, meta2, options);
|
|
271
|
+
}
|
|
272
|
+
/** Returns the EntityComparator instance used for diffing entities. */
|
|
273
|
+
getComparator() {
|
|
274
|
+
return this.#comparator;
|
|
275
|
+
}
|
|
276
|
+
createEntity(data, meta, options) {
|
|
277
|
+
const schema = this.#driver.getSchemaName(meta, options);
|
|
278
|
+
if (options.newEntity || meta.forceConstructor || meta.virtual) {
|
|
279
|
+
if (meta.polymorphs) {
|
|
280
|
+
throw new Error(`Cannot create entity ${meta.className}, class prototype is unknown`);
|
|
281
|
+
}
|
|
282
|
+
const params = this.extractConstructorParams(meta, data, options);
|
|
283
|
+
const Entity = meta.class;
|
|
284
|
+
// creates new instance via constructor as this is the new entity
|
|
285
|
+
const entity = new Entity(...params);
|
|
286
|
+
// creating managed entity instance when `forceEntityConstructor` is enabled,
|
|
287
|
+
// we need to wipe all the values as they would cause update queries on next flush
|
|
288
|
+
if (!options.newEntity && (meta.forceConstructor || this.#config.get('forceEntityConstructor'))) {
|
|
289
|
+
meta.props
|
|
290
|
+
.filter(prop => prop.persist !== false && !prop.primary && data[prop.name] === undefined)
|
|
291
|
+
.forEach(prop => delete entity[prop.name]);
|
|
292
|
+
}
|
|
293
|
+
if (meta.virtual) {
|
|
303
294
|
return entity;
|
|
295
|
+
}
|
|
296
|
+
helper(entity).__schema = schema;
|
|
297
|
+
if (options.initialized) {
|
|
298
|
+
EntityHelper.ensurePropagation(entity);
|
|
299
|
+
}
|
|
300
|
+
return entity;
|
|
304
301
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
for (const item of items) {
|
|
322
|
-
// Embedded sub-properties need all defaults since the DB can't apply them within JSON columns.
|
|
323
|
-
this.assignDefaultValues(item, prop.targetMeta);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
302
|
+
// creates new entity instance, bypassing constructor call as its already persisted entity
|
|
303
|
+
const entity = Object.create(meta.class.prototype);
|
|
304
|
+
helper(entity).__managed = true;
|
|
305
|
+
helper(entity).__processing = !meta.embeddable && !meta.virtual;
|
|
306
|
+
helper(entity).__schema = schema;
|
|
307
|
+
if (options.merge && !options.newEntity) {
|
|
308
|
+
this.#hydrator.hydrateReference(
|
|
309
|
+
entity,
|
|
310
|
+
meta,
|
|
311
|
+
data,
|
|
312
|
+
this,
|
|
313
|
+
options.convertCustomTypes,
|
|
314
|
+
options.schema,
|
|
315
|
+
options.parentSchema,
|
|
316
|
+
);
|
|
317
|
+
this.unitOfWork.register(entity);
|
|
327
318
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
this.#hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.#driver.getSchemaName(meta, options), options.normalizeAccessors);
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
this.#hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.#driver.getSchemaName(meta, options), options.normalizeAccessors);
|
|
334
|
-
}
|
|
335
|
-
Utils.keys(data).forEach(key => {
|
|
336
|
-
helper(entity)?.__loadedProperties.add(key);
|
|
337
|
-
helper(entity)?.__serializationContext.fields?.add(key);
|
|
338
|
-
});
|
|
339
|
-
const processOnCreateHooksEarly = options.processOnCreateHooksEarly ?? this.#config.get('processOnCreateHooksEarly');
|
|
340
|
-
if (options.newEntity && processOnCreateHooksEarly) {
|
|
341
|
-
this.assignDefaultValues(entity, meta);
|
|
342
|
-
}
|
|
319
|
+
if (options.initialized) {
|
|
320
|
+
EntityHelper.ensurePropagation(entity);
|
|
343
321
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
322
|
+
return entity;
|
|
323
|
+
}
|
|
324
|
+
/** @internal */
|
|
325
|
+
assignDefaultValues(entity, meta, onCreateOnly) {
|
|
326
|
+
for (const prop of meta.props) {
|
|
327
|
+
if (
|
|
328
|
+
prop.embedded ||
|
|
329
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) ||
|
|
330
|
+
(prop.getter && !prop.setter)
|
|
331
|
+
) {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (prop.onCreate) {
|
|
335
|
+
entity[prop.name] ??= prop.onCreate(entity, this.#em);
|
|
336
|
+
} else if (!onCreateOnly && prop.default != null && !isRaw(prop.default) && entity[prop.name] === undefined) {
|
|
337
|
+
entity[prop.name] = prop.default;
|
|
338
|
+
}
|
|
339
|
+
if (prop.kind === ReferenceKind.EMBEDDED && entity[prop.name]) {
|
|
340
|
+
const items = prop.array ? entity[prop.name] : [entity[prop.name]];
|
|
341
|
+
for (const item of items) {
|
|
342
|
+
// Embedded sub-properties need all defaults since the DB can't apply them within JSON columns.
|
|
343
|
+
this.assignDefaultValues(item, prop.targetMeta);
|
|
348
344
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
hydrate(entity, meta, data, options) {
|
|
349
|
+
if (options.initialized) {
|
|
350
|
+
this.#hydrator.hydrate(
|
|
351
|
+
entity,
|
|
352
|
+
meta,
|
|
353
|
+
data,
|
|
354
|
+
this,
|
|
355
|
+
'full',
|
|
356
|
+
options.newEntity,
|
|
357
|
+
options.convertCustomTypes,
|
|
358
|
+
options.schema,
|
|
359
|
+
this.#driver.getSchemaName(meta, options),
|
|
360
|
+
options.normalizeAccessors,
|
|
361
|
+
);
|
|
362
|
+
} else {
|
|
363
|
+
this.#hydrator.hydrateReference(
|
|
364
|
+
entity,
|
|
365
|
+
meta,
|
|
366
|
+
data,
|
|
367
|
+
this,
|
|
368
|
+
options.convertCustomTypes,
|
|
369
|
+
options.schema,
|
|
370
|
+
this.#driver.getSchemaName(meta, options),
|
|
371
|
+
options.normalizeAccessors,
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
Utils.keys(data).forEach(key => {
|
|
375
|
+
helper(entity)?.__loadedProperties.add(key);
|
|
376
|
+
helper(entity)?.__serializationContext.fields?.add(key);
|
|
377
|
+
});
|
|
378
|
+
const processOnCreateHooksEarly =
|
|
379
|
+
options.processOnCreateHooksEarly ?? this.#config.get('processOnCreateHooksEarly');
|
|
380
|
+
if (options.newEntity && processOnCreateHooksEarly) {
|
|
381
|
+
this.assignDefaultValues(entity, meta);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
findEntity(data, meta, options) {
|
|
385
|
+
const schema = this.#driver.getSchemaName(meta, options);
|
|
386
|
+
if (meta.simplePK) {
|
|
387
|
+
return this.unitOfWork.getById(meta.class, data[meta.primaryKeys[0]], schema);
|
|
354
388
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
389
|
+
if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
392
|
+
const pks = Utils.getOrderedPrimaryKeys(data, meta, this.#platform, options.convertCustomTypes);
|
|
393
|
+
return this.unitOfWork.getById(meta.class, pks, schema);
|
|
394
|
+
}
|
|
395
|
+
processDiscriminatorColumn(meta, data) {
|
|
396
|
+
// Handle STI discriminator (persisted column)
|
|
397
|
+
if (meta.root.inheritanceType === 'sti') {
|
|
398
|
+
const prop = meta.properties[meta.root.discriminatorColumn];
|
|
399
|
+
const value = data[prop.name];
|
|
400
|
+
const type = meta.root.discriminatorMap[value];
|
|
401
|
+
meta = type ? this.#metadata.get(type) : meta;
|
|
402
|
+
return meta;
|
|
403
|
+
}
|
|
404
|
+
// Handle TPT discriminator (computed at query time)
|
|
405
|
+
if (meta.root.inheritanceType === 'tpt' && meta.root.discriminatorMap) {
|
|
406
|
+
const value = data[meta.root.tptDiscriminatorColumn];
|
|
407
|
+
if (value) {
|
|
408
|
+
const type = meta.root.discriminatorMap[value];
|
|
409
|
+
meta = type ? this.#metadata.get(type) : meta;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return meta;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* denormalize PK to value required by driver (e.g. ObjectId)
|
|
416
|
+
*/
|
|
417
|
+
denormalizePrimaryKey(meta, data) {
|
|
418
|
+
const pk = meta.getPrimaryProp();
|
|
419
|
+
const spk = meta.properties[meta.serializedPrimaryKey];
|
|
420
|
+
if (!spk?.serializedPrimaryKey) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (pk.type === 'ObjectId' && (data[pk.name] != null || data[spk.name] != null)) {
|
|
424
|
+
data[pk.name] = this.#platform.denormalizePrimaryKey(data[spk.name] || data[pk.name]);
|
|
425
|
+
delete data[spk.name];
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* returns parameters for entity constructor, creating references from plain ids
|
|
430
|
+
*/
|
|
431
|
+
extractConstructorParams(meta, data, options) {
|
|
432
|
+
if (!meta.constructorParams) {
|
|
433
|
+
return [data];
|
|
434
|
+
}
|
|
435
|
+
return meta.constructorParams.map(k => {
|
|
436
|
+
const prop = meta.properties[k];
|
|
437
|
+
const value = data[k];
|
|
438
|
+
if (prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && value) {
|
|
439
|
+
const pk = Reference.unwrapReference(value);
|
|
440
|
+
const entity = this.unitOfWork.getById(prop.targetMeta.class, pk, options.schema, true);
|
|
441
|
+
if (entity) {
|
|
442
|
+
return entity;
|
|
363
443
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const value = data[meta.root.tptDiscriminatorColumn];
|
|
367
|
-
if (value) {
|
|
368
|
-
const type = meta.root.discriminatorMap[value];
|
|
369
|
-
meta = type ? this.#metadata.get(type) : meta;
|
|
370
|
-
}
|
|
444
|
+
if (Utils.isEntity(value)) {
|
|
445
|
+
return value;
|
|
371
446
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
* denormalize PK to value required by driver (e.g. ObjectId)
|
|
376
|
-
*/
|
|
377
|
-
denormalizePrimaryKey(meta, data) {
|
|
378
|
-
const pk = meta.getPrimaryProp();
|
|
379
|
-
const spk = meta.properties[meta.serializedPrimaryKey];
|
|
380
|
-
if (!spk?.serializedPrimaryKey) {
|
|
381
|
-
return;
|
|
447
|
+
const nakedPk = Utils.extractPK(value, prop.targetMeta, true);
|
|
448
|
+
if (Utils.isObject(value) && !nakedPk) {
|
|
449
|
+
return this.create(prop.targetMeta.class, value, options);
|
|
382
450
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
451
|
+
const { newEntity, initialized, ...rest } = options;
|
|
452
|
+
const target = this.createReference(prop.targetMeta.class, nakedPk, rest);
|
|
453
|
+
return Reference.wrapReference(target, prop);
|
|
454
|
+
}
|
|
455
|
+
if (prop?.kind === ReferenceKind.EMBEDDED && value) {
|
|
456
|
+
/* v8 ignore next */
|
|
457
|
+
if (Utils.isEntity(value)) {
|
|
458
|
+
return value;
|
|
386
459
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
460
|
+
return this.createEmbeddable(prop.targetMeta.class, value, options);
|
|
461
|
+
}
|
|
462
|
+
if (!prop) {
|
|
463
|
+
const tmp = { ...data };
|
|
464
|
+
for (const prop of meta.props) {
|
|
465
|
+
if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (
|
|
469
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
470
|
+
Utils.isPlainObject(tmp[prop.name]) &&
|
|
471
|
+
!Utils.extractPK(tmp[prop.name], prop.targetMeta, true)
|
|
472
|
+
) {
|
|
473
|
+
tmp[prop.name] = Reference.wrapReference(this.create(prop.targetMeta.class, tmp[prop.name], options), prop);
|
|
474
|
+
} else if (prop.kind === ReferenceKind.SCALAR) {
|
|
475
|
+
tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.#platform);
|
|
476
|
+
}
|
|
394
477
|
}
|
|
395
|
-
return
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
const nakedPk = Utils.extractPK(value, prop.targetMeta, true);
|
|
408
|
-
if (Utils.isObject(value) && !nakedPk) {
|
|
409
|
-
return this.create(prop.targetMeta.class, value, options);
|
|
410
|
-
}
|
|
411
|
-
const { newEntity, initialized, ...rest } = options;
|
|
412
|
-
const target = this.createReference(prop.targetMeta.class, nakedPk, rest);
|
|
413
|
-
return Reference.wrapReference(target, prop);
|
|
414
|
-
}
|
|
415
|
-
if (prop?.kind === ReferenceKind.EMBEDDED && value) {
|
|
416
|
-
/* v8 ignore next */
|
|
417
|
-
if (Utils.isEntity(value)) {
|
|
418
|
-
return value;
|
|
419
|
-
}
|
|
420
|
-
return this.createEmbeddable(prop.targetMeta.class, value, options);
|
|
421
|
-
}
|
|
422
|
-
if (!prop) {
|
|
423
|
-
const tmp = { ...data };
|
|
424
|
-
for (const prop of meta.props) {
|
|
425
|
-
if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
|
|
426
|
-
continue;
|
|
427
|
-
}
|
|
428
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
429
|
-
Utils.isPlainObject(tmp[prop.name]) &&
|
|
430
|
-
!Utils.extractPK(tmp[prop.name], prop.targetMeta, true)) {
|
|
431
|
-
tmp[prop.name] = Reference.wrapReference(this.create(prop.targetMeta.class, tmp[prop.name], options), prop);
|
|
432
|
-
}
|
|
433
|
-
else if (prop.kind === ReferenceKind.SCALAR) {
|
|
434
|
-
tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.#platform);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
return tmp;
|
|
438
|
-
}
|
|
439
|
-
if (options.convertCustomTypes && prop.customType && value != null) {
|
|
440
|
-
return prop.customType.convertToJSValue(value, this.#platform);
|
|
441
|
-
}
|
|
442
|
-
return value;
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
get unitOfWork() {
|
|
446
|
-
return this.#em.getUnitOfWork(false);
|
|
447
|
-
}
|
|
478
|
+
return tmp;
|
|
479
|
+
}
|
|
480
|
+
if (options.convertCustomTypes && prop.customType && value != null) {
|
|
481
|
+
return prop.customType.convertToJSValue(value, this.#platform);
|
|
482
|
+
}
|
|
483
|
+
return value;
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
get unitOfWork() {
|
|
487
|
+
return this.#em.getUnitOfWork(false);
|
|
488
|
+
}
|
|
448
489
|
}
|