@mikro-orm/core 7.0.0-rc.2 → 7.0.0
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 +4 -16
- package/EntityManager.js +248 -181
- package/MikroORM.d.ts +4 -6
- package/MikroORM.js +24 -24
- package/README.md +5 -4
- package/cache/FileCacheAdapter.d.ts +1 -5
- package/cache/FileCacheAdapter.js +22 -24
- package/cache/GeneratedCacheAdapter.d.ts +1 -1
- package/cache/GeneratedCacheAdapter.js +6 -6
- package/cache/MemoryCacheAdapter.d.ts +1 -2
- package/cache/MemoryCacheAdapter.js +8 -8
- package/cache/index.d.ts +1 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +1 -0
- package/connections/Connection.js +43 -14
- package/drivers/DatabaseDriver.d.ts +0 -2
- package/drivers/DatabaseDriver.js +28 -12
- package/drivers/IDatabaseDriver.d.ts +43 -0
- package/entity/Collection.d.ts +1 -9
- package/entity/Collection.js +124 -108
- package/entity/EntityAssigner.js +23 -11
- package/entity/EntityFactory.d.ts +1 -8
- package/entity/EntityFactory.js +79 -59
- package/entity/EntityHelper.js +25 -16
- package/entity/EntityLoader.d.ts +1 -3
- package/entity/EntityLoader.js +90 -60
- package/entity/Reference.d.ts +2 -3
- package/entity/Reference.js +48 -19
- package/entity/WrappedEntity.d.ts +4 -2
- package/entity/WrappedEntity.js +5 -1
- package/entity/defineEntity.d.ts +42 -85
- package/entity/utils.js +28 -26
- package/entity/validators.js +2 -1
- package/enums.d.ts +2 -1
- package/enums.js +13 -17
- package/errors.d.ts +11 -11
- package/errors.js +8 -8
- package/events/EventManager.d.ts +1 -4
- package/events/EventManager.js +26 -23
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/exceptions.js +9 -2
- package/hydration/ObjectHydrator.d.ts +1 -2
- package/hydration/ObjectHydrator.js +41 -27
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.js +6 -7
- package/logging/Logger.d.ts +2 -1
- package/logging/colors.js +2 -5
- package/logging/index.d.ts +1 -1
- package/logging/index.js +0 -1
- package/metadata/EntitySchema.d.ts +3 -3
- package/metadata/EntitySchema.js +12 -2
- package/metadata/MetadataDiscovery.d.ts +1 -9
- package/metadata/MetadataDiscovery.js +251 -179
- package/metadata/MetadataProvider.js +26 -1
- package/metadata/MetadataStorage.d.ts +1 -5
- package/metadata/MetadataStorage.js +37 -39
- package/metadata/MetadataValidator.js +20 -5
- package/metadata/discover-entities.js +1 -1
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +0 -1
- package/metadata/types.d.ts +2 -2
- package/naming-strategy/AbstractNamingStrategy.js +6 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +1 -1
- package/naming-strategy/index.d.ts +1 -1
- package/naming-strategy/index.js +0 -1
- package/not-supported.js +5 -1
- package/package.json +38 -38
- package/platforms/Platform.d.ts +24 -1
- package/platforms/Platform.js +106 -27
- package/serialization/EntitySerializer.js +8 -4
- package/serialization/EntityTransformer.js +4 -1
- package/serialization/SerializationContext.d.ts +4 -8
- package/serialization/SerializationContext.js +21 -16
- package/types/UuidType.d.ts +2 -0
- package/types/UuidType.js +14 -2
- package/types/index.d.ts +2 -1
- package/typings.d.ts +35 -24
- package/typings.js +9 -9
- package/unit-of-work/ChangeSet.js +4 -4
- package/unit-of-work/ChangeSetComputer.d.ts +1 -6
- package/unit-of-work/ChangeSetComputer.js +29 -27
- package/unit-of-work/ChangeSetPersister.d.ts +1 -9
- package/unit-of-work/ChangeSetPersister.js +63 -58
- package/unit-of-work/CommitOrderCalculator.d.ts +1 -4
- package/unit-of-work/CommitOrderCalculator.js +17 -15
- package/unit-of-work/IdentityMap.d.ts +2 -5
- package/unit-of-work/IdentityMap.js +18 -18
- package/unit-of-work/UnitOfWork.d.ts +12 -20
- package/unit-of-work/UnitOfWork.js +228 -191
- package/utils/AbstractMigrator.d.ts +2 -2
- package/utils/AbstractMigrator.js +10 -12
- package/utils/AbstractSchemaGenerator.js +2 -1
- package/utils/AsyncContext.js +1 -1
- package/utils/Configuration.d.ts +90 -189
- package/utils/Configuration.js +97 -77
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +8 -6
- package/utils/DataloaderUtils.js +15 -12
- package/utils/EntityComparator.d.ts +8 -15
- package/utils/EntityComparator.js +100 -92
- package/utils/QueryHelper.d.ts +16 -1
- package/utils/QueryHelper.js +108 -50
- package/utils/RawQueryFragment.d.ts +4 -4
- package/utils/RawQueryFragment.js +3 -2
- package/utils/TransactionManager.js +3 -3
- package/utils/Utils.d.ts +2 -2
- package/utils/Utils.js +39 -32
- package/utils/clone.js +5 -0
- package/utils/env-vars.js +6 -5
- package/utils/fs-utils.d.ts +3 -17
- package/utils/fs-utils.js +2 -5
- package/utils/upsert-utils.js +7 -4
|
@@ -16,38 +16,38 @@ import { createAsyncContext } from '../utils/AsyncContext.js';
|
|
|
16
16
|
// to deal with validation for flush inside flush hooks and `Promise.all`
|
|
17
17
|
const insideFlush = createAsyncContext();
|
|
18
18
|
export class UnitOfWork {
|
|
19
|
-
em;
|
|
20
19
|
/** map of references to managed entities */
|
|
21
|
-
identityMap;
|
|
22
|
-
persistStack = new Set();
|
|
23
|
-
removeStack = new Set();
|
|
24
|
-
orphanRemoveStack = new Set();
|
|
25
|
-
changeSets = new Map();
|
|
26
|
-
collectionUpdates = new Set();
|
|
27
|
-
extraUpdates = new Set();
|
|
28
|
-
metadata;
|
|
29
|
-
platform;
|
|
30
|
-
eventManager;
|
|
31
|
-
comparator;
|
|
32
|
-
changeSetComputer;
|
|
33
|
-
changeSetPersister;
|
|
34
|
-
queuedActions = new Set();
|
|
35
|
-
loadedEntities = new Set();
|
|
36
|
-
flushQueue = [];
|
|
37
|
-
working = false;
|
|
20
|
+
#identityMap;
|
|
21
|
+
#persistStack = new Set();
|
|
22
|
+
#removeStack = new Set();
|
|
23
|
+
#orphanRemoveStack = new Set();
|
|
24
|
+
#changeSets = new Map();
|
|
25
|
+
#collectionUpdates = new Set();
|
|
26
|
+
#extraUpdates = new Set();
|
|
27
|
+
#metadata;
|
|
28
|
+
#platform;
|
|
29
|
+
#eventManager;
|
|
30
|
+
#comparator;
|
|
31
|
+
#changeSetComputer;
|
|
32
|
+
#changeSetPersister;
|
|
33
|
+
#queuedActions = new Set();
|
|
34
|
+
#loadedEntities = new Set();
|
|
35
|
+
#flushQueue = [];
|
|
36
|
+
#working = false;
|
|
37
|
+
#em;
|
|
38
38
|
constructor(em) {
|
|
39
|
-
this
|
|
40
|
-
this
|
|
41
|
-
this
|
|
42
|
-
this
|
|
43
|
-
this
|
|
44
|
-
this
|
|
45
|
-
this
|
|
46
|
-
this
|
|
39
|
+
this.#em = em;
|
|
40
|
+
this.#metadata = this.#em.getMetadata();
|
|
41
|
+
this.#platform = this.#em.getPlatform();
|
|
42
|
+
this.#identityMap = new IdentityMap(this.#platform.getDefaultSchemaName());
|
|
43
|
+
this.#eventManager = this.#em.getEventManager();
|
|
44
|
+
this.#comparator = this.#em.getComparator();
|
|
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);
|
|
50
|
-
wrapped.__em = this
|
|
50
|
+
wrapped.__em = this.#em;
|
|
51
51
|
if (!wrapped.hasPrimaryKey()) {
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
@@ -56,11 +56,11 @@ export class UnitOfWork {
|
|
|
56
56
|
if (!wrapped.__managed && visited) {
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
|
-
this
|
|
59
|
+
this.#identityMap.store(entity);
|
|
60
60
|
// if visited is available, we are cascading, and need to be careful when resetting the entity data
|
|
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
|
-
wrapped.__originalEntityData = this
|
|
63
|
+
wrapped.__originalEntityData = this.#comparator.prepareEntity(entity);
|
|
64
64
|
}
|
|
65
65
|
this.cascade(entity, Cascade.MERGE, visited ?? new Set());
|
|
66
66
|
}
|
|
@@ -70,13 +70,14 @@ export class UnitOfWork {
|
|
|
70
70
|
* @internal
|
|
71
71
|
*/
|
|
72
72
|
normalizeEntityData(meta, data) {
|
|
73
|
-
const forceUndefined = this
|
|
73
|
+
const forceUndefined = this.#em.config.get('forceUndefined');
|
|
74
74
|
for (const key of Utils.keys(data)) {
|
|
75
75
|
const prop = meta.properties[key];
|
|
76
76
|
if (!prop) {
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
79
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
79
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
|
|
80
|
+
Utils.isPlainObject(data[prop.name])) {
|
|
80
81
|
// Skip polymorphic relations - they use PolymorphicRef wrapper
|
|
81
82
|
if (!prop.polymorphic) {
|
|
82
83
|
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
@@ -86,13 +87,17 @@ export class UnitOfWork {
|
|
|
86
87
|
for (const p of prop.targetMeta.props) {
|
|
87
88
|
/* v8 ignore next */
|
|
88
89
|
const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
|
|
89
|
-
data[prefix + p.name] = data[prop.name][p.name];
|
|
90
|
+
data[(prefix + p.name)] = data[prop.name][p.name];
|
|
90
91
|
}
|
|
91
92
|
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
92
93
|
}
|
|
93
94
|
if (prop.hydrate === false && prop.customType?.ensureComparable(meta, prop)) {
|
|
94
|
-
const converted = prop.customType.convertToJSValue(data[key], this
|
|
95
|
-
|
|
95
|
+
const converted = prop.customType.convertToJSValue(data[key], this.#platform, {
|
|
96
|
+
key,
|
|
97
|
+
mode: 'hydration',
|
|
98
|
+
force: true,
|
|
99
|
+
});
|
|
100
|
+
data[key] = prop.customType.convertToDatabaseValue(converted, this.#platform, { key, mode: 'hydration' });
|
|
96
101
|
}
|
|
97
102
|
if (forceUndefined) {
|
|
98
103
|
if (data[key] === null) {
|
|
@@ -105,17 +110,17 @@ export class UnitOfWork {
|
|
|
105
110
|
* @internal
|
|
106
111
|
*/
|
|
107
112
|
register(entity, data, options) {
|
|
108
|
-
this
|
|
113
|
+
this.#identityMap.store(entity);
|
|
109
114
|
EntityHelper.ensurePropagation(entity);
|
|
110
115
|
if (options?.newEntity) {
|
|
111
116
|
return entity;
|
|
112
117
|
}
|
|
113
|
-
const forceUndefined = this
|
|
118
|
+
const forceUndefined = this.#em.config.get('forceUndefined');
|
|
114
119
|
const wrapped = helper(entity);
|
|
115
120
|
if (options?.loaded && wrapped.__initialized && !wrapped.__onLoadFired) {
|
|
116
|
-
this
|
|
121
|
+
this.#loadedEntities.add(entity);
|
|
117
122
|
}
|
|
118
|
-
wrapped.__em ??= this
|
|
123
|
+
wrapped.__em ??= this.#em;
|
|
119
124
|
wrapped.__managed = true;
|
|
120
125
|
if (data && (options?.refresh || !wrapped.__originalEntityData)) {
|
|
121
126
|
this.normalizeEntityData(wrapped.__meta, data);
|
|
@@ -133,13 +138,19 @@ export class UnitOfWork {
|
|
|
133
138
|
* @internal
|
|
134
139
|
*/
|
|
135
140
|
async dispatchOnLoadEvent() {
|
|
136
|
-
for (const entity of this
|
|
137
|
-
if (this
|
|
138
|
-
await this
|
|
141
|
+
for (const entity of this.#loadedEntities) {
|
|
142
|
+
if (this.#eventManager.hasListeners(EventType.onLoad, entity.__meta)) {
|
|
143
|
+
await this.#eventManager.dispatchEvent(EventType.onLoad, { entity, meta: entity.__meta, em: this.#em });
|
|
139
144
|
helper(entity).__onLoadFired = true;
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
|
-
this
|
|
147
|
+
this.#loadedEntities.clear();
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* @internal
|
|
151
|
+
*/
|
|
152
|
+
unmarkAsLoaded(entity) {
|
|
153
|
+
this.#loadedEntities.delete(entity);
|
|
143
154
|
}
|
|
144
155
|
/**
|
|
145
156
|
* 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`.
|
|
@@ -148,7 +159,7 @@ export class UnitOfWork {
|
|
|
148
159
|
if (id == null || (Array.isArray(id) && id.length === 0)) {
|
|
149
160
|
return undefined;
|
|
150
161
|
}
|
|
151
|
-
const meta = this
|
|
162
|
+
const meta = this.#metadata.find(entityName).root;
|
|
152
163
|
let hash;
|
|
153
164
|
if (meta.simplePK) {
|
|
154
165
|
hash = '' + id;
|
|
@@ -157,7 +168,7 @@ export class UnitOfWork {
|
|
|
157
168
|
let keys = Array.isArray(id) ? Utils.flatten(id) : [id];
|
|
158
169
|
keys = meta.getPrimaryProps(true).map((p, i) => {
|
|
159
170
|
if (!convertCustomTypes && p.customType) {
|
|
160
|
-
return p.customType.convertToDatabaseValue(keys[i], this
|
|
171
|
+
return p.customType.convertToDatabaseValue(keys[i], this.#platform, {
|
|
161
172
|
key: p.name,
|
|
162
173
|
mode: 'hydration',
|
|
163
174
|
});
|
|
@@ -166,11 +177,11 @@ export class UnitOfWork {
|
|
|
166
177
|
});
|
|
167
178
|
hash = Utils.getPrimaryKeyHash(keys);
|
|
168
179
|
}
|
|
169
|
-
schema ??= meta.schema ?? this
|
|
180
|
+
schema ??= meta.schema ?? this.#em.config.getSchema();
|
|
170
181
|
if (schema) {
|
|
171
182
|
hash = `${schema}:${hash}`;
|
|
172
183
|
}
|
|
173
|
-
return this
|
|
184
|
+
return this.#identityMap.getByHash(meta, hash);
|
|
174
185
|
}
|
|
175
186
|
/**
|
|
176
187
|
* Returns entity from the identity map by an alternate key (non-PK property).
|
|
@@ -178,15 +189,15 @@ export class UnitOfWork {
|
|
|
178
189
|
* If false (default), the value is assumed to be in JS format already.
|
|
179
190
|
*/
|
|
180
191
|
getByKey(entityName, key, value, schema, convertCustomTypes) {
|
|
181
|
-
const meta = this
|
|
182
|
-
schema ??= meta.schema ?? this
|
|
192
|
+
const meta = this.#metadata.find(entityName).root;
|
|
193
|
+
schema ??= meta.schema ?? this.#em.config.getSchema();
|
|
183
194
|
const prop = meta.properties[key];
|
|
184
195
|
// Convert from DB format to JS format if needed
|
|
185
196
|
if (convertCustomTypes && prop?.customType) {
|
|
186
|
-
value = prop.customType.convertToJSValue(value, this
|
|
197
|
+
value = prop.customType.convertToJSValue(value, this.#platform, { mode: 'hydration' });
|
|
187
198
|
}
|
|
188
|
-
const hash = this
|
|
189
|
-
return this
|
|
199
|
+
const hash = this.#identityMap.getKeyHash(key, '' + value, schema);
|
|
200
|
+
return this.#identityMap.getByHash(meta, hash);
|
|
190
201
|
}
|
|
191
202
|
/**
|
|
192
203
|
* Stores an entity in the identity map under an alternate key (non-PK property).
|
|
@@ -196,18 +207,18 @@ export class UnitOfWork {
|
|
|
196
207
|
*/
|
|
197
208
|
storeByKey(entity, key, value, schema, convertCustomTypes) {
|
|
198
209
|
const meta = entity.__meta.root;
|
|
199
|
-
schema ??= meta.schema ?? this
|
|
210
|
+
schema ??= meta.schema ?? this.#em.config.getSchema();
|
|
200
211
|
const prop = meta.properties[key];
|
|
201
212
|
// Convert from DB format to JS format if needed
|
|
202
213
|
if (convertCustomTypes && prop?.customType) {
|
|
203
|
-
value = prop.customType.convertToJSValue(value, this
|
|
214
|
+
value = prop.customType.convertToJSValue(value, this.#platform, { mode: 'hydration' });
|
|
204
215
|
}
|
|
205
216
|
// Set the property on the entity
|
|
206
217
|
entity[key] = value;
|
|
207
|
-
this
|
|
218
|
+
this.#identityMap.storeByKey(entity, key, '' + value, schema);
|
|
208
219
|
}
|
|
209
220
|
tryGetById(entityName, where, schema, strict = true) {
|
|
210
|
-
const pk = Utils.extractPK(where, this
|
|
221
|
+
const pk = Utils.extractPK(where, this.#metadata.find(entityName), strict);
|
|
211
222
|
if (!pk) {
|
|
212
223
|
return null;
|
|
213
224
|
}
|
|
@@ -217,7 +228,7 @@ export class UnitOfWork {
|
|
|
217
228
|
* Returns map of all managed entities.
|
|
218
229
|
*/
|
|
219
230
|
getIdentityMap() {
|
|
220
|
-
return this
|
|
231
|
+
return this.#identityMap;
|
|
221
232
|
}
|
|
222
233
|
/**
|
|
223
234
|
* Returns stored snapshot of entity state that is used for change set computation.
|
|
@@ -226,42 +237,42 @@ export class UnitOfWork {
|
|
|
226
237
|
return helper(entity).__originalEntityData;
|
|
227
238
|
}
|
|
228
239
|
getPersistStack() {
|
|
229
|
-
return this
|
|
240
|
+
return this.#persistStack;
|
|
230
241
|
}
|
|
231
242
|
getRemoveStack() {
|
|
232
|
-
return this
|
|
243
|
+
return this.#removeStack;
|
|
233
244
|
}
|
|
234
245
|
getChangeSets() {
|
|
235
|
-
return [...this
|
|
246
|
+
return [...this.#changeSets.values()];
|
|
236
247
|
}
|
|
237
248
|
getCollectionUpdates() {
|
|
238
|
-
return [...this
|
|
249
|
+
return [...this.#collectionUpdates];
|
|
239
250
|
}
|
|
240
251
|
getExtraUpdates() {
|
|
241
|
-
return this
|
|
252
|
+
return this.#extraUpdates;
|
|
242
253
|
}
|
|
243
254
|
shouldAutoFlush(meta) {
|
|
244
255
|
if (insideFlush.getStore()) {
|
|
245
256
|
return false;
|
|
246
257
|
}
|
|
247
|
-
if (this
|
|
258
|
+
if (this.#queuedActions.has(meta.class) || this.#queuedActions.has(meta.root.class)) {
|
|
248
259
|
return true;
|
|
249
260
|
}
|
|
250
|
-
if (meta.discriminatorMap && Object.values(meta.discriminatorMap).some(v => this
|
|
261
|
+
if (meta.discriminatorMap && Object.values(meta.discriminatorMap).some(v => this.#queuedActions.has(v))) {
|
|
251
262
|
return true;
|
|
252
263
|
}
|
|
253
264
|
return false;
|
|
254
265
|
}
|
|
255
266
|
clearActionsQueue() {
|
|
256
|
-
this
|
|
267
|
+
this.#queuedActions.clear();
|
|
257
268
|
}
|
|
258
269
|
computeChangeSet(entity, type) {
|
|
259
270
|
const wrapped = helper(entity);
|
|
260
271
|
if (type === ChangeSetType.DELETE || type === ChangeSetType.DELETE_EARLY) {
|
|
261
|
-
this
|
|
272
|
+
this.#changeSets.set(entity, new ChangeSet(entity, type, {}, wrapped.__meta));
|
|
262
273
|
return;
|
|
263
274
|
}
|
|
264
|
-
const cs = this
|
|
275
|
+
const cs = this.#changeSetComputer.computeChangeSet(entity);
|
|
265
276
|
if (!cs || this.checkUniqueProps(cs)) {
|
|
266
277
|
return;
|
|
267
278
|
}
|
|
@@ -270,32 +281,32 @@ export class UnitOfWork {
|
|
|
270
281
|
cs.type = type;
|
|
271
282
|
}
|
|
272
283
|
this.initIdentifier(entity);
|
|
273
|
-
this
|
|
274
|
-
this
|
|
275
|
-
wrapped.__originalEntityData = this
|
|
284
|
+
this.#changeSets.set(entity, cs);
|
|
285
|
+
this.#persistStack.delete(entity);
|
|
286
|
+
wrapped.__originalEntityData = this.#comparator.prepareEntity(entity);
|
|
276
287
|
}
|
|
277
288
|
recomputeSingleChangeSet(entity) {
|
|
278
|
-
const changeSet = this
|
|
289
|
+
const changeSet = this.#changeSets.get(entity);
|
|
279
290
|
if (!changeSet) {
|
|
280
291
|
return;
|
|
281
292
|
}
|
|
282
|
-
const cs = this
|
|
293
|
+
const cs = this.#changeSetComputer.computeChangeSet(entity);
|
|
283
294
|
if (cs && !this.checkUniqueProps(cs)) {
|
|
284
295
|
Object.assign(changeSet.payload, cs.payload);
|
|
285
|
-
helper(entity).__originalEntityData = this
|
|
296
|
+
helper(entity).__originalEntityData = this.#comparator.prepareEntity(entity);
|
|
286
297
|
}
|
|
287
298
|
}
|
|
288
299
|
persist(entity, visited, options = {}) {
|
|
289
300
|
EntityHelper.ensurePropagation(entity);
|
|
290
|
-
if (options.checkRemoveStack && this
|
|
301
|
+
if (options.checkRemoveStack && this.#removeStack.has(entity)) {
|
|
291
302
|
return;
|
|
292
303
|
}
|
|
293
304
|
const wrapped = helper(entity);
|
|
294
|
-
this
|
|
295
|
-
this
|
|
296
|
-
this
|
|
305
|
+
this.#persistStack.add(entity);
|
|
306
|
+
this.#queuedActions.add(wrapped.__meta.class);
|
|
307
|
+
this.#removeStack.delete(entity);
|
|
297
308
|
if (!wrapped.__managed && wrapped.hasPrimaryKey()) {
|
|
298
|
-
this
|
|
309
|
+
this.#identityMap.store(entity);
|
|
299
310
|
}
|
|
300
311
|
if (options.cascade ?? true) {
|
|
301
312
|
this.cascade(entity, Cascade.PERSIST, visited, options);
|
|
@@ -303,13 +314,13 @@ export class UnitOfWork {
|
|
|
303
314
|
}
|
|
304
315
|
remove(entity, visited, options = {}) {
|
|
305
316
|
// allow removing not managed entities if they are not part of the persist stack
|
|
306
|
-
if (helper(entity).__managed || !this
|
|
307
|
-
this
|
|
308
|
-
this
|
|
317
|
+
if (helper(entity).__managed || !this.#persistStack.has(entity)) {
|
|
318
|
+
this.#removeStack.add(entity);
|
|
319
|
+
this.#queuedActions.add(helper(entity).__meta.class);
|
|
309
320
|
}
|
|
310
321
|
else {
|
|
311
|
-
this
|
|
312
|
-
this
|
|
322
|
+
this.#persistStack.delete(entity);
|
|
323
|
+
this.#identityMap.delete(entity);
|
|
313
324
|
}
|
|
314
325
|
// remove from referencing relations that are nullable
|
|
315
326
|
for (const prop of helper(entity).__meta.bidirectionalRelations) {
|
|
@@ -322,7 +333,7 @@ export class UnitOfWork {
|
|
|
322
333
|
}
|
|
323
334
|
continue;
|
|
324
335
|
}
|
|
325
|
-
const target = relation
|
|
336
|
+
const target = relation?.[inverseProp];
|
|
326
337
|
if (relation && Utils.isCollection(target)) {
|
|
327
338
|
target.removeWithoutPropagation(entity);
|
|
328
339
|
}
|
|
@@ -332,12 +343,12 @@ export class UnitOfWork {
|
|
|
332
343
|
}
|
|
333
344
|
}
|
|
334
345
|
async commit() {
|
|
335
|
-
if (this
|
|
346
|
+
if (this.#working) {
|
|
336
347
|
if (insideFlush.getStore()) {
|
|
337
348
|
throw ValidationError.cannotCommit();
|
|
338
349
|
}
|
|
339
350
|
return new Promise((resolve, reject) => {
|
|
340
|
-
this
|
|
351
|
+
this.#flushQueue.push(() => {
|
|
341
352
|
return insideFlush.run(true, () => {
|
|
342
353
|
return this.doCommit().then(resolve, reject);
|
|
343
354
|
});
|
|
@@ -345,51 +356,51 @@ export class UnitOfWork {
|
|
|
345
356
|
});
|
|
346
357
|
}
|
|
347
358
|
try {
|
|
348
|
-
this
|
|
359
|
+
this.#working = true;
|
|
349
360
|
await insideFlush.run(true, () => this.doCommit());
|
|
350
|
-
while (this
|
|
351
|
-
await this
|
|
361
|
+
while (this.#flushQueue.length) {
|
|
362
|
+
await this.#flushQueue.shift()();
|
|
352
363
|
}
|
|
353
364
|
}
|
|
354
365
|
finally {
|
|
355
366
|
this.postCommitCleanup();
|
|
356
|
-
this
|
|
367
|
+
this.#working = false;
|
|
357
368
|
}
|
|
358
369
|
}
|
|
359
370
|
async doCommit() {
|
|
360
|
-
const oldTx = this
|
|
371
|
+
const oldTx = this.#em.getTransactionContext();
|
|
361
372
|
try {
|
|
362
|
-
await this
|
|
373
|
+
await this.#eventManager.dispatchEvent(EventType.beforeFlush, { em: this.#em, uow: this });
|
|
363
374
|
this.computeChangeSets();
|
|
364
|
-
for (const cs of this
|
|
375
|
+
for (const cs of this.#changeSets.values()) {
|
|
365
376
|
cs.entity.__helper.__processing = true;
|
|
366
377
|
}
|
|
367
|
-
await this
|
|
378
|
+
await this.#eventManager.dispatchEvent(EventType.onFlush, { em: this.#em, uow: this });
|
|
368
379
|
this.filterCollectionUpdates();
|
|
369
380
|
// nothing to do, do not start transaction
|
|
370
|
-
if (this
|
|
371
|
-
await this
|
|
381
|
+
if (this.#changeSets.size === 0 && this.#collectionUpdates.size === 0 && this.#extraUpdates.size === 0) {
|
|
382
|
+
await this.#eventManager.dispatchEvent(EventType.afterFlush, { em: this.#em, uow: this });
|
|
372
383
|
return;
|
|
373
384
|
}
|
|
374
385
|
const groups = this.getChangeSetGroups();
|
|
375
|
-
const platform = this
|
|
376
|
-
const runInTransaction = !this
|
|
386
|
+
const platform = this.#em.getPlatform();
|
|
387
|
+
const runInTransaction = !this.#em.isInTransaction() && platform.supportsTransactions() && this.#em.config.get('implicitTransactions');
|
|
377
388
|
if (runInTransaction) {
|
|
378
|
-
const loggerContext = Utils.merge({ id: this
|
|
379
|
-
await this
|
|
389
|
+
const loggerContext = Utils.merge({ id: this.#em._id }, this.#em.getLoggerContext({ disableContextResolution: true }));
|
|
390
|
+
await this.#em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
|
|
380
391
|
ctx: oldTx,
|
|
381
|
-
eventBroadcaster: new TransactionEventBroadcaster(this
|
|
392
|
+
eventBroadcaster: new TransactionEventBroadcaster(this.#em),
|
|
382
393
|
loggerContext,
|
|
383
394
|
});
|
|
384
395
|
}
|
|
385
396
|
else {
|
|
386
|
-
await this.persistToDatabase(groups, this
|
|
397
|
+
await this.persistToDatabase(groups, this.#em.getTransactionContext());
|
|
387
398
|
}
|
|
388
399
|
this.resetTransaction(oldTx);
|
|
389
|
-
for (const cs of this
|
|
400
|
+
for (const cs of this.#changeSets.values()) {
|
|
390
401
|
cs.entity.__helper.__processing = false;
|
|
391
402
|
}
|
|
392
|
-
await this
|
|
403
|
+
await this.#eventManager.dispatchEvent(EventType.afterFlush, { em: this.#em, uow: this });
|
|
393
404
|
}
|
|
394
405
|
finally {
|
|
395
406
|
this.resetTransaction(oldTx);
|
|
@@ -399,7 +410,7 @@ export class UnitOfWork {
|
|
|
399
410
|
if (!this.getById(entity.constructor, helper(entity).__primaryKeys, helper(entity).__schema)) {
|
|
400
411
|
throw ValidationError.entityNotManaged(entity);
|
|
401
412
|
}
|
|
402
|
-
const meta = this
|
|
413
|
+
const meta = this.#metadata.find(entity.constructor);
|
|
403
414
|
if (options.lockMode === LockMode.OPTIMISTIC) {
|
|
404
415
|
await this.lockOptimistic(entity, meta, options.lockVersion);
|
|
405
416
|
}
|
|
@@ -408,22 +419,25 @@ export class UnitOfWork {
|
|
|
408
419
|
}
|
|
409
420
|
}
|
|
410
421
|
clear() {
|
|
411
|
-
this
|
|
412
|
-
this
|
|
422
|
+
this.#identityMap.clear();
|
|
423
|
+
this.#loadedEntities.clear();
|
|
413
424
|
this.postCommitCleanup();
|
|
414
425
|
}
|
|
415
426
|
unsetIdentity(entity) {
|
|
416
|
-
this
|
|
427
|
+
this.#identityMap.delete(entity);
|
|
417
428
|
const wrapped = helper(entity);
|
|
418
429
|
const serializedPK = wrapped.getSerializedPrimaryKey();
|
|
419
430
|
// remove references of this entity in all managed entities, otherwise flushing could reinsert the entity
|
|
420
431
|
for (const { meta, prop } of wrapped.__meta.referencingProperties) {
|
|
421
|
-
for (const referrer of this
|
|
432
|
+
for (const referrer of this.#identityMap.getStore(meta).values()) {
|
|
422
433
|
const rel = Reference.unwrapReference(referrer[prop.name]);
|
|
423
434
|
if (Utils.isCollection(rel)) {
|
|
424
435
|
rel.removeWithoutPropagation(entity);
|
|
425
436
|
}
|
|
426
|
-
else if (rel &&
|
|
437
|
+
else if (rel &&
|
|
438
|
+
(prop.mapToPk
|
|
439
|
+
? helper(this.#em.getReference(prop.targetMeta.class, rel)).getSerializedPrimaryKey() === serializedPK
|
|
440
|
+
: rel === entity)) {
|
|
427
441
|
if (prop.formula) {
|
|
428
442
|
delete referrer[prop.name];
|
|
429
443
|
}
|
|
@@ -438,43 +452,43 @@ export class UnitOfWork {
|
|
|
438
452
|
wrapped.__managed = false;
|
|
439
453
|
}
|
|
440
454
|
computeChangeSets() {
|
|
441
|
-
this
|
|
455
|
+
this.#changeSets.clear();
|
|
442
456
|
const visited = new Set();
|
|
443
|
-
for (const entity of this
|
|
457
|
+
for (const entity of this.#removeStack) {
|
|
444
458
|
this.cascade(entity, Cascade.REMOVE, visited);
|
|
445
459
|
}
|
|
446
460
|
visited.clear();
|
|
447
|
-
for (const entity of this
|
|
448
|
-
if (!this
|
|
461
|
+
for (const entity of this.#identityMap) {
|
|
462
|
+
if (!this.#removeStack.has(entity) && !this.#persistStack.has(entity) && !this.#orphanRemoveStack.has(entity)) {
|
|
449
463
|
this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
|
|
450
464
|
}
|
|
451
465
|
}
|
|
452
|
-
for (const entity of this
|
|
466
|
+
for (const entity of this.#persistStack) {
|
|
453
467
|
this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
|
|
454
468
|
}
|
|
455
469
|
visited.clear();
|
|
456
|
-
for (const entity of this
|
|
470
|
+
for (const entity of this.#persistStack) {
|
|
457
471
|
this.findNewEntities(entity, visited);
|
|
458
472
|
}
|
|
459
|
-
for (const entity of this
|
|
473
|
+
for (const entity of this.#orphanRemoveStack) {
|
|
460
474
|
if (!helper(entity).__processing) {
|
|
461
|
-
this
|
|
475
|
+
this.#removeStack.add(entity);
|
|
462
476
|
}
|
|
463
477
|
}
|
|
464
478
|
// Check insert stack if there are any entities matching something from delete stack. This can happen when recreating entities.
|
|
465
479
|
const inserts = {};
|
|
466
|
-
for (const cs of this
|
|
480
|
+
for (const cs of this.#changeSets.values()) {
|
|
467
481
|
if (cs.type === ChangeSetType.CREATE) {
|
|
468
482
|
inserts[cs.meta.uniqueName] ??= [];
|
|
469
483
|
inserts[cs.meta.uniqueName].push(cs);
|
|
470
484
|
}
|
|
471
485
|
}
|
|
472
|
-
for (const cs of this
|
|
486
|
+
for (const cs of this.#changeSets.values()) {
|
|
473
487
|
if (cs.type === ChangeSetType.UPDATE) {
|
|
474
488
|
this.findEarlyUpdates(cs, inserts[cs.meta.uniqueName]);
|
|
475
489
|
}
|
|
476
490
|
}
|
|
477
|
-
for (const entity of this
|
|
491
|
+
for (const entity of this.#removeStack) {
|
|
478
492
|
const wrapped = helper(entity);
|
|
479
493
|
/* v8 ignore next */
|
|
480
494
|
if (wrapped.__processing) {
|
|
@@ -499,7 +513,7 @@ export class UnitOfWork {
|
|
|
499
513
|
if (!props.some(prop => prop.name in changeSet.payload)) {
|
|
500
514
|
return;
|
|
501
515
|
}
|
|
502
|
-
for (const cs of this
|
|
516
|
+
for (const cs of this.#changeSets.values()) {
|
|
503
517
|
for (const prop of props) {
|
|
504
518
|
if (prop.name in cs.payload && cs.rootMeta === changeSet.rootMeta && cs.type === changeSet.type) {
|
|
505
519
|
conflicts = true;
|
|
@@ -512,7 +526,13 @@ export class UnitOfWork {
|
|
|
512
526
|
if (!conflicts) {
|
|
513
527
|
return;
|
|
514
528
|
}
|
|
515
|
-
this
|
|
529
|
+
this.#extraUpdates.add([
|
|
530
|
+
changeSet.entity,
|
|
531
|
+
props.map(p => p.name),
|
|
532
|
+
props.map(p => changeSet.entity[p.name]),
|
|
533
|
+
changeSet,
|
|
534
|
+
type,
|
|
535
|
+
]);
|
|
516
536
|
for (const p of props) {
|
|
517
537
|
delete changeSet.entity[p.name];
|
|
518
538
|
delete changeSet.payload[p.name];
|
|
@@ -521,21 +541,21 @@ export class UnitOfWork {
|
|
|
521
541
|
scheduleOrphanRemoval(entity, visited) {
|
|
522
542
|
if (entity) {
|
|
523
543
|
const wrapped = helper(entity);
|
|
524
|
-
wrapped.__em = this
|
|
525
|
-
this
|
|
526
|
-
this
|
|
544
|
+
wrapped.__em = this.#em;
|
|
545
|
+
this.#orphanRemoveStack.add(entity);
|
|
546
|
+
this.#queuedActions.add(wrapped.__meta.class);
|
|
527
547
|
this.cascade(entity, Cascade.SCHEDULE_ORPHAN_REMOVAL, visited);
|
|
528
548
|
}
|
|
529
549
|
}
|
|
530
550
|
cancelOrphanRemoval(entity, visited) {
|
|
531
|
-
this
|
|
551
|
+
this.#orphanRemoveStack.delete(entity);
|
|
532
552
|
this.cascade(entity, Cascade.CANCEL_ORPHAN_REMOVAL, visited);
|
|
533
553
|
}
|
|
534
554
|
getOrphanRemoveStack() {
|
|
535
|
-
return this
|
|
555
|
+
return this.#orphanRemoveStack;
|
|
536
556
|
}
|
|
537
557
|
getChangeSetPersister() {
|
|
538
|
-
return this
|
|
558
|
+
return this.#changeSetPersister;
|
|
539
559
|
}
|
|
540
560
|
findNewEntities(entity, visited, idx = 0, processed = new Set()) {
|
|
541
561
|
if (visited.has(entity)) {
|
|
@@ -544,11 +564,11 @@ export class UnitOfWork {
|
|
|
544
564
|
visited.add(entity);
|
|
545
565
|
processed.add(entity);
|
|
546
566
|
const wrapped = helper(entity);
|
|
547
|
-
if (wrapped.__processing || this
|
|
567
|
+
if (wrapped.__processing || this.#removeStack.has(entity) || this.#orphanRemoveStack.has(entity)) {
|
|
548
568
|
return;
|
|
549
569
|
}
|
|
550
570
|
// Set entityManager default schema
|
|
551
|
-
wrapped.__schema ??= this
|
|
571
|
+
wrapped.__schema ??= this.#em.schema;
|
|
552
572
|
this.initIdentifier(entity);
|
|
553
573
|
for (const prop of wrapped.__meta.relations) {
|
|
554
574
|
const targets = Utils.unwrapProperty(entity, wrapped.__meta, prop);
|
|
@@ -557,14 +577,14 @@ export class UnitOfWork {
|
|
|
557
577
|
this.processReference(entity, prop, kind, visited, processed, idx);
|
|
558
578
|
}
|
|
559
579
|
}
|
|
560
|
-
const changeSet = this
|
|
580
|
+
const changeSet = this.#changeSetComputer.computeChangeSet(entity);
|
|
561
581
|
if (changeSet && !this.checkUniqueProps(changeSet)) {
|
|
562
582
|
// For TPT child entities, create changesets for each table in hierarchy
|
|
563
583
|
if (wrapped.__meta.inheritanceType === 'tpt' && wrapped.__meta.tptParent) {
|
|
564
584
|
this.createTPTChangeSets(entity, changeSet);
|
|
565
585
|
}
|
|
566
586
|
else {
|
|
567
|
-
this
|
|
587
|
+
this.#changeSets.set(entity, changeSet);
|
|
568
588
|
}
|
|
569
589
|
}
|
|
570
590
|
}
|
|
@@ -620,7 +640,7 @@ export class UnitOfWork {
|
|
|
620
640
|
if (parentChangeSets.length > 0) {
|
|
621
641
|
leafCs.tptChangeSets = parentChangeSets;
|
|
622
642
|
}
|
|
623
|
-
this
|
|
643
|
+
this.#changeSets.set(entity, leafCs);
|
|
624
644
|
}
|
|
625
645
|
}
|
|
626
646
|
/**
|
|
@@ -633,7 +653,7 @@ export class UnitOfWork {
|
|
|
633
653
|
// when changing a unique nullable property (or a 1:1 relation), we can't do it in a single
|
|
634
654
|
// query as it would cause unique constraint violations
|
|
635
655
|
const uniqueProps = changeSet.meta.uniqueProps.filter(prop => {
|
|
636
|
-
return
|
|
656
|
+
return prop.nullable || changeSet.type !== ChangeSetType.CREATE;
|
|
637
657
|
});
|
|
638
658
|
this.scheduleExtraUpdate(changeSet, uniqueProps);
|
|
639
659
|
return changeSet.type === ChangeSetType.UPDATE && !Utils.hasObjectKeys(changeSet.payload);
|
|
@@ -643,25 +663,33 @@ export class UnitOfWork {
|
|
|
643
663
|
if (!wrapped.__meta.hasUniqueProps) {
|
|
644
664
|
return [];
|
|
645
665
|
}
|
|
646
|
-
const simpleUniqueHashes = wrapped.__meta.uniqueProps
|
|
666
|
+
const simpleUniqueHashes = wrapped.__meta.uniqueProps
|
|
667
|
+
.map(prop => {
|
|
647
668
|
if (entity[prop.name] != null) {
|
|
648
|
-
return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
|
|
669
|
+
return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
|
|
670
|
+
? entity[prop.name]
|
|
671
|
+
: helper(entity[prop.name]).getSerializedPrimaryKey();
|
|
649
672
|
}
|
|
650
673
|
if (wrapped.__originalEntityData?.[prop.name] != null) {
|
|
651
674
|
return Utils.getPrimaryKeyHash(Utils.asArray(wrapped.__originalEntityData[prop.name]));
|
|
652
675
|
}
|
|
653
676
|
return undefined;
|
|
654
|
-
})
|
|
655
|
-
|
|
677
|
+
})
|
|
678
|
+
.filter(i => i);
|
|
679
|
+
const compoundUniqueHashes = wrapped.__meta.uniques
|
|
680
|
+
.map(unique => {
|
|
656
681
|
const props = Utils.asArray(unique.properties);
|
|
657
682
|
if (props.every(prop => entity[prop] != null)) {
|
|
658
683
|
return Utils.getPrimaryKeyHash(props.map(p => {
|
|
659
684
|
const prop = wrapped.__meta.properties[p];
|
|
660
|
-
return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
|
|
685
|
+
return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
|
|
686
|
+
? entity[prop.name]
|
|
687
|
+
: helper(entity[prop.name]).getSerializedPrimaryKey();
|
|
661
688
|
}));
|
|
662
689
|
}
|
|
663
690
|
return undefined;
|
|
664
|
-
})
|
|
691
|
+
})
|
|
692
|
+
.filter(i => i);
|
|
665
693
|
return simpleUniqueHashes.concat(compoundUniqueHashes);
|
|
666
694
|
}
|
|
667
695
|
initIdentifier(entity) {
|
|
@@ -693,7 +721,8 @@ export class UnitOfWork {
|
|
|
693
721
|
return this.processToOneReference(kind, visited, processed, idx);
|
|
694
722
|
}
|
|
695
723
|
if (Utils.isCollection(kind)) {
|
|
696
|
-
kind
|
|
724
|
+
kind
|
|
725
|
+
.getItems(false)
|
|
697
726
|
.filter(item => !item.__helper.__originalEntityData)
|
|
698
727
|
.forEach(item => {
|
|
699
728
|
// propagate schema from parent
|
|
@@ -711,29 +740,30 @@ export class UnitOfWork {
|
|
|
711
740
|
}
|
|
712
741
|
processToManyReference(collection, visited, processed, parent, prop) {
|
|
713
742
|
if (this.isCollectionSelfReferenced(collection, processed)) {
|
|
714
|
-
this
|
|
743
|
+
this.#extraUpdates.add([parent, prop.name, collection, undefined, ChangeSetType.UPDATE]);
|
|
715
744
|
const coll = new Collection(parent);
|
|
716
745
|
coll.property = prop;
|
|
717
746
|
parent[prop.name] = coll;
|
|
718
747
|
return;
|
|
719
748
|
}
|
|
720
|
-
collection
|
|
749
|
+
collection
|
|
750
|
+
.getItems(false)
|
|
721
751
|
.filter(item => !item.__helper.__originalEntityData)
|
|
722
752
|
.forEach(item => this.findNewEntities(item, visited, 0, processed));
|
|
723
753
|
}
|
|
724
754
|
async runHooks(type, changeSet, sync = false) {
|
|
725
755
|
const meta = changeSet.meta;
|
|
726
|
-
if (!this
|
|
756
|
+
if (!this.#eventManager.hasListeners(type, meta)) {
|
|
727
757
|
return;
|
|
728
758
|
}
|
|
729
759
|
if (!sync) {
|
|
730
|
-
await this
|
|
760
|
+
await this.#eventManager.dispatchEvent(type, { entity: changeSet.entity, meta, em: this.#em, changeSet });
|
|
731
761
|
return;
|
|
732
762
|
}
|
|
733
|
-
const copy = this
|
|
734
|
-
await this
|
|
735
|
-
const current = this
|
|
736
|
-
const diff = this
|
|
763
|
+
const copy = this.#comparator.prepareEntity(changeSet.entity);
|
|
764
|
+
await this.#eventManager.dispatchEvent(type, { entity: changeSet.entity, meta, em: this.#em, changeSet });
|
|
765
|
+
const current = this.#comparator.prepareEntity(changeSet.entity);
|
|
766
|
+
const diff = this.#comparator.diffEntities(changeSet.meta.class, copy, current);
|
|
737
767
|
Object.assign(changeSet.payload, diff);
|
|
738
768
|
const wrapped = helper(changeSet.entity);
|
|
739
769
|
if (wrapped.__identifier) {
|
|
@@ -748,19 +778,19 @@ export class UnitOfWork {
|
|
|
748
778
|
}
|
|
749
779
|
}
|
|
750
780
|
postCommitCleanup() {
|
|
751
|
-
for (const cs of this
|
|
781
|
+
for (const cs of this.#changeSets.values()) {
|
|
752
782
|
const wrapped = helper(cs.entity);
|
|
753
783
|
wrapped.__processing = false;
|
|
754
784
|
delete wrapped.__pk;
|
|
755
785
|
}
|
|
756
|
-
this
|
|
757
|
-
this
|
|
758
|
-
this
|
|
759
|
-
this
|
|
760
|
-
this
|
|
761
|
-
this
|
|
762
|
-
this
|
|
763
|
-
this
|
|
786
|
+
this.#persistStack.clear();
|
|
787
|
+
this.#removeStack.clear();
|
|
788
|
+
this.#orphanRemoveStack.clear();
|
|
789
|
+
this.#changeSets.clear();
|
|
790
|
+
this.#collectionUpdates.clear();
|
|
791
|
+
this.#extraUpdates.clear();
|
|
792
|
+
this.#queuedActions.clear();
|
|
793
|
+
this.#working = false;
|
|
764
794
|
}
|
|
765
795
|
cascade(entity, type, visited = new Set(), options = {}) {
|
|
766
796
|
if (visited.has(entity)) {
|
|
@@ -809,7 +839,8 @@ export class UnitOfWork {
|
|
|
809
839
|
return filtered.some(items => processed.has(items));
|
|
810
840
|
}
|
|
811
841
|
shouldCascade(prop, type) {
|
|
812
|
-
if ([Cascade.REMOVE, Cascade.SCHEDULE_ORPHAN_REMOVAL, Cascade.CANCEL_ORPHAN_REMOVAL, Cascade.ALL].includes(type) &&
|
|
842
|
+
if ([Cascade.REMOVE, Cascade.SCHEDULE_ORPHAN_REMOVAL, Cascade.CANCEL_ORPHAN_REMOVAL, Cascade.ALL].includes(type) &&
|
|
843
|
+
prop.orphanRemoval) {
|
|
813
844
|
return true;
|
|
814
845
|
}
|
|
815
846
|
// ignore user settings for merge, it is kept only for back compatibility, this should have never been configurable
|
|
@@ -819,10 +850,10 @@ export class UnitOfWork {
|
|
|
819
850
|
return prop.cascade && (prop.cascade.includes(type) || prop.cascade.includes(Cascade.ALL));
|
|
820
851
|
}
|
|
821
852
|
async lockPessimistic(entity, options) {
|
|
822
|
-
if (!this
|
|
853
|
+
if (!this.#em.isInTransaction()) {
|
|
823
854
|
throw ValidationError.transactionRequired();
|
|
824
855
|
}
|
|
825
|
-
await this
|
|
856
|
+
await this.#em.getDriver().lockPessimistic(entity, { ctx: this.#em.getTransactionContext(), ...options });
|
|
826
857
|
}
|
|
827
858
|
async lockOptimistic(entity, meta, version) {
|
|
828
859
|
if (!meta.versionProperty) {
|
|
@@ -845,11 +876,15 @@ export class UnitOfWork {
|
|
|
845
876
|
const target = Reference.unwrapReference(reference);
|
|
846
877
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && target && !prop.mapToPk) {
|
|
847
878
|
if (!Utils.isEntity(target)) {
|
|
848
|
-
entity[prop.name] = this
|
|
879
|
+
entity[prop.name] = this.#em.getReference(prop.targetMeta.class, target, {
|
|
880
|
+
wrapped: !!prop.ref,
|
|
881
|
+
});
|
|
849
882
|
}
|
|
850
883
|
else if (!helper(target).__initialized && !helper(target).__em) {
|
|
851
884
|
const pk = helper(target).getPrimaryKey();
|
|
852
|
-
entity[prop.name] = this
|
|
885
|
+
entity[prop.name] = this.#em.getReference(prop.targetMeta.class, pk, {
|
|
886
|
+
wrapped: !!prop.ref,
|
|
887
|
+
});
|
|
853
888
|
}
|
|
854
889
|
}
|
|
855
890
|
// perf: set the `Collection._property` to skip the getter, as it can be slow when there are a lot of relations
|
|
@@ -866,7 +901,7 @@ export class UnitOfWork {
|
|
|
866
901
|
}
|
|
867
902
|
async persistToDatabase(groups, ctx) {
|
|
868
903
|
if (ctx) {
|
|
869
|
-
this
|
|
904
|
+
this.#em.setTransactionContext(ctx);
|
|
870
905
|
}
|
|
871
906
|
const commitOrder = this.getCommitOrder();
|
|
872
907
|
const commitOrderReversed = [...commitOrder].reverse();
|
|
@@ -898,7 +933,7 @@ export class UnitOfWork {
|
|
|
898
933
|
}
|
|
899
934
|
// take snapshots of all persisted collections
|
|
900
935
|
const visited = new Set();
|
|
901
|
-
for (const changeSet of this
|
|
936
|
+
for (const changeSet of this.#changeSets.values()) {
|
|
902
937
|
this.takeCollectionSnapshots(changeSet.entity, visited);
|
|
903
938
|
}
|
|
904
939
|
}
|
|
@@ -907,15 +942,15 @@ export class UnitOfWork {
|
|
|
907
942
|
return;
|
|
908
943
|
}
|
|
909
944
|
const props = changeSets[0].meta.root.relations.filter(prop => {
|
|
910
|
-
return (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner)
|
|
911
|
-
|
|
912
|
-
|
|
945
|
+
return ((prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner) ||
|
|
946
|
+
prop.kind === ReferenceKind.MANY_TO_ONE ||
|
|
947
|
+
(prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && !this.#platform.usesPivotTable()));
|
|
913
948
|
});
|
|
914
949
|
for (const changeSet of changeSets) {
|
|
915
950
|
this.findExtraUpdates(changeSet, props);
|
|
916
951
|
await this.runHooks(EventType.beforeCreate, changeSet, true);
|
|
917
952
|
}
|
|
918
|
-
await this
|
|
953
|
+
await this.#changeSetPersister.executeInserts(changeSets, { ctx });
|
|
919
954
|
for (const changeSet of changeSets) {
|
|
920
955
|
this.register(changeSet.entity, changeSet.payload, { refresh: true });
|
|
921
956
|
await this.runHooks(EventType.afterCreate, changeSet);
|
|
@@ -929,7 +964,7 @@ export class UnitOfWork {
|
|
|
929
964
|
}
|
|
930
965
|
if (Utils.isCollection(ref)) {
|
|
931
966
|
ref.getItems(false).some(item => {
|
|
932
|
-
const cs = this
|
|
967
|
+
const cs = this.#changeSets.get(Reference.unwrapReference(item));
|
|
933
968
|
const isScheduledForInsert = cs?.type === ChangeSetType.CREATE && !cs.persisted;
|
|
934
969
|
if (isScheduledForInsert) {
|
|
935
970
|
this.scheduleExtraUpdate(changeSet, [prop]);
|
|
@@ -946,7 +981,7 @@ export class UnitOfWork {
|
|
|
946
981
|
}
|
|
947
982
|
// For TPT entities, check if the ROOT table's changeset has been persisted
|
|
948
983
|
// (since the FK is to the root table, not the concrete entity's table)
|
|
949
|
-
let cs = this
|
|
984
|
+
let cs = this.#changeSets.get(refEntity);
|
|
950
985
|
if (cs?.tptChangeSets?.length) {
|
|
951
986
|
// Root table changeset is the last one (ordered immediate parent → root)
|
|
952
987
|
cs = cs.tptChangeSets[cs.tptChangeSets.length - 1];
|
|
@@ -978,13 +1013,14 @@ export class UnitOfWork {
|
|
|
978
1013
|
for (const changeSet of changeSets) {
|
|
979
1014
|
await this.runHooks(EventType.beforeUpdate, changeSet, true);
|
|
980
1015
|
}
|
|
981
|
-
await this
|
|
1016
|
+
await this.#changeSetPersister.executeUpdates(changeSets, batched, { ctx });
|
|
982
1017
|
for (const changeSet of changeSets) {
|
|
983
1018
|
const wrapped = helper(changeSet.entity);
|
|
984
|
-
wrapped.__originalEntityData = this
|
|
1019
|
+
wrapped.__originalEntityData = this.#comparator.prepareEntity(changeSet.entity);
|
|
985
1020
|
if (!wrapped.__initialized) {
|
|
986
1021
|
for (const prop of changeSet.meta.relations) {
|
|
987
|
-
if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
|
|
1022
|
+
if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
|
|
1023
|
+
changeSet.entity[prop.name] == null) {
|
|
988
1024
|
changeSet.entity[prop.name] = Collection.create(changeSet.entity, prop.name, undefined, wrapped.isInitialized());
|
|
989
1025
|
}
|
|
990
1026
|
}
|
|
@@ -1000,7 +1036,7 @@ export class UnitOfWork {
|
|
|
1000
1036
|
for (const changeSet of changeSets) {
|
|
1001
1037
|
await this.runHooks(EventType.beforeDelete, changeSet, true);
|
|
1002
1038
|
}
|
|
1003
|
-
await this
|
|
1039
|
+
await this.#changeSetPersister.executeDeletes(changeSets, { ctx });
|
|
1004
1040
|
for (const changeSet of changeSets) {
|
|
1005
1041
|
this.unsetIdentity(changeSet.entity);
|
|
1006
1042
|
await this.runHooks(EventType.afterDelete, changeSet);
|
|
@@ -1008,17 +1044,17 @@ export class UnitOfWork {
|
|
|
1008
1044
|
}
|
|
1009
1045
|
async commitExtraUpdates(type, ctx) {
|
|
1010
1046
|
const extraUpdates = [];
|
|
1011
|
-
for (const extraUpdate of this
|
|
1047
|
+
for (const extraUpdate of this.#extraUpdates) {
|
|
1012
1048
|
if (extraUpdate[4] !== type) {
|
|
1013
1049
|
continue;
|
|
1014
1050
|
}
|
|
1015
1051
|
if (Array.isArray(extraUpdate[1])) {
|
|
1016
|
-
extraUpdate[1].forEach((p, i) => extraUpdate[0][p] = extraUpdate[2][i]);
|
|
1052
|
+
extraUpdate[1].forEach((p, i) => (extraUpdate[0][p] = extraUpdate[2][i]));
|
|
1017
1053
|
}
|
|
1018
1054
|
else {
|
|
1019
1055
|
extraUpdate[0][extraUpdate[1]] = extraUpdate[2];
|
|
1020
1056
|
}
|
|
1021
|
-
const changeSet = this
|
|
1057
|
+
const changeSet = this.#changeSetComputer.computeChangeSet(extraUpdate[0]);
|
|
1022
1058
|
if (changeSet) {
|
|
1023
1059
|
extraUpdates.push([changeSet, extraUpdate[3]]);
|
|
1024
1060
|
}
|
|
@@ -1033,21 +1069,21 @@ export class UnitOfWork {
|
|
|
1033
1069
|
}
|
|
1034
1070
|
async commitCollectionUpdates(ctx) {
|
|
1035
1071
|
this.filterCollectionUpdates();
|
|
1036
|
-
const loggerContext = Utils.merge({ id: this
|
|
1037
|
-
await this
|
|
1072
|
+
const loggerContext = Utils.merge({ id: this.#em._id }, this.#em.getLoggerContext({ disableContextResolution: true }));
|
|
1073
|
+
await this.#em.getDriver().syncCollections(this.#collectionUpdates, {
|
|
1038
1074
|
ctx,
|
|
1039
|
-
schema: this
|
|
1075
|
+
schema: this.#em.schema,
|
|
1040
1076
|
loggerContext,
|
|
1041
1077
|
});
|
|
1042
|
-
for (const coll of this
|
|
1078
|
+
for (const coll of this.#collectionUpdates) {
|
|
1043
1079
|
coll.takeSnapshot();
|
|
1044
1080
|
}
|
|
1045
1081
|
}
|
|
1046
1082
|
filterCollectionUpdates() {
|
|
1047
|
-
for (const coll of this
|
|
1083
|
+
for (const coll of this.#collectionUpdates) {
|
|
1048
1084
|
let skip = true;
|
|
1049
1085
|
if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
|
|
1050
|
-
if (this
|
|
1086
|
+
if (this.#platform.usesPivotTable()) {
|
|
1051
1087
|
skip = false;
|
|
1052
1088
|
}
|
|
1053
1089
|
}
|
|
@@ -1058,7 +1094,7 @@ export class UnitOfWork {
|
|
|
1058
1094
|
skip = false;
|
|
1059
1095
|
}
|
|
1060
1096
|
if (skip) {
|
|
1061
|
-
this
|
|
1097
|
+
this.#collectionUpdates.delete(coll);
|
|
1062
1098
|
}
|
|
1063
1099
|
}
|
|
1064
1100
|
}
|
|
@@ -1075,7 +1111,8 @@ export class UnitOfWork {
|
|
|
1075
1111
|
};
|
|
1076
1112
|
const addToGroup = (cs) => {
|
|
1077
1113
|
// 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) &&
|
|
1114
|
+
if ((cs.type === ChangeSetType.UPDATE || cs.type === ChangeSetType.UPDATE_EARLY) &&
|
|
1115
|
+
!Utils.hasObjectKeys(cs.payload)) {
|
|
1079
1116
|
return;
|
|
1080
1117
|
}
|
|
1081
1118
|
const group = groups[cs.type];
|
|
@@ -1086,7 +1123,7 @@ export class UnitOfWork {
|
|
|
1086
1123
|
group.set(groupKey, classGroup);
|
|
1087
1124
|
}
|
|
1088
1125
|
};
|
|
1089
|
-
for (const cs of this
|
|
1126
|
+
for (const cs of this.#changeSets.values()) {
|
|
1090
1127
|
addToGroup(cs);
|
|
1091
1128
|
for (const parentCs of cs.tptChangeSets ?? []) {
|
|
1092
1129
|
addToGroup(parentCs);
|
|
@@ -1097,7 +1134,7 @@ export class UnitOfWork {
|
|
|
1097
1134
|
getCommitOrder() {
|
|
1098
1135
|
const calc = new CommitOrderCalculator();
|
|
1099
1136
|
const set = new Set();
|
|
1100
|
-
this
|
|
1137
|
+
this.#changeSets.forEach(cs => {
|
|
1101
1138
|
if (cs.meta.inheritanceType === 'tpt') {
|
|
1102
1139
|
set.add(cs.meta);
|
|
1103
1140
|
for (const parentCs of cs.tptChangeSets ?? []) {
|
|
@@ -1125,14 +1162,14 @@ export class UnitOfWork {
|
|
|
1125
1162
|
calc.addDependency(meta.tptParent._id, meta._id, 1);
|
|
1126
1163
|
}
|
|
1127
1164
|
}
|
|
1128
|
-
return calc.sort().map(id => this
|
|
1165
|
+
return calc.sort().map(id => this.#metadata.getById(id));
|
|
1129
1166
|
}
|
|
1130
1167
|
resetTransaction(oldTx) {
|
|
1131
1168
|
if (oldTx) {
|
|
1132
|
-
this
|
|
1169
|
+
this.#em.setTransactionContext(oldTx);
|
|
1133
1170
|
}
|
|
1134
1171
|
else {
|
|
1135
|
-
this
|
|
1172
|
+
this.#em.resetTransactionContext();
|
|
1136
1173
|
}
|
|
1137
1174
|
}
|
|
1138
1175
|
/**
|