@mikro-orm/core 7.0.0-dev.7 → 7.0.0-dev.71
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 -42
- package/EntityManager.js +282 -194
- package/MikroORM.d.ts +11 -29
- package/MikroORM.js +33 -127
- package/README.md +3 -2
- package/cache/FileCacheAdapter.js +1 -2
- package/connections/Connection.d.ts +11 -7
- package/connections/Connection.js +16 -14
- package/drivers/DatabaseDriver.d.ts +11 -5
- package/drivers/DatabaseDriver.js +13 -4
- package/drivers/IDatabaseDriver.d.ts +27 -5
- package/entity/BaseEntity.d.ts +0 -1
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +98 -30
- package/entity/Collection.js +432 -93
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +9 -1
- package/entity/EntityFactory.d.ts +7 -0
- package/entity/EntityFactory.js +63 -40
- package/entity/EntityHelper.js +26 -9
- package/entity/EntityLoader.d.ts +5 -4
- package/entity/EntityLoader.js +69 -36
- package/entity/EntityRepository.d.ts +1 -1
- package/entity/EntityValidator.js +4 -4
- 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 +549 -0
- package/entity/defineEntity.js +529 -0
- package/entity/index.d.ts +2 -1
- package/entity/index.js +2 -1
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +15 -3
- package/enums.d.ts +20 -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 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/SimpleLogger.d.ts +1 -1
- package/metadata/EntitySchema.d.ts +9 -13
- package/metadata/EntitySchema.js +44 -26
- package/metadata/MetadataDiscovery.d.ts +6 -7
- package/metadata/MetadataDiscovery.js +161 -162
- package/metadata/MetadataProvider.d.ts +2 -2
- package/metadata/MetadataProvider.js +15 -0
- package/metadata/MetadataStorage.d.ts +0 -4
- package/metadata/MetadataStorage.js +6 -10
- package/metadata/MetadataValidator.d.ts +0 -7
- package/metadata/MetadataValidator.js +4 -13
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +39 -0
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +1 -1
- package/metadata/types.d.ts +480 -0
- package/metadata/types.js +1 -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 +11 -10
- package/platforms/Platform.d.ts +6 -10
- package/platforms/Platform.js +6 -22
- 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/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +1 -2
- package/types/BigIntType.d.ts +8 -6
- 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 +112 -77
- package/typings.js +32 -32
- 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 +111 -54
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +10 -8
- package/utils/Configuration.d.ts +200 -191
- package/utils/Configuration.js +141 -152
- package/utils/ConfigurationLoader.d.ts +3 -44
- package/utils/ConfigurationLoader.js +26 -239
- 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 +107 -60
- package/utils/QueryHelper.d.ts +9 -1
- package/utils/QueryHelper.js +69 -8
- 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 +17 -84
- package/utils/Utils.js +132 -252
- 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/decorators/Check.d.ts +0 -3
- package/decorators/Check.js +0 -13
- package/decorators/CreateRequestContext.d.ts +0 -3
- package/decorators/CreateRequestContext.js +0 -32
- package/decorators/Embeddable.d.ts +0 -8
- package/decorators/Embeddable.js +0 -11
- package/decorators/Embedded.d.ts +0 -18
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -18
- package/decorators/Entity.js +0 -12
- package/decorators/Enum.d.ts +0 -9
- package/decorators/Enum.js +0 -16
- package/decorators/Filter.d.ts +0 -2
- package/decorators/Filter.js +0 -8
- package/decorators/Formula.d.ts +0 -4
- package/decorators/Formula.js +0 -15
- package/decorators/Indexed.d.ts +0 -19
- package/decorators/Indexed.js +0 -20
- package/decorators/ManyToMany.d.ts +0 -40
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -30
- package/decorators/ManyToOne.js +0 -14
- package/decorators/OneToMany.d.ts +0 -28
- package/decorators/OneToMany.js +0 -17
- package/decorators/OneToOne.d.ts +0 -24
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -8
- package/decorators/PrimaryKey.js +0 -20
- package/decorators/Property.d.ts +0 -250
- package/decorators/Property.js +0 -32
- package/decorators/Transactional.d.ts +0 -13
- package/decorators/Transactional.js +0 -28
- package/decorators/hooks.d.ts +0 -16
- package/decorators/hooks.js +0 -47
- package/decorators/index.d.ts +0 -17
- package/decorators/index.js +0 -17
- package/entity/ArrayCollection.d.ts +0 -116
- package/entity/ArrayCollection.js +0 -402
- package/metadata/ReflectMetadataProvider.d.ts +0 -8
- package/metadata/ReflectMetadataProvider.js +0 -44
- package/utils/resolveContextProvider.d.ts +0 -10
- package/utils/resolveContextProvider.js +0 -28
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,7 +147,7 @@ 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,
|
|
150
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
152
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, []);
|
|
@@ -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;
|
|
496
596
|
}
|
|
497
|
-
|
|
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));
|
|
601
|
+
}
|
|
602
|
+
return entity;
|
|
498
603
|
}
|
|
499
604
|
/**
|
|
500
605
|
* Finds first entity matching your `where` query.
|
|
@@ -522,7 +627,7 @@ export class EntityManager {
|
|
|
522
627
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
523
628
|
em.validator.validateEmptyWhere(where);
|
|
524
629
|
em.checkLockRequirements(options.lockMode, meta);
|
|
525
|
-
const isOptimisticLocking =
|
|
630
|
+
const isOptimisticLocking = options.lockMode == null || options.lockMode === LockMode.OPTIMISTIC;
|
|
526
631
|
if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
|
|
527
632
|
return em.lockAndPopulate(meta, entity, where, options);
|
|
528
633
|
}
|
|
@@ -530,21 +635,22 @@ 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,
|
|
550
656
|
em,
|
|
@@ -655,24 +761,7 @@ export class EntityManager {
|
|
|
655
761
|
}
|
|
656
762
|
}
|
|
657
763
|
}
|
|
658
|
-
|
|
659
|
-
const propIndex = !isRaw(unique) && unique.findIndex(p => data[p] != null);
|
|
660
|
-
if (options.onConflictFields || where == null) {
|
|
661
|
-
if (propIndex !== false && propIndex >= 0) {
|
|
662
|
-
where = { [unique[propIndex]]: data[unique[propIndex]] };
|
|
663
|
-
}
|
|
664
|
-
else if (meta.uniques.length > 0) {
|
|
665
|
-
for (const u of meta.uniques) {
|
|
666
|
-
if (Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
667
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
668
|
-
o[key] = data[key];
|
|
669
|
-
return o;
|
|
670
|
-
}, {});
|
|
671
|
-
break;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
}
|
|
764
|
+
where = getWhereCondition(meta, options.onConflictFields, data, where).where;
|
|
676
765
|
data = QueryHelper.processObjectParams(data);
|
|
677
766
|
em.validator.validateParams(data, 'insert data');
|
|
678
767
|
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
@@ -717,6 +806,7 @@ export class EntityManager {
|
|
|
717
806
|
ctx: em.transactionContext,
|
|
718
807
|
convertCustomTypes: true,
|
|
719
808
|
connectionType: 'write',
|
|
809
|
+
schema: options.schema,
|
|
720
810
|
});
|
|
721
811
|
em.getHydrator().hydrate(entity, meta, data2, em.entityFactory, 'full', false, true);
|
|
722
812
|
}
|
|
@@ -814,31 +904,17 @@ export class EntityManager {
|
|
|
814
904
|
}
|
|
815
905
|
}
|
|
816
906
|
}
|
|
817
|
-
const unique = meta.props.filter(p => p.unique).map(p => p.name);
|
|
818
|
-
propIndex = unique.findIndex(p =>
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
where = { [unique[propIndex]]: row[unique[propIndex]] };
|
|
822
|
-
}
|
|
823
|
-
else if (meta.uniques.length > 0) {
|
|
824
|
-
for (const u of meta.uniques) {
|
|
825
|
-
if (Utils.asArray(u.properties).every(p => row[p] != null)) {
|
|
826
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
827
|
-
o[key] = row[key];
|
|
828
|
-
return o;
|
|
829
|
-
}, {});
|
|
830
|
-
break;
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
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;
|
|
836
911
|
where = QueryHelper.processWhere({
|
|
837
|
-
where,
|
|
912
|
+
where: tmp.where,
|
|
838
913
|
entityName,
|
|
839
914
|
metadata: this.metadata,
|
|
840
915
|
platform: this.getPlatform(),
|
|
841
916
|
});
|
|
917
|
+
row = QueryHelper.processObjectParams(row);
|
|
842
918
|
em.validator.validateParams(row, 'insert data');
|
|
843
919
|
allData.push(row);
|
|
844
920
|
allWhere.push(where);
|
|
@@ -880,7 +956,7 @@ export class EntityManager {
|
|
|
880
956
|
const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
|
|
881
957
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
882
958
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
883
|
-
const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
|
|
959
|
+
const add = new Set(propIndex !== false && propIndex >= 0 ? [unique[propIndex]] : []);
|
|
884
960
|
for (const cond of loadPK.values()) {
|
|
885
961
|
Utils.keys(cond).forEach(key => add.add(key));
|
|
886
962
|
}
|
|
@@ -897,6 +973,7 @@ export class EntityManager {
|
|
|
897
973
|
ctx: em.transactionContext,
|
|
898
974
|
convertCustomTypes: true,
|
|
899
975
|
connectionType: 'write',
|
|
976
|
+
schema: options.schema,
|
|
900
977
|
});
|
|
901
978
|
for (const [entity, cond] of loadPK.entries()) {
|
|
902
979
|
const row = data2.find(row => {
|
|
@@ -953,45 +1030,37 @@ export class EntityManager {
|
|
|
953
1030
|
}
|
|
954
1031
|
/**
|
|
955
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
|
+
* ```
|
|
956
1056
|
*/
|
|
957
1057
|
async transactional(cb, options = {}) {
|
|
958
1058
|
const em = this.getContext(false);
|
|
959
1059
|
if (this.disableTransactions || em.disableTransactions) {
|
|
960
1060
|
return cb(em);
|
|
961
1061
|
}
|
|
962
|
-
const
|
|
963
|
-
|
|
964
|
-
flushMode: options.flushMode,
|
|
965
|
-
cloneEventManager: true,
|
|
966
|
-
disableTransactions: options.ignoreNestedTransactions,
|
|
967
|
-
loggerContext: options.loggerContext,
|
|
968
|
-
});
|
|
969
|
-
options.ctx ??= em.transactionContext;
|
|
970
|
-
const propagateToUpperContext = !em.global || this.config.get('allowGlobalContext');
|
|
971
|
-
return TransactionContext.create(fork, async () => {
|
|
972
|
-
return fork.getConnection().transactional(async (trx) => {
|
|
973
|
-
fork.transactionContext = trx;
|
|
974
|
-
if (propagateToUpperContext) {
|
|
975
|
-
fork.eventManager.registerSubscriber({
|
|
976
|
-
afterFlush(args) {
|
|
977
|
-
args.uow.getChangeSets()
|
|
978
|
-
.filter(cs => [ChangeSetType.DELETE, ChangeSetType.DELETE_EARLY].includes(cs.type))
|
|
979
|
-
.forEach(cs => em.unitOfWork.unsetIdentity(cs.entity));
|
|
980
|
-
},
|
|
981
|
-
});
|
|
982
|
-
}
|
|
983
|
-
const ret = await cb(fork);
|
|
984
|
-
await fork.flush();
|
|
985
|
-
if (propagateToUpperContext) {
|
|
986
|
-
// ensure all entities from inner context are merged to the upper one
|
|
987
|
-
for (const entity of fork.unitOfWork.getIdentityMap()) {
|
|
988
|
-
em.unitOfWork.register(entity);
|
|
989
|
-
entity.__helper.__em = em;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
return ret;
|
|
993
|
-
}, { ...options, eventBroadcaster: new TransactionEventBroadcaster(fork, { topLevelTransaction: !options.ctx }) });
|
|
994
|
-
});
|
|
1062
|
+
const manager = new TransactionManager(this);
|
|
1063
|
+
return manager.handle(cb, options);
|
|
995
1064
|
}
|
|
996
1065
|
/**
|
|
997
1066
|
* Starts new transaction bound to this EntityManager. Use `ctx` parameter to provide the parent when nesting transactions.
|
|
@@ -1171,22 +1240,35 @@ export class EntityManager {
|
|
|
1171
1240
|
* via second parameter. By default, it will return already loaded entities without modifying them.
|
|
1172
1241
|
*/
|
|
1173
1242
|
merge(entityName, data, options = {}) {
|
|
1174
|
-
const em = this.getContext();
|
|
1175
1243
|
if (Utils.isEntity(entityName)) {
|
|
1176
|
-
return
|
|
1244
|
+
return this.merge(entityName.constructor.name, entityName, data);
|
|
1177
1245
|
}
|
|
1246
|
+
const em = options.disableContextResolution ? this : this.getContext();
|
|
1178
1247
|
options.schema ??= em._schema;
|
|
1248
|
+
options.validate ??= true;
|
|
1249
|
+
options.cascade ??= true;
|
|
1179
1250
|
entityName = Utils.className(entityName);
|
|
1180
|
-
|
|
1251
|
+
if (options.validate) {
|
|
1252
|
+
em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1253
|
+
}
|
|
1181
1254
|
let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
|
|
1182
1255
|
if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
|
|
1183
1256
|
return entity;
|
|
1184
1257
|
}
|
|
1185
1258
|
const meta = em.metadata.find(entityName);
|
|
1186
1259
|
const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
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);
|
|
1190
1272
|
return entity;
|
|
1191
1273
|
}
|
|
1192
1274
|
/**
|
|
@@ -1211,6 +1293,7 @@ export class EntityManager {
|
|
|
1211
1293
|
...options,
|
|
1212
1294
|
newEntity: !options.managed,
|
|
1213
1295
|
merge: options.managed,
|
|
1296
|
+
normalizeAccessors: true,
|
|
1214
1297
|
});
|
|
1215
1298
|
options.persist ??= em.config.get('persistOnCreate');
|
|
1216
1299
|
if (options.persist && !this.getMetadata(entityName).embeddable) {
|
|
@@ -1249,10 +1332,8 @@ export class EntityManager {
|
|
|
1249
1332
|
async count(entityName, where = {}, options = {}) {
|
|
1250
1333
|
const em = this.getContext(false);
|
|
1251
1334
|
// Shallow copy options since the object will be modified when deleting orderBy
|
|
1252
|
-
options = {
|
|
1253
|
-
|
|
1254
|
-
...options,
|
|
1255
|
-
};
|
|
1335
|
+
options = { ...options };
|
|
1336
|
+
em.prepareOptions(options);
|
|
1256
1337
|
entityName = Utils.className(entityName);
|
|
1257
1338
|
await em.tryFlush(entityName, options);
|
|
1258
1339
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
@@ -1262,15 +1343,15 @@ export class EntityManager {
|
|
|
1262
1343
|
const meta = em.metadata.find(entityName);
|
|
1263
1344
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
1264
1345
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
1265
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
1346
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
1266
1347
|
em.validator.validateParams(where);
|
|
1267
1348
|
delete options.orderBy;
|
|
1268
1349
|
const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
|
|
1269
1350
|
const cached = await em.tryCache(entityName, options.cache, cacheKey);
|
|
1270
|
-
if (cached?.data) {
|
|
1351
|
+
if (cached?.data !== undefined) {
|
|
1271
1352
|
return cached.data;
|
|
1272
1353
|
}
|
|
1273
|
-
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 });
|
|
1274
1355
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1275
1356
|
return +count;
|
|
1276
1357
|
}
|
|
@@ -1297,13 +1378,6 @@ export class EntityManager {
|
|
|
1297
1378
|
}
|
|
1298
1379
|
return this;
|
|
1299
1380
|
}
|
|
1300
|
-
/**
|
|
1301
|
-
* Persists your entity immediately, flushing all not yet persisted changes to the database too.
|
|
1302
|
-
* Equivalent to `em.persist(e).flush()`.
|
|
1303
|
-
*/
|
|
1304
|
-
async persistAndFlush(entity) {
|
|
1305
|
-
await this.persist(entity).flush();
|
|
1306
|
-
}
|
|
1307
1381
|
/**
|
|
1308
1382
|
* Marks entity for removal.
|
|
1309
1383
|
* A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
|
|
@@ -1327,13 +1401,6 @@ export class EntityManager {
|
|
|
1327
1401
|
}
|
|
1328
1402
|
return em;
|
|
1329
1403
|
}
|
|
1330
|
-
/**
|
|
1331
|
-
* Removes an entity instance immediately, flushing all not yet persisted changes to the database too.
|
|
1332
|
-
* Equivalent to `em.remove(e).flush()`
|
|
1333
|
-
*/
|
|
1334
|
-
async removeAndFlush(entity) {
|
|
1335
|
-
await this.remove(entity).flush();
|
|
1336
|
-
}
|
|
1337
1404
|
/**
|
|
1338
1405
|
* Flushes all changes to objects that have been queued up to now to the database.
|
|
1339
1406
|
* This effectively synchronizes the in-memory state of managed objects with the database.
|
|
@@ -1396,7 +1463,7 @@ export class EntityManager {
|
|
|
1396
1463
|
const em = this.getContext();
|
|
1397
1464
|
em.prepareOptions(options);
|
|
1398
1465
|
const entityName = arr[0].constructor.name;
|
|
1399
|
-
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
|
|
1466
|
+
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1400
1467
|
await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1401
1468
|
return entities;
|
|
1402
1469
|
}
|
|
@@ -1432,6 +1499,9 @@ export class EntityManager {
|
|
|
1432
1499
|
for (const entity of em.unitOfWork.getIdentityMap()) {
|
|
1433
1500
|
fork.unitOfWork.register(entity);
|
|
1434
1501
|
}
|
|
1502
|
+
for (const entity of em.unitOfWork.getPersistStack()) {
|
|
1503
|
+
fork.unitOfWork.persist(entity);
|
|
1504
|
+
}
|
|
1435
1505
|
for (const entity of em.unitOfWork.getOrphanRemoveStack()) {
|
|
1436
1506
|
fork.unitOfWork.getOrphanRemoveStack().add(entity);
|
|
1437
1507
|
}
|
|
@@ -1453,6 +1523,12 @@ export class EntityManager {
|
|
|
1453
1523
|
getEntityFactory() {
|
|
1454
1524
|
return this.getContext().entityFactory;
|
|
1455
1525
|
}
|
|
1526
|
+
/**
|
|
1527
|
+
* @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
|
|
1528
|
+
*/
|
|
1529
|
+
getEntityLoader() {
|
|
1530
|
+
return this.getContext().entityLoader;
|
|
1531
|
+
}
|
|
1456
1532
|
/**
|
|
1457
1533
|
* Gets the Hydrator used by the EntityManager.
|
|
1458
1534
|
*/
|
|
@@ -1549,7 +1625,6 @@ export class EntityManager {
|
|
|
1549
1625
|
...options,
|
|
1550
1626
|
...this.getPopulateWhere(where, options),
|
|
1551
1627
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
1552
|
-
convertCustomTypes: false,
|
|
1553
1628
|
ignoreLazyScalarProperties: true,
|
|
1554
1629
|
lookup: false,
|
|
1555
1630
|
});
|
|
@@ -1622,7 +1697,7 @@ export class EntityManager {
|
|
|
1622
1697
|
options.flags.push(QueryFlag.INFER_POPULATE);
|
|
1623
1698
|
return [];
|
|
1624
1699
|
}
|
|
1625
|
-
if (
|
|
1700
|
+
if (typeof field === 'string') {
|
|
1626
1701
|
return [{ field, strategy: options.strategy }];
|
|
1627
1702
|
}
|
|
1628
1703
|
return [field];
|
|
@@ -1672,7 +1747,7 @@ export class EntityManager {
|
|
|
1672
1747
|
throw new ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
|
|
1673
1748
|
}
|
|
1674
1749
|
options.schema ??= this._schema;
|
|
1675
|
-
options.logging = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1750
|
+
options.logging = options.loggerContext = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1676
1751
|
}
|
|
1677
1752
|
/**
|
|
1678
1753
|
* @internal
|
|
@@ -1696,31 +1771,31 @@ export class EntityManager {
|
|
|
1696
1771
|
const em = this.getContext();
|
|
1697
1772
|
const cacheKey = Array.isArray(config) ? config[0] : JSON.stringify(key);
|
|
1698
1773
|
const cached = await em.resultCache.get(cacheKey);
|
|
1699
|
-
if (cached) {
|
|
1700
|
-
|
|
1701
|
-
if (Array.isArray(cached) && merge) {
|
|
1702
|
-
data = cached.map(item => em.entityFactory.create(entityName, item, {
|
|
1703
|
-
merge: true,
|
|
1704
|
-
convertCustomTypes: true,
|
|
1705
|
-
refresh,
|
|
1706
|
-
recomputeSnapshot: true,
|
|
1707
|
-
}));
|
|
1708
|
-
}
|
|
1709
|
-
else if (Utils.isObject(cached) && merge) {
|
|
1710
|
-
data = em.entityFactory.create(entityName, cached, {
|
|
1711
|
-
merge: true,
|
|
1712
|
-
convertCustomTypes: true,
|
|
1713
|
-
refresh,
|
|
1714
|
-
recomputeSnapshot: true,
|
|
1715
|
-
});
|
|
1716
|
-
}
|
|
1717
|
-
else {
|
|
1718
|
-
data = cached;
|
|
1719
|
-
}
|
|
1720
|
-
await em.unitOfWork.dispatchOnLoadEvent();
|
|
1721
|
-
return { key: cacheKey, data };
|
|
1774
|
+
if (!cached) {
|
|
1775
|
+
return { key: cacheKey, data: cached };
|
|
1722
1776
|
}
|
|
1723
|
-
|
|
1777
|
+
let data;
|
|
1778
|
+
if (Array.isArray(cached) && merge) {
|
|
1779
|
+
data = cached.map(item => em.entityFactory.create(entityName, item, {
|
|
1780
|
+
merge: true,
|
|
1781
|
+
convertCustomTypes: true,
|
|
1782
|
+
refresh,
|
|
1783
|
+
recomputeSnapshot: true,
|
|
1784
|
+
}));
|
|
1785
|
+
}
|
|
1786
|
+
else if (Utils.isObject(cached) && merge) {
|
|
1787
|
+
data = em.entityFactory.create(entityName, cached, {
|
|
1788
|
+
merge: true,
|
|
1789
|
+
convertCustomTypes: true,
|
|
1790
|
+
refresh,
|
|
1791
|
+
recomputeSnapshot: true,
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
else {
|
|
1795
|
+
data = cached;
|
|
1796
|
+
}
|
|
1797
|
+
await em.unitOfWork.dispatchOnLoadEvent();
|
|
1798
|
+
return { key: cacheKey, data };
|
|
1724
1799
|
}
|
|
1725
1800
|
/**
|
|
1726
1801
|
* @internal
|
|
@@ -1729,7 +1804,7 @@ export class EntityManager {
|
|
|
1729
1804
|
config ??= this.config.get('resultCache').global;
|
|
1730
1805
|
if (config) {
|
|
1731
1806
|
const em = this.getContext();
|
|
1732
|
-
const expiration = Array.isArray(config) ? config[1] : (
|
|
1807
|
+
const expiration = Array.isArray(config) ? config[1] : (typeof config === 'number' ? config : undefined);
|
|
1733
1808
|
await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
|
|
1734
1809
|
}
|
|
1735
1810
|
}
|
|
@@ -1762,6 +1837,19 @@ export class EntityManager {
|
|
|
1762
1837
|
set schema(schema) {
|
|
1763
1838
|
this.getContext(false)._schema = schema ?? undefined;
|
|
1764
1839
|
}
|
|
1840
|
+
/** @internal */
|
|
1841
|
+
async getDataLoader(type) {
|
|
1842
|
+
const em = this.getContext();
|
|
1843
|
+
if (em.loaders[type]) {
|
|
1844
|
+
return em.loaders[type];
|
|
1845
|
+
}
|
|
1846
|
+
const DataLoader = await DataloaderUtils.getDataLoader();
|
|
1847
|
+
switch (type) {
|
|
1848
|
+
case 'ref': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
|
|
1849
|
+
case '1:m': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
|
|
1850
|
+
case 'm:n': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1765
1853
|
/**
|
|
1766
1854
|
* Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
|
|
1767
1855
|
* if executed inside request context handler.
|