@mikro-orm/core 7.0.0-dev.23 → 7.0.0-dev.231
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 +91 -59
- package/EntityManager.js +303 -251
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +109 -143
- package/README.md +2 -0
- package/cache/FileCacheAdapter.d.ts +1 -1
- package/cache/FileCacheAdapter.js +17 -8
- 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 +118 -35
- package/drivers/IDatabaseDriver.d.ts +42 -19
- package/entity/BaseEntity.d.ts +61 -2
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +101 -29
- package/entity/Collection.js +436 -104
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +26 -18
- package/entity/EntityFactory.d.ts +7 -1
- package/entity/EntityFactory.js +83 -54
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +48 -15
- package/entity/EntityLoader.d.ts +7 -6
- package/entity/EntityLoader.js +221 -93
- package/entity/EntityRepository.d.ts +27 -7
- 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 +21 -12
- package/entity/WrappedEntity.d.ts +0 -5
- package/entity/WrappedEntity.js +2 -7
- package/entity/defineEntity.d.ts +380 -310
- package/entity/defineEntity.js +124 -273
- package/entity/index.d.ts +2 -2
- package/entity/index.js +2 -2
- package/entity/utils.js +1 -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 +87 -35
- 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 +778 -325
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +46 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +70 -37
- 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 +526 -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 +19 -11
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +7 -14
- package/platforms/Platform.js +20 -43
- package/serialization/EntitySerializer.d.ts +5 -0
- package/serialization/EntitySerializer.js +47 -27
- package/serialization/EntityTransformer.js +28 -18
- 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.d.ts +8 -6
- 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.d.ts +6 -4
- 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 +381 -171
- package/typings.js +97 -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 +1 -3
- package/unit-of-work/ChangeSetComputer.js +35 -14
- package/unit-of-work/ChangeSetPersister.d.ts +7 -3
- package/unit-of-work/ChangeSetPersister.js +83 -25
- 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 +258 -92
- 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 -209
- package/utils/Configuration.js +150 -192
- package/utils/ConfigurationLoader.d.ts +1 -54
- package/utils/ConfigurationLoader.js +1 -352
- 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 +158 -58
- 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 +28 -4
- package/utils/Utils.d.ts +14 -127
- package/utils/Utils.js +85 -397
- 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 +33 -0
- package/utils/fs-utils.js +192 -0
- package/utils/index.d.ts +1 -1
- package/utils/index.js +1 -1
- 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
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import { EntityIdentifier } from '../entity/EntityIdentifier.js';
|
|
2
|
+
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
2
3
|
import { helper } from '../entity/wrap.js';
|
|
3
4
|
import { ChangeSetType } from './ChangeSet.js';
|
|
4
5
|
import { isRaw } from '../utils/RawQueryFragment.js';
|
|
5
6
|
import { Utils } from '../utils/Utils.js';
|
|
6
|
-
import { OptimisticLockError } from '../errors.js';
|
|
7
|
+
import { OptimisticLockError, ValidationError } from '../errors.js';
|
|
7
8
|
import { ReferenceKind } from '../enums.js';
|
|
8
9
|
export class ChangeSetPersister {
|
|
9
10
|
driver;
|
|
10
11
|
metadata;
|
|
11
12
|
hydrator;
|
|
12
13
|
factory;
|
|
13
|
-
validator;
|
|
14
14
|
config;
|
|
15
15
|
em;
|
|
16
16
|
platform;
|
|
17
17
|
comparator;
|
|
18
18
|
usesReturningStatement;
|
|
19
|
-
constructor(driver, metadata, hydrator, factory,
|
|
19
|
+
constructor(driver, metadata, hydrator, factory, config, em) {
|
|
20
20
|
this.driver = driver;
|
|
21
21
|
this.metadata = metadata;
|
|
22
22
|
this.hydrator = hydrator;
|
|
23
23
|
this.factory = factory;
|
|
24
|
-
this.validator = validator;
|
|
25
24
|
this.config = config;
|
|
26
25
|
this.em = em;
|
|
27
26
|
this.platform = this.driver.getPlatform();
|
|
@@ -32,7 +31,7 @@ export class ChangeSetPersister {
|
|
|
32
31
|
if (!withSchema) {
|
|
33
32
|
return this.runForEachSchema(changeSets, 'executeInserts', options);
|
|
34
33
|
}
|
|
35
|
-
const meta =
|
|
34
|
+
const meta = changeSets[0].meta;
|
|
36
35
|
changeSets.forEach(changeSet => this.processProperties(changeSet));
|
|
37
36
|
if (changeSets.length > 1 && this.config.get('useBatchInserts', this.platform.usesBatchInserts())) {
|
|
38
37
|
return this.persistNewEntities(meta, changeSets, options);
|
|
@@ -45,9 +44,12 @@ export class ChangeSetPersister {
|
|
|
45
44
|
if (!withSchema) {
|
|
46
45
|
return this.runForEachSchema(changeSets, 'executeUpdates', options, batched);
|
|
47
46
|
}
|
|
48
|
-
const meta =
|
|
47
|
+
const meta = changeSets[0].meta;
|
|
49
48
|
changeSets.forEach(changeSet => this.processProperties(changeSet));
|
|
50
|
-
|
|
49
|
+
// For STI with conflicting fieldNames (renamedFrom properties), we can't batch mixed child types
|
|
50
|
+
const hasSTIConflicts = meta.root.props.some(p => p.renamedFrom);
|
|
51
|
+
const hasMixedTypes = hasSTIConflicts && changeSets.some(cs => cs.meta.class !== meta.class);
|
|
52
|
+
if (batched && changeSets.length > 1 && !hasMixedTypes && this.config.get('useBatchUpdates', this.platform.usesBatchUpdates())) {
|
|
51
53
|
return this.persistManagedEntities(meta, changeSets, options);
|
|
52
54
|
}
|
|
53
55
|
for (const changeSet of changeSets) {
|
|
@@ -65,7 +67,7 @@ export class ChangeSetPersister {
|
|
|
65
67
|
const chunk = changeSets.slice(i, i + size);
|
|
66
68
|
const pks = chunk.map(cs => cs.getPrimaryKey());
|
|
67
69
|
options = this.prepareOptions(meta, options);
|
|
68
|
-
await this.driver.nativeDelete(meta.root.
|
|
70
|
+
await this.driver.nativeDelete(meta.root.class, { [pk]: { $in: pks } }, options);
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
async runForEachSchema(changeSets, method, options, ...args) {
|
|
@@ -81,13 +83,32 @@ export class ChangeSetPersister {
|
|
|
81
83
|
await this[method](group, ...args, options, true);
|
|
82
84
|
}
|
|
83
85
|
}
|
|
86
|
+
validateRequired(entity) {
|
|
87
|
+
const wrapped = helper(entity);
|
|
88
|
+
for (const prop of wrapped.__meta.props) {
|
|
89
|
+
if (!prop.nullable &&
|
|
90
|
+
!prop.autoincrement &&
|
|
91
|
+
!prop.default &&
|
|
92
|
+
!prop.defaultRaw &&
|
|
93
|
+
!prop.onCreate &&
|
|
94
|
+
!prop.generated &&
|
|
95
|
+
!prop.embedded &&
|
|
96
|
+
![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) &&
|
|
97
|
+
prop.name !== wrapped.__meta.root.discriminatorColumn &&
|
|
98
|
+
prop.type !== 'ObjectId' &&
|
|
99
|
+
prop.persist !== false &&
|
|
100
|
+
entity[prop.name] == null) {
|
|
101
|
+
throw ValidationError.propertyRequired(entity, prop);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
84
105
|
processProperties(changeSet) {
|
|
85
|
-
const meta =
|
|
106
|
+
const meta = changeSet.meta;
|
|
86
107
|
for (const prop of meta.relations) {
|
|
87
108
|
this.processProperty(changeSet, prop);
|
|
88
109
|
}
|
|
89
110
|
if (changeSet.type === ChangeSetType.CREATE && this.config.get('validateRequired')) {
|
|
90
|
-
this.
|
|
111
|
+
this.validateRequired(changeSet.entity);
|
|
91
112
|
}
|
|
92
113
|
}
|
|
93
114
|
async persistNewEntity(meta, changeSet, options) {
|
|
@@ -95,7 +116,9 @@ export class ChangeSetPersister {
|
|
|
95
116
|
options = this.prepareOptions(meta, options, {
|
|
96
117
|
convertCustomTypes: false,
|
|
97
118
|
});
|
|
98
|
-
|
|
119
|
+
this.resolveTPTIdentifiers(changeSet);
|
|
120
|
+
// Use changeSet's own meta for STI entities to get correct field mappings
|
|
121
|
+
const res = await this.driver.nativeInsertMany(changeSet.meta.class, [changeSet.payload], options);
|
|
99
122
|
if (!wrapped.hasPrimaryKey()) {
|
|
100
123
|
this.mapPrimaryKey(meta, res.insertId, changeSet);
|
|
101
124
|
}
|
|
@@ -132,7 +155,10 @@ export class ChangeSetPersister {
|
|
|
132
155
|
convertCustomTypes: false,
|
|
133
156
|
processCollections: false,
|
|
134
157
|
});
|
|
135
|
-
const
|
|
158
|
+
for (const changeSet of changeSets) {
|
|
159
|
+
this.resolveTPTIdentifiers(changeSet);
|
|
160
|
+
}
|
|
161
|
+
const res = await this.driver.nativeInsertMany(meta.class, changeSets.map(cs => cs.payload), options);
|
|
136
162
|
for (let i = 0; i < changeSets.length; i++) {
|
|
137
163
|
const changeSet = changeSets[i];
|
|
138
164
|
const wrapped = helper(changeSet.entity);
|
|
@@ -150,7 +176,7 @@ export class ChangeSetPersister {
|
|
|
150
176
|
}
|
|
151
177
|
}
|
|
152
178
|
async persistManagedEntity(changeSet, options) {
|
|
153
|
-
const meta =
|
|
179
|
+
const meta = changeSet.meta;
|
|
154
180
|
const res = await this.updateEntity(meta, changeSet, options);
|
|
155
181
|
this.checkOptimisticLock(meta, changeSet, res);
|
|
156
182
|
this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
|
|
@@ -191,7 +217,7 @@ export class ChangeSetPersister {
|
|
|
191
217
|
cond.push(where);
|
|
192
218
|
payload.push(changeSet.payload);
|
|
193
219
|
}
|
|
194
|
-
const res = await this.driver.nativeUpdateMany(meta.
|
|
220
|
+
const res = await this.driver.nativeUpdateMany(meta.class, cond, payload, options);
|
|
195
221
|
const map = new Map();
|
|
196
222
|
res.rows?.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));
|
|
197
223
|
for (const changeSet of changeSets) {
|
|
@@ -243,13 +269,13 @@ export class ChangeSetPersister {
|
|
|
243
269
|
convertCustomTypes: false,
|
|
244
270
|
});
|
|
245
271
|
if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)) {
|
|
246
|
-
return this.driver.nativeUpdate(changeSet.
|
|
272
|
+
return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
|
|
247
273
|
}
|
|
248
274
|
if (meta.versionProperty) {
|
|
249
275
|
cond[meta.versionProperty] = this.platform.quoteVersionValue(changeSet.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
|
|
250
276
|
}
|
|
251
277
|
this.checkConcurrencyKeys(meta, changeSet, cond);
|
|
252
|
-
return this.driver.nativeUpdate(changeSet.
|
|
278
|
+
return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
|
|
253
279
|
}
|
|
254
280
|
async checkOptimisticLocks(meta, changeSets, options) {
|
|
255
281
|
if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSets.every(cs => cs.entity[meta.versionProperty] == null))) {
|
|
@@ -269,7 +295,7 @@ export class ChangeSetPersister {
|
|
|
269
295
|
options = this.prepareOptions(meta, options, {
|
|
270
296
|
fields: primaryKeys,
|
|
271
297
|
});
|
|
272
|
-
const res = await this.driver.find(meta.root.
|
|
298
|
+
const res = await this.driver.find(meta.root.class, { $or }, options);
|
|
273
299
|
if (res.length !== changeSets.length) {
|
|
274
300
|
const compare = (a, b, keys) => keys.every(k => a[k] === b[k]);
|
|
275
301
|
const entity = changeSets.find(cs => {
|
|
@@ -290,11 +316,22 @@ export class ChangeSetPersister {
|
|
|
290
316
|
async reloadVersionValues(meta, changeSets, options) {
|
|
291
317
|
const reloadProps = meta.versionProperty && !this.usesReturningStatement ? [meta.properties[meta.versionProperty]] : [];
|
|
292
318
|
if (changeSets[0].type === ChangeSetType.CREATE) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
.
|
|
319
|
+
for (const prop of meta.props) {
|
|
320
|
+
if (prop.persist === false) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (isRaw(changeSets[0].entity[prop.name])) {
|
|
324
|
+
reloadProps.push(prop);
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
// do not reload things that already had a runtime value
|
|
328
|
+
if (changeSets[0].entity[prop.name] != null || prop.defaultRaw === 'null') {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (prop.autoincrement || prop.generated || prop.defaultRaw) {
|
|
332
|
+
reloadProps.push(prop);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
298
335
|
}
|
|
299
336
|
if (changeSets[0].type === ChangeSetType.UPDATE) {
|
|
300
337
|
const returning = new Set();
|
|
@@ -328,7 +365,7 @@ export class ChangeSetPersister {
|
|
|
328
365
|
options = this.prepareOptions(meta, options, {
|
|
329
366
|
fields: Utils.unique(reloadProps.map(prop => prop.name)),
|
|
330
367
|
});
|
|
331
|
-
const data = await this.driver.find(meta.
|
|
368
|
+
const data = await this.driver.find(meta.class, { [pk]: { $in: pks } }, options);
|
|
332
369
|
const map = new Map();
|
|
333
370
|
data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.platform, true), item));
|
|
334
371
|
for (const changeSet of changeSets) {
|
|
@@ -337,13 +374,34 @@ export class ChangeSetPersister {
|
|
|
337
374
|
Object.assign(changeSet.payload, data); // merge to the changeset payload, so it gets saved to the entity snapshot
|
|
338
375
|
}
|
|
339
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* For TPT child tables, resolve EntityIdentifier values in PK fields.
|
|
379
|
+
* The parent table insert assigns the actual PK value, which the child table references.
|
|
380
|
+
*/
|
|
381
|
+
resolveTPTIdentifiers(changeSet) {
|
|
382
|
+
if (changeSet.meta.inheritanceType !== 'tpt' || !changeSet.meta.tptParent) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
for (const pk of changeSet.meta.primaryKeys) {
|
|
386
|
+
const value = changeSet.payload[pk];
|
|
387
|
+
if (value instanceof EntityIdentifier) {
|
|
388
|
+
changeSet.payload[pk] = value.getValue();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
340
392
|
processProperty(changeSet, prop) {
|
|
341
|
-
const meta =
|
|
393
|
+
const meta = changeSet.meta;
|
|
342
394
|
const value = changeSet.payload[prop.name]; // for inline embeddables
|
|
343
395
|
if (value instanceof EntityIdentifier) {
|
|
344
396
|
changeSet.payload[prop.name] = value.getValue();
|
|
345
397
|
return;
|
|
346
398
|
}
|
|
399
|
+
if (value instanceof PolymorphicRef) {
|
|
400
|
+
if (value.id instanceof EntityIdentifier) {
|
|
401
|
+
value.id = value.id.getValue();
|
|
402
|
+
}
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
347
405
|
if (Array.isArray(value) && value.every(item => item instanceof EntityIdentifier)) {
|
|
348
406
|
changeSet.payload[prop.name] = value.map(item => item.getValue());
|
|
349
407
|
return;
|
|
@@ -371,7 +429,7 @@ export class ChangeSetPersister {
|
|
|
371
429
|
if ((!this.usesReturningStatement && !upsert) || !row || !Utils.hasObjectKeys(row)) {
|
|
372
430
|
return;
|
|
373
431
|
}
|
|
374
|
-
const mapped = this.comparator.mapResult(meta
|
|
432
|
+
const mapped = this.comparator.mapResult(meta, row);
|
|
375
433
|
if (entity) {
|
|
376
434
|
this.hydrator.hydrate(entity, meta, mapped, this.factory, 'full', false, true);
|
|
377
435
|
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EntityProperty } from '../typings.js';
|
|
2
2
|
export declare const enum NodeState {
|
|
3
3
|
NOT_VISITED = 0,
|
|
4
4
|
IN_PROGRESS = 1,
|
|
5
5
|
VISITED = 2
|
|
6
6
|
}
|
|
7
|
+
type Hash = number;
|
|
7
8
|
export interface Node {
|
|
8
|
-
hash:
|
|
9
|
+
hash: Hash;
|
|
9
10
|
state: NodeState;
|
|
10
|
-
dependencies:
|
|
11
|
+
dependencies: Map<Hash, Edge>;
|
|
11
12
|
}
|
|
12
13
|
export interface Edge {
|
|
13
|
-
from:
|
|
14
|
-
to:
|
|
14
|
+
from: Hash;
|
|
15
|
+
to: Hash;
|
|
15
16
|
weight: number;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
@@ -32,23 +33,23 @@ export declare class CommitOrderCalculator {
|
|
|
32
33
|
/**
|
|
33
34
|
* Checks for node existence in graph.
|
|
34
35
|
*/
|
|
35
|
-
hasNode(hash:
|
|
36
|
+
hasNode(hash: Hash): boolean;
|
|
36
37
|
/**
|
|
37
38
|
* Adds a new node to the graph, assigning its hash.
|
|
38
39
|
*/
|
|
39
|
-
addNode(hash:
|
|
40
|
+
addNode(hash: Hash): void;
|
|
40
41
|
/**
|
|
41
42
|
* Adds a new dependency (edge) to the graph using their hashes.
|
|
42
43
|
*/
|
|
43
|
-
addDependency(from:
|
|
44
|
-
discoverProperty(prop: EntityProperty, entityName:
|
|
44
|
+
addDependency(from: Hash, to: Hash, weight: number): void;
|
|
45
|
+
discoverProperty(prop: EntityProperty, entityName: Hash): void;
|
|
45
46
|
/**
|
|
46
47
|
* Return a valid order list of all current nodes.
|
|
47
48
|
* The desired topological sorting is the reverse post order of these searches.
|
|
48
49
|
*
|
|
49
50
|
* @internal Highly performance-sensitive method.
|
|
50
51
|
*/
|
|
51
|
-
sort():
|
|
52
|
+
sort(): Hash[];
|
|
52
53
|
/**
|
|
53
54
|
* Visit a given node definition for reordering.
|
|
54
55
|
*
|
|
@@ -60,3 +61,4 @@ export declare class CommitOrderCalculator {
|
|
|
60
61
|
*/
|
|
61
62
|
private visitOpenNode;
|
|
62
63
|
}
|
|
64
|
+
export {};
|
|
@@ -17,26 +17,26 @@ export var NodeState;
|
|
|
17
17
|
*/
|
|
18
18
|
export class CommitOrderCalculator {
|
|
19
19
|
/** Matrix of nodes, keys are provided hashes and values are the node definition objects. */
|
|
20
|
-
nodes =
|
|
20
|
+
nodes = new Map();
|
|
21
21
|
/** Volatile variable holding calculated nodes during sorting process. */
|
|
22
22
|
sortedNodeList = [];
|
|
23
23
|
/**
|
|
24
24
|
* Checks for node existence in graph.
|
|
25
25
|
*/
|
|
26
26
|
hasNode(hash) {
|
|
27
|
-
return
|
|
27
|
+
return this.nodes.has(hash);
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Adds a new node to the graph, assigning its hash.
|
|
31
31
|
*/
|
|
32
32
|
addNode(hash) {
|
|
33
|
-
this.nodes
|
|
33
|
+
this.nodes.set(hash, { hash, state: 0 /* NodeState.NOT_VISITED */, dependencies: new Map() });
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* Adds a new dependency (edge) to the graph using their hashes.
|
|
37
37
|
*/
|
|
38
38
|
addDependency(from, to, weight) {
|
|
39
|
-
this.nodes
|
|
39
|
+
this.nodes.get(from).dependencies.set(to, { from, to, weight });
|
|
40
40
|
}
|
|
41
41
|
discoverProperty(prop, entityName) {
|
|
42
42
|
const toOneOwner = (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner) || prop.kind === ReferenceKind.MANY_TO_ONE;
|
|
@@ -44,8 +44,8 @@ export class CommitOrderCalculator {
|
|
|
44
44
|
if (!toOneOwner && !toManyOwner) {
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
-
const propertyType = prop.targetMeta?.root.
|
|
48
|
-
if (
|
|
47
|
+
const propertyType = prop.targetMeta?.root._id;
|
|
48
|
+
if (propertyType == null || !this.hasNode(propertyType)) {
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
this.addDependency(propertyType, entityName, prop.nullable || prop.persist === false ? 0 : 1);
|
|
@@ -57,14 +57,14 @@ export class CommitOrderCalculator {
|
|
|
57
57
|
* @internal Highly performance-sensitive method.
|
|
58
58
|
*/
|
|
59
59
|
sort() {
|
|
60
|
-
for (const vertex of
|
|
60
|
+
for (const vertex of this.nodes.values()) {
|
|
61
61
|
if (vertex.state !== 0 /* NodeState.NOT_VISITED */) {
|
|
62
62
|
continue;
|
|
63
63
|
}
|
|
64
64
|
this.visit(vertex);
|
|
65
65
|
}
|
|
66
66
|
const sortedList = this.sortedNodeList.reverse();
|
|
67
|
-
this.nodes =
|
|
67
|
+
this.nodes = new Map();
|
|
68
68
|
this.sortedNodeList = [];
|
|
69
69
|
return sortedList;
|
|
70
70
|
}
|
|
@@ -75,8 +75,8 @@ export class CommitOrderCalculator {
|
|
|
75
75
|
*/
|
|
76
76
|
visit(node) {
|
|
77
77
|
node.state = 1 /* NodeState.IN_PROGRESS */;
|
|
78
|
-
for (const edge of
|
|
79
|
-
const target = this.nodes
|
|
78
|
+
for (const edge of node.dependencies.values()) {
|
|
79
|
+
const target = this.nodes.get(edge.to);
|
|
80
80
|
switch (target.state) {
|
|
81
81
|
case 2 /* NodeState.VISITED */: break; // Do nothing, since node was already visited
|
|
82
82
|
case 1 /* NodeState.IN_PROGRESS */:
|
|
@@ -94,11 +94,11 @@ export class CommitOrderCalculator {
|
|
|
94
94
|
* Visits all target's dependencies if in cycle with given node
|
|
95
95
|
*/
|
|
96
96
|
visitOpenNode(node, target, edge) {
|
|
97
|
-
if (!target.dependencies
|
|
97
|
+
if (!target.dependencies.has(node.hash) || target.dependencies.get(node.hash).weight >= edge.weight) {
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
|
-
for (const edge of
|
|
101
|
-
const targetNode = this.nodes
|
|
100
|
+
for (const edge of target.dependencies.values()) {
|
|
101
|
+
const targetNode = this.nodes.get(edge.to);
|
|
102
102
|
if (targetNode.state === 0 /* NodeState.NOT_VISITED */) {
|
|
103
103
|
this.visit(targetNode);
|
|
104
104
|
}
|
|
@@ -3,7 +3,14 @@ export declare class IdentityMap {
|
|
|
3
3
|
private readonly defaultSchema?;
|
|
4
4
|
constructor(defaultSchema?: string | undefined);
|
|
5
5
|
private readonly registry;
|
|
6
|
+
/** Tracks alternate key hashes for each entity so we can clean them up on delete */
|
|
7
|
+
private readonly alternateKeys;
|
|
6
8
|
store<T>(item: T): void;
|
|
9
|
+
/**
|
|
10
|
+
* Stores an entity under an alternate key (non-PK property).
|
|
11
|
+
* This allows looking up entities by unique properties that are not the primary key.
|
|
12
|
+
*/
|
|
13
|
+
storeByKey<T>(item: T, key: string, value: string, schema?: string): void;
|
|
7
14
|
delete<T>(item: T): void;
|
|
8
15
|
getByHash<T>(meta: EntityMetadata<T>, hash: string): T | undefined;
|
|
9
16
|
getStore<T>(meta: EntityMetadata<T>): Map<string, T>;
|
|
@@ -16,4 +23,9 @@ export declare class IdentityMap {
|
|
|
16
23
|
*/
|
|
17
24
|
get<T>(hash: string): T | undefined;
|
|
18
25
|
private getPkHash;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a hash for an alternate key lookup.
|
|
28
|
+
* Format: `[key]value` or `schema:[key]value`
|
|
29
|
+
*/
|
|
30
|
+
getKeyHash(key: string, value: string, schema?: string): string;
|
|
19
31
|
}
|
|
@@ -4,11 +4,38 @@ export class IdentityMap {
|
|
|
4
4
|
this.defaultSchema = defaultSchema;
|
|
5
5
|
}
|
|
6
6
|
registry = new Map();
|
|
7
|
+
/** Tracks alternate key hashes for each entity so we can clean them up on delete */
|
|
8
|
+
alternateKeys = new WeakMap();
|
|
7
9
|
store(item) {
|
|
8
10
|
this.getStore(item.__meta.root).set(this.getPkHash(item), item);
|
|
9
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Stores an entity under an alternate key (non-PK property).
|
|
14
|
+
* This allows looking up entities by unique properties that are not the primary key.
|
|
15
|
+
*/
|
|
16
|
+
storeByKey(item, key, value, schema) {
|
|
17
|
+
const hash = this.getKeyHash(key, value, schema);
|
|
18
|
+
this.getStore(item.__meta.root).set(hash, item);
|
|
19
|
+
// Track this alternate key so we can clean it up when the entity is deleted
|
|
20
|
+
let keys = this.alternateKeys.get(item);
|
|
21
|
+
if (!keys) {
|
|
22
|
+
keys = new Set();
|
|
23
|
+
this.alternateKeys.set(item, keys);
|
|
24
|
+
}
|
|
25
|
+
keys.add(hash);
|
|
26
|
+
}
|
|
10
27
|
delete(item) {
|
|
11
|
-
|
|
28
|
+
const meta = item.__meta.root;
|
|
29
|
+
const store = this.getStore(meta);
|
|
30
|
+
store.delete(this.getPkHash(item));
|
|
31
|
+
// Also delete any alternate key entries for this entity
|
|
32
|
+
const altKeys = this.alternateKeys.get(item);
|
|
33
|
+
if (altKeys) {
|
|
34
|
+
for (const hash of altKeys) {
|
|
35
|
+
store.delete(hash);
|
|
36
|
+
}
|
|
37
|
+
this.alternateKeys.delete(item);
|
|
38
|
+
}
|
|
12
39
|
}
|
|
13
40
|
getByHash(meta, hash) {
|
|
14
41
|
const store = this.getStore(meta);
|
|
@@ -69,4 +96,15 @@ export class IdentityMap {
|
|
|
69
96
|
}
|
|
70
97
|
return hash;
|
|
71
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Creates a hash for an alternate key lookup.
|
|
101
|
+
* Format: `[key]value` or `schema:[key]value`
|
|
102
|
+
*/
|
|
103
|
+
getKeyHash(key, value, schema) {
|
|
104
|
+
const hash = `[${key}]${value}`;
|
|
105
|
+
if (schema) {
|
|
106
|
+
return schema + ':' + hash;
|
|
107
|
+
}
|
|
108
|
+
return hash;
|
|
109
|
+
}
|
|
72
110
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyEntity, EntityData, EntityMetadata, EntityProperty, FilterQuery, Primary } from '../typings.js';
|
|
1
|
+
import type { AnyEntity, EntityData, EntityMetadata, EntityName, EntityProperty, FilterQuery, Primary } from '../typings.js';
|
|
2
2
|
import { Collection } from '../entity/Collection.js';
|
|
3
3
|
import { Reference } from '../entity/Reference.js';
|
|
4
4
|
import { ChangeSet, ChangeSetType } from './ChangeSet.js';
|
|
@@ -28,6 +28,12 @@ export declare class UnitOfWork {
|
|
|
28
28
|
private working;
|
|
29
29
|
constructor(em: EntityManager);
|
|
30
30
|
merge<T extends object>(entity: T, visited?: Set<AnyEntity>): void;
|
|
31
|
+
/**
|
|
32
|
+
* Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
|
|
33
|
+
* we need to normalize the shape, so relation values are only raw FKs. This method handles that.
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
normalizeEntityData<T extends object>(meta: EntityMetadata<T>, data: EntityData<T>): void;
|
|
31
37
|
/**
|
|
32
38
|
* @internal
|
|
33
39
|
*/
|
|
@@ -39,8 +45,21 @@ export declare class UnitOfWork {
|
|
|
39
45
|
/**
|
|
40
46
|
* Returns entity from the identity map. For composite keys, you need to pass an array of PKs in the same order as they are defined in `meta.primaryKeys`.
|
|
41
47
|
*/
|
|
42
|
-
getById<T extends object>(entityName:
|
|
43
|
-
|
|
48
|
+
getById<T extends object>(entityName: EntityName<T>, id: Primary<T> | Primary<T>[], schema?: string, convertCustomTypes?: boolean): T | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Returns entity from the identity map by an alternate key (non-PK property).
|
|
51
|
+
* @param convertCustomTypes - If true, the value is in database format and will be converted to JS format for lookup.
|
|
52
|
+
* If false (default), the value is assumed to be in JS format already.
|
|
53
|
+
*/
|
|
54
|
+
getByKey<T extends object>(entityName: EntityName<T>, key: string, value: unknown, schema?: string, convertCustomTypes?: boolean): T | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Stores an entity in the identity map under an alternate key (non-PK property).
|
|
57
|
+
* Also sets the property value on the entity.
|
|
58
|
+
* @param convertCustomTypes - If true, the value is in database format and will be converted to JS format.
|
|
59
|
+
* If false (default), the value is assumed to be in JS format already.
|
|
60
|
+
*/
|
|
61
|
+
storeByKey<T extends object>(entity: T, key: string, value: unknown, schema?: string, convertCustomTypes?: boolean): void;
|
|
62
|
+
tryGetById<T extends object>(entityName: EntityName<T>, where: FilterQuery<T>, schema?: string, strict?: boolean): T | null;
|
|
44
63
|
/**
|
|
45
64
|
* Returns map of all managed entities.
|
|
46
65
|
*/
|
|
@@ -77,6 +96,11 @@ export declare class UnitOfWork {
|
|
|
77
96
|
getOrphanRemoveStack(): Set<AnyEntity>;
|
|
78
97
|
getChangeSetPersister(): ChangeSetPersister;
|
|
79
98
|
private findNewEntities;
|
|
99
|
+
/**
|
|
100
|
+
* For TPT inheritance, creates separate changesets for each table in the hierarchy.
|
|
101
|
+
* Uses the same entity instance for all changesets - only the metadata and payload differ.
|
|
102
|
+
*/
|
|
103
|
+
private createTPTChangeSets;
|
|
80
104
|
/**
|
|
81
105
|
* Returns `true` when the change set should be skipped as it will be empty after the extra update.
|
|
82
106
|
*/
|