@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
package/EntityManager.js
CHANGED
|
@@ -27,26 +27,27 @@ export class EntityManager {
|
|
|
27
27
|
config;
|
|
28
28
|
driver;
|
|
29
29
|
metadata;
|
|
30
|
-
useContext;
|
|
31
30
|
eventManager;
|
|
32
|
-
static counter = 1;
|
|
33
|
-
|
|
31
|
+
static #counter = 1;
|
|
32
|
+
/** @internal */
|
|
33
|
+
_id = EntityManager.#counter++;
|
|
34
34
|
global = false;
|
|
35
35
|
name;
|
|
36
|
-
loaders = {};
|
|
37
|
-
repositoryMap = new Map();
|
|
38
|
-
entityLoader;
|
|
39
|
-
comparator;
|
|
40
|
-
entityFactory;
|
|
41
|
-
unitOfWork;
|
|
42
|
-
resultCache;
|
|
43
|
-
filters = {};
|
|
44
|
-
filterParams = {};
|
|
36
|
+
#loaders = {};
|
|
37
|
+
#repositoryMap = new Map();
|
|
38
|
+
#entityLoader;
|
|
39
|
+
#comparator;
|
|
40
|
+
#entityFactory;
|
|
41
|
+
#unitOfWork;
|
|
42
|
+
#resultCache;
|
|
43
|
+
#filters = {};
|
|
44
|
+
#filterParams = {};
|
|
45
45
|
loggerContext;
|
|
46
|
-
transactionContext;
|
|
47
|
-
disableTransactions;
|
|
48
|
-
flushMode;
|
|
49
|
-
|
|
46
|
+
#transactionContext;
|
|
47
|
+
#disableTransactions;
|
|
48
|
+
#flushMode;
|
|
49
|
+
#schema;
|
|
50
|
+
#useContext;
|
|
50
51
|
/**
|
|
51
52
|
* @internal
|
|
52
53
|
*/
|
|
@@ -54,15 +55,15 @@ export class EntityManager {
|
|
|
54
55
|
this.config = config;
|
|
55
56
|
this.driver = driver;
|
|
56
57
|
this.metadata = metadata;
|
|
57
|
-
this.useContext = useContext;
|
|
58
58
|
this.eventManager = eventManager;
|
|
59
|
-
this
|
|
59
|
+
this.#useContext = useContext;
|
|
60
|
+
this.#entityLoader = new EntityLoader(this);
|
|
60
61
|
this.name = this.config.get('contextName');
|
|
61
|
-
this
|
|
62
|
-
this
|
|
63
|
-
this
|
|
64
|
-
this
|
|
65
|
-
this
|
|
62
|
+
this.#comparator = this.config.getComparator(this.metadata);
|
|
63
|
+
this.#resultCache = this.config.getResultCacheAdapter();
|
|
64
|
+
this.#disableTransactions = this.config.get('disableTransactions');
|
|
65
|
+
this.#entityFactory = new EntityFactory(this);
|
|
66
|
+
this.#unitOfWork = new UnitOfWork(this);
|
|
66
67
|
}
|
|
67
68
|
/**
|
|
68
69
|
* Gets the Driver instance used by this EntityManager.
|
|
@@ -88,11 +89,11 @@ export class EntityManager {
|
|
|
88
89
|
*/
|
|
89
90
|
getRepository(entityName) {
|
|
90
91
|
const meta = this.metadata.get(entityName);
|
|
91
|
-
if (!this
|
|
92
|
+
if (!this.#repositoryMap.has(meta)) {
|
|
92
93
|
const RepositoryClass = this.config.getRepositoryClass(meta.repository);
|
|
93
|
-
this
|
|
94
|
+
this.#repositoryMap.set(meta, new RepositoryClass(this, entityName));
|
|
94
95
|
}
|
|
95
|
-
return this
|
|
96
|
+
return this.#repositoryMap.get(meta);
|
|
96
97
|
}
|
|
97
98
|
/**
|
|
98
99
|
* Shortcut for `em.getRepository()`.
|
|
@@ -123,12 +124,12 @@ export class EntityManager {
|
|
|
123
124
|
else {
|
|
124
125
|
options.orderBy ??= {};
|
|
125
126
|
}
|
|
126
|
-
options.populate = await em.preparePopulate(entityName, options);
|
|
127
|
+
options.populate = (await em.preparePopulate(entityName, options));
|
|
127
128
|
const populate = options.populate;
|
|
128
129
|
const cacheKey = em.cacheKey(entityName, options, 'em.find', where);
|
|
129
130
|
const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
|
|
130
131
|
if (cached?.data) {
|
|
131
|
-
await em
|
|
132
|
+
await em.#entityLoader.populate(entityName, cached.data, populate, {
|
|
132
133
|
...options,
|
|
133
134
|
...em.getPopulateWhere(where, options),
|
|
134
135
|
ignoreLazyScalarProperties: true,
|
|
@@ -141,14 +142,15 @@ export class EntityManager {
|
|
|
141
142
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
142
143
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
143
144
|
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
144
|
-
|
|
145
|
+
await em.processUnionWhere(entityName, options, 'read');
|
|
146
|
+
const results = await em.driver.find(entityName, where, { ctx: em.#transactionContext, em, ...options });
|
|
145
147
|
if (results.length === 0) {
|
|
146
148
|
await em.storeCache(options.cache, cached, []);
|
|
147
149
|
return [];
|
|
148
150
|
}
|
|
149
151
|
const ret = [];
|
|
150
152
|
for (const data of results) {
|
|
151
|
-
const entity = em
|
|
153
|
+
const entity = em.#entityFactory.create(entityName, data, {
|
|
152
154
|
merge: true,
|
|
153
155
|
refresh: options.refresh,
|
|
154
156
|
schema: options.schema,
|
|
@@ -157,13 +159,13 @@ export class EntityManager {
|
|
|
157
159
|
ret.push(entity);
|
|
158
160
|
}
|
|
159
161
|
const unique = Utils.unique(ret);
|
|
160
|
-
await em
|
|
162
|
+
await em.#entityLoader.populate(entityName, unique, populate, {
|
|
161
163
|
...options,
|
|
162
164
|
...em.getPopulateWhere(where, options),
|
|
163
165
|
ignoreLazyScalarProperties: true,
|
|
164
166
|
lookup: false,
|
|
165
167
|
});
|
|
166
|
-
await em
|
|
168
|
+
await em.#unitOfWork.dispatchOnLoadEvent();
|
|
167
169
|
if (meta.virtual) {
|
|
168
170
|
await em.storeCache(options.cache, cached, () => ret);
|
|
169
171
|
}
|
|
@@ -194,10 +196,10 @@ export class EntityManager {
|
|
|
194
196
|
em.prepareOptions(options);
|
|
195
197
|
options.strategy = 'joined';
|
|
196
198
|
await em.tryFlush(entityName, options);
|
|
197
|
-
const where = await em.processWhere(entityName, options.where ?? {}, options, 'read');
|
|
199
|
+
const where = (await em.processWhere(entityName, options.where ?? {}, options, 'read'));
|
|
198
200
|
validateParams(where);
|
|
199
201
|
options.orderBy = options.orderBy || {};
|
|
200
|
-
options.populate = await em.preparePopulate(entityName, options);
|
|
202
|
+
options.populate = (await em.preparePopulate(entityName, options));
|
|
201
203
|
const meta = this.metadata.get(entityName);
|
|
202
204
|
options = { ...options };
|
|
203
205
|
// save the original hint value so we know it was infer/all
|
|
@@ -205,13 +207,13 @@ export class EntityManager {
|
|
|
205
207
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
206
208
|
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
207
209
|
const stream = em.driver.stream(entityName, where, {
|
|
208
|
-
ctx: em
|
|
210
|
+
ctx: em.#transactionContext,
|
|
209
211
|
mapResults: false,
|
|
210
212
|
...options,
|
|
211
213
|
});
|
|
212
214
|
for await (const data of stream) {
|
|
213
215
|
const fork = em.fork();
|
|
214
|
-
const entity = fork
|
|
216
|
+
const entity = fork.#entityFactory.create(entityName, data, {
|
|
215
217
|
refresh: options.refresh,
|
|
216
218
|
schema: options.schema,
|
|
217
219
|
convertCustomTypes: true,
|
|
@@ -221,7 +223,7 @@ export class EntityManager {
|
|
|
221
223
|
fields: options.fields,
|
|
222
224
|
exclude: options.exclude,
|
|
223
225
|
});
|
|
224
|
-
await fork
|
|
226
|
+
await fork.#unitOfWork.dispatchOnLoadEvent();
|
|
225
227
|
fork.clear();
|
|
226
228
|
yield entity;
|
|
227
229
|
}
|
|
@@ -253,20 +255,20 @@ export class EntityManager {
|
|
|
253
255
|
options.entity = Utils.asArray(options.entity).map(n => Utils.className(n));
|
|
254
256
|
}
|
|
255
257
|
options.default ??= true;
|
|
256
|
-
this.getContext(false)
|
|
258
|
+
this.getContext(false).#filters[options.name] = options;
|
|
257
259
|
}
|
|
258
260
|
/**
|
|
259
261
|
* Sets filter parameter values globally inside context defined by this entity manager.
|
|
260
262
|
* If you want to set shared value for all contexts, be sure to use the root entity manager.
|
|
261
263
|
*/
|
|
262
264
|
setFilterParams(name, args) {
|
|
263
|
-
this.getContext()
|
|
265
|
+
this.getContext().#filterParams[name] = args;
|
|
264
266
|
}
|
|
265
267
|
/**
|
|
266
268
|
* Returns filter parameters for given filter set in this context.
|
|
267
269
|
*/
|
|
268
270
|
getFilterParams(name) {
|
|
269
|
-
return this.getContext()
|
|
271
|
+
return this.getContext().#filterParams[name];
|
|
270
272
|
}
|
|
271
273
|
/**
|
|
272
274
|
* Sets logger context for this entity manager.
|
|
@@ -283,7 +285,7 @@ export class EntityManager {
|
|
|
283
285
|
return em.loggerContext;
|
|
284
286
|
}
|
|
285
287
|
setFlushMode(flushMode) {
|
|
286
|
-
this.getContext(false)
|
|
288
|
+
this.getContext(false).#flushMode = flushMode;
|
|
287
289
|
}
|
|
288
290
|
async processWhere(entityName, where, options, type) {
|
|
289
291
|
where = QueryHelper.processWhere({
|
|
@@ -298,6 +300,14 @@ export class EntityManager {
|
|
|
298
300
|
where = this.applyDiscriminatorCondition(entityName, where);
|
|
299
301
|
return where;
|
|
300
302
|
}
|
|
303
|
+
async processUnionWhere(entityName, options, type) {
|
|
304
|
+
if (options.unionWhere?.length) {
|
|
305
|
+
if (!this.driver.getPlatform().supportsUnionWhere()) {
|
|
306
|
+
throw new Error(`unionWhere is only supported on SQL drivers`);
|
|
307
|
+
}
|
|
308
|
+
options.unionWhere = (await Promise.all(options.unionWhere.map(branch => this.processWhere(entityName, branch, options, type))));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
301
311
|
// this method only handles the problem for mongo driver, SQL drivers have their implementation inside QueryBuilder
|
|
302
312
|
applyDiscriminatorCondition(entityName, where) {
|
|
303
313
|
const meta = this.metadata.find(entityName);
|
|
@@ -314,7 +324,10 @@ export class EntityManager {
|
|
|
314
324
|
};
|
|
315
325
|
lookUpChildren(children, meta.class);
|
|
316
326
|
/* v8 ignore next */
|
|
317
|
-
where[meta.root.discriminatorColumn] =
|
|
327
|
+
where[meta.root.discriminatorColumn] =
|
|
328
|
+
children.length > 0
|
|
329
|
+
? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] }
|
|
330
|
+
: meta.discriminatorValue;
|
|
318
331
|
return where;
|
|
319
332
|
}
|
|
320
333
|
createPopulateWhere(cond, options) {
|
|
@@ -381,10 +394,11 @@ export class EntityManager {
|
|
|
381
394
|
}
|
|
382
395
|
const ret = options.populate;
|
|
383
396
|
for (const prop of meta.relations) {
|
|
384
|
-
if (prop.object
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
397
|
+
if (prop.object ||
|
|
398
|
+
![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) ||
|
|
399
|
+
!((options.fields?.length ?? 0) === 0 ||
|
|
400
|
+
options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`))) ||
|
|
401
|
+
(parent?.class === prop.targetMeta.root.class && parent.propName === prop.inversedBy)) {
|
|
388
402
|
continue;
|
|
389
403
|
}
|
|
390
404
|
options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
|
|
@@ -424,14 +438,12 @@ export class EntityManager {
|
|
|
424
438
|
const ret = [];
|
|
425
439
|
const active = new Set();
|
|
426
440
|
const push = (source) => {
|
|
427
|
-
const activeFilters = QueryHelper
|
|
428
|
-
.getActiveFilters(meta, options, source)
|
|
429
|
-
.filter(f => !active.has(f.name));
|
|
441
|
+
const activeFilters = QueryHelper.getActiveFilters(meta, options, source).filter(f => !active.has(f.name));
|
|
430
442
|
filters.push(...activeFilters);
|
|
431
443
|
activeFilters.forEach(f => active.add(f.name));
|
|
432
444
|
};
|
|
433
445
|
push(this.config.get('filters'));
|
|
434
|
-
push(this
|
|
446
|
+
push(this.#filters);
|
|
435
447
|
push(meta.filters);
|
|
436
448
|
if (filters.length === 0) {
|
|
437
449
|
return where;
|
|
@@ -440,7 +452,8 @@ export class EntityManager {
|
|
|
440
452
|
let cond;
|
|
441
453
|
if (filter.cond instanceof Function) {
|
|
442
454
|
// @ts-ignore
|
|
443
|
-
|
|
455
|
+
// oxfmt-ignore
|
|
456
|
+
const args = Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().#filterParams[filter.name];
|
|
444
457
|
if (!args && filter.cond.length > 0 && filter.args !== false) {
|
|
445
458
|
throw new Error(`No arguments provided for filter '${filter.name}'`);
|
|
446
459
|
}
|
|
@@ -575,21 +588,29 @@ export class EntityManager {
|
|
|
575
588
|
});
|
|
576
589
|
const em = this.getContext();
|
|
577
590
|
if (!reloaded) {
|
|
578
|
-
em
|
|
591
|
+
em.#unitOfWork.unsetIdentity(entity);
|
|
579
592
|
return null;
|
|
580
593
|
}
|
|
581
594
|
let found = false;
|
|
582
|
-
for (const e of fork
|
|
595
|
+
for (const e of fork.#unitOfWork.getIdentityMap()) {
|
|
583
596
|
const ref = em.getReference(e.constructor, helper(e).getPrimaryKey());
|
|
584
597
|
const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: false });
|
|
585
|
-
em.config
|
|
586
|
-
|
|
598
|
+
em.config
|
|
599
|
+
.getHydrator(this.metadata)
|
|
600
|
+
.hydrate(ref, helper(ref).__meta, data, em.#entityFactory, 'full', false, false);
|
|
601
|
+
Utils.merge(helper(ref).__originalEntityData, this.#comparator.prepareEntity(e));
|
|
587
602
|
found ||= ref === entity;
|
|
588
603
|
}
|
|
589
604
|
if (!found) {
|
|
590
|
-
const data = helper(reloaded).serialize({
|
|
591
|
-
|
|
592
|
-
|
|
605
|
+
const data = helper(reloaded).serialize({
|
|
606
|
+
ignoreSerializers: true,
|
|
607
|
+
includeHidden: true,
|
|
608
|
+
convertCustomTypes: true,
|
|
609
|
+
});
|
|
610
|
+
em.config
|
|
611
|
+
.getHydrator(this.metadata)
|
|
612
|
+
.hydrate(entity, wrapped.__meta, data, em.#entityFactory, 'full', false, true);
|
|
613
|
+
Utils.merge(wrapped.__originalEntityData, this.#comparator.prepareEntity(reloaded));
|
|
593
614
|
}
|
|
594
615
|
return entity;
|
|
595
616
|
}
|
|
@@ -606,7 +627,7 @@ export class EntityManager {
|
|
|
606
627
|
}
|
|
607
628
|
const em = this.getContext();
|
|
608
629
|
em.prepareOptions(options);
|
|
609
|
-
let entity = em
|
|
630
|
+
let entity = em.#unitOfWork.tryGetById(entityName, where, options.schema);
|
|
610
631
|
// query for a not managed entity which is already in the identity map as it
|
|
611
632
|
// was provided with a PK this entity does not exist in the db, there can't
|
|
612
633
|
// be any relations to it, so no need to deal with the populate hint
|
|
@@ -623,12 +644,12 @@ export class EntityManager {
|
|
|
623
644
|
return em.lockAndPopulate(meta, entity, where, options);
|
|
624
645
|
}
|
|
625
646
|
validateParams(where);
|
|
626
|
-
options.populate = await em.preparePopulate(entityName, options);
|
|
647
|
+
options.populate = (await em.preparePopulate(entityName, options));
|
|
627
648
|
const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
|
|
628
649
|
const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
|
|
629
650
|
if (cached?.data !== undefined) {
|
|
630
651
|
if (cached.data) {
|
|
631
|
-
await em
|
|
652
|
+
await em.#entityLoader.populate(entityName, [cached.data], options.populate, {
|
|
632
653
|
...options,
|
|
633
654
|
...em.getPopulateWhere(where, options),
|
|
634
655
|
ignoreLazyScalarProperties: true,
|
|
@@ -642,8 +663,9 @@ export class EntityManager {
|
|
|
642
663
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
643
664
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
644
665
|
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
666
|
+
await em.processUnionWhere(entityName, options, 'read');
|
|
645
667
|
const data = await em.driver.findOne(entityName, where, {
|
|
646
|
-
ctx: em
|
|
668
|
+
ctx: em.#transactionContext,
|
|
647
669
|
em,
|
|
648
670
|
...options,
|
|
649
671
|
});
|
|
@@ -651,14 +673,14 @@ export class EntityManager {
|
|
|
651
673
|
await em.storeCache(options.cache, cached, null);
|
|
652
674
|
return null;
|
|
653
675
|
}
|
|
654
|
-
entity = em
|
|
676
|
+
entity = em.#entityFactory.create(entityName, data, {
|
|
655
677
|
merge: true,
|
|
656
678
|
refresh: options.refresh,
|
|
657
679
|
schema: options.schema,
|
|
658
680
|
convertCustomTypes: true,
|
|
659
681
|
});
|
|
660
682
|
await em.lockAndPopulate(meta, entity, where, options);
|
|
661
|
-
await em
|
|
683
|
+
await em.#unitOfWork.dispatchOnLoadEvent();
|
|
662
684
|
await em.storeCache(options.cache, cached, () => helper(entity).toPOJO());
|
|
663
685
|
return entity;
|
|
664
686
|
}
|
|
@@ -736,17 +758,17 @@ export class EntityManager {
|
|
|
736
758
|
if (Utils.isEntity(data)) {
|
|
737
759
|
entity = data;
|
|
738
760
|
if (helper(entity).__managed && helper(entity).__em === em && !this.config.get('upsertManaged')) {
|
|
739
|
-
em
|
|
761
|
+
em.#entityFactory.mergeData(meta, entity, data, { initialized: true });
|
|
740
762
|
return entity;
|
|
741
763
|
}
|
|
742
764
|
where = helper(entity).getPrimaryKey();
|
|
743
|
-
data = em
|
|
765
|
+
data = em.#comparator.prepareEntity(entity);
|
|
744
766
|
}
|
|
745
767
|
else {
|
|
746
768
|
data = Utils.copy(QueryHelper.processParams(data));
|
|
747
769
|
where = Utils.extractPK(data, meta);
|
|
748
770
|
if (where && !this.config.get('upsertManaged')) {
|
|
749
|
-
const exists = em
|
|
771
|
+
const exists = em.#unitOfWork.getById(entityName, where, options.schema);
|
|
750
772
|
if (exists) {
|
|
751
773
|
return em.assign(exists, data);
|
|
752
774
|
}
|
|
@@ -759,20 +781,23 @@ export class EntityManager {
|
|
|
759
781
|
await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
|
|
760
782
|
}
|
|
761
783
|
const ret = await em.driver.nativeUpdate(entityName, where, data, {
|
|
762
|
-
ctx: em
|
|
784
|
+
ctx: em.#transactionContext,
|
|
763
785
|
upsert: true,
|
|
764
786
|
convertCustomTypes,
|
|
765
787
|
...options,
|
|
766
788
|
});
|
|
767
|
-
em
|
|
768
|
-
entity ??= em
|
|
789
|
+
em.#unitOfWork.getChangeSetPersister().mapReturnedValues(entity, data, ret.row, meta, true);
|
|
790
|
+
entity ??= em.#entityFactory.create(entityName, data, {
|
|
769
791
|
refresh: true,
|
|
770
792
|
initialized: true,
|
|
771
793
|
schema: options.schema,
|
|
772
794
|
});
|
|
773
|
-
const uniqueFields = options.onConflictFields ??
|
|
795
|
+
const uniqueFields = options.onConflictFields ??
|
|
796
|
+
(Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys);
|
|
774
797
|
const returning = getOnConflictReturningFields(meta, data, uniqueFields, options);
|
|
775
|
-
if (options.onConflictAction === 'ignore' ||
|
|
798
|
+
if (options.onConflictAction === 'ignore' ||
|
|
799
|
+
!helper(entity).hasPrimaryKey() ||
|
|
800
|
+
(returning.length > 0 && !(this.getPlatform().usesReturningStatement() && ret.row))) {
|
|
776
801
|
const where = {};
|
|
777
802
|
if (Array.isArray(uniqueFields)) {
|
|
778
803
|
for (const prop of uniqueFields) {
|
|
@@ -794,16 +819,16 @@ export class EntityManager {
|
|
|
794
819
|
}
|
|
795
820
|
const data2 = await this.driver.findOne(meta.class, where, {
|
|
796
821
|
fields: returning,
|
|
797
|
-
ctx: em
|
|
822
|
+
ctx: em.#transactionContext,
|
|
798
823
|
convertCustomTypes: true,
|
|
799
824
|
connectionType: 'write',
|
|
800
825
|
schema: options.schema,
|
|
801
826
|
});
|
|
802
|
-
em.getHydrator().hydrate(entity, meta, data2, em
|
|
827
|
+
em.getHydrator().hydrate(entity, meta, data2, em.#entityFactory, 'full', false, true);
|
|
803
828
|
}
|
|
804
829
|
// recompute the data as there might be some values missing (e.g. those with db column defaults)
|
|
805
|
-
const snapshot = this
|
|
806
|
-
em
|
|
830
|
+
const snapshot = this.#comparator.prepareEntity(entity);
|
|
831
|
+
em.#unitOfWork.register(entity, snapshot, { refresh: true });
|
|
807
832
|
if (em.eventManager.hasListeners(EventType.afterUpsert, meta)) {
|
|
808
833
|
await em.eventManager.dispatchEvent(EventType.afterUpsert, { entity, em, meta }, meta);
|
|
809
834
|
}
|
|
@@ -874,19 +899,19 @@ export class EntityManager {
|
|
|
874
899
|
if (Utils.isEntity(row)) {
|
|
875
900
|
const entity = row;
|
|
876
901
|
if (helper(entity).__managed && helper(entity).__em === em && !this.config.get('upsertManaged')) {
|
|
877
|
-
em
|
|
902
|
+
em.#entityFactory.mergeData(meta, entity, row, { initialized: true });
|
|
878
903
|
entities.set(entity, row);
|
|
879
904
|
entitiesByData.set(row, entity);
|
|
880
905
|
continue;
|
|
881
906
|
}
|
|
882
907
|
where = helper(entity).getPrimaryKey();
|
|
883
|
-
row = em
|
|
908
|
+
row = em.#comparator.prepareEntity(entity);
|
|
884
909
|
}
|
|
885
910
|
else {
|
|
886
911
|
row = data[i] = Utils.copy(QueryHelper.processParams(row));
|
|
887
912
|
where = Utils.extractPK(row, meta);
|
|
888
913
|
if (where && !this.config.get('upsertManaged')) {
|
|
889
|
-
const exists = em
|
|
914
|
+
const exists = em.#unitOfWork.getById(entityName, where, options.schema);
|
|
890
915
|
if (exists) {
|
|
891
916
|
em.assign(exists, row);
|
|
892
917
|
entities.set(exists, row);
|
|
@@ -896,7 +921,9 @@ export class EntityManager {
|
|
|
896
921
|
}
|
|
897
922
|
}
|
|
898
923
|
const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
|
|
899
|
-
propIndex =
|
|
924
|
+
propIndex =
|
|
925
|
+
!isRaw(unique) &&
|
|
926
|
+
unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
|
|
900
927
|
const tmp = getWhereCondition(meta, options.onConflictFields, row, where);
|
|
901
928
|
propIndex = tmp.propIndex;
|
|
902
929
|
where = QueryHelper.processWhere({
|
|
@@ -920,7 +947,7 @@ export class EntityManager {
|
|
|
920
947
|
}
|
|
921
948
|
}
|
|
922
949
|
const res = await em.driver.nativeUpdateMany(entityName, allWhere, allData, {
|
|
923
|
-
ctx: em
|
|
950
|
+
ctx: em.#transactionContext,
|
|
924
951
|
upsert: true,
|
|
925
952
|
convertCustomTypes,
|
|
926
953
|
...options,
|
|
@@ -929,12 +956,16 @@ export class EntityManager {
|
|
|
929
956
|
entitiesByData.clear();
|
|
930
957
|
const loadPK = new Map();
|
|
931
958
|
allData.forEach((row, i) => {
|
|
932
|
-
em
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
959
|
+
em.#unitOfWork
|
|
960
|
+
.getChangeSetPersister()
|
|
961
|
+
.mapReturnedValues(Utils.isEntity(data[i]) ? data[i] : null, Utils.isEntity(data[i]) ? {} : data[i], res.rows?.[i], meta, true);
|
|
962
|
+
const entity = Utils.isEntity(data[i])
|
|
963
|
+
? data[i]
|
|
964
|
+
: em.#entityFactory.create(entityName, row, {
|
|
965
|
+
refresh: true,
|
|
966
|
+
initialized: true,
|
|
967
|
+
schema: options.schema,
|
|
968
|
+
});
|
|
938
969
|
if (!helper(entity).hasPrimaryKey()) {
|
|
939
970
|
loadPK.set(entity, allWhere[i]);
|
|
940
971
|
}
|
|
@@ -942,12 +973,15 @@ export class EntityManager {
|
|
|
942
973
|
entitiesByData.set(row, entity);
|
|
943
974
|
});
|
|
944
975
|
// skip if we got the PKs via returning statement (`rows`)
|
|
976
|
+
// oxfmt-ignore
|
|
945
977
|
const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(allWhere[0]) ? Object.keys(allWhere[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
|
|
946
978
|
const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
|
|
947
979
|
const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
|
|
948
980
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
949
981
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
950
|
-
const add = new Set(propIndex !== false && propIndex >= 0
|
|
982
|
+
const add = new Set(propIndex !== false && propIndex >= 0
|
|
983
|
+
? [unique[propIndex]]
|
|
984
|
+
: []);
|
|
951
985
|
for (const cond of loadPK.values()) {
|
|
952
986
|
Utils.keys(cond).forEach(key => add.add(key));
|
|
953
987
|
}
|
|
@@ -960,8 +994,10 @@ export class EntityManager {
|
|
|
960
994
|
});
|
|
961
995
|
});
|
|
962
996
|
const data2 = await this.driver.find(meta.class, where, {
|
|
963
|
-
fields: returning
|
|
964
|
-
|
|
997
|
+
fields: returning
|
|
998
|
+
.concat(...add)
|
|
999
|
+
.concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
|
|
1000
|
+
ctx: em.#transactionContext,
|
|
965
1001
|
convertCustomTypes: true,
|
|
966
1002
|
connectionType: 'write',
|
|
967
1003
|
schema: options.schema,
|
|
@@ -974,13 +1010,13 @@ export class EntityManager {
|
|
|
974
1010
|
tmp[k] = row[k];
|
|
975
1011
|
}
|
|
976
1012
|
});
|
|
977
|
-
return this
|
|
1013
|
+
return this.#comparator.matching(entityName, cond, tmp);
|
|
978
1014
|
});
|
|
979
1015
|
/* v8 ignore next */
|
|
980
1016
|
if (!row) {
|
|
981
1017
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
982
1018
|
}
|
|
983
|
-
em.getHydrator().hydrate(entity, meta, row, em
|
|
1019
|
+
em.getHydrator().hydrate(entity, meta, row, em.#entityFactory, 'full', false, true);
|
|
984
1020
|
}
|
|
985
1021
|
if (loadPK.size !== data2.length && Array.isArray(uniqueFields)) {
|
|
986
1022
|
for (let i = 0; i < allData.length; i++) {
|
|
@@ -997,20 +1033,20 @@ export class EntityManager {
|
|
|
997
1033
|
a[b] = item[b];
|
|
998
1034
|
return a;
|
|
999
1035
|
}, {});
|
|
1000
|
-
return this
|
|
1036
|
+
return this.#comparator.matching(entityName, cond, pk);
|
|
1001
1037
|
});
|
|
1002
1038
|
/* v8 ignore next */
|
|
1003
1039
|
if (!row) {
|
|
1004
1040
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
1005
1041
|
}
|
|
1006
|
-
em.getHydrator().hydrate(entity, meta, row, em
|
|
1042
|
+
em.getHydrator().hydrate(entity, meta, row, em.#entityFactory, 'full');
|
|
1007
1043
|
}
|
|
1008
1044
|
}
|
|
1009
1045
|
}
|
|
1010
1046
|
for (const [entity] of entities) {
|
|
1011
1047
|
// recompute the data as there might be some values missing (e.g. those with db column defaults)
|
|
1012
|
-
const snapshot = this
|
|
1013
|
-
em
|
|
1048
|
+
const snapshot = this.#comparator.prepareEntity(entity);
|
|
1049
|
+
em.#unitOfWork.register(entity, snapshot, { refresh: true });
|
|
1014
1050
|
}
|
|
1015
1051
|
if (em.eventManager.hasListeners(EventType.afterUpsert, meta)) {
|
|
1016
1052
|
for (const [entity] of entities) {
|
|
@@ -1047,7 +1083,7 @@ export class EntityManager {
|
|
|
1047
1083
|
*/
|
|
1048
1084
|
async transactional(cb, options = {}) {
|
|
1049
1085
|
const em = this.getContext(false);
|
|
1050
|
-
if (this
|
|
1086
|
+
if (this.#disableTransactions || em.#disableTransactions) {
|
|
1051
1087
|
return cb(em);
|
|
1052
1088
|
}
|
|
1053
1089
|
const manager = new TransactionManager(this);
|
|
@@ -1057,11 +1093,11 @@ export class EntityManager {
|
|
|
1057
1093
|
* Starts new transaction bound to this EntityManager. Use `ctx` parameter to provide the parent when nesting transactions.
|
|
1058
1094
|
*/
|
|
1059
1095
|
async begin(options = {}) {
|
|
1060
|
-
if (this
|
|
1096
|
+
if (this.#disableTransactions) {
|
|
1061
1097
|
return;
|
|
1062
1098
|
}
|
|
1063
1099
|
const em = this.getContext(false);
|
|
1064
|
-
em
|
|
1100
|
+
em.#transactionContext = await em.getConnection('write').begin({
|
|
1065
1101
|
...options,
|
|
1066
1102
|
eventBroadcaster: new TransactionEventBroadcaster(em, { topLevelTransaction: !options.ctx }),
|
|
1067
1103
|
});
|
|
@@ -1071,31 +1107,31 @@ export class EntityManager {
|
|
|
1071
1107
|
*/
|
|
1072
1108
|
async commit() {
|
|
1073
1109
|
const em = this.getContext(false);
|
|
1074
|
-
if (this
|
|
1110
|
+
if (this.#disableTransactions) {
|
|
1075
1111
|
await em.flush();
|
|
1076
1112
|
return;
|
|
1077
1113
|
}
|
|
1078
|
-
if (!em
|
|
1114
|
+
if (!em.#transactionContext) {
|
|
1079
1115
|
throw ValidationError.transactionRequired();
|
|
1080
1116
|
}
|
|
1081
1117
|
await em.flush();
|
|
1082
|
-
await em.getConnection('write').commit(em
|
|
1083
|
-
|
|
1118
|
+
await em.getConnection('write').commit(em.#transactionContext, new TransactionEventBroadcaster(em));
|
|
1119
|
+
em.#transactionContext = undefined;
|
|
1084
1120
|
}
|
|
1085
1121
|
/**
|
|
1086
1122
|
* Rollbacks the transaction bound to this EntityManager.
|
|
1087
1123
|
*/
|
|
1088
1124
|
async rollback() {
|
|
1089
|
-
if (this
|
|
1125
|
+
if (this.#disableTransactions) {
|
|
1090
1126
|
return;
|
|
1091
1127
|
}
|
|
1092
1128
|
const em = this.getContext(false);
|
|
1093
|
-
if (!em
|
|
1129
|
+
if (!em.#transactionContext) {
|
|
1094
1130
|
throw ValidationError.transactionRequired();
|
|
1095
1131
|
}
|
|
1096
|
-
await em.getConnection('write').rollback(em
|
|
1097
|
-
|
|
1098
|
-
em
|
|
1132
|
+
await em.getConnection('write').rollback(em.#transactionContext, new TransactionEventBroadcaster(em));
|
|
1133
|
+
em.#transactionContext = undefined;
|
|
1134
|
+
em.#unitOfWork.clearActionsQueue();
|
|
1099
1135
|
}
|
|
1100
1136
|
/**
|
|
1101
1137
|
* Runs your callback wrapped inside a database transaction.
|
|
@@ -1124,19 +1160,22 @@ export class EntityManager {
|
|
|
1124
1160
|
}
|
|
1125
1161
|
if (!helper(data).__managed) {
|
|
1126
1162
|
// the entity might have been created via `em.create()`, which adds it to the persist stack automatically
|
|
1127
|
-
em
|
|
1163
|
+
em.#unitOfWork.getPersistStack().delete(data);
|
|
1128
1164
|
// it can be also in the identity map if it had a PK value already
|
|
1129
|
-
em
|
|
1165
|
+
em.#unitOfWork.unsetIdentity(data);
|
|
1130
1166
|
}
|
|
1131
1167
|
const meta = helper(data).__meta;
|
|
1132
|
-
const payload = em
|
|
1168
|
+
const payload = em.#comparator.prepareEntity(data);
|
|
1133
1169
|
const cs = new ChangeSet(data, ChangeSetType.CREATE, payload, meta);
|
|
1134
|
-
await em
|
|
1170
|
+
await em.#unitOfWork.getChangeSetPersister().executeInserts([cs], { ctx: em.#transactionContext, ...options });
|
|
1135
1171
|
return cs.getPrimaryKey();
|
|
1136
1172
|
}
|
|
1137
1173
|
data = QueryHelper.processObjectParams(data);
|
|
1138
1174
|
validateParams(data, 'insert data');
|
|
1139
|
-
const res = await em.driver.nativeInsert(entityName, data, {
|
|
1175
|
+
const res = await em.driver.nativeInsert(entityName, data, {
|
|
1176
|
+
ctx: em.#transactionContext,
|
|
1177
|
+
...options,
|
|
1178
|
+
});
|
|
1140
1179
|
return res.insertId;
|
|
1141
1180
|
}
|
|
1142
1181
|
/**
|
|
@@ -1164,19 +1203,22 @@ export class EntityManager {
|
|
|
1164
1203
|
}
|
|
1165
1204
|
if (!helper(row).__managed) {
|
|
1166
1205
|
// the entity might have been created via `em.create()`, which adds it to the persist stack automatically
|
|
1167
|
-
em
|
|
1206
|
+
em.#unitOfWork.getPersistStack().delete(row);
|
|
1168
1207
|
// it can be also in the identity map if it had a PK value already
|
|
1169
|
-
em
|
|
1208
|
+
em.#unitOfWork.unsetIdentity(row);
|
|
1170
1209
|
}
|
|
1171
|
-
const payload = em
|
|
1210
|
+
const payload = em.#comparator.prepareEntity(row);
|
|
1172
1211
|
return new ChangeSet(row, ChangeSetType.CREATE, payload, meta);
|
|
1173
1212
|
});
|
|
1174
|
-
await em
|
|
1213
|
+
await em.#unitOfWork.getChangeSetPersister().executeInserts(css, { ctx: em.#transactionContext, ...options });
|
|
1175
1214
|
return css.map(cs => cs.getPrimaryKey());
|
|
1176
1215
|
}
|
|
1177
1216
|
data = data.map(row => QueryHelper.processObjectParams(row));
|
|
1178
1217
|
data.forEach(row => validateParams(row, 'insert data'));
|
|
1179
|
-
const res = await em.driver.nativeInsertMany(entityName, data, {
|
|
1218
|
+
const res = await em.driver.nativeInsertMany(entityName, data, {
|
|
1219
|
+
ctx: em.#transactionContext,
|
|
1220
|
+
...options,
|
|
1221
|
+
});
|
|
1180
1222
|
if (res.insertedIds) {
|
|
1181
1223
|
return res.insertedIds;
|
|
1182
1224
|
}
|
|
@@ -1188,11 +1230,16 @@ export class EntityManager {
|
|
|
1188
1230
|
async nativeUpdate(entityName, where, data, options = {}) {
|
|
1189
1231
|
const em = this.getContext(false);
|
|
1190
1232
|
em.prepareOptions(options);
|
|
1233
|
+
await em.processUnionWhere(entityName, options, 'update');
|
|
1191
1234
|
data = QueryHelper.processObjectParams(data);
|
|
1192
1235
|
where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
|
|
1193
1236
|
validateParams(data, 'update data');
|
|
1194
1237
|
validateParams(where, 'update condition');
|
|
1195
|
-
const res = await em.driver.nativeUpdate(entityName, where, data, {
|
|
1238
|
+
const res = await em.driver.nativeUpdate(entityName, where, data, {
|
|
1239
|
+
ctx: em.#transactionContext,
|
|
1240
|
+
em,
|
|
1241
|
+
...options,
|
|
1242
|
+
});
|
|
1196
1243
|
return res.affectedRows;
|
|
1197
1244
|
}
|
|
1198
1245
|
/**
|
|
@@ -1201,9 +1248,14 @@ export class EntityManager {
|
|
|
1201
1248
|
async nativeDelete(entityName, where, options = {}) {
|
|
1202
1249
|
const em = this.getContext(false);
|
|
1203
1250
|
em.prepareOptions(options);
|
|
1204
|
-
|
|
1251
|
+
await em.processUnionWhere(entityName, options, 'delete');
|
|
1252
|
+
where = (await em.processWhere(entityName, where, options, 'delete'));
|
|
1205
1253
|
validateParams(where, 'delete condition');
|
|
1206
|
-
const res = await em.driver.nativeDelete(entityName, where, {
|
|
1254
|
+
const res = await em.driver.nativeDelete(entityName, where, {
|
|
1255
|
+
ctx: em.#transactionContext,
|
|
1256
|
+
em,
|
|
1257
|
+
...options,
|
|
1258
|
+
});
|
|
1207
1259
|
return res.affectedRows;
|
|
1208
1260
|
}
|
|
1209
1261
|
/**
|
|
@@ -1214,7 +1266,10 @@ export class EntityManager {
|
|
|
1214
1266
|
const data = this.driver.mapResult(result, meta);
|
|
1215
1267
|
for (const k of Object.keys(data)) {
|
|
1216
1268
|
const prop = meta.properties[k];
|
|
1217
|
-
if (prop?.kind === ReferenceKind.SCALAR &&
|
|
1269
|
+
if (prop?.kind === ReferenceKind.SCALAR &&
|
|
1270
|
+
SCALAR_TYPES.has(prop.runtimeType) &&
|
|
1271
|
+
!prop.customType &&
|
|
1272
|
+
(prop.setter || !prop.getter)) {
|
|
1218
1273
|
validateProperty(prop, data[k], data);
|
|
1219
1274
|
}
|
|
1220
1275
|
}
|
|
@@ -1234,18 +1289,20 @@ export class EntityManager {
|
|
|
1234
1289
|
return this.merge(entityName.constructor, entityName, data);
|
|
1235
1290
|
}
|
|
1236
1291
|
const em = options.disableContextResolution ? this : this.getContext();
|
|
1237
|
-
options.schema ??= em
|
|
1292
|
+
options.schema ??= em.#schema;
|
|
1238
1293
|
options.validate ??= true;
|
|
1239
1294
|
options.cascade ??= true;
|
|
1240
1295
|
validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1241
|
-
let entity = em
|
|
1296
|
+
let entity = em.#unitOfWork.tryGetById(entityName, data, options.schema, false);
|
|
1242
1297
|
if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
|
|
1243
1298
|
return entity;
|
|
1244
1299
|
}
|
|
1245
1300
|
const dataIsEntity = Utils.isEntity(data);
|
|
1246
|
-
entity = dataIsEntity
|
|
1301
|
+
entity = dataIsEntity
|
|
1302
|
+
? data
|
|
1303
|
+
: em.#entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1247
1304
|
const visited = options.cascade ? undefined : new Set([entity]);
|
|
1248
|
-
em
|
|
1305
|
+
em.#unitOfWork.merge(entity, visited);
|
|
1249
1306
|
return entity;
|
|
1250
1307
|
}
|
|
1251
1308
|
/**
|
|
@@ -1265,8 +1322,8 @@ export class EntityManager {
|
|
|
1265
1322
|
*/
|
|
1266
1323
|
create(entityName, data, options = {}) {
|
|
1267
1324
|
const em = this.getContext();
|
|
1268
|
-
options.schema ??= em
|
|
1269
|
-
const entity = em
|
|
1325
|
+
options.schema ??= em.#schema;
|
|
1326
|
+
const entity = em.#entityFactory.create(entityName, data, {
|
|
1270
1327
|
...options,
|
|
1271
1328
|
newEntity: !options.managed,
|
|
1272
1329
|
merge: options.managed,
|
|
@@ -1313,7 +1370,7 @@ export class EntityManager {
|
|
|
1313
1370
|
em.prepareOptions(options);
|
|
1314
1371
|
await em.tryFlush(entityName, options);
|
|
1315
1372
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
1316
|
-
options.populate = await em.preparePopulate(entityName, options);
|
|
1373
|
+
options.populate = (await em.preparePopulate(entityName, options));
|
|
1317
1374
|
options = { ...options };
|
|
1318
1375
|
// save the original hint value so we know it was infer/all
|
|
1319
1376
|
const meta = em.metadata.find(entityName);
|
|
@@ -1322,12 +1379,13 @@ export class EntityManager {
|
|
|
1322
1379
|
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
1323
1380
|
validateParams(where);
|
|
1324
1381
|
delete options.orderBy;
|
|
1382
|
+
await em.processUnionWhere(entityName, options, 'read');
|
|
1325
1383
|
const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
|
|
1326
1384
|
const cached = await em.tryCache(entityName, options.cache, cacheKey);
|
|
1327
1385
|
if (cached?.data !== undefined) {
|
|
1328
1386
|
return cached.data;
|
|
1329
1387
|
}
|
|
1330
|
-
const count = await em.driver.count(entityName, where, { ctx: em
|
|
1388
|
+
const count = await em.driver.count(entityName, where, { ctx: em.#transactionContext, em, ...options });
|
|
1331
1389
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1332
1390
|
return +count;
|
|
1333
1391
|
}
|
|
@@ -1339,7 +1397,7 @@ export class EntityManager {
|
|
|
1339
1397
|
const em = this.getContext();
|
|
1340
1398
|
if (Utils.isEntity(entity)) {
|
|
1341
1399
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
1342
|
-
em
|
|
1400
|
+
em.#unitOfWork.persist(entity, undefined, { cascade: false });
|
|
1343
1401
|
return em;
|
|
1344
1402
|
}
|
|
1345
1403
|
const entities = Utils.asArray(entity);
|
|
@@ -1350,7 +1408,7 @@ export class EntityManager {
|
|
|
1350
1408
|
throw ValidationError.notDiscoveredEntity(ent, meta);
|
|
1351
1409
|
}
|
|
1352
1410
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
1353
|
-
em
|
|
1411
|
+
em.#unitOfWork.persist(Reference.unwrapReference(ent), undefined, { cascade: false });
|
|
1354
1412
|
}
|
|
1355
1413
|
return this;
|
|
1356
1414
|
}
|
|
@@ -1364,7 +1422,7 @@ export class EntityManager {
|
|
|
1364
1422
|
const em = this.getContext();
|
|
1365
1423
|
if (Utils.isEntity(entity)) {
|
|
1366
1424
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
1367
|
-
em
|
|
1425
|
+
em.#unitOfWork.remove(entity, undefined, { cascade: false });
|
|
1368
1426
|
return em;
|
|
1369
1427
|
}
|
|
1370
1428
|
const entities = Utils.asArray(entity, true);
|
|
@@ -1373,7 +1431,7 @@ export class EntityManager {
|
|
|
1373
1431
|
throw new Error(`You need to pass entity instance or reference to 'em.remove()'. To remove entities by condition, use 'em.nativeDelete()'.`);
|
|
1374
1432
|
}
|
|
1375
1433
|
// do not cascade just yet, cascading of entities in remove stack is done when flushing
|
|
1376
|
-
em
|
|
1434
|
+
em.#unitOfWork.remove(Reference.unwrapReference(ent), undefined, { cascade: false });
|
|
1377
1435
|
}
|
|
1378
1436
|
return em;
|
|
1379
1437
|
}
|
|
@@ -1389,7 +1447,7 @@ export class EntityManager {
|
|
|
1389
1447
|
*/
|
|
1390
1448
|
async tryFlush(entityName, options) {
|
|
1391
1449
|
const em = this.getContext();
|
|
1392
|
-
const flushMode = options.flushMode ?? em
|
|
1450
|
+
const flushMode = options.flushMode ?? em.#flushMode ?? em.config.get('flushMode');
|
|
1393
1451
|
const meta = em.metadata.get(entityName);
|
|
1394
1452
|
if (flushMode === FlushMode.COMMIT) {
|
|
1395
1453
|
return;
|
|
@@ -1402,7 +1460,7 @@ export class EntityManager {
|
|
|
1402
1460
|
* Clears the EntityManager. All entities that are currently managed by this EntityManager become detached.
|
|
1403
1461
|
*/
|
|
1404
1462
|
clear() {
|
|
1405
|
-
this.getContext()
|
|
1463
|
+
this.getContext().#unitOfWork.clear();
|
|
1406
1464
|
}
|
|
1407
1465
|
/**
|
|
1408
1466
|
* Checks whether given property can be populated on the entity.
|
|
@@ -1437,7 +1495,7 @@ export class EntityManager {
|
|
|
1437
1495
|
em.prepareOptions(options);
|
|
1438
1496
|
const entityName = arr[0].constructor;
|
|
1439
1497
|
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1440
|
-
await em
|
|
1498
|
+
await em.#entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1441
1499
|
return entities;
|
|
1442
1500
|
}
|
|
1443
1501
|
/**
|
|
@@ -1458,25 +1516,26 @@ export class EntityManager {
|
|
|
1458
1516
|
const allowGlobalContext = em.config.get('allowGlobalContext');
|
|
1459
1517
|
em.config.set('allowGlobalContext', true);
|
|
1460
1518
|
const fork = new em.constructor(em.config, em.driver, em.metadata, options.useContext, eventManager);
|
|
1461
|
-
fork.setFlushMode(options.flushMode ?? em
|
|
1462
|
-
fork
|
|
1519
|
+
fork.setFlushMode(options.flushMode ?? em.#flushMode);
|
|
1520
|
+
fork.#disableTransactions =
|
|
1521
|
+
options.disableTransactions ?? this.#disableTransactions ?? this.config.get('disableTransactions');
|
|
1463
1522
|
em.config.set('allowGlobalContext', allowGlobalContext);
|
|
1464
1523
|
if (options.keepTransactionContext) {
|
|
1465
|
-
fork
|
|
1524
|
+
fork.#transactionContext = em.#transactionContext;
|
|
1466
1525
|
}
|
|
1467
|
-
fork
|
|
1468
|
-
fork
|
|
1526
|
+
fork.#filters = { ...em.#filters };
|
|
1527
|
+
fork.#filterParams = Utils.copy(em.#filterParams);
|
|
1469
1528
|
fork.loggerContext = Utils.merge({}, em.loggerContext, options.loggerContext);
|
|
1470
|
-
fork
|
|
1529
|
+
fork.#schema = options.schema ?? em.#schema;
|
|
1471
1530
|
if (!options.clear) {
|
|
1472
|
-
for (const entity of em
|
|
1473
|
-
fork
|
|
1531
|
+
for (const entity of em.#unitOfWork.getIdentityMap()) {
|
|
1532
|
+
fork.#unitOfWork.register(entity);
|
|
1474
1533
|
}
|
|
1475
|
-
for (const entity of em
|
|
1476
|
-
fork
|
|
1534
|
+
for (const entity of em.#unitOfWork.getPersistStack()) {
|
|
1535
|
+
fork.#unitOfWork.persist(entity);
|
|
1477
1536
|
}
|
|
1478
|
-
for (const entity of em
|
|
1479
|
-
fork
|
|
1537
|
+
for (const entity of em.#unitOfWork.getOrphanRemoveStack()) {
|
|
1538
|
+
fork.#unitOfWork.getOrphanRemoveStack().add(entity);
|
|
1480
1539
|
}
|
|
1481
1540
|
}
|
|
1482
1541
|
return fork;
|
|
@@ -1486,21 +1545,21 @@ export class EntityManager {
|
|
|
1486
1545
|
*/
|
|
1487
1546
|
getUnitOfWork(useContext = true) {
|
|
1488
1547
|
if (!useContext) {
|
|
1489
|
-
return this
|
|
1548
|
+
return this.#unitOfWork;
|
|
1490
1549
|
}
|
|
1491
|
-
return this.getContext()
|
|
1550
|
+
return this.getContext().#unitOfWork;
|
|
1492
1551
|
}
|
|
1493
1552
|
/**
|
|
1494
1553
|
* Gets the EntityFactory used by the EntityManager.
|
|
1495
1554
|
*/
|
|
1496
1555
|
getEntityFactory() {
|
|
1497
|
-
return this.getContext()
|
|
1556
|
+
return this.getContext().#entityFactory;
|
|
1498
1557
|
}
|
|
1499
1558
|
/**
|
|
1500
1559
|
* @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
|
|
1501
1560
|
*/
|
|
1502
1561
|
getEntityLoader() {
|
|
1503
|
-
return this.getContext()
|
|
1562
|
+
return this.getContext().#entityLoader;
|
|
1504
1563
|
}
|
|
1505
1564
|
/**
|
|
1506
1565
|
* Gets the Hydrator used by the EntityManager.
|
|
@@ -1513,7 +1572,7 @@ export class EntityManager {
|
|
|
1513
1572
|
* @internal
|
|
1514
1573
|
*/
|
|
1515
1574
|
getContext(validate = true) {
|
|
1516
|
-
if (!this
|
|
1575
|
+
if (!this.#useContext) {
|
|
1517
1576
|
return this;
|
|
1518
1577
|
}
|
|
1519
1578
|
let em = TransactionContext.getEntityManager(this.name); // prefer the tx context
|
|
@@ -1534,13 +1593,13 @@ export class EntityManager {
|
|
|
1534
1593
|
* Checks whether this EntityManager is currently operating inside a database transaction.
|
|
1535
1594
|
*/
|
|
1536
1595
|
isInTransaction() {
|
|
1537
|
-
return !!this.getContext(false)
|
|
1596
|
+
return !!this.getContext(false).#transactionContext;
|
|
1538
1597
|
}
|
|
1539
1598
|
/**
|
|
1540
1599
|
* Gets the transaction context (driver dependent object used to make sure queries are executed on same connection).
|
|
1541
1600
|
*/
|
|
1542
1601
|
getTransactionContext() {
|
|
1543
|
-
return this.getContext(false)
|
|
1602
|
+
return this.getContext(false).#transactionContext;
|
|
1544
1603
|
}
|
|
1545
1604
|
/**
|
|
1546
1605
|
* Sets the transaction context.
|
|
@@ -1550,14 +1609,14 @@ export class EntityManager {
|
|
|
1550
1609
|
this.resetTransactionContext();
|
|
1551
1610
|
}
|
|
1552
1611
|
else {
|
|
1553
|
-
this.getContext(false)
|
|
1612
|
+
this.getContext(false).#transactionContext = ctx;
|
|
1554
1613
|
}
|
|
1555
1614
|
}
|
|
1556
1615
|
/**
|
|
1557
1616
|
* Resets the transaction context.
|
|
1558
1617
|
*/
|
|
1559
1618
|
resetTransactionContext() {
|
|
1560
|
-
|
|
1619
|
+
this.getContext(false).#transactionContext = undefined;
|
|
1561
1620
|
}
|
|
1562
1621
|
/**
|
|
1563
1622
|
* Gets the `MetadataStorage` (without parameters) or `EntityMetadata` instance when provided with the `entityName` parameter.
|
|
@@ -1572,7 +1631,7 @@ export class EntityManager {
|
|
|
1572
1631
|
* Gets the EntityComparator.
|
|
1573
1632
|
*/
|
|
1574
1633
|
getComparator() {
|
|
1575
|
-
return this
|
|
1634
|
+
return this.#comparator;
|
|
1576
1635
|
}
|
|
1577
1636
|
checkLockRequirements(mode, meta) {
|
|
1578
1637
|
if (!mode) {
|
|
@@ -1593,7 +1652,7 @@ export class EntityManager {
|
|
|
1593
1652
|
});
|
|
1594
1653
|
}
|
|
1595
1654
|
const preparedPopulate = await this.preparePopulate(meta.class, options);
|
|
1596
|
-
await this
|
|
1655
|
+
await this.#entityLoader.populate(meta.class, [entity], preparedPopulate, {
|
|
1597
1656
|
...options,
|
|
1598
1657
|
...this.getPopulateWhere(where, options),
|
|
1599
1658
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
@@ -1626,11 +1685,14 @@ export class EntityManager {
|
|
|
1626
1685
|
const ret = [];
|
|
1627
1686
|
for (let field of fields) {
|
|
1628
1687
|
if (field === PopulatePath.ALL || field.startsWith(`${PopulatePath.ALL}.`)) {
|
|
1629
|
-
ret.push(...meta.props
|
|
1688
|
+
ret.push(...meta.props
|
|
1689
|
+
.filter(prop => prop.lazy || [ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind))
|
|
1690
|
+
.map(prop => prop.name));
|
|
1630
1691
|
continue;
|
|
1631
1692
|
}
|
|
1632
1693
|
field = field.split(':')[0];
|
|
1633
|
-
if (!field.includes('.') &&
|
|
1694
|
+
if (!field.includes('.') &&
|
|
1695
|
+
![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(meta.properties[field].kind)) {
|
|
1634
1696
|
ret.push(field);
|
|
1635
1697
|
continue;
|
|
1636
1698
|
}
|
|
@@ -1654,12 +1716,13 @@ export class EntityManager {
|
|
|
1654
1716
|
options.populate = pruneToOneRelations(meta, this.buildFields(options.fields));
|
|
1655
1717
|
}
|
|
1656
1718
|
if (!options.populate) {
|
|
1657
|
-
const populate = this
|
|
1719
|
+
const populate = this.#entityLoader.normalizePopulate(entityName, [], options.strategy, true, options.exclude);
|
|
1658
1720
|
await this.autoJoinRefsForFilters(meta, { ...options, populate });
|
|
1659
1721
|
return populate;
|
|
1660
1722
|
}
|
|
1661
1723
|
if (typeof options.populate !== 'boolean') {
|
|
1662
|
-
options.populate = Utils.asArray(options.populate)
|
|
1724
|
+
options.populate = Utils.asArray(options.populate)
|
|
1725
|
+
.map(field => {
|
|
1663
1726
|
/* v8 ignore next */
|
|
1664
1727
|
if (typeof field === 'boolean' || field === PopulatePath.ALL) {
|
|
1665
1728
|
return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
|
|
@@ -1674,9 +1737,10 @@ export class EntityManager {
|
|
|
1674
1737
|
return [{ field, strategy: options.strategy }];
|
|
1675
1738
|
}
|
|
1676
1739
|
return [field];
|
|
1677
|
-
})
|
|
1740
|
+
})
|
|
1741
|
+
.flat();
|
|
1678
1742
|
}
|
|
1679
|
-
const populate = this
|
|
1743
|
+
const populate = this.#entityLoader.normalizePopulate(entityName, options.populate, options.strategy, true, options.exclude);
|
|
1680
1744
|
const invalid = populate.find(({ field }) => !this.canPopulate(entityName, field));
|
|
1681
1745
|
if (validate && invalid) {
|
|
1682
1746
|
throw ValidationError.invalidPropertyName(entityName, invalid.field);
|
|
@@ -1722,7 +1786,7 @@ export class EntityManager {
|
|
|
1722
1786
|
if (!Utils.isEmpty(options.fields) && !Utils.isEmpty(options.exclude)) {
|
|
1723
1787
|
throw new ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
|
|
1724
1788
|
}
|
|
1725
|
-
options.schema ??= this
|
|
1789
|
+
options.schema ??= this.#schema;
|
|
1726
1790
|
options.logging = options.loggerContext = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1727
1791
|
}
|
|
1728
1792
|
/**
|
|
@@ -1746,7 +1810,7 @@ export class EntityManager {
|
|
|
1746
1810
|
}
|
|
1747
1811
|
const em = this.getContext();
|
|
1748
1812
|
const cacheKey = Array.isArray(config) ? config[0] : JSON.stringify(key);
|
|
1749
|
-
const cached = await em
|
|
1813
|
+
const cached = await em.#resultCache.get(cacheKey);
|
|
1750
1814
|
if (!cached) {
|
|
1751
1815
|
return { key: cacheKey, data: cached };
|
|
1752
1816
|
}
|
|
@@ -1758,15 +1822,15 @@ export class EntityManager {
|
|
|
1758
1822
|
recomputeSnapshot: true,
|
|
1759
1823
|
};
|
|
1760
1824
|
if (Array.isArray(cached) && merge) {
|
|
1761
|
-
data = cached.map(item => em
|
|
1825
|
+
data = cached.map(item => em.#entityFactory.create(entityName, item, createOptions));
|
|
1762
1826
|
}
|
|
1763
1827
|
else if (Utils.isObject(cached) && merge) {
|
|
1764
|
-
data = em
|
|
1828
|
+
data = em.#entityFactory.create(entityName, cached, createOptions);
|
|
1765
1829
|
}
|
|
1766
1830
|
else {
|
|
1767
1831
|
data = cached;
|
|
1768
1832
|
}
|
|
1769
|
-
await em
|
|
1833
|
+
await em.#unitOfWork.dispatchOnLoadEvent();
|
|
1770
1834
|
return { key: cacheKey, data };
|
|
1771
1835
|
}
|
|
1772
1836
|
/**
|
|
@@ -1776,8 +1840,8 @@ export class EntityManager {
|
|
|
1776
1840
|
config ??= this.config.get('resultCache').global;
|
|
1777
1841
|
if (config) {
|
|
1778
1842
|
const em = this.getContext();
|
|
1779
|
-
const expiration = Array.isArray(config) ? config[1] :
|
|
1780
|
-
await em
|
|
1843
|
+
const expiration = Array.isArray(config) ? config[1] : typeof config === 'number' ? config : undefined;
|
|
1844
|
+
await em.#resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
|
|
1781
1845
|
}
|
|
1782
1846
|
}
|
|
1783
1847
|
/**
|
|
@@ -1793,34 +1857,37 @@ export class EntityManager {
|
|
|
1793
1857
|
* ```
|
|
1794
1858
|
*/
|
|
1795
1859
|
async clearCache(cacheKey) {
|
|
1796
|
-
await this.getContext()
|
|
1860
|
+
await this.getContext().#resultCache.remove(cacheKey);
|
|
1797
1861
|
}
|
|
1798
1862
|
/**
|
|
1799
1863
|
* Returns the default schema of this EntityManager. Respects the context, so global EM will give you the contextual schema
|
|
1800
1864
|
* if executed inside request context handler.
|
|
1801
1865
|
*/
|
|
1802
1866
|
get schema() {
|
|
1803
|
-
return this.getContext(false)
|
|
1867
|
+
return this.getContext(false).#schema;
|
|
1804
1868
|
}
|
|
1805
1869
|
/**
|
|
1806
1870
|
* Sets the default schema of this EntityManager. Respects the context, so global EM will set the contextual schema
|
|
1807
1871
|
* if executed inside request context handler.
|
|
1808
1872
|
*/
|
|
1809
1873
|
set schema(schema) {
|
|
1810
|
-
this.getContext(false)
|
|
1874
|
+
this.getContext(false).#schema = schema ?? undefined;
|
|
1811
1875
|
}
|
|
1812
1876
|
/** @internal */
|
|
1813
1877
|
async getDataLoader(type) {
|
|
1814
1878
|
const em = this.getContext();
|
|
1815
|
-
if (em
|
|
1816
|
-
return em
|
|
1879
|
+
if (em.#loaders[type]) {
|
|
1880
|
+
return em.#loaders[type];
|
|
1817
1881
|
}
|
|
1818
1882
|
const { DataloaderUtils } = await import('@mikro-orm/core/dataloader');
|
|
1819
1883
|
const DataLoader = await DataloaderUtils.getDataLoader();
|
|
1820
1884
|
switch (type) {
|
|
1821
|
-
case 'ref':
|
|
1822
|
-
|
|
1823
|
-
case 'm
|
|
1885
|
+
case 'ref':
|
|
1886
|
+
return (em.#loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
|
|
1887
|
+
case '1:m':
|
|
1888
|
+
return (em.#loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
|
|
1889
|
+
case 'm:n':
|
|
1890
|
+
return (em.#loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
|
|
1824
1891
|
}
|
|
1825
1892
|
}
|
|
1826
1893
|
/**
|