@mikro-orm/core 7.0.0-dev.3 → 7.0.0-dev.300
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EntityManager.d.ts +114 -63
- package/EntityManager.js +385 -310
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +109 -143
- package/README.md +3 -2
- 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 +16 -7
- package/connections/Connection.js +23 -14
- package/drivers/DatabaseDriver.d.ts +25 -16
- package/drivers/DatabaseDriver.js +119 -36
- package/drivers/IDatabaseDriver.d.ts +125 -23
- package/entity/BaseEntity.d.ts +63 -4
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +102 -31
- package/entity/Collection.js +446 -108
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +26 -18
- package/entity/EntityFactory.d.ts +13 -1
- package/entity/EntityFactory.js +106 -60
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +65 -20
- package/entity/EntityLoader.d.ts +13 -11
- package/entity/EntityLoader.js +257 -107
- 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 +9 -12
- package/entity/Reference.js +34 -9
- package/entity/WrappedEntity.d.ts +3 -8
- package/entity/WrappedEntity.js +3 -8
- package/entity/defineEntity.d.ts +753 -0
- package/entity/defineEntity.js +537 -0
- package/entity/index.d.ts +4 -2
- package/entity/index.js +4 -2
- package/entity/utils.d.ts +13 -1
- package/entity/utils.js +49 -4
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +65 -0
- package/enums.d.ts +23 -8
- package/enums.js +15 -1
- package/errors.d.ts +25 -9
- package/errors.js +67 -21
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +19 -11
- package/events/EventSubscriber.d.ts +3 -1
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +89 -36
- 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 +53 -27
- package/metadata/EntitySchema.js +125 -52
- package/metadata/MetadataDiscovery.d.ts +64 -10
- package/metadata/MetadataDiscovery.js +823 -344
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +66 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +71 -38
- package/metadata/MetadataValidator.d.ts +32 -9
- package/metadata/MetadataValidator.js +198 -42
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +40 -0
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +1 -1
- package/metadata/types.d.ts +577 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
- package/naming-strategy/AbstractNamingStrategy.js +20 -2
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
- package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
- package/naming-strategy/MongoNamingStrategy.js +6 -6
- package/naming-strategy/NamingStrategy.d.ts +28 -4
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/not-supported.d.ts +2 -0
- package/not-supported.js +4 -0
- package/package.json +22 -11
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +14 -16
- package/platforms/Platform.js +24 -44
- package/serialization/EntitySerializer.d.ts +8 -3
- package/serialization/EntitySerializer.js +47 -27
- package/serialization/EntityTransformer.js +33 -21
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +16 -13
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.d.ts +9 -6
- package/types/BigIntType.js +4 -1
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +2 -1
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.d.ts +6 -4
- package/types/DecimalType.js +3 -3
- package/types/DoubleType.js +2 -2
- 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 +469 -175
- package/typings.js +120 -45
- package/unit-of-work/ChangeSet.d.ts +4 -6
- package/unit-of-work/ChangeSet.js +4 -5
- package/unit-of-work/ChangeSetComputer.d.ts +3 -8
- package/unit-of-work/ChangeSetComputer.js +44 -21
- package/unit-of-work/ChangeSetPersister.d.ts +15 -12
- package/unit-of-work/ChangeSetPersister.js +113 -45
- 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 +28 -3
- package/unit-of-work/UnitOfWork.js +315 -110
- package/utils/AbstractMigrator.d.ts +101 -0
- package/utils/AbstractMigrator.js +305 -0
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +32 -18
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +801 -207
- package/utils/Configuration.js +150 -191
- package/utils/ConfigurationLoader.d.ts +1 -54
- package/utils/ConfigurationLoader.js +1 -352
- package/utils/Cursor.d.ts +3 -6
- package/utils/Cursor.js +27 -11
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +65 -17
- package/utils/EntityComparator.d.ts +21 -10
- package/utils/EntityComparator.js +243 -106
- package/utils/QueryHelper.d.ts +24 -6
- package/utils/QueryHelper.js +122 -26
- package/utils/RawQueryFragment.d.ts +60 -32
- package/utils/RawQueryFragment.js +69 -66
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +223 -0
- package/utils/Utils.d.ts +15 -122
- package/utils/Utils.js +108 -376
- package/utils/clone.js +8 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +97 -0
- package/utils/fs-utils.d.ts +34 -0
- package/utils/fs-utils.js +196 -0
- package/utils/index.d.ts +2 -3
- package/utils/index.js +2 -3
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +55 -4
- 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 -18
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -18
- package/decorators/Entity.js +0 -13
- 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 -5
- package/decorators/Formula.js +0 -15
- package/decorators/Indexed.d.ts +0 -17
- package/decorators/Indexed.js +0 -20
- package/decorators/ManyToMany.d.ts +0 -40
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -30
- 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 -24
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -9
- 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 -13
- 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 -116
- package/entity/ArrayCollection.js +0 -395
- 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,4 +1,3 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
1
|
import { Collection } from '../entity/Collection.js';
|
|
3
2
|
import { EntityHelper } from '../entity/EntityHelper.js';
|
|
4
3
|
import { helper } from '../entity/wrap.js';
|
|
@@ -13,8 +12,9 @@ import { Cascade, DeferMode, EventType, LockMode, ReferenceKind } from '../enums
|
|
|
13
12
|
import { OptimisticLockError, ValidationError } from '../errors.js';
|
|
14
13
|
import { TransactionEventBroadcaster } from '../events/TransactionEventBroadcaster.js';
|
|
15
14
|
import { IdentityMap } from './IdentityMap.js';
|
|
15
|
+
import { createAsyncContext } from '../utils/AsyncContext.js';
|
|
16
16
|
// to deal with validation for flush inside flush hooks and `Promise.all`
|
|
17
|
-
const insideFlush =
|
|
17
|
+
const insideFlush = createAsyncContext();
|
|
18
18
|
export class UnitOfWork {
|
|
19
19
|
em;
|
|
20
20
|
/** map of references to managed entities */
|
|
@@ -42,8 +42,8 @@ export class UnitOfWork {
|
|
|
42
42
|
this.identityMap = new IdentityMap(this.platform.getDefaultSchemaName());
|
|
43
43
|
this.eventManager = this.em.getEventManager();
|
|
44
44
|
this.comparator = this.em.getComparator();
|
|
45
|
-
this.changeSetComputer = new ChangeSetComputer(this.em
|
|
46
|
-
this.changeSetPersister = new ChangeSetPersister(this.em
|
|
45
|
+
this.changeSetComputer = new ChangeSetComputer(this.em, this.collectionUpdates);
|
|
46
|
+
this.changeSetPersister = new ChangeSetPersister(this.em);
|
|
47
47
|
}
|
|
48
48
|
merge(entity, visited) {
|
|
49
49
|
const wrapped = helper(entity);
|
|
@@ -61,10 +61,46 @@ export class UnitOfWork {
|
|
|
61
61
|
// as there can be some entity with already changed state that is not yet flushed
|
|
62
62
|
if (wrapped.__initialized && (!visited || !wrapped.__originalEntityData)) {
|
|
63
63
|
wrapped.__originalEntityData = this.comparator.prepareEntity(entity);
|
|
64
|
-
wrapped.__touched = false;
|
|
65
64
|
}
|
|
66
65
|
this.cascade(entity, Cascade.MERGE, visited ?? new Set());
|
|
67
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
|
|
69
|
+
* we need to normalize the shape, so relation values are only raw FKs. This method handles that.
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
normalizeEntityData(meta, data) {
|
|
73
|
+
const forceUndefined = this.em.config.get('forceUndefined');
|
|
74
|
+
for (const key of Utils.keys(data)) {
|
|
75
|
+
const prop = meta.properties[key];
|
|
76
|
+
if (!prop) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
|
|
80
|
+
// Skip polymorphic relations - they use PolymorphicRef wrapper
|
|
81
|
+
if (!prop.polymorphic) {
|
|
82
|
+
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
|
|
86
|
+
for (const p of prop.targetMeta.props) {
|
|
87
|
+
/* v8 ignore next */
|
|
88
|
+
const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
|
|
89
|
+
data[prefix + p.name] = data[prop.name][p.name];
|
|
90
|
+
}
|
|
91
|
+
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
92
|
+
}
|
|
93
|
+
if (prop.hydrate === false && prop.customType?.ensureComparable(meta, prop)) {
|
|
94
|
+
const converted = prop.customType.convertToJSValue(data[key], this.platform, { key, mode: 'hydration', force: true });
|
|
95
|
+
data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
|
|
96
|
+
}
|
|
97
|
+
if (forceUndefined) {
|
|
98
|
+
if (data[key] === null) {
|
|
99
|
+
data[key] = undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
68
104
|
/**
|
|
69
105
|
* @internal
|
|
70
106
|
*/
|
|
@@ -82,31 +118,14 @@ export class UnitOfWork {
|
|
|
82
118
|
wrapped.__em ??= this.em;
|
|
83
119
|
wrapped.__managed = true;
|
|
84
120
|
if (data && (options?.refresh || !wrapped.__originalEntityData)) {
|
|
121
|
+
this.normalizeEntityData(wrapped.__meta, data);
|
|
85
122
|
for (const key of Utils.keys(data)) {
|
|
86
123
|
const prop = wrapped.__meta.properties[key];
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
wrapped.__loadedProperties.add(key);
|
|
91
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
|
|
92
|
-
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta.primaryKeys, true);
|
|
93
|
-
}
|
|
94
|
-
else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
|
|
95
|
-
for (const p of prop.targetMeta.props) {
|
|
96
|
-
/* v8 ignore next */
|
|
97
|
-
const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
|
|
98
|
-
data[prefix + p.name] = data[prop.name][p.name];
|
|
99
|
-
}
|
|
100
|
-
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta.primaryKeys, true);
|
|
101
|
-
}
|
|
102
|
-
if (forceUndefined) {
|
|
103
|
-
if (data[key] === null) {
|
|
104
|
-
data[key] = undefined;
|
|
105
|
-
}
|
|
124
|
+
if (prop) {
|
|
125
|
+
wrapped.__loadedProperties.add(key);
|
|
106
126
|
}
|
|
107
127
|
}
|
|
108
128
|
wrapped.__originalEntityData = data;
|
|
109
|
-
wrapped.__touched = false;
|
|
110
129
|
}
|
|
111
130
|
return entity;
|
|
112
131
|
}
|
|
@@ -125,7 +144,7 @@ export class UnitOfWork {
|
|
|
125
144
|
/**
|
|
126
145
|
* 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`.
|
|
127
146
|
*/
|
|
128
|
-
getById(entityName, id, schema) {
|
|
147
|
+
getById(entityName, id, schema, convertCustomTypes) {
|
|
129
148
|
if (id == null || (Array.isArray(id) && id.length === 0)) {
|
|
130
149
|
return undefined;
|
|
131
150
|
}
|
|
@@ -135,7 +154,16 @@ export class UnitOfWork {
|
|
|
135
154
|
hash = '' + id;
|
|
136
155
|
}
|
|
137
156
|
else {
|
|
138
|
-
|
|
157
|
+
let keys = Array.isArray(id) ? Utils.flatten(id) : [id];
|
|
158
|
+
keys = meta.getPrimaryProps(true).map((p, i) => {
|
|
159
|
+
if (!convertCustomTypes && p.customType) {
|
|
160
|
+
return p.customType.convertToDatabaseValue(keys[i], this.platform, {
|
|
161
|
+
key: p.name,
|
|
162
|
+
mode: 'hydration',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return keys[i];
|
|
166
|
+
});
|
|
139
167
|
hash = Utils.getPrimaryKeyHash(keys);
|
|
140
168
|
}
|
|
141
169
|
schema ??= meta.schema ?? this.em.config.getSchema();
|
|
@@ -144,6 +172,40 @@ export class UnitOfWork {
|
|
|
144
172
|
}
|
|
145
173
|
return this.identityMap.getByHash(meta, hash);
|
|
146
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Returns entity from the identity map by an alternate key (non-PK property).
|
|
177
|
+
* @param convertCustomTypes - If true, the value is in database format and will be converted to JS format for lookup.
|
|
178
|
+
* If false (default), the value is assumed to be in JS format already.
|
|
179
|
+
*/
|
|
180
|
+
getByKey(entityName, key, value, schema, convertCustomTypes) {
|
|
181
|
+
const meta = this.metadata.find(entityName).root;
|
|
182
|
+
schema ??= meta.schema ?? this.em.config.getSchema();
|
|
183
|
+
const prop = meta.properties[key];
|
|
184
|
+
// Convert from DB format to JS format if needed
|
|
185
|
+
if (convertCustomTypes && prop?.customType) {
|
|
186
|
+
value = prop.customType.convertToJSValue(value, this.platform, { mode: 'hydration' });
|
|
187
|
+
}
|
|
188
|
+
const hash = this.identityMap.getKeyHash(key, '' + value, schema);
|
|
189
|
+
return this.identityMap.getByHash(meta, hash);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Stores an entity in the identity map under an alternate key (non-PK property).
|
|
193
|
+
* Also sets the property value on the entity.
|
|
194
|
+
* @param convertCustomTypes - If true, the value is in database format and will be converted to JS format.
|
|
195
|
+
* If false (default), the value is assumed to be in JS format already.
|
|
196
|
+
*/
|
|
197
|
+
storeByKey(entity, key, value, schema, convertCustomTypes) {
|
|
198
|
+
const meta = entity.__meta.root;
|
|
199
|
+
schema ??= meta.schema ?? this.em.config.getSchema();
|
|
200
|
+
const prop = meta.properties[key];
|
|
201
|
+
// Convert from DB format to JS format if needed
|
|
202
|
+
if (convertCustomTypes && prop?.customType) {
|
|
203
|
+
value = prop.customType.convertToJSValue(value, this.platform, { mode: 'hydration' });
|
|
204
|
+
}
|
|
205
|
+
// Set the property on the entity
|
|
206
|
+
entity[key] = value;
|
|
207
|
+
this.identityMap.storeByKey(entity, key, '' + value, schema);
|
|
208
|
+
}
|
|
147
209
|
tryGetById(entityName, where, schema, strict = true) {
|
|
148
210
|
const pk = Utils.extractPK(where, this.metadata.find(entityName), strict);
|
|
149
211
|
if (!pk) {
|
|
@@ -182,13 +244,11 @@ export class UnitOfWork {
|
|
|
182
244
|
if (insideFlush.getStore()) {
|
|
183
245
|
return false;
|
|
184
246
|
}
|
|
185
|
-
if (this.queuedActions.has(meta.
|
|
247
|
+
if (this.queuedActions.has(meta.class) || this.queuedActions.has(meta.root.class)) {
|
|
186
248
|
return true;
|
|
187
249
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return true;
|
|
191
|
-
}
|
|
250
|
+
if (meta.discriminatorMap && Object.values(meta.discriminatorMap).some(v => this.queuedActions.has(v))) {
|
|
251
|
+
return true;
|
|
192
252
|
}
|
|
193
253
|
return false;
|
|
194
254
|
}
|
|
@@ -197,7 +257,7 @@ export class UnitOfWork {
|
|
|
197
257
|
}
|
|
198
258
|
computeChangeSet(entity, type) {
|
|
199
259
|
const wrapped = helper(entity);
|
|
200
|
-
if (type) {
|
|
260
|
+
if (type === ChangeSetType.DELETE || type === ChangeSetType.DELETE_EARLY) {
|
|
201
261
|
this.changeSets.set(entity, new ChangeSet(entity, type, {}, wrapped.__meta));
|
|
202
262
|
return;
|
|
203
263
|
}
|
|
@@ -205,11 +265,14 @@ export class UnitOfWork {
|
|
|
205
265
|
if (!cs || this.checkUniqueProps(cs)) {
|
|
206
266
|
return;
|
|
207
267
|
}
|
|
268
|
+
/* v8 ignore next */
|
|
269
|
+
if (type) {
|
|
270
|
+
cs.type = type;
|
|
271
|
+
}
|
|
208
272
|
this.initIdentifier(entity);
|
|
209
273
|
this.changeSets.set(entity, cs);
|
|
210
274
|
this.persistStack.delete(entity);
|
|
211
275
|
wrapped.__originalEntityData = this.comparator.prepareEntity(entity);
|
|
212
|
-
wrapped.__touched = false;
|
|
213
276
|
}
|
|
214
277
|
recomputeSingleChangeSet(entity) {
|
|
215
278
|
const changeSet = this.changeSets.get(entity);
|
|
@@ -220,7 +283,6 @@ export class UnitOfWork {
|
|
|
220
283
|
if (cs && !this.checkUniqueProps(cs)) {
|
|
221
284
|
Object.assign(changeSet.payload, cs.payload);
|
|
222
285
|
helper(entity).__originalEntityData = this.comparator.prepareEntity(entity);
|
|
223
|
-
helper(entity).__touched = false;
|
|
224
286
|
}
|
|
225
287
|
}
|
|
226
288
|
persist(entity, visited, options = {}) {
|
|
@@ -230,7 +292,7 @@ export class UnitOfWork {
|
|
|
230
292
|
}
|
|
231
293
|
const wrapped = helper(entity);
|
|
232
294
|
this.persistStack.add(entity);
|
|
233
|
-
this.queuedActions.add(wrapped.__meta.
|
|
295
|
+
this.queuedActions.add(wrapped.__meta.class);
|
|
234
296
|
this.removeStack.delete(entity);
|
|
235
297
|
if (!wrapped.__managed && wrapped.hasPrimaryKey()) {
|
|
236
298
|
this.identityMap.store(entity);
|
|
@@ -243,7 +305,7 @@ export class UnitOfWork {
|
|
|
243
305
|
// allow removing not managed entities if they are not part of the persist stack
|
|
244
306
|
if (helper(entity).__managed || !this.persistStack.has(entity)) {
|
|
245
307
|
this.removeStack.add(entity);
|
|
246
|
-
this.queuedActions.add(helper(entity).__meta.
|
|
308
|
+
this.queuedActions.add(helper(entity).__meta.class);
|
|
247
309
|
}
|
|
248
310
|
else {
|
|
249
311
|
this.persistStack.delete(entity);
|
|
@@ -303,17 +365,21 @@ export class UnitOfWork {
|
|
|
303
365
|
cs.entity.__helper.__processing = true;
|
|
304
366
|
}
|
|
305
367
|
await this.eventManager.dispatchEvent(EventType.onFlush, { em: this.em, uow: this });
|
|
368
|
+
this.filterCollectionUpdates();
|
|
306
369
|
// nothing to do, do not start transaction
|
|
307
370
|
if (this.changeSets.size === 0 && this.collectionUpdates.size === 0 && this.extraUpdates.size === 0) {
|
|
308
|
-
|
|
371
|
+
await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this });
|
|
372
|
+
return;
|
|
309
373
|
}
|
|
310
374
|
const groups = this.getChangeSetGroups();
|
|
311
375
|
const platform = this.em.getPlatform();
|
|
312
376
|
const runInTransaction = !this.em.isInTransaction() && platform.supportsTransactions() && this.em.config.get('implicitTransactions');
|
|
313
377
|
if (runInTransaction) {
|
|
378
|
+
const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
|
|
314
379
|
await this.em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
|
|
315
380
|
ctx: oldTx,
|
|
316
381
|
eventBroadcaster: new TransactionEventBroadcaster(this.em),
|
|
382
|
+
loggerContext,
|
|
317
383
|
});
|
|
318
384
|
}
|
|
319
385
|
else {
|
|
@@ -330,10 +396,10 @@ export class UnitOfWork {
|
|
|
330
396
|
}
|
|
331
397
|
}
|
|
332
398
|
async lock(entity, options) {
|
|
333
|
-
if (!this.getById(entity.constructor
|
|
399
|
+
if (!this.getById(entity.constructor, helper(entity).__primaryKeys, helper(entity).__schema)) {
|
|
334
400
|
throw ValidationError.entityNotManaged(entity);
|
|
335
401
|
}
|
|
336
|
-
const meta = this.metadata.find(entity.constructor
|
|
402
|
+
const meta = this.metadata.find(entity.constructor);
|
|
337
403
|
if (options.lockMode === LockMode.OPTIMISTIC) {
|
|
338
404
|
await this.lockOptimistic(entity, meta, options.lockVersion);
|
|
339
405
|
}
|
|
@@ -357,7 +423,7 @@ export class UnitOfWork {
|
|
|
357
423
|
if (Utils.isCollection(rel)) {
|
|
358
424
|
rel.removeWithoutPropagation(entity);
|
|
359
425
|
}
|
|
360
|
-
else if (rel && (prop.mapToPk ? helper(this.em.getReference(prop.
|
|
426
|
+
else if (rel && (prop.mapToPk ? helper(this.em.getReference(prop.targetMeta.class, rel)).getSerializedPrimaryKey() === serializedPK : rel === entity)) {
|
|
361
427
|
if (prop.formula) {
|
|
362
428
|
delete referrer[prop.name];
|
|
363
429
|
}
|
|
@@ -369,7 +435,6 @@ export class UnitOfWork {
|
|
|
369
435
|
}
|
|
370
436
|
delete wrapped.__identifier;
|
|
371
437
|
delete wrapped.__originalEntityData;
|
|
372
|
-
wrapped.__touched = false;
|
|
373
438
|
wrapped.__managed = false;
|
|
374
439
|
}
|
|
375
440
|
computeChangeSets() {
|
|
@@ -379,14 +444,14 @@ export class UnitOfWork {
|
|
|
379
444
|
this.cascade(entity, Cascade.REMOVE, visited);
|
|
380
445
|
}
|
|
381
446
|
visited.clear();
|
|
382
|
-
for (const entity of this.persistStack) {
|
|
383
|
-
this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
|
|
384
|
-
}
|
|
385
447
|
for (const entity of this.identityMap) {
|
|
386
448
|
if (!this.removeStack.has(entity) && !this.persistStack.has(entity) && !this.orphanRemoveStack.has(entity)) {
|
|
387
449
|
this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
|
|
388
450
|
}
|
|
389
451
|
}
|
|
452
|
+
for (const entity of this.persistStack) {
|
|
453
|
+
this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
|
|
454
|
+
}
|
|
390
455
|
visited.clear();
|
|
391
456
|
for (const entity of this.persistStack) {
|
|
392
457
|
this.findNewEntities(entity, visited);
|
|
@@ -400,24 +465,24 @@ export class UnitOfWork {
|
|
|
400
465
|
const inserts = {};
|
|
401
466
|
for (const cs of this.changeSets.values()) {
|
|
402
467
|
if (cs.type === ChangeSetType.CREATE) {
|
|
403
|
-
inserts[cs.meta.
|
|
404
|
-
inserts[cs.meta.
|
|
468
|
+
inserts[cs.meta.uniqueName] ??= [];
|
|
469
|
+
inserts[cs.meta.uniqueName].push(cs);
|
|
405
470
|
}
|
|
406
471
|
}
|
|
407
472
|
for (const cs of this.changeSets.values()) {
|
|
408
473
|
if (cs.type === ChangeSetType.UPDATE) {
|
|
409
|
-
this.findEarlyUpdates(cs, inserts[cs.meta.
|
|
474
|
+
this.findEarlyUpdates(cs, inserts[cs.meta.uniqueName]);
|
|
410
475
|
}
|
|
411
476
|
}
|
|
412
477
|
for (const entity of this.removeStack) {
|
|
413
478
|
const wrapped = helper(entity);
|
|
414
|
-
/* v8 ignore next
|
|
479
|
+
/* v8 ignore next */
|
|
415
480
|
if (wrapped.__processing) {
|
|
416
481
|
continue;
|
|
417
482
|
}
|
|
418
483
|
const deletePkHash = [wrapped.getSerializedPrimaryKey(), ...this.expandUniqueProps(entity)];
|
|
419
484
|
let type = ChangeSetType.DELETE;
|
|
420
|
-
for (const cs of inserts[wrapped.__meta.
|
|
485
|
+
for (const cs of inserts[wrapped.__meta.uniqueName] ?? []) {
|
|
421
486
|
if (deletePkHash.some(hash => hash === cs.getSerializedPrimaryKey() || this.expandUniqueProps(cs.entity).find(child => hash === child))) {
|
|
422
487
|
type = ChangeSetType.DELETE_EARLY;
|
|
423
488
|
}
|
|
@@ -436,7 +501,7 @@ export class UnitOfWork {
|
|
|
436
501
|
}
|
|
437
502
|
for (const cs of this.changeSets.values()) {
|
|
438
503
|
for (const prop of props) {
|
|
439
|
-
if (prop.name in cs.payload && cs.
|
|
504
|
+
if (prop.name in cs.payload && cs.rootMeta === changeSet.rootMeta && cs.type === changeSet.type) {
|
|
440
505
|
conflicts = true;
|
|
441
506
|
if (changeSet.payload[prop.name] == null) {
|
|
442
507
|
type = ChangeSetType.UPDATE_EARLY;
|
|
@@ -455,9 +520,10 @@ export class UnitOfWork {
|
|
|
455
520
|
}
|
|
456
521
|
scheduleOrphanRemoval(entity, visited) {
|
|
457
522
|
if (entity) {
|
|
458
|
-
helper(entity)
|
|
523
|
+
const wrapped = helper(entity);
|
|
524
|
+
wrapped.__em = this.em;
|
|
459
525
|
this.orphanRemoveStack.add(entity);
|
|
460
|
-
this.queuedActions.add(
|
|
526
|
+
this.queuedActions.add(wrapped.__meta.class);
|
|
461
527
|
this.cascade(entity, Cascade.SCHEDULE_ORPHAN_REMOVAL, visited);
|
|
462
528
|
}
|
|
463
529
|
}
|
|
@@ -493,7 +559,68 @@ export class UnitOfWork {
|
|
|
493
559
|
}
|
|
494
560
|
const changeSet = this.changeSetComputer.computeChangeSet(entity);
|
|
495
561
|
if (changeSet && !this.checkUniqueProps(changeSet)) {
|
|
496
|
-
|
|
562
|
+
// For TPT child entities, create changesets for each table in hierarchy
|
|
563
|
+
if (wrapped.__meta.inheritanceType === 'tpt' && wrapped.__meta.tptParent) {
|
|
564
|
+
this.createTPTChangeSets(entity, changeSet);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
this.changeSets.set(entity, changeSet);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* For TPT inheritance, creates separate changesets for each table in the hierarchy.
|
|
573
|
+
* Uses the same entity instance for all changesets - only the metadata and payload differ.
|
|
574
|
+
*/
|
|
575
|
+
createTPTChangeSets(entity, originalChangeSet) {
|
|
576
|
+
const meta = helper(entity).__meta;
|
|
577
|
+
const isCreate = originalChangeSet.type === ChangeSetType.CREATE;
|
|
578
|
+
let current = meta;
|
|
579
|
+
let leafCs;
|
|
580
|
+
const parentChangeSets = [];
|
|
581
|
+
while (current) {
|
|
582
|
+
const isRoot = !current.tptParent;
|
|
583
|
+
const payload = {};
|
|
584
|
+
for (const prop of current.ownProps) {
|
|
585
|
+
if (prop.name in originalChangeSet.payload) {
|
|
586
|
+
payload[prop.name] = originalChangeSet.payload[prop.name];
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
// For CREATE on non-root tables, include the PK (EntityIdentifier for deferred resolution)
|
|
590
|
+
if (isCreate && !isRoot) {
|
|
591
|
+
const wrapped = helper(entity);
|
|
592
|
+
const identifier = wrapped.__identifier;
|
|
593
|
+
const identifiers = Array.isArray(identifier) ? identifier : [identifier];
|
|
594
|
+
for (let i = 0; i < current.primaryKeys.length; i++) {
|
|
595
|
+
const pk = current.primaryKeys[i];
|
|
596
|
+
payload[pk] = identifiers[i] ?? originalChangeSet.payload[pk];
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (!isCreate && Object.keys(payload).length === 0) {
|
|
600
|
+
current = current.tptParent;
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
const cs = new ChangeSet(entity, originalChangeSet.type, payload, current);
|
|
604
|
+
if (current === meta) {
|
|
605
|
+
cs.originalEntity = originalChangeSet.originalEntity;
|
|
606
|
+
leafCs = cs;
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
parentChangeSets.push(cs);
|
|
610
|
+
}
|
|
611
|
+
current = current.tptParent;
|
|
612
|
+
}
|
|
613
|
+
// When only parent properties changed (UPDATE), leaf payload is empty—create a stub anchor
|
|
614
|
+
if (!leafCs && parentChangeSets.length > 0) {
|
|
615
|
+
leafCs = new ChangeSet(entity, originalChangeSet.type, {}, meta);
|
|
616
|
+
leafCs.originalEntity = originalChangeSet.originalEntity;
|
|
617
|
+
}
|
|
618
|
+
// Store the leaf changeset in the main map (entity as key), with parent CSs attached
|
|
619
|
+
if (leafCs) {
|
|
620
|
+
if (parentChangeSets.length > 0) {
|
|
621
|
+
leafCs.tptChangeSets = parentChangeSets;
|
|
622
|
+
}
|
|
623
|
+
this.changeSets.set(entity, leafCs);
|
|
497
624
|
}
|
|
498
625
|
}
|
|
499
626
|
/**
|
|
@@ -542,13 +669,22 @@ export class UnitOfWork {
|
|
|
542
669
|
if (!wrapped || wrapped.__identifier || wrapped.hasPrimaryKey()) {
|
|
543
670
|
return;
|
|
544
671
|
}
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
672
|
+
const pks = wrapped.__meta.getPrimaryProps();
|
|
673
|
+
const idents = [];
|
|
674
|
+
for (const pk of pks) {
|
|
675
|
+
if (pk.kind === ReferenceKind.SCALAR) {
|
|
676
|
+
idents.push(new EntityIdentifier(entity[pk.name]));
|
|
677
|
+
}
|
|
678
|
+
else if (entity[pk.name]) {
|
|
679
|
+
this.initIdentifier(entity[pk.name]);
|
|
680
|
+
idents.push(helper(entity[pk.name])?.__identifier);
|
|
681
|
+
}
|
|
548
682
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
683
|
+
if (pks.length === 1) {
|
|
684
|
+
wrapped.__identifier = idents[0];
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
wrapped.__identifier = idents;
|
|
552
688
|
}
|
|
553
689
|
}
|
|
554
690
|
processReference(parent, prop, kind, visited, processed, idx) {
|
|
@@ -597,11 +733,18 @@ export class UnitOfWork {
|
|
|
597
733
|
const copy = this.comparator.prepareEntity(changeSet.entity);
|
|
598
734
|
await this.eventManager.dispatchEvent(type, { entity: changeSet.entity, meta, em: this.em, changeSet });
|
|
599
735
|
const current = this.comparator.prepareEntity(changeSet.entity);
|
|
600
|
-
const diff = this.comparator.diffEntities(changeSet.
|
|
736
|
+
const diff = this.comparator.diffEntities(changeSet.meta.class, copy, current);
|
|
601
737
|
Object.assign(changeSet.payload, diff);
|
|
602
738
|
const wrapped = helper(changeSet.entity);
|
|
603
|
-
if (wrapped.__identifier
|
|
604
|
-
|
|
739
|
+
if (wrapped.__identifier) {
|
|
740
|
+
const idents = Utils.asArray(wrapped.__identifier);
|
|
741
|
+
let i = 0;
|
|
742
|
+
for (const pk of wrapped.__meta.primaryKeys) {
|
|
743
|
+
if (diff[pk]) {
|
|
744
|
+
idents[i].setValue(diff[pk]);
|
|
745
|
+
}
|
|
746
|
+
i++;
|
|
747
|
+
}
|
|
605
748
|
}
|
|
606
749
|
}
|
|
607
750
|
postCommitCleanup() {
|
|
@@ -685,7 +828,7 @@ export class UnitOfWork {
|
|
|
685
828
|
if (!meta.versionProperty) {
|
|
686
829
|
throw OptimisticLockError.notVersioned(meta);
|
|
687
830
|
}
|
|
688
|
-
if (
|
|
831
|
+
if (typeof version === 'undefined') {
|
|
689
832
|
return;
|
|
690
833
|
}
|
|
691
834
|
const wrapped = helper(entity);
|
|
@@ -699,26 +842,26 @@ export class UnitOfWork {
|
|
|
699
842
|
}
|
|
700
843
|
fixMissingReference(entity, prop) {
|
|
701
844
|
const reference = entity[prop.name];
|
|
702
|
-
const
|
|
703
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
704
|
-
if (!Utils.isEntity(
|
|
705
|
-
entity[prop.name] = this.em.getReference(prop.
|
|
845
|
+
const target = Reference.unwrapReference(reference);
|
|
846
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && target && !prop.mapToPk) {
|
|
847
|
+
if (!Utils.isEntity(target)) {
|
|
848
|
+
entity[prop.name] = this.em.getReference(prop.targetMeta.class, target, { wrapped: !!prop.ref });
|
|
706
849
|
}
|
|
707
|
-
else if (!helper(
|
|
708
|
-
const pk = helper(
|
|
709
|
-
entity[prop.name] = this.em.getReference(prop.
|
|
850
|
+
else if (!helper(target).__initialized && !helper(target).__em) {
|
|
851
|
+
const pk = helper(target).getPrimaryKey();
|
|
852
|
+
entity[prop.name] = this.em.getReference(prop.targetMeta.class, pk, { wrapped: !!prop.ref });
|
|
710
853
|
}
|
|
711
854
|
}
|
|
712
|
-
// perf: set the `Collection._property` to skip the getter, as it can be slow when there
|
|
713
|
-
if (Utils.isCollection(
|
|
714
|
-
|
|
855
|
+
// perf: set the `Collection._property` to skip the getter, as it can be slow when there are a lot of relations
|
|
856
|
+
if (Utils.isCollection(target)) {
|
|
857
|
+
target.property = prop;
|
|
715
858
|
}
|
|
716
859
|
const isCollection = [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind);
|
|
717
|
-
if (isCollection && Array.isArray(
|
|
860
|
+
if (isCollection && Array.isArray(target)) {
|
|
718
861
|
const collection = new Collection(entity);
|
|
719
862
|
collection.property = prop;
|
|
720
863
|
entity[prop.name] = collection;
|
|
721
|
-
collection.set(
|
|
864
|
+
collection.set(target);
|
|
722
865
|
}
|
|
723
866
|
}
|
|
724
867
|
async persistToDatabase(groups, ctx) {
|
|
@@ -728,30 +871,30 @@ export class UnitOfWork {
|
|
|
728
871
|
const commitOrder = this.getCommitOrder();
|
|
729
872
|
const commitOrderReversed = [...commitOrder].reverse();
|
|
730
873
|
// early delete - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
|
|
731
|
-
for (const
|
|
732
|
-
await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(
|
|
874
|
+
for (const meta of commitOrderReversed) {
|
|
875
|
+
await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(meta) ?? [], ctx);
|
|
733
876
|
}
|
|
734
877
|
// early update - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
|
|
735
|
-
for (const
|
|
736
|
-
await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(
|
|
878
|
+
for (const meta of commitOrder) {
|
|
879
|
+
await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(meta) ?? [], ctx);
|
|
737
880
|
}
|
|
738
881
|
// extra updates
|
|
739
882
|
await this.commitExtraUpdates(ChangeSetType.UPDATE_EARLY, ctx);
|
|
740
883
|
// create
|
|
741
|
-
for (const
|
|
742
|
-
await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(
|
|
884
|
+
for (const meta of commitOrder) {
|
|
885
|
+
await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(meta) ?? [], ctx);
|
|
743
886
|
}
|
|
744
887
|
// update
|
|
745
|
-
for (const
|
|
746
|
-
await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(
|
|
888
|
+
for (const meta of commitOrder) {
|
|
889
|
+
await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(meta) ?? [], ctx);
|
|
747
890
|
}
|
|
748
891
|
// extra updates
|
|
749
892
|
await this.commitExtraUpdates(ChangeSetType.UPDATE, ctx);
|
|
750
893
|
// collection updates
|
|
751
894
|
await this.commitCollectionUpdates(ctx);
|
|
752
895
|
// delete - entity deletions need to be in reverse commit order
|
|
753
|
-
for (const
|
|
754
|
-
await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(
|
|
896
|
+
for (const meta of commitOrderReversed) {
|
|
897
|
+
await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(meta) ?? [], ctx);
|
|
755
898
|
}
|
|
756
899
|
// take snapshots of all persisted collections
|
|
757
900
|
const visited = new Set();
|
|
@@ -787,16 +930,28 @@ export class UnitOfWork {
|
|
|
787
930
|
if (Utils.isCollection(ref)) {
|
|
788
931
|
ref.getItems(false).some(item => {
|
|
789
932
|
const cs = this.changeSets.get(Reference.unwrapReference(item));
|
|
790
|
-
const isScheduledForInsert = cs
|
|
933
|
+
const isScheduledForInsert = cs?.type === ChangeSetType.CREATE && !cs.persisted;
|
|
791
934
|
if (isScheduledForInsert) {
|
|
792
935
|
this.scheduleExtraUpdate(changeSet, [prop]);
|
|
793
936
|
return true;
|
|
794
937
|
}
|
|
795
938
|
return false;
|
|
796
939
|
});
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
const refEntity = Reference.unwrapReference(ref);
|
|
943
|
+
// For mapToPk properties, the value is a primitive (string/array), not an entity
|
|
944
|
+
if (!Utils.isEntity(refEntity)) {
|
|
945
|
+
continue;
|
|
946
|
+
}
|
|
947
|
+
// For TPT entities, check if the ROOT table's changeset has been persisted
|
|
948
|
+
// (since the FK is to the root table, not the concrete entity's table)
|
|
949
|
+
let cs = this.changeSets.get(refEntity);
|
|
950
|
+
if (cs?.tptChangeSets?.length) {
|
|
951
|
+
// Root table changeset is the last one (ordered immediate parent → root)
|
|
952
|
+
cs = cs.tptChangeSets[cs.tptChangeSets.length - 1];
|
|
797
953
|
}
|
|
798
|
-
const
|
|
799
|
-
const isScheduledForInsert = cs && cs.type === ChangeSetType.CREATE && !cs.persisted;
|
|
954
|
+
const isScheduledForInsert = cs?.type === ChangeSetType.CREATE && !cs.persisted;
|
|
800
955
|
if (isScheduledForInsert) {
|
|
801
956
|
this.scheduleExtraUpdate(changeSet, [prop]);
|
|
802
957
|
}
|
|
@@ -825,9 +980,16 @@ export class UnitOfWork {
|
|
|
825
980
|
}
|
|
826
981
|
await this.changeSetPersister.executeUpdates(changeSets, batched, { ctx });
|
|
827
982
|
for (const changeSet of changeSets) {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
983
|
+
const wrapped = helper(changeSet.entity);
|
|
984
|
+
wrapped.__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
|
|
985
|
+
if (!wrapped.__initialized) {
|
|
986
|
+
for (const prop of changeSet.meta.relations) {
|
|
987
|
+
if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) && changeSet.entity[prop.name] == null) {
|
|
988
|
+
changeSet.entity[prop.name] = Collection.create(changeSet.entity, prop.name, undefined, wrapped.isInitialized());
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
wrapped.__initialized = true;
|
|
992
|
+
}
|
|
831
993
|
await this.runHooks(EventType.afterUpdate, changeSet);
|
|
832
994
|
}
|
|
833
995
|
}
|
|
@@ -870,23 +1032,34 @@ export class UnitOfWork {
|
|
|
870
1032
|
}
|
|
871
1033
|
}
|
|
872
1034
|
async commitCollectionUpdates(ctx) {
|
|
873
|
-
|
|
1035
|
+
this.filterCollectionUpdates();
|
|
1036
|
+
const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
|
|
1037
|
+
await this.em.getDriver().syncCollections(this.collectionUpdates, {
|
|
1038
|
+
ctx,
|
|
1039
|
+
schema: this.em.schema,
|
|
1040
|
+
loggerContext,
|
|
1041
|
+
});
|
|
1042
|
+
for (const coll of this.collectionUpdates) {
|
|
1043
|
+
coll.takeSnapshot();
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
filterCollectionUpdates() {
|
|
874
1047
|
for (const coll of this.collectionUpdates) {
|
|
1048
|
+
let skip = true;
|
|
875
1049
|
if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
|
|
876
1050
|
if (this.platform.usesPivotTable()) {
|
|
877
|
-
|
|
1051
|
+
skip = false;
|
|
878
1052
|
}
|
|
879
1053
|
}
|
|
880
1054
|
else if (coll.property.kind === ReferenceKind.ONE_TO_MANY && coll.getSnapshot() === undefined) {
|
|
881
|
-
|
|
1055
|
+
skip = false;
|
|
882
1056
|
}
|
|
883
1057
|
else if (coll.property.kind === ReferenceKind.MANY_TO_MANY && !coll.property.owner) {
|
|
884
|
-
|
|
1058
|
+
skip = false;
|
|
1059
|
+
}
|
|
1060
|
+
if (skip) {
|
|
1061
|
+
this.collectionUpdates.delete(coll);
|
|
885
1062
|
}
|
|
886
|
-
}
|
|
887
|
-
await this.em.getDriver().syncCollections(collectionUpdates, { ctx });
|
|
888
|
-
for (const coll of this.collectionUpdates) {
|
|
889
|
-
coll.takeSnapshot();
|
|
890
1063
|
}
|
|
891
1064
|
}
|
|
892
1065
|
/**
|
|
@@ -900,12 +1073,23 @@ export class UnitOfWork {
|
|
|
900
1073
|
[ChangeSetType.UPDATE_EARLY]: new Map(),
|
|
901
1074
|
[ChangeSetType.DELETE_EARLY]: new Map(),
|
|
902
1075
|
};
|
|
903
|
-
|
|
1076
|
+
const addToGroup = (cs) => {
|
|
1077
|
+
// Skip stub TPT changesets with empty payload (e.g. leaf with no own-property changes on UPDATE)
|
|
1078
|
+
if ((cs.type === ChangeSetType.UPDATE || cs.type === ChangeSetType.UPDATE_EARLY) && !Utils.hasObjectKeys(cs.payload)) {
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
904
1081
|
const group = groups[cs.type];
|
|
905
|
-
const
|
|
1082
|
+
const groupKey = cs.meta.inheritanceType === 'tpt' ? cs.meta : cs.rootMeta;
|
|
1083
|
+
const classGroup = group.get(groupKey) ?? [];
|
|
906
1084
|
classGroup.push(cs);
|
|
907
|
-
if (!group.has(
|
|
908
|
-
group.set(
|
|
1085
|
+
if (!group.has(groupKey)) {
|
|
1086
|
+
group.set(groupKey, classGroup);
|
|
1087
|
+
}
|
|
1088
|
+
};
|
|
1089
|
+
for (const cs of this.changeSets.values()) {
|
|
1090
|
+
addToGroup(cs);
|
|
1091
|
+
for (const parentCs of cs.tptChangeSets ?? []) {
|
|
1092
|
+
addToGroup(parentCs);
|
|
909
1093
|
}
|
|
910
1094
|
}
|
|
911
1095
|
return groups;
|
|
@@ -913,14 +1097,35 @@ export class UnitOfWork {
|
|
|
913
1097
|
getCommitOrder() {
|
|
914
1098
|
const calc = new CommitOrderCalculator();
|
|
915
1099
|
const set = new Set();
|
|
916
|
-
this.changeSets.forEach(cs =>
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1100
|
+
this.changeSets.forEach(cs => {
|
|
1101
|
+
if (cs.meta.inheritanceType === 'tpt') {
|
|
1102
|
+
set.add(cs.meta);
|
|
1103
|
+
for (const parentCs of cs.tptChangeSets ?? []) {
|
|
1104
|
+
set.add(parentCs.meta);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
else {
|
|
1108
|
+
set.add(cs.rootMeta);
|
|
1109
|
+
}
|
|
1110
|
+
});
|
|
1111
|
+
set.forEach(meta => calc.addNode(meta._id));
|
|
1112
|
+
for (const meta of set) {
|
|
1113
|
+
for (const prop of meta.relations) {
|
|
1114
|
+
if (prop.polymorphTargets) {
|
|
1115
|
+
for (const targetMeta of prop.polymorphTargets) {
|
|
1116
|
+
calc.discoverProperty({ ...prop, targetMeta }, meta._id);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
calc.discoverProperty(prop, meta._id);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
// For TPT, parent table must be inserted BEFORE child tables
|
|
1124
|
+
if (meta.inheritanceType === 'tpt' && meta.tptParent && set.has(meta.tptParent)) {
|
|
1125
|
+
calc.addDependency(meta.tptParent._id, meta._id, 1);
|
|
921
1126
|
}
|
|
922
1127
|
}
|
|
923
|
-
return calc.sort();
|
|
1128
|
+
return calc.sort().map(id => this.metadata.getById(id));
|
|
924
1129
|
}
|
|
925
1130
|
resetTransaction(oldTx) {
|
|
926
1131
|
if (oldTx) {
|