@mikro-orm/core 7.0.0-dev.32 → 7.0.0-dev.321
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 +71 -63
- package/EntityManager.js +365 -283
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +109 -142
- package/README.md +7 -4
- package/cache/FileCacheAdapter.d.ts +1 -2
- package/cache/FileCacheAdapter.js +19 -14
- package/cache/GeneratedCacheAdapter.d.ts +0 -1
- package/cache/GeneratedCacheAdapter.js +0 -2
- package/cache/index.d.ts +1 -2
- package/cache/index.js +0 -2
- package/connections/Connection.d.ts +12 -5
- package/connections/Connection.js +37 -15
- package/drivers/DatabaseDriver.d.ts +25 -18
- package/drivers/DatabaseDriver.js +144 -45
- 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 +473 -115
- package/entity/EntityAssigner.js +37 -25
- package/entity/EntityFactory.d.ts +7 -1
- package/entity/EntityFactory.js +116 -64
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +69 -27
- package/entity/EntityLoader.d.ts +11 -10
- package/entity/EntityLoader.js +264 -102
- 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 +2 -6
- package/entity/Reference.js +52 -19
- package/entity/WrappedEntity.d.ts +3 -8
- package/entity/WrappedEntity.js +6 -7
- package/entity/defineEntity.d.ts +525 -311
- 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 +46 -11
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +66 -0
- package/enums.d.ts +8 -6
- package/enums.js +13 -17
- package/errors.d.ts +20 -10
- package/errors.js +63 -31
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +24 -13
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/exceptions.js +9 -2
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +105 -46
- package/index.d.ts +2 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/DefaultLogger.js +3 -4
- package/logging/SimpleLogger.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +5 -7
- package/logging/index.d.ts +2 -1
- package/logging/index.js +1 -1
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +11 -0
- package/metadata/EntitySchema.d.ts +47 -23
- package/metadata/EntitySchema.js +103 -34
- package/metadata/MetadataDiscovery.d.ts +64 -9
- package/metadata/MetadataDiscovery.js +867 -354
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +71 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +72 -41
- package/metadata/MetadataValidator.d.ts +32 -9
- package/metadata/MetadataValidator.js +214 -44
- 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 +0 -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 +26 -5
- 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/naming-strategy/index.d.ts +1 -1
- package/naming-strategy/index.js +0 -1
- package/not-supported.d.ts +2 -0
- package/not-supported.js +8 -0
- package/package.json +47 -36
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +33 -15
- package/platforms/Platform.js +125 -69
- package/serialization/EntitySerializer.d.ts +6 -3
- package/serialization/EntitySerializer.js +53 -29
- package/serialization/EntityTransformer.js +33 -21
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +4 -4
- 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/UuidType.d.ts +2 -0
- package/types/UuidType.js +14 -2
- package/types/index.d.ts +3 -2
- package/typings.d.ts +427 -170
- package/typings.js +100 -45
- package/unit-of-work/ChangeSet.d.ts +4 -6
- package/unit-of-work/ChangeSet.js +8 -9
- package/unit-of-work/ChangeSetComputer.d.ts +3 -8
- package/unit-of-work/ChangeSetComputer.js +49 -26
- package/unit-of-work/ChangeSetPersister.d.ts +13 -12
- package/unit-of-work/ChangeSetPersister.js +107 -44
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +17 -15
- package/unit-of-work/IdentityMap.d.ts +12 -0
- package/unit-of-work/IdentityMap.js +39 -1
- package/unit-of-work/UnitOfWork.d.ts +34 -4
- package/unit-of-work/UnitOfWork.js +294 -107
- package/utils/AbstractMigrator.d.ts +101 -0
- package/utils/AbstractMigrator.js +303 -0
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +30 -18
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +796 -211
- package/utils/Configuration.js +160 -197
- package/utils/ConfigurationLoader.d.ts +1 -52
- package/utils/ConfigurationLoader.js +1 -330
- package/utils/Cursor.d.ts +0 -3
- package/utils/Cursor.js +29 -14
- package/utils/DataloaderUtils.d.ts +10 -5
- package/utils/DataloaderUtils.js +42 -22
- package/utils/EntityComparator.d.ts +16 -9
- package/utils/EntityComparator.js +202 -96
- package/utils/QueryHelper.d.ts +34 -7
- package/utils/QueryHelper.js +183 -72
- package/utils/RawQueryFragment.d.ts +28 -34
- package/utils/RawQueryFragment.js +37 -72
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +11 -7
- package/utils/Utils.d.ts +16 -127
- package/utils/Utils.js +106 -401
- package/utils/clone.js +13 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +98 -0
- package/utils/fs-utils.d.ts +34 -0
- package/utils/fs-utils.js +193 -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 +51 -5
- 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;
|
|
@@ -72,13 +71,16 @@ export class EntityAssigner {
|
|
|
72
71
|
value = customType.convertToJSValue(value, options.platform);
|
|
73
72
|
}
|
|
74
73
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop?.kind) && value != null) {
|
|
75
|
-
if (options.updateNestedEntities &&
|
|
74
|
+
if (options.updateNestedEntities &&
|
|
75
|
+
Object.hasOwn(entity, propName) &&
|
|
76
|
+
Utils.isEntity(entity[propName], true) &&
|
|
77
|
+
Utils.isPlainObject(value)) {
|
|
76
78
|
const unwrappedEntity = Reference.unwrapReference(entity[propName]);
|
|
77
79
|
const wrapped = helper(unwrappedEntity);
|
|
78
80
|
if (options.updateByPrimaryKey) {
|
|
79
81
|
const pk = Utils.extractPK(value, prop.targetMeta);
|
|
80
82
|
if (pk) {
|
|
81
|
-
const ref = options.em.getReference(prop.
|
|
83
|
+
const ref = options.em.getReference(prop.targetMeta.class, pk, options);
|
|
82
84
|
// if the PK differs, we want to change the target entity, not update it
|
|
83
85
|
const wrappedChild = helper(ref);
|
|
84
86
|
const sameTarget = wrappedChild.getSerializedPrimaryKey() === wrapped.getSerializedPrimaryKey();
|
|
@@ -94,13 +96,16 @@ export class EntityAssigner {
|
|
|
94
96
|
}
|
|
95
97
|
return EntityAssigner.assignReference(entity, value, prop, options.em, options);
|
|
96
98
|
}
|
|
97
|
-
if (prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.
|
|
98
|
-
|
|
99
|
+
if (prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && (prop.setter || !prop.getter)) {
|
|
100
|
+
validateProperty(prop, value, entity);
|
|
101
|
+
return (entity[prop.name] = value);
|
|
99
102
|
}
|
|
100
103
|
if (prop.kind === ReferenceKind.EMBEDDED && EntityAssigner.validateEM(options.em)) {
|
|
101
104
|
return EntityAssigner.assignEmbeddable(entity, value, prop, options.em, options);
|
|
102
105
|
}
|
|
103
|
-
if (options.mergeObjectProperties &&
|
|
106
|
+
if (options.mergeObjectProperties &&
|
|
107
|
+
Utils.isPlainObject(entity[propName]) &&
|
|
108
|
+
Utils.isPlainObject(value)) {
|
|
104
109
|
entity[propName] ??= {};
|
|
105
110
|
entity[propName] = Utils.merge({}, entity[propName], value);
|
|
106
111
|
}
|
|
@@ -120,7 +125,7 @@ export class EntityAssigner {
|
|
|
120
125
|
}
|
|
121
126
|
const meta2 = helper(ref).__meta;
|
|
122
127
|
const prop2 = meta2.properties[prop.inversedBy || prop.mappedBy];
|
|
123
|
-
/* v8 ignore next
|
|
128
|
+
/* v8 ignore next */
|
|
124
129
|
if (prop2 && !ref[prop2.name]) {
|
|
125
130
|
if (Reference.isReference(ref)) {
|
|
126
131
|
ref.unwrap()[prop2.name] = Reference.wrapReference(entity, prop2);
|
|
@@ -141,13 +146,15 @@ export class EntityAssigner {
|
|
|
141
146
|
entity[prop.name] = Reference.wrapReference(value, prop);
|
|
142
147
|
}
|
|
143
148
|
else if (Utils.isPrimaryKey(value, true) && EntityAssigner.validateEM(em)) {
|
|
144
|
-
entity[prop.name] = prop.mapToPk
|
|
149
|
+
entity[prop.name] = prop.mapToPk
|
|
150
|
+
? value
|
|
151
|
+
: Reference.wrapReference(em.getReference(prop.targetMeta.class, value, options), prop);
|
|
145
152
|
}
|
|
146
153
|
else if (Utils.isPlainObject(value) && options.merge && EntityAssigner.validateEM(em)) {
|
|
147
|
-
entity[prop.name] = Reference.wrapReference(em.merge(prop.
|
|
154
|
+
entity[prop.name] = Reference.wrapReference(em.merge(prop.targetMeta.class, value, options), prop);
|
|
148
155
|
}
|
|
149
156
|
else if (Utils.isPlainObject(value) && EntityAssigner.validateEM(em)) {
|
|
150
|
-
entity[prop.name] = Reference.wrapReference(em.create(prop.
|
|
157
|
+
entity[prop.name] = Reference.wrapReference(em.create(prop.targetMeta.class, value, options), prop);
|
|
151
158
|
}
|
|
152
159
|
else {
|
|
153
160
|
const name = entity.constructor.name;
|
|
@@ -166,27 +173,31 @@ export class EntityAssigner {
|
|
|
166
173
|
if (options.updateNestedEntities && options.updateByPrimaryKey && Utils.isPlainObject(item)) {
|
|
167
174
|
const pk = Utils.extractPK(item, prop.targetMeta);
|
|
168
175
|
if (pk && EntityAssigner.validateEM(em)) {
|
|
169
|
-
const ref = em.getUnitOfWork().getById(prop.
|
|
176
|
+
const ref = em.getUnitOfWork().getById(prop.targetMeta.class, pk, options.schema);
|
|
170
177
|
if (ref) {
|
|
171
178
|
return EntityAssigner.assign(ref, item, options);
|
|
172
179
|
}
|
|
173
180
|
}
|
|
174
181
|
return this.createCollectionItem(item, em, prop, invalid, options);
|
|
175
182
|
}
|
|
176
|
-
/* v8 ignore next
|
|
177
|
-
if (options.updateNestedEntities &&
|
|
183
|
+
/* v8 ignore next */
|
|
184
|
+
if (options.updateNestedEntities &&
|
|
185
|
+
!options.updateByPrimaryKey &&
|
|
186
|
+
collection[idx] &&
|
|
187
|
+
helper(collection[idx])?.isInitialized()) {
|
|
178
188
|
return EntityAssigner.assign(collection[idx], item, options);
|
|
179
189
|
}
|
|
180
190
|
return this.createCollectionItem(item, em, prop, invalid, options);
|
|
181
191
|
});
|
|
182
192
|
if (invalid.length > 0) {
|
|
183
193
|
const name = entity.constructor.name;
|
|
184
|
-
throw
|
|
194
|
+
throw ValidationError.invalidCollectionValues(name, prop.name, invalid);
|
|
185
195
|
}
|
|
186
196
|
if (Array.isArray(value)) {
|
|
187
197
|
collection.set(items);
|
|
188
198
|
}
|
|
189
|
-
else {
|
|
199
|
+
else {
|
|
200
|
+
// append to the collection in case of assigning a single value instead of array
|
|
190
201
|
collection.add(items);
|
|
191
202
|
}
|
|
192
203
|
}
|
|
@@ -207,11 +218,12 @@ export class EntityAssigner {
|
|
|
207
218
|
entity[propName].push(...Object.values(tmp));
|
|
208
219
|
});
|
|
209
220
|
}
|
|
210
|
-
const create = () => EntityAssigner.validateEM(em) &&
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
221
|
+
const create = () => EntityAssigner.validateEM(em) &&
|
|
222
|
+
em.getEntityFactory().createEmbeddable(prop.targetMeta.class, value, {
|
|
223
|
+
convertCustomTypes: options.convertCustomTypes,
|
|
224
|
+
newEntity: options.mergeEmbeddedProperties ? !('propName' in entity) : true,
|
|
225
|
+
});
|
|
226
|
+
entity[propName] = (options.mergeEmbeddedProperties ? entity[propName] || create() : create());
|
|
215
227
|
Object.keys(value).forEach(key => {
|
|
216
228
|
EntityAssigner.assignProperty(entity[propName], key, prop.embeddedProps, value, options);
|
|
217
229
|
});
|
|
@@ -221,13 +233,13 @@ export class EntityAssigner {
|
|
|
221
233
|
return item;
|
|
222
234
|
}
|
|
223
235
|
if (Utils.isPrimaryKey(item) && EntityAssigner.validateEM(em)) {
|
|
224
|
-
return em.getReference(prop.
|
|
236
|
+
return em.getReference(prop.targetMeta.class, item, options);
|
|
225
237
|
}
|
|
226
238
|
if (Utils.isPlainObject(item) && options.merge && EntityAssigner.validateEM(em)) {
|
|
227
|
-
return em.merge(prop.
|
|
239
|
+
return em.merge(prop.targetMeta.class, item, options);
|
|
228
240
|
}
|
|
229
241
|
if (Utils.isPlainObject(item) && EntityAssigner.validateEM(em)) {
|
|
230
|
-
return em.create(prop.
|
|
242
|
+
return em.create(prop.targetMeta.class, item, options);
|
|
231
243
|
}
|
|
232
244
|
invalid.push(item);
|
|
233
245
|
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
|
|
@@ -70,10 +70,16 @@ export class EntityFactory {
|
|
|
70
70
|
if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
71
71
|
continue;
|
|
72
72
|
}
|
|
73
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
73
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
74
|
+
Utils.isPlainObject(data[prop.name])) {
|
|
74
75
|
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
75
76
|
}
|
|
76
|
-
|
|
77
|
+
if (prop.customType instanceof JsonType && this.platform.convertsJsonAutomatically()) {
|
|
78
|
+
data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.platform, {
|
|
79
|
+
key: prop.name,
|
|
80
|
+
mode: 'hydration',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
77
83
|
}
|
|
78
84
|
}
|
|
79
85
|
}
|
|
@@ -81,8 +87,7 @@ export class EntityFactory {
|
|
|
81
87
|
else {
|
|
82
88
|
this.hydrate(entity, meta2, data, options);
|
|
83
89
|
}
|
|
84
|
-
|
|
85
|
-
if (exists && meta.discriminatorColumn && !(entity instanceof meta2.class)) {
|
|
90
|
+
if (exists && meta.root.inheritanceType && !(entity instanceof meta2.class)) {
|
|
86
91
|
Object.setPrototypeOf(entity, meta2.prototype);
|
|
87
92
|
}
|
|
88
93
|
if (options.merge && wrapped.hasPrimaryKey()) {
|
|
@@ -108,58 +113,90 @@ export class EntityFactory {
|
|
|
108
113
|
data = QueryHelper.processParams(data);
|
|
109
114
|
const existsData = this.comparator.prepareEntity(entity);
|
|
110
115
|
const originalEntityData = helper(entity).__originalEntityData ?? {};
|
|
111
|
-
const diff = this.comparator.diffEntities(meta.
|
|
116
|
+
const diff = this.comparator.diffEntities(meta.class, originalEntityData, existsData);
|
|
112
117
|
// version properties are not part of entity snapshots
|
|
113
|
-
if (meta.versionProperty &&
|
|
118
|
+
if (meta.versionProperty &&
|
|
119
|
+
data[meta.versionProperty] &&
|
|
120
|
+
data[meta.versionProperty] !== originalEntityData[meta.versionProperty]) {
|
|
114
121
|
diff[meta.versionProperty] = data[meta.versionProperty];
|
|
115
122
|
}
|
|
116
|
-
const diff2 = this.comparator.diffEntities(meta.
|
|
123
|
+
const diff2 = this.comparator.diffEntities(meta.class, existsData, data, { includeInverseSides: true });
|
|
117
124
|
// do not override values changed by user
|
|
118
125
|
Utils.keys(diff).forEach(key => delete diff2[key]);
|
|
119
|
-
Utils.keys(diff2)
|
|
126
|
+
Utils.keys(diff2)
|
|
127
|
+
.filter(key => {
|
|
120
128
|
// ignore null values if there is already present non-null value
|
|
121
129
|
if (existsData[key] != null) {
|
|
122
130
|
return diff2[key] == null;
|
|
123
131
|
}
|
|
124
132
|
return diff2[key] === undefined;
|
|
125
|
-
})
|
|
133
|
+
})
|
|
134
|
+
.forEach(key => delete diff2[key]);
|
|
126
135
|
// but always add collection properties and formulas if they are part of the `data`
|
|
127
136
|
Utils.keys(data)
|
|
128
|
-
.filter(key => meta.properties[key]?.formula ||
|
|
129
|
-
.
|
|
137
|
+
.filter(key => meta.properties[key]?.formula ||
|
|
138
|
+
[ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind))
|
|
139
|
+
.forEach(key => (diff2[key] = data[key]));
|
|
130
140
|
// rehydrated with the new values, skip those changed by user
|
|
131
|
-
|
|
141
|
+
// use full hydration if the entity is already initialized, even if the caller used `initialized: false`
|
|
142
|
+
// (e.g. from createReference), otherwise scalar properties in diff2 won't be applied
|
|
143
|
+
const initialized = options.initialized || helper(entity).__initialized;
|
|
144
|
+
this.hydrate(entity, meta, diff2, initialized ? { ...options, initialized } : options);
|
|
132
145
|
// we need to update the entity data only with keys that were not present before
|
|
133
146
|
const nullVal = this.config.get('forceUndefined') ? undefined : null;
|
|
134
147
|
Utils.keys(diff2).forEach(key => {
|
|
135
148
|
const prop = meta.properties[key];
|
|
136
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
149
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
150
|
+
Utils.isPlainObject(data[prop.name])) {
|
|
151
|
+
// oxfmt-ignore
|
|
137
152
|
diff2[key] = entity[prop.name] ? helper(entity[prop.name]).getPrimaryKey(options.convertCustomTypes) : null;
|
|
138
153
|
}
|
|
154
|
+
if (!options.convertCustomTypes &&
|
|
155
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE, ReferenceKind.SCALAR].includes(prop.kind) &&
|
|
156
|
+
prop.customType?.ensureComparable(meta, prop) &&
|
|
157
|
+
diff2[key] != null) {
|
|
158
|
+
const converted = prop.customType.convertToJSValue(diff2[key], this.platform, { force: true });
|
|
159
|
+
diff2[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { fromQuery: true });
|
|
160
|
+
}
|
|
139
161
|
originalEntityData[key] = diff2[key] === null ? nullVal : diff2[key];
|
|
140
162
|
helper(entity).__loadedProperties.add(key);
|
|
141
163
|
});
|
|
142
164
|
// in case of joined loading strategy, we need to cascade the merging to possibly loaded relations manually
|
|
143
165
|
meta.relations.forEach(prop => {
|
|
144
|
-
if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
|
|
166
|
+
if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
|
|
167
|
+
Array.isArray(data[prop.name])) {
|
|
145
168
|
// instead of trying to match the collection items (which could easily fail if the collection was loaded with different ordering),
|
|
146
169
|
// we just create the entity from scratch, which will automatically pick the right one from the identity map and call `mergeData` on it
|
|
147
170
|
data[prop.name]
|
|
148
171
|
.filter(child => Utils.isPlainObject(child)) // objects with prototype can be PKs (e.g. `ObjectId`)
|
|
149
|
-
.forEach(child => this.create(prop.
|
|
172
|
+
.forEach(child => this.create(prop.targetMeta.class, child, options)); // we can ignore the value, we just care about the `mergeData` call
|
|
150
173
|
return;
|
|
151
174
|
}
|
|
152
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
153
|
-
|
|
175
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
176
|
+
Utils.isPlainObject(data[prop.name]) &&
|
|
177
|
+
entity[prop.name] &&
|
|
178
|
+
helper(entity[prop.name]).__initialized) {
|
|
179
|
+
this.create(prop.targetMeta.class, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
|
|
154
180
|
}
|
|
155
181
|
});
|
|
156
|
-
|
|
182
|
+
this.unitOfWork.normalizeEntityData(meta, originalEntityData);
|
|
157
183
|
}
|
|
158
184
|
createReference(entityName, id, options = {}) {
|
|
159
185
|
options.convertCustomTypes ??= true;
|
|
160
|
-
entityName = Utils.className(entityName);
|
|
161
186
|
const meta = this.metadata.get(entityName);
|
|
162
187
|
const schema = this.driver.getSchemaName(meta, options);
|
|
188
|
+
// Handle alternate key lookup
|
|
189
|
+
if (options.key) {
|
|
190
|
+
const value = '' + (Array.isArray(id) ? id[0] : Utils.isPlainObject(id) ? id[options.key] : id);
|
|
191
|
+
const exists = this.unitOfWork.getByKey(entityName, options.key, value, schema, options.convertCustomTypes);
|
|
192
|
+
if (exists) {
|
|
193
|
+
return exists;
|
|
194
|
+
}
|
|
195
|
+
// Create entity stub - storeByKey will set the alternate key property and store in identity map
|
|
196
|
+
const entity = this.create(entityName, {}, { ...options, initialized: false });
|
|
197
|
+
this.unitOfWork.storeByKey(entity, options.key, value, schema, options.convertCustomTypes);
|
|
198
|
+
return entity;
|
|
199
|
+
}
|
|
163
200
|
if (meta.simplePK) {
|
|
164
201
|
const exists = this.unitOfWork.getById(entityName, id, schema);
|
|
165
202
|
if (exists) {
|
|
@@ -182,7 +219,6 @@ export class EntityFactory {
|
|
|
182
219
|
return this.create(entityName, id, { ...options, initialized: false });
|
|
183
220
|
}
|
|
184
221
|
createEmbeddable(entityName, data, options = {}) {
|
|
185
|
-
entityName = Utils.className(entityName);
|
|
186
222
|
data = { ...data };
|
|
187
223
|
const meta = this.metadata.get(entityName);
|
|
188
224
|
const meta2 = this.processDiscriminatorColumn(meta, data);
|
|
@@ -194,7 +230,7 @@ export class EntityFactory {
|
|
|
194
230
|
createEntity(data, meta, options) {
|
|
195
231
|
const schema = this.driver.getSchemaName(meta, options);
|
|
196
232
|
if (options.newEntity || meta.forceConstructor || meta.virtual) {
|
|
197
|
-
if (
|
|
233
|
+
if (meta.polymorphs) {
|
|
198
234
|
throw new Error(`Cannot create entity ${meta.className}, class prototype is unknown`);
|
|
199
235
|
}
|
|
200
236
|
const params = this.extractConstructorParams(meta, data, options);
|
|
@@ -240,10 +276,10 @@ export class EntityFactory {
|
|
|
240
276
|
}
|
|
241
277
|
hydrate(entity, meta, data, options) {
|
|
242
278
|
if (options.initialized) {
|
|
243
|
-
this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
|
|
279
|
+
this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options), options.normalizeAccessors);
|
|
244
280
|
}
|
|
245
281
|
else {
|
|
246
|
-
this.hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
|
|
282
|
+
this.hydrator.hydrateReference(entity, meta, data, this, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options), options.normalizeAccessors);
|
|
247
283
|
}
|
|
248
284
|
Utils.keys(data).forEach(key => {
|
|
249
285
|
helper(entity)?.__loadedProperties.add(key);
|
|
@@ -257,75 +293,91 @@ export class EntityFactory {
|
|
|
257
293
|
findEntity(data, meta, options) {
|
|
258
294
|
const schema = this.driver.getSchemaName(meta, options);
|
|
259
295
|
if (meta.simplePK) {
|
|
260
|
-
return this.unitOfWork.getById(meta.
|
|
296
|
+
return this.unitOfWork.getById(meta.class, data[meta.primaryKeys[0]], schema);
|
|
261
297
|
}
|
|
262
298
|
if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
|
|
263
299
|
return undefined;
|
|
264
300
|
}
|
|
265
301
|
const pks = Utils.getOrderedPrimaryKeys(data, meta, this.platform, options.convertCustomTypes);
|
|
266
|
-
return this.unitOfWork.getById(meta.
|
|
302
|
+
return this.unitOfWork.getById(meta.class, pks, schema);
|
|
267
303
|
}
|
|
268
304
|
processDiscriminatorColumn(meta, data) {
|
|
269
|
-
|
|
305
|
+
// Handle STI discriminator (persisted column)
|
|
306
|
+
if (meta.root.inheritanceType === 'sti') {
|
|
307
|
+
const prop = meta.properties[meta.root.discriminatorColumn];
|
|
308
|
+
const value = data[prop.name];
|
|
309
|
+
const type = meta.root.discriminatorMap[value];
|
|
310
|
+
meta = type ? this.metadata.get(type) : meta;
|
|
270
311
|
return meta;
|
|
271
312
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
313
|
+
// Handle TPT discriminator (computed at query time)
|
|
314
|
+
if (meta.root.inheritanceType === 'tpt' && meta.root.discriminatorMap) {
|
|
315
|
+
const value = data[meta.root.tptDiscriminatorColumn];
|
|
316
|
+
if (value) {
|
|
317
|
+
const type = meta.root.discriminatorMap[value];
|
|
318
|
+
meta = type ? this.metadata.get(type) : meta;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
276
321
|
return meta;
|
|
277
322
|
}
|
|
278
323
|
/**
|
|
279
324
|
* denormalize PK to value required by driver (e.g. ObjectId)
|
|
280
325
|
*/
|
|
281
|
-
denormalizePrimaryKey(
|
|
282
|
-
const pk =
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
data[
|
|
326
|
+
denormalizePrimaryKey(meta, data) {
|
|
327
|
+
const pk = meta.getPrimaryProp();
|
|
328
|
+
const spk = meta.properties[meta.serializedPrimaryKey];
|
|
329
|
+
if (!spk?.serializedPrimaryKey) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (pk.type === 'ObjectId' && (data[pk.name] != null || data[spk.name] != null)) {
|
|
333
|
+
data[pk.name] = this.platform.denormalizePrimaryKey((data[spk.name] || data[pk.name]));
|
|
334
|
+
delete data[spk.name];
|
|
290
335
|
}
|
|
291
336
|
}
|
|
292
337
|
/**
|
|
293
338
|
* returns parameters for entity constructor, creating references from plain ids
|
|
294
339
|
*/
|
|
295
340
|
extractConstructorParams(meta, data, options) {
|
|
341
|
+
if (!meta.constructorParams) {
|
|
342
|
+
return [data];
|
|
343
|
+
}
|
|
296
344
|
return meta.constructorParams.map(k => {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
345
|
+
const prop = meta.properties[k];
|
|
346
|
+
const value = data[k];
|
|
347
|
+
if (prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && value) {
|
|
348
|
+
const pk = Reference.unwrapReference(value);
|
|
349
|
+
const entity = this.unitOfWork.getById(prop.targetMeta.class, pk, options.schema, true);
|
|
300
350
|
if (entity) {
|
|
301
351
|
return entity;
|
|
302
352
|
}
|
|
303
|
-
if (Utils.isEntity(
|
|
304
|
-
return
|
|
353
|
+
if (Utils.isEntity(value)) {
|
|
354
|
+
return value;
|
|
305
355
|
}
|
|
306
|
-
const nakedPk = Utils.extractPK(
|
|
307
|
-
if (Utils.isObject(
|
|
308
|
-
return this.create(
|
|
356
|
+
const nakedPk = Utils.extractPK(value, prop.targetMeta, true);
|
|
357
|
+
if (Utils.isObject(value) && !nakedPk) {
|
|
358
|
+
return this.create(prop.targetMeta.class, value, options);
|
|
309
359
|
}
|
|
310
360
|
const { newEntity, initialized, ...rest } = options;
|
|
311
|
-
const target = this.createReference(
|
|
312
|
-
return Reference.wrapReference(target,
|
|
361
|
+
const target = this.createReference(prop.targetMeta.class, nakedPk, rest);
|
|
362
|
+
return Reference.wrapReference(target, prop);
|
|
313
363
|
}
|
|
314
|
-
if (
|
|
315
|
-
/* v8 ignore next
|
|
316
|
-
if (Utils.isEntity(
|
|
317
|
-
return
|
|
364
|
+
if (prop?.kind === ReferenceKind.EMBEDDED && value) {
|
|
365
|
+
/* v8 ignore next */
|
|
366
|
+
if (Utils.isEntity(value)) {
|
|
367
|
+
return value;
|
|
318
368
|
}
|
|
319
|
-
return this.createEmbeddable(
|
|
369
|
+
return this.createEmbeddable(prop.targetMeta.class, value, options);
|
|
320
370
|
}
|
|
321
|
-
if (!
|
|
371
|
+
if (!prop) {
|
|
322
372
|
const tmp = { ...data };
|
|
323
373
|
for (const prop of meta.props) {
|
|
324
374
|
if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
|
|
325
375
|
continue;
|
|
326
376
|
}
|
|
327
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
328
|
-
|
|
377
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
378
|
+
Utils.isPlainObject(tmp[prop.name]) &&
|
|
379
|
+
!Utils.extractPK(tmp[prop.name], prop.targetMeta, true)) {
|
|
380
|
+
tmp[prop.name] = Reference.wrapReference(this.create(prop.targetMeta.class, tmp[prop.name], options), prop);
|
|
329
381
|
}
|
|
330
382
|
else if (prop.kind === ReferenceKind.SCALAR) {
|
|
331
383
|
tmp[prop.name] = prop.customType.convertToJSValue(tmp[prop.name], this.platform);
|
|
@@ -333,10 +385,10 @@ export class EntityFactory {
|
|
|
333
385
|
}
|
|
334
386
|
return tmp;
|
|
335
387
|
}
|
|
336
|
-
if (options.convertCustomTypes &&
|
|
337
|
-
return
|
|
388
|
+
if (options.convertCustomTypes && prop.customType && value != null) {
|
|
389
|
+
return prop.customType.convertToJSValue(value, this.platform);
|
|
338
390
|
}
|
|
339
|
-
return
|
|
391
|
+
return value;
|
|
340
392
|
});
|
|
341
393
|
}
|
|
342
394
|
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;
|