@mikro-orm/core 7.0.0-dev.6 → 7.0.0-dev.60
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 +85 -32
- package/EntityManager.js +281 -178
- package/MikroORM.d.ts +8 -8
- package/MikroORM.js +31 -74
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +2 -1
- package/cache/FileCacheAdapter.js +5 -4
- package/connections/Connection.d.ts +11 -7
- package/connections/Connection.js +16 -13
- package/decorators/Embedded.d.ts +5 -11
- package/decorators/Entity.d.ts +18 -3
- package/decorators/Indexed.d.ts +2 -2
- package/decorators/ManyToMany.d.ts +2 -0
- package/decorators/ManyToOne.d.ts +4 -0
- package/decorators/OneToOne.d.ts +4 -0
- package/decorators/Property.d.ts +53 -9
- package/decorators/Transactional.d.ts +3 -1
- package/decorators/Transactional.js +6 -3
- package/decorators/index.d.ts +1 -1
- package/drivers/DatabaseDriver.d.ts +11 -5
- package/drivers/DatabaseDriver.js +13 -4
- package/drivers/IDatabaseDriver.d.ts +29 -5
- package/entity/ArrayCollection.d.ts +6 -4
- package/entity/ArrayCollection.js +26 -9
- package/entity/BaseEntity.d.ts +0 -1
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +3 -4
- package/entity/Collection.js +34 -17
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +9 -1
- package/entity/EntityFactory.d.ts +7 -0
- package/entity/EntityFactory.js +40 -22
- package/entity/EntityHelper.js +25 -8
- package/entity/EntityLoader.d.ts +5 -4
- package/entity/EntityLoader.js +69 -36
- package/entity/EntityRepository.d.ts +1 -1
- package/entity/EntityValidator.js +2 -2
- package/entity/Reference.d.ts +9 -7
- package/entity/Reference.js +32 -5
- package/entity/WrappedEntity.d.ts +0 -2
- package/entity/WrappedEntity.js +1 -5
- package/entity/defineEntity.d.ts +555 -0
- package/entity/defineEntity.js +529 -0
- package/entity/index.d.ts +2 -0
- package/entity/index.js +2 -0
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +15 -3
- package/enums.d.ts +18 -5
- package/enums.js +13 -0
- package/errors.d.ts +6 -1
- package/errors.js +14 -4
- package/events/EventSubscriber.d.ts +3 -1
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +35 -24
- package/index.d.ts +2 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/SimpleLogger.d.ts +1 -1
- package/metadata/EntitySchema.d.ts +8 -4
- package/metadata/EntitySchema.js +40 -20
- package/metadata/MetadataDiscovery.d.ts +5 -7
- package/metadata/MetadataDiscovery.js +150 -155
- package/metadata/MetadataStorage.js +1 -1
- package/metadata/MetadataValidator.js +4 -3
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +39 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
- package/naming-strategy/AbstractNamingStrategy.js +7 -1
- package/naming-strategy/NamingStrategy.d.ts +11 -1
- package/package.json +14 -7
- package/platforms/Platform.d.ts +5 -8
- package/platforms/Platform.js +4 -17
- package/serialization/EntitySerializer.d.ts +2 -0
- package/serialization/EntitySerializer.js +29 -11
- package/serialization/EntityTransformer.js +22 -12
- package/serialization/SerializationContext.js +14 -11
- package/types/BigIntType.d.ts +9 -6
- package/types/BigIntType.js +3 -0
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +2 -1
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.d.ts +6 -4
- package/types/DecimalType.js +1 -1
- package/types/DoubleType.js +1 -1
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/Type.d.ts +2 -1
- package/types/Type.js +1 -1
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +0 -3
- package/types/index.d.ts +1 -1
- package/typings.d.ts +94 -50
- package/typings.js +31 -31
- package/unit-of-work/ChangeSetComputer.js +8 -3
- package/unit-of-work/ChangeSetPersister.d.ts +4 -2
- package/unit-of-work/ChangeSetPersister.js +37 -16
- package/unit-of-work/UnitOfWork.d.ts +8 -1
- package/unit-of-work/UnitOfWork.js +110 -53
- package/utils/AbstractSchemaGenerator.js +3 -1
- package/utils/Configuration.d.ts +201 -184
- package/utils/Configuration.js +143 -151
- package/utils/ConfigurationLoader.d.ts +9 -22
- package/utils/ConfigurationLoader.js +53 -76
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +3 -0
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +53 -7
- package/utils/EntityComparator.d.ts +8 -4
- package/utils/EntityComparator.js +105 -58
- package/utils/QueryHelper.d.ts +9 -1
- package/utils/QueryHelper.js +66 -5
- package/utils/RawQueryFragment.d.ts +36 -4
- package/utils/RawQueryFragment.js +34 -13
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +223 -0
- package/utils/Utils.d.ts +13 -12
- package/utils/Utils.js +106 -66
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/upsert-utils.d.ts +7 -2
- package/utils/upsert-utils.js +52 -1
package/EntityManager.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { inspect } from 'node:util';
|
|
2
|
-
import
|
|
3
|
-
import { getOnConflictReturningFields } from './utils/upsert-utils.js';
|
|
2
|
+
import { getOnConflictReturningFields, getWhereCondition } from './utils/upsert-utils.js';
|
|
4
3
|
import { Utils } from './utils/Utils.js';
|
|
5
4
|
import { Cursor } from './utils/Cursor.js';
|
|
6
5
|
import { DataloaderUtils } from './utils/DataloaderUtils.js';
|
|
@@ -19,6 +18,8 @@ import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePat
|
|
|
19
18
|
import { EventManager } from './events/EventManager.js';
|
|
20
19
|
import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
|
|
21
20
|
import { OptimisticLockError, ValidationError } from './errors.js';
|
|
21
|
+
import { getLoadingStrategy } from './entity/utils.js';
|
|
22
|
+
import { TransactionManager } from './utils/TransactionManager.js';
|
|
22
23
|
/**
|
|
23
24
|
* The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
|
|
24
25
|
* such as UnitOfWork, Query Language, and Repository API.
|
|
@@ -34,8 +35,7 @@ export class EntityManager {
|
|
|
34
35
|
_id = EntityManager.counter++;
|
|
35
36
|
global = false;
|
|
36
37
|
name;
|
|
37
|
-
|
|
38
|
-
colLoader = new DataLoader(DataloaderUtils.getColBatchLoadFn(this));
|
|
38
|
+
loaders = {};
|
|
39
39
|
validator;
|
|
40
40
|
repositoryMap = {};
|
|
41
41
|
entityLoader;
|
|
@@ -137,7 +137,6 @@ export class EntityManager {
|
|
|
137
137
|
await em.entityLoader.populate(entityName, cached.data, populate, {
|
|
138
138
|
...options,
|
|
139
139
|
...em.getPopulateWhere(where, options),
|
|
140
|
-
convertCustomTypes: false,
|
|
141
140
|
ignoreLazyScalarProperties: true,
|
|
142
141
|
lookup: false,
|
|
143
142
|
});
|
|
@@ -148,8 +147,8 @@ export class EntityManager {
|
|
|
148
147
|
// save the original hint value so we know it was infer/all
|
|
149
148
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
150
149
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
151
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
152
|
-
const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, ...options });
|
|
150
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
151
|
+
const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
153
152
|
if (results.length === 0) {
|
|
154
153
|
await em.storeCache(options.cache, cached, []);
|
|
155
154
|
return [];
|
|
@@ -168,7 +167,6 @@ export class EntityManager {
|
|
|
168
167
|
await em.entityLoader.populate(entityName, unique, populate, {
|
|
169
168
|
...options,
|
|
170
169
|
...em.getPopulateWhere(where, options),
|
|
171
|
-
convertCustomTypes: false,
|
|
172
170
|
ignoreLazyScalarProperties: true,
|
|
173
171
|
lookup: false,
|
|
174
172
|
});
|
|
@@ -181,6 +179,61 @@ export class EntityManager {
|
|
|
181
179
|
}
|
|
182
180
|
return unique;
|
|
183
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Finds all entities and returns an async iterable (async generator) that yields results one by one.
|
|
184
|
+
* The results are merged and mapped to entity instances, without adding them to the identity map.
|
|
185
|
+
* You can disable merging by passing the options `{ mergeResults: false }`.
|
|
186
|
+
* With `mergeResults` disabled, to-many collections will contain at most one item, and you will get duplicate
|
|
187
|
+
* root entities when there are multiple items in the populated collection.
|
|
188
|
+
* This is useful for processing large datasets without loading everything into memory at once.
|
|
189
|
+
*
|
|
190
|
+
* ```ts
|
|
191
|
+
* const stream = em.stream(Book, { populate: ['author'] });
|
|
192
|
+
*
|
|
193
|
+
* for await (const book of stream) {
|
|
194
|
+
* // book is an instance of Book entity
|
|
195
|
+
* console.log(book.title, book.author.name);
|
|
196
|
+
* }
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
async *stream(entityName, options = {}) {
|
|
200
|
+
const em = this.getContext();
|
|
201
|
+
em.prepareOptions(options);
|
|
202
|
+
options.strategy = 'joined';
|
|
203
|
+
await em.tryFlush(entityName, options);
|
|
204
|
+
entityName = Utils.className(entityName);
|
|
205
|
+
const where = await em.processWhere(entityName, options.where ?? {}, options, 'read');
|
|
206
|
+
em.validator.validateParams(where);
|
|
207
|
+
options.orderBy = options.orderBy || {};
|
|
208
|
+
options.populate = await em.preparePopulate(entityName, options);
|
|
209
|
+
const meta = this.metadata.get(entityName);
|
|
210
|
+
options = { ...options };
|
|
211
|
+
// save the original hint value so we know it was infer/all
|
|
212
|
+
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
213
|
+
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
214
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
215
|
+
const stream = em.driver.stream(entityName, where, {
|
|
216
|
+
ctx: em.transactionContext,
|
|
217
|
+
mapResults: false,
|
|
218
|
+
...options,
|
|
219
|
+
});
|
|
220
|
+
for await (const data of stream) {
|
|
221
|
+
const fork = em.fork();
|
|
222
|
+
const entity = fork.entityFactory.create(entityName, data, {
|
|
223
|
+
refresh: options.refresh,
|
|
224
|
+
schema: options.schema,
|
|
225
|
+
convertCustomTypes: true,
|
|
226
|
+
});
|
|
227
|
+
helper(entity).setSerializationContext({
|
|
228
|
+
populate: options.populate,
|
|
229
|
+
fields: options.fields,
|
|
230
|
+
exclude: options.exclude,
|
|
231
|
+
});
|
|
232
|
+
await fork.unitOfWork.dispatchOnLoadEvent();
|
|
233
|
+
fork.clear();
|
|
234
|
+
yield entity;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
184
237
|
/**
|
|
185
238
|
* Finds all entities of given type, optionally matching the `where` condition provided in the `options` parameter.
|
|
186
239
|
*/
|
|
@@ -203,12 +256,12 @@ export class EntityManager {
|
|
|
203
256
|
/**
|
|
204
257
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
205
258
|
*/
|
|
206
|
-
addFilter(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
options.entity = Utils.asArray(entityName).map(n => Utils.className(n));
|
|
259
|
+
addFilter(options) {
|
|
260
|
+
if (options.entity) {
|
|
261
|
+
options.entity = Utils.asArray(options.entity).map(n => Utils.className(n));
|
|
210
262
|
}
|
|
211
|
-
|
|
263
|
+
options.default ??= true;
|
|
264
|
+
this.getContext(false).filters[options.name] = options;
|
|
212
265
|
}
|
|
213
266
|
/**
|
|
214
267
|
* Sets filter parameter values globally inside context defined by this entity manager.
|
|
@@ -232,8 +285,8 @@ export class EntityManager {
|
|
|
232
285
|
/**
|
|
233
286
|
* Gets logger context for this entity manager.
|
|
234
287
|
*/
|
|
235
|
-
getLoggerContext() {
|
|
236
|
-
const em = this.getContext();
|
|
288
|
+
getLoggerContext(options) {
|
|
289
|
+
const em = options?.disableContextResolution ? this : this.getContext();
|
|
237
290
|
em.loggerContext ??= {};
|
|
238
291
|
return em.loggerContext;
|
|
239
292
|
}
|
|
@@ -283,28 +336,39 @@ export class EntityManager {
|
|
|
283
336
|
}
|
|
284
337
|
return ret;
|
|
285
338
|
}
|
|
286
|
-
async getJoinedFilters(meta,
|
|
339
|
+
async getJoinedFilters(meta, options) {
|
|
340
|
+
if (!this.config.get('filtersOnRelations') || !options.populate) {
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
287
343
|
const ret = {};
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
344
|
+
for (const hint of options.populate) {
|
|
345
|
+
const field = hint.field.split(':')[0];
|
|
346
|
+
const prop = meta.properties[field];
|
|
347
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
348
|
+
const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
|
|
349
|
+
if (!joined && !hint.filter) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
const filters = QueryHelper.mergePropertyFilters(prop.filters, options.filters);
|
|
353
|
+
const where = await this.applyFilters(prop.type, {}, filters, 'read', {
|
|
354
|
+
...options,
|
|
355
|
+
populate: hint.children,
|
|
356
|
+
});
|
|
357
|
+
const where2 = await this.getJoinedFilters(prop.targetMeta, {
|
|
358
|
+
...options,
|
|
359
|
+
filters,
|
|
360
|
+
populate: hint.children,
|
|
361
|
+
populateWhere: PopulateHint.ALL,
|
|
362
|
+
});
|
|
363
|
+
if (Utils.hasObjectKeys(where)) {
|
|
364
|
+
ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
|
|
365
|
+
}
|
|
366
|
+
if (where2 && Utils.hasObjectKeys(where2)) {
|
|
367
|
+
if (ret[field]) {
|
|
368
|
+
Utils.merge(ret[field], where2);
|
|
300
369
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
Utils.merge(ret[field], where2);
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
ret[field] = where2;
|
|
307
|
-
}
|
|
370
|
+
else {
|
|
371
|
+
ret[field] = where2;
|
|
308
372
|
}
|
|
309
373
|
}
|
|
310
374
|
}
|
|
@@ -313,27 +377,45 @@ export class EntityManager {
|
|
|
313
377
|
/**
|
|
314
378
|
* When filters are active on M:1 or 1:1 relations, we need to ref join them eagerly as they might affect the FK value.
|
|
315
379
|
*/
|
|
316
|
-
async autoJoinRefsForFilters(meta, options) {
|
|
317
|
-
if (!meta || !this.config.get('autoJoinRefsForFilters')) {
|
|
380
|
+
async autoJoinRefsForFilters(meta, options, parent) {
|
|
381
|
+
if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
|
|
318
382
|
return;
|
|
319
383
|
}
|
|
320
|
-
const props = meta.relations.filter(prop => {
|
|
321
|
-
return !prop.object && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
322
|
-
&& ((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)));
|
|
323
|
-
});
|
|
324
384
|
const ret = options.populate;
|
|
325
|
-
for (const prop of
|
|
326
|
-
|
|
385
|
+
for (const prop of meta.relations) {
|
|
386
|
+
if (prop.object
|
|
387
|
+
|| ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
388
|
+
|| !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
389
|
+
|| (parent?.className === prop.targetMeta.root.className && parent.propName === prop.inversedBy)) {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
|
|
393
|
+
const cond = await this.applyFilters(prop.type, {}, options.filters, 'read', options);
|
|
327
394
|
if (!Utils.isEmpty(cond)) {
|
|
328
395
|
const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
|
|
329
|
-
|
|
330
|
-
|
|
396
|
+
let found = false;
|
|
397
|
+
for (const hint of populated) {
|
|
398
|
+
if (!hint.all) {
|
|
399
|
+
hint.filter = true;
|
|
400
|
+
}
|
|
401
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
402
|
+
if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === LoadStrategy.JOINED)) {
|
|
403
|
+
found = true;
|
|
404
|
+
}
|
|
331
405
|
}
|
|
332
|
-
|
|
406
|
+
if (!found) {
|
|
333
407
|
ret.push({ field: `${prop.name}:ref`, strategy: LoadStrategy.JOINED, filter: true });
|
|
334
408
|
}
|
|
335
409
|
}
|
|
336
410
|
}
|
|
411
|
+
for (const hint of ret) {
|
|
412
|
+
const [field, ref] = hint.field.split(':');
|
|
413
|
+
const prop = meta?.properties[field];
|
|
414
|
+
if (prop && !ref) {
|
|
415
|
+
hint.children ??= [];
|
|
416
|
+
await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { className: meta.root.className, propName: prop.name });
|
|
417
|
+
}
|
|
418
|
+
}
|
|
337
419
|
}
|
|
338
420
|
/**
|
|
339
421
|
* @internal
|
|
@@ -363,7 +445,7 @@ export class EntityManager {
|
|
|
363
445
|
let cond;
|
|
364
446
|
if (filter.cond instanceof Function) {
|
|
365
447
|
// @ts-ignore
|
|
366
|
-
const args = Utils.isPlainObject(options[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
|
|
448
|
+
const args = Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
|
|
367
449
|
if (!args && filter.cond.length > 0 && filter.args !== false) {
|
|
368
450
|
throw new Error(`No arguments provided for filter '${filter.name}'`);
|
|
369
451
|
}
|
|
@@ -372,13 +454,17 @@ export class EntityManager {
|
|
|
372
454
|
else {
|
|
373
455
|
cond = filter.cond;
|
|
374
456
|
}
|
|
375
|
-
|
|
457
|
+
cond = QueryHelper.processWhere({
|
|
376
458
|
where: cond,
|
|
377
459
|
entityName,
|
|
378
460
|
metadata: this.metadata,
|
|
379
461
|
platform: this.driver.getPlatform(),
|
|
380
462
|
aliased: type === 'read',
|
|
381
|
-
})
|
|
463
|
+
});
|
|
464
|
+
if (filter.strict) {
|
|
465
|
+
Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
|
|
466
|
+
}
|
|
467
|
+
ret.push(cond);
|
|
382
468
|
}
|
|
383
469
|
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
|
|
384
470
|
return conds.length > 1 ? { $and: conds } : conds[0];
|
|
@@ -433,6 +519,10 @@ export class EntityManager {
|
|
|
433
519
|
* });
|
|
434
520
|
* ```
|
|
435
521
|
*
|
|
522
|
+
* The options also support an `includeCount` (true by default) option. If set to false, the `totalCount` is not
|
|
523
|
+
* returned as part of the cursor. This is useful for performance reason, when you don't care about the total number
|
|
524
|
+
* of pages.
|
|
525
|
+
*
|
|
436
526
|
* The `Cursor` object provides the following interface:
|
|
437
527
|
*
|
|
438
528
|
* ```ts
|
|
@@ -442,7 +532,7 @@ export class EntityManager {
|
|
|
442
532
|
* User { ... },
|
|
443
533
|
* User { ... },
|
|
444
534
|
* ],
|
|
445
|
-
* totalCount: 50,
|
|
535
|
+
* totalCount: 50, // not included if `includeCount: false`
|
|
446
536
|
* startCursor: 'WzRd',
|
|
447
537
|
* endCursor: 'WzZd',
|
|
448
538
|
* hasPrevPage: true,
|
|
@@ -457,7 +547,9 @@ export class EntityManager {
|
|
|
457
547
|
if (Utils.isEmpty(options.orderBy)) {
|
|
458
548
|
throw new Error('Explicit `orderBy` option required');
|
|
459
549
|
}
|
|
460
|
-
const [entities, count] =
|
|
550
|
+
const [entities, count] = options.includeCount !== false
|
|
551
|
+
? await em.findAndCount(entityName, where, options)
|
|
552
|
+
: [await em.find(entityName, where, options)];
|
|
461
553
|
return new Cursor(entities, count, options, this.metadata.get(entityName));
|
|
462
554
|
}
|
|
463
555
|
/**
|
|
@@ -483,18 +575,31 @@ export class EntityManager {
|
|
|
483
575
|
async refresh(entity, options = {}) {
|
|
484
576
|
const fork = this.fork({ keepTransactionContext: true });
|
|
485
577
|
const entityName = entity.constructor.name;
|
|
578
|
+
const wrapped = helper(entity);
|
|
486
579
|
const reloaded = await fork.findOne(entityName, entity, {
|
|
487
|
-
schema:
|
|
580
|
+
schema: wrapped.__schema,
|
|
488
581
|
...options,
|
|
489
582
|
flushMode: FlushMode.COMMIT,
|
|
490
583
|
});
|
|
491
|
-
|
|
492
|
-
|
|
584
|
+
const em = this.getContext();
|
|
585
|
+
if (!reloaded) {
|
|
586
|
+
em.unitOfWork.unsetIdentity(entity);
|
|
587
|
+
return null;
|
|
493
588
|
}
|
|
494
|
-
|
|
495
|
-
|
|
589
|
+
let found = false;
|
|
590
|
+
for (const e of fork.unitOfWork.getIdentityMap()) {
|
|
591
|
+
const ref = em.getReference(e.constructor.name, helper(e).getPrimaryKey());
|
|
592
|
+
const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true });
|
|
593
|
+
em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, true);
|
|
594
|
+
Utils.merge(helper(ref).__originalEntityData, this.comparator.prepareEntity(e));
|
|
595
|
+
found ||= ref === entity;
|
|
596
|
+
}
|
|
597
|
+
if (!found) {
|
|
598
|
+
const data = helper(reloaded).serialize({ ignoreSerializers: true, includeHidden: true });
|
|
599
|
+
em.config.getHydrator(this.metadata).hydrate(entity, wrapped.__meta, data, em.entityFactory, 'full', false, true);
|
|
600
|
+
Utils.merge(wrapped.__originalEntityData, this.comparator.prepareEntity(reloaded));
|
|
496
601
|
}
|
|
497
|
-
return
|
|
602
|
+
return entity;
|
|
498
603
|
}
|
|
499
604
|
/**
|
|
500
605
|
* Finds first entity matching your `where` query.
|
|
@@ -530,23 +635,25 @@ export class EntityManager {
|
|
|
530
635
|
options.populate = await em.preparePopulate(entityName, options);
|
|
531
636
|
const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
|
|
532
637
|
const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
|
|
533
|
-
if (cached?.data) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
638
|
+
if (cached?.data !== undefined) {
|
|
639
|
+
if (cached.data) {
|
|
640
|
+
await em.entityLoader.populate(entityName, [cached.data], options.populate, {
|
|
641
|
+
...options,
|
|
642
|
+
...em.getPopulateWhere(where, options),
|
|
643
|
+
ignoreLazyScalarProperties: true,
|
|
644
|
+
lookup: false,
|
|
645
|
+
});
|
|
646
|
+
}
|
|
541
647
|
return cached.data;
|
|
542
648
|
}
|
|
543
649
|
options = { ...options };
|
|
544
650
|
// save the original hint value so we know it was infer/all
|
|
545
651
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
546
652
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
547
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
653
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
548
654
|
const data = await em.driver.findOne(entityName, where, {
|
|
549
655
|
ctx: em.transactionContext,
|
|
656
|
+
em,
|
|
550
657
|
...options,
|
|
551
658
|
});
|
|
552
659
|
if (!data) {
|
|
@@ -654,24 +761,7 @@ export class EntityManager {
|
|
|
654
761
|
}
|
|
655
762
|
}
|
|
656
763
|
}
|
|
657
|
-
|
|
658
|
-
const propIndex = !isRaw(unique) && unique.findIndex(p => data[p] != null);
|
|
659
|
-
if (options.onConflictFields || where == null) {
|
|
660
|
-
if (propIndex !== false && propIndex >= 0) {
|
|
661
|
-
where = { [unique[propIndex]]: data[unique[propIndex]] };
|
|
662
|
-
}
|
|
663
|
-
else if (meta.uniques.length > 0) {
|
|
664
|
-
for (const u of meta.uniques) {
|
|
665
|
-
if (Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
666
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
667
|
-
o[key] = data[key];
|
|
668
|
-
return o;
|
|
669
|
-
}, {});
|
|
670
|
-
break;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
}
|
|
764
|
+
where = getWhereCondition(meta, options.onConflictFields, data, where).where;
|
|
675
765
|
data = QueryHelper.processObjectParams(data);
|
|
676
766
|
em.validator.validateParams(data, 'insert data');
|
|
677
767
|
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
@@ -716,6 +806,7 @@ export class EntityManager {
|
|
|
716
806
|
ctx: em.transactionContext,
|
|
717
807
|
convertCustomTypes: true,
|
|
718
808
|
connectionType: 'write',
|
|
809
|
+
schema: options.schema,
|
|
719
810
|
});
|
|
720
811
|
em.getHydrator().hydrate(entity, meta, data2, em.entityFactory, 'full', false, true);
|
|
721
812
|
}
|
|
@@ -813,31 +904,17 @@ export class EntityManager {
|
|
|
813
904
|
}
|
|
814
905
|
}
|
|
815
906
|
}
|
|
816
|
-
const unique = meta.props.filter(p => p.unique).map(p => p.name);
|
|
817
|
-
propIndex = unique.findIndex(p =>
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
where = { [unique[propIndex]]: row[unique[propIndex]] };
|
|
821
|
-
}
|
|
822
|
-
else if (meta.uniques.length > 0) {
|
|
823
|
-
for (const u of meta.uniques) {
|
|
824
|
-
if (Utils.asArray(u.properties).every(p => row[p] != null)) {
|
|
825
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
826
|
-
o[key] = row[key];
|
|
827
|
-
return o;
|
|
828
|
-
}, {});
|
|
829
|
-
break;
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
row = QueryHelper.processObjectParams(row);
|
|
907
|
+
const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
|
|
908
|
+
propIndex = !isRaw(unique) && unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
|
|
909
|
+
const tmp = getWhereCondition(meta, options.onConflictFields, row, where);
|
|
910
|
+
propIndex = tmp.propIndex;
|
|
835
911
|
where = QueryHelper.processWhere({
|
|
836
|
-
where,
|
|
912
|
+
where: tmp.where,
|
|
837
913
|
entityName,
|
|
838
914
|
metadata: this.metadata,
|
|
839
915
|
platform: this.getPlatform(),
|
|
840
916
|
});
|
|
917
|
+
row = QueryHelper.processObjectParams(row);
|
|
841
918
|
em.validator.validateParams(row, 'insert data');
|
|
842
919
|
allData.push(row);
|
|
843
920
|
allWhere.push(where);
|
|
@@ -879,7 +956,7 @@ export class EntityManager {
|
|
|
879
956
|
const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
|
|
880
957
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
881
958
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
882
|
-
const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
|
|
959
|
+
const add = new Set(propIndex !== false && propIndex >= 0 ? [unique[propIndex]] : []);
|
|
883
960
|
for (const cond of loadPK.values()) {
|
|
884
961
|
Utils.keys(cond).forEach(key => add.add(key));
|
|
885
962
|
}
|
|
@@ -896,6 +973,7 @@ export class EntityManager {
|
|
|
896
973
|
ctx: em.transactionContext,
|
|
897
974
|
convertCustomTypes: true,
|
|
898
975
|
connectionType: 'write',
|
|
976
|
+
schema: options.schema,
|
|
899
977
|
});
|
|
900
978
|
for (const [entity, cond] of loadPK.entries()) {
|
|
901
979
|
const row = data2.find(row => {
|
|
@@ -952,45 +1030,37 @@ export class EntityManager {
|
|
|
952
1030
|
}
|
|
953
1031
|
/**
|
|
954
1032
|
* Runs your callback wrapped inside a database transaction.
|
|
1033
|
+
*
|
|
1034
|
+
* If a transaction is already active, a new savepoint (nested transaction) will be created by default. This behavior
|
|
1035
|
+
* can be controlled via the `propagation` option. Use the provided EntityManager instance for all operations that
|
|
1036
|
+
* should be part of the transaction. You can safely use a global EntityManager instance from a DI container, as this
|
|
1037
|
+
* method automatically creates an async context for the transaction.
|
|
1038
|
+
*
|
|
1039
|
+
* **Concurrency note:** When running multiple transactions concurrently (e.g. in parallel requests or jobs), use the
|
|
1040
|
+
* `clear: true` option. This ensures the callback runs in a clear fork of the EntityManager, providing full isolation
|
|
1041
|
+
* between concurrent transactional handlers. Using `clear: true` is an alternative to forking explicitly and calling
|
|
1042
|
+
* the method on the new fork – it already provides the necessary isolation for safe concurrent usage.
|
|
1043
|
+
*
|
|
1044
|
+
* **Propagation note:** Changes made within a transaction (whether top-level or nested) are always propagated to the
|
|
1045
|
+
* parent context, unless the parent context is a global one. If you want to avoid that, fork the EntityManager first
|
|
1046
|
+
* and then call this method on the fork.
|
|
1047
|
+
*
|
|
1048
|
+
* **Example:**
|
|
1049
|
+
* ```ts
|
|
1050
|
+
* await em.transactional(async (em) => {
|
|
1051
|
+
* const author = new Author('Jon');
|
|
1052
|
+
* em.persist(author);
|
|
1053
|
+
* // flush is called automatically at the end of the callback
|
|
1054
|
+
* });
|
|
1055
|
+
* ```
|
|
955
1056
|
*/
|
|
956
1057
|
async transactional(cb, options = {}) {
|
|
957
1058
|
const em = this.getContext(false);
|
|
958
1059
|
if (this.disableTransactions || em.disableTransactions) {
|
|
959
1060
|
return cb(em);
|
|
960
1061
|
}
|
|
961
|
-
const
|
|
962
|
-
|
|
963
|
-
flushMode: options.flushMode,
|
|
964
|
-
cloneEventManager: true,
|
|
965
|
-
disableTransactions: options.ignoreNestedTransactions,
|
|
966
|
-
loggerContext: options.loggerContext,
|
|
967
|
-
});
|
|
968
|
-
options.ctx ??= em.transactionContext;
|
|
969
|
-
const propagateToUpperContext = !em.global || this.config.get('allowGlobalContext');
|
|
970
|
-
return TransactionContext.create(fork, async () => {
|
|
971
|
-
return fork.getConnection().transactional(async (trx) => {
|
|
972
|
-
fork.transactionContext = trx;
|
|
973
|
-
if (propagateToUpperContext) {
|
|
974
|
-
fork.eventManager.registerSubscriber({
|
|
975
|
-
afterFlush(args) {
|
|
976
|
-
args.uow.getChangeSets()
|
|
977
|
-
.filter(cs => [ChangeSetType.DELETE, ChangeSetType.DELETE_EARLY].includes(cs.type))
|
|
978
|
-
.forEach(cs => em.unitOfWork.unsetIdentity(cs.entity));
|
|
979
|
-
},
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
|
-
const ret = await cb(fork);
|
|
983
|
-
await fork.flush();
|
|
984
|
-
if (propagateToUpperContext) {
|
|
985
|
-
// ensure all entities from inner context are merged to the upper one
|
|
986
|
-
for (const entity of fork.unitOfWork.getIdentityMap()) {
|
|
987
|
-
em.unitOfWork.register(entity);
|
|
988
|
-
entity.__helper.__em = em;
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
return ret;
|
|
992
|
-
}, { ...options, eventBroadcaster: new TransactionEventBroadcaster(fork, { topLevelTransaction: !options.ctx }) });
|
|
993
|
-
});
|
|
1062
|
+
const manager = new TransactionManager(this);
|
|
1063
|
+
return manager.handle(cb, options);
|
|
994
1064
|
}
|
|
995
1065
|
/**
|
|
996
1066
|
* Starts new transaction bound to this EntityManager. Use `ctx` parameter to provide the parent when nesting transactions.
|
|
@@ -1170,22 +1240,35 @@ export class EntityManager {
|
|
|
1170
1240
|
* via second parameter. By default, it will return already loaded entities without modifying them.
|
|
1171
1241
|
*/
|
|
1172
1242
|
merge(entityName, data, options = {}) {
|
|
1173
|
-
const em = this.getContext();
|
|
1174
1243
|
if (Utils.isEntity(entityName)) {
|
|
1175
|
-
return
|
|
1244
|
+
return this.merge(entityName.constructor.name, entityName, data);
|
|
1176
1245
|
}
|
|
1246
|
+
const em = options.disableContextResolution ? this : this.getContext();
|
|
1177
1247
|
options.schema ??= em._schema;
|
|
1248
|
+
options.validate ??= true;
|
|
1249
|
+
options.cascade ??= true;
|
|
1178
1250
|
entityName = Utils.className(entityName);
|
|
1179
|
-
|
|
1251
|
+
if (options.validate) {
|
|
1252
|
+
em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1253
|
+
}
|
|
1180
1254
|
let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
|
|
1181
1255
|
if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
|
|
1182
1256
|
return entity;
|
|
1183
1257
|
}
|
|
1184
1258
|
const meta = em.metadata.find(entityName);
|
|
1185
1259
|
const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1260
|
+
const dataIsEntity = Utils.isEntity(data);
|
|
1261
|
+
if (options.keepIdentity && entity && dataIsEntity && entity !== data) {
|
|
1262
|
+
helper(entity).__data = helper(data).__data;
|
|
1263
|
+
helper(entity).__originalEntityData = helper(data).__originalEntityData;
|
|
1264
|
+
return entity;
|
|
1265
|
+
}
|
|
1266
|
+
entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1267
|
+
if (options.validate) {
|
|
1268
|
+
em.validator.validate(entity, data, childMeta ?? meta);
|
|
1269
|
+
}
|
|
1270
|
+
const visited = options.cascade ? undefined : new Set([entity]);
|
|
1271
|
+
em.unitOfWork.merge(entity, visited);
|
|
1189
1272
|
return entity;
|
|
1190
1273
|
}
|
|
1191
1274
|
/**
|
|
@@ -1210,6 +1293,7 @@ export class EntityManager {
|
|
|
1210
1293
|
...options,
|
|
1211
1294
|
newEntity: !options.managed,
|
|
1212
1295
|
merge: options.managed,
|
|
1296
|
+
normalizeAccessors: true,
|
|
1213
1297
|
});
|
|
1214
1298
|
options.persist ??= em.config.get('persistOnCreate');
|
|
1215
1299
|
if (options.persist && !this.getMetadata(entityName).embeddable) {
|
|
@@ -1248,10 +1332,8 @@ export class EntityManager {
|
|
|
1248
1332
|
async count(entityName, where = {}, options = {}) {
|
|
1249
1333
|
const em = this.getContext(false);
|
|
1250
1334
|
// Shallow copy options since the object will be modified when deleting orderBy
|
|
1251
|
-
options = {
|
|
1252
|
-
|
|
1253
|
-
...options,
|
|
1254
|
-
};
|
|
1335
|
+
options = { ...options };
|
|
1336
|
+
em.prepareOptions(options);
|
|
1255
1337
|
entityName = Utils.className(entityName);
|
|
1256
1338
|
await em.tryFlush(entityName, options);
|
|
1257
1339
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
@@ -1261,15 +1343,15 @@ export class EntityManager {
|
|
|
1261
1343
|
const meta = em.metadata.find(entityName);
|
|
1262
1344
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
1263
1345
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
1264
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
1346
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
1265
1347
|
em.validator.validateParams(where);
|
|
1266
1348
|
delete options.orderBy;
|
|
1267
1349
|
const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
|
|
1268
1350
|
const cached = await em.tryCache(entityName, options.cache, cacheKey);
|
|
1269
|
-
if (cached?.data) {
|
|
1351
|
+
if (cached?.data !== undefined) {
|
|
1270
1352
|
return cached.data;
|
|
1271
1353
|
}
|
|
1272
|
-
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, ...options });
|
|
1354
|
+
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
1273
1355
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1274
1356
|
return +count;
|
|
1275
1357
|
}
|
|
@@ -1395,7 +1477,7 @@ export class EntityManager {
|
|
|
1395
1477
|
const em = this.getContext();
|
|
1396
1478
|
em.prepareOptions(options);
|
|
1397
1479
|
const entityName = arr[0].constructor.name;
|
|
1398
|
-
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
|
|
1480
|
+
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1399
1481
|
await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1400
1482
|
return entities;
|
|
1401
1483
|
}
|
|
@@ -1431,6 +1513,9 @@ export class EntityManager {
|
|
|
1431
1513
|
for (const entity of em.unitOfWork.getIdentityMap()) {
|
|
1432
1514
|
fork.unitOfWork.register(entity);
|
|
1433
1515
|
}
|
|
1516
|
+
for (const entity of em.unitOfWork.getPersistStack()) {
|
|
1517
|
+
fork.unitOfWork.persist(entity);
|
|
1518
|
+
}
|
|
1434
1519
|
for (const entity of em.unitOfWork.getOrphanRemoveStack()) {
|
|
1435
1520
|
fork.unitOfWork.getOrphanRemoveStack().add(entity);
|
|
1436
1521
|
}
|
|
@@ -1452,6 +1537,12 @@ export class EntityManager {
|
|
|
1452
1537
|
getEntityFactory() {
|
|
1453
1538
|
return this.getContext().entityFactory;
|
|
1454
1539
|
}
|
|
1540
|
+
/**
|
|
1541
|
+
* @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
|
|
1542
|
+
*/
|
|
1543
|
+
getEntityLoader() {
|
|
1544
|
+
return this.getContext().entityLoader;
|
|
1545
|
+
}
|
|
1455
1546
|
/**
|
|
1456
1547
|
* Gets the Hydrator used by the EntityManager.
|
|
1457
1548
|
*/
|
|
@@ -1548,7 +1639,6 @@ export class EntityManager {
|
|
|
1548
1639
|
...options,
|
|
1549
1640
|
...this.getPopulateWhere(where, options),
|
|
1550
1641
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
1551
|
-
convertCustomTypes: false,
|
|
1552
1642
|
ignoreLazyScalarProperties: true,
|
|
1553
1643
|
lookup: false,
|
|
1554
1644
|
});
|
|
@@ -1671,7 +1761,7 @@ export class EntityManager {
|
|
|
1671
1761
|
throw new ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
|
|
1672
1762
|
}
|
|
1673
1763
|
options.schema ??= this._schema;
|
|
1674
|
-
options.logging = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1764
|
+
options.logging = options.loggerContext = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1675
1765
|
}
|
|
1676
1766
|
/**
|
|
1677
1767
|
* @internal
|
|
@@ -1695,31 +1785,31 @@ export class EntityManager {
|
|
|
1695
1785
|
const em = this.getContext();
|
|
1696
1786
|
const cacheKey = Array.isArray(config) ? config[0] : JSON.stringify(key);
|
|
1697
1787
|
const cached = await em.resultCache.get(cacheKey);
|
|
1698
|
-
if (cached) {
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
return { key: cacheKey, data };
|
|
1788
|
+
if (!cached) {
|
|
1789
|
+
return { key: cacheKey, data: cached };
|
|
1790
|
+
}
|
|
1791
|
+
let data;
|
|
1792
|
+
if (Array.isArray(cached) && merge) {
|
|
1793
|
+
data = cached.map(item => em.entityFactory.create(entityName, item, {
|
|
1794
|
+
merge: true,
|
|
1795
|
+
convertCustomTypes: true,
|
|
1796
|
+
refresh,
|
|
1797
|
+
recomputeSnapshot: true,
|
|
1798
|
+
}));
|
|
1799
|
+
}
|
|
1800
|
+
else if (Utils.isObject(cached) && merge) {
|
|
1801
|
+
data = em.entityFactory.create(entityName, cached, {
|
|
1802
|
+
merge: true,
|
|
1803
|
+
convertCustomTypes: true,
|
|
1804
|
+
refresh,
|
|
1805
|
+
recomputeSnapshot: true,
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
else {
|
|
1809
|
+
data = cached;
|
|
1721
1810
|
}
|
|
1722
|
-
|
|
1811
|
+
await em.unitOfWork.dispatchOnLoadEvent();
|
|
1812
|
+
return { key: cacheKey, data };
|
|
1723
1813
|
}
|
|
1724
1814
|
/**
|
|
1725
1815
|
* @internal
|
|
@@ -1761,6 +1851,19 @@ export class EntityManager {
|
|
|
1761
1851
|
set schema(schema) {
|
|
1762
1852
|
this.getContext(false)._schema = schema ?? undefined;
|
|
1763
1853
|
}
|
|
1854
|
+
/** @internal */
|
|
1855
|
+
async getDataLoader(type) {
|
|
1856
|
+
const em = this.getContext();
|
|
1857
|
+
if (em.loaders[type]) {
|
|
1858
|
+
return em.loaders[type];
|
|
1859
|
+
}
|
|
1860
|
+
const DataLoader = await DataloaderUtils.getDataLoader();
|
|
1861
|
+
switch (type) {
|
|
1862
|
+
case 'ref': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
|
|
1863
|
+
case '1:m': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
|
|
1864
|
+
case 'm:n': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1764
1867
|
/**
|
|
1765
1868
|
* Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
|
|
1766
1869
|
* if executed inside request context handler.
|