@mikro-orm/core 7.0.0-dev.1 → 7.0.0-dev.100
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 +96 -58
- package/EntityManager.js +465 -395
- package/MikroORM.d.ts +45 -35
- package/MikroORM.js +109 -160
- package/README.md +3 -2
- package/cache/CacheAdapter.js +1 -2
- package/cache/FileCacheAdapter.d.ts +2 -2
- package/cache/FileCacheAdapter.js +20 -27
- package/cache/GeneratedCacheAdapter.d.ts +2 -3
- package/cache/GeneratedCacheAdapter.js +1 -7
- package/cache/MemoryCacheAdapter.d.ts +1 -1
- package/cache/MemoryCacheAdapter.js +1 -5
- package/cache/NullCacheAdapter.d.ts +1 -1
- package/cache/NullCacheAdapter.js +1 -5
- package/cache/index.d.ts +4 -5
- package/cache/index.js +4 -21
- package/connections/Connection.d.ts +22 -14
- package/connections/Connection.js +27 -23
- package/connections/index.d.ts +1 -1
- package/connections/index.js +1 -17
- package/drivers/DatabaseDriver.d.ts +25 -15
- package/drivers/DatabaseDriver.js +77 -64
- package/drivers/IDatabaseDriver.d.ts +40 -16
- package/drivers/IDatabaseDriver.js +1 -4
- package/drivers/index.d.ts +2 -2
- package/drivers/index.js +2 -18
- package/entity/BaseEntity.d.ts +6 -7
- package/entity/BaseEntity.js +16 -23
- package/entity/Collection.d.ts +98 -34
- package/entity/Collection.js +466 -131
- package/entity/EntityAssigner.d.ts +3 -3
- package/entity/EntityAssigner.js +67 -64
- package/entity/EntityFactory.d.ts +10 -3
- package/entity/EntityFactory.js +112 -91
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +66 -53
- package/entity/EntityIdentifier.d.ts +1 -1
- package/entity/EntityIdentifier.js +1 -5
- package/entity/EntityLoader.d.ts +8 -7
- package/entity/EntityLoader.js +161 -128
- package/entity/EntityRepository.d.ts +8 -8
- package/entity/EntityRepository.js +7 -11
- package/entity/Reference.d.ts +10 -13
- package/entity/Reference.js +64 -46
- package/entity/WrappedEntity.d.ts +12 -17
- package/entity/WrappedEntity.js +22 -31
- package/entity/defineEntity.d.ts +568 -0
- package/entity/defineEntity.js +529 -0
- package/entity/index.d.ts +14 -13
- package/entity/index.js +14 -29
- package/entity/utils.d.ts +8 -1
- package/entity/utils.js +22 -13
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +65 -0
- package/entity/wrap.d.ts +1 -1
- package/entity/wrap.js +2 -6
- package/enums.d.ts +24 -9
- package/enums.js +50 -41
- package/errors.d.ts +11 -3
- package/errors.js +42 -32
- package/events/EventManager.d.ts +5 -4
- package/events/EventManager.js +26 -22
- package/events/EventSubscriber.d.ts +8 -5
- package/events/EventSubscriber.js +1 -2
- package/events/TransactionEventBroadcaster.d.ts +3 -3
- package/events/TransactionEventBroadcaster.js +1 -5
- package/events/index.d.ts +3 -3
- package/events/index.js +3 -19
- package/exceptions.js +18 -39
- package/hydration/Hydrator.d.ts +5 -5
- package/hydration/Hydrator.js +2 -6
- package/hydration/ObjectHydrator.d.ts +7 -7
- package/hydration/ObjectHydrator.js +58 -50
- package/hydration/index.d.ts +2 -2
- package/hydration/index.js +2 -18
- package/index.d.ts +21 -21
- package/index.js +20 -46
- package/logging/DefaultLogger.d.ts +2 -2
- package/logging/DefaultLogger.js +10 -13
- package/logging/Logger.d.ts +1 -1
- package/logging/Logger.js +1 -2
- package/logging/SimpleLogger.d.ts +3 -3
- package/logging/SimpleLogger.js +2 -6
- package/logging/colors.js +1 -5
- package/logging/index.d.ts +5 -4
- package/logging/index.js +5 -20
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +16 -0
- package/metadata/EntitySchema.d.ts +14 -10
- package/metadata/EntitySchema.js +78 -64
- package/metadata/MetadataDiscovery.d.ts +11 -14
- package/metadata/MetadataDiscovery.js +278 -317
- package/metadata/MetadataProvider.d.ts +13 -4
- package/metadata/MetadataProvider.js +47 -8
- package/metadata/MetadataStorage.d.ts +2 -7
- package/metadata/MetadataStorage.js +19 -35
- package/metadata/MetadataValidator.d.ts +3 -10
- package/metadata/MetadataValidator.js +51 -64
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +40 -0
- package/metadata/index.d.ts +6 -6
- package/metadata/index.js +6 -22
- package/metadata/types.d.ts +480 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +7 -3
- package/naming-strategy/AbstractNamingStrategy.js +11 -9
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +1 -1
- package/naming-strategy/EntityCaseNamingStrategy.js +2 -6
- package/naming-strategy/MongoNamingStrategy.d.ts +1 -1
- package/naming-strategy/MongoNamingStrategy.js +2 -6
- package/naming-strategy/NamingStrategy.d.ts +12 -2
- package/naming-strategy/NamingStrategy.js +1 -2
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +1 -1
- package/naming-strategy/UnderscoreNamingStrategy.js +2 -6
- package/naming-strategy/index.d.ts +5 -5
- package/naming-strategy/index.js +5 -21
- package/not-supported.d.ts +2 -0
- package/not-supported.js +4 -0
- package/package.json +19 -20
- package/platforms/ExceptionConverter.d.ts +2 -2
- package/platforms/ExceptionConverter.js +4 -8
- package/platforms/Platform.d.ts +15 -22
- package/platforms/Platform.js +58 -88
- package/platforms/index.d.ts +2 -2
- package/platforms/index.js +2 -18
- package/serialization/EntitySerializer.d.ts +4 -2
- package/serialization/EntitySerializer.js +64 -51
- package/serialization/EntityTransformer.d.ts +1 -1
- package/serialization/EntityTransformer.js +48 -42
- package/serialization/SerializationContext.d.ts +2 -2
- package/serialization/SerializationContext.js +24 -25
- package/serialization/index.d.ts +3 -3
- package/serialization/index.js +3 -19
- package/types/ArrayType.d.ts +3 -3
- package/types/ArrayType.js +6 -11
- package/types/BigIntType.d.ts +12 -9
- package/types/BigIntType.js +6 -6
- package/types/BlobType.d.ts +3 -4
- package/types/BlobType.js +2 -11
- package/types/BooleanType.d.ts +5 -4
- package/types/BooleanType.js +5 -6
- package/types/CharacterType.d.ts +3 -3
- package/types/CharacterType.js +2 -6
- package/types/DateTimeType.d.ts +3 -3
- package/types/DateTimeType.js +2 -6
- package/types/DateType.d.ts +3 -3
- package/types/DateType.js +2 -6
- package/types/DecimalType.d.ts +9 -7
- package/types/DecimalType.js +5 -8
- package/types/DoubleType.d.ts +3 -3
- package/types/DoubleType.js +4 -7
- package/types/EnumArrayType.d.ts +4 -4
- package/types/EnumArrayType.js +4 -10
- package/types/EnumType.d.ts +3 -3
- package/types/EnumType.js +2 -6
- package/types/FloatType.d.ts +3 -3
- package/types/FloatType.js +2 -6
- package/types/IntegerType.d.ts +3 -3
- package/types/IntegerType.js +2 -6
- package/types/IntervalType.d.ts +3 -3
- package/types/IntervalType.js +2 -6
- package/types/JsonType.d.ts +4 -4
- package/types/JsonType.js +9 -8
- package/types/MediumIntType.d.ts +3 -3
- package/types/MediumIntType.js +2 -6
- package/types/SmallIntType.d.ts +3 -3
- package/types/SmallIntType.js +2 -6
- package/types/StringType.d.ts +3 -3
- package/types/StringType.js +2 -6
- package/types/TextType.d.ts +3 -3
- package/types/TextType.js +2 -6
- package/types/TimeType.d.ts +3 -3
- package/types/TimeType.js +4 -8
- package/types/TinyIntType.d.ts +3 -3
- package/types/TinyIntType.js +3 -6
- package/types/Type.d.ts +4 -6
- package/types/Type.js +6 -10
- package/types/Uint8ArrayType.d.ts +3 -4
- package/types/Uint8ArrayType.js +3 -12
- package/types/UnknownType.d.ts +3 -3
- package/types/UnknownType.js +2 -6
- package/types/UuidType.d.ts +3 -3
- package/types/UuidType.js +2 -6
- package/types/index.d.ts +25 -25
- package/types/index.js +52 -79
- package/typings.d.ts +134 -93
- package/typings.js +67 -65
- package/unit-of-work/ChangeSet.d.ts +1 -4
- package/unit-of-work/ChangeSet.js +13 -17
- package/unit-of-work/ChangeSetComputer.d.ts +8 -9
- package/unit-of-work/ChangeSetComputer.js +36 -38
- package/unit-of-work/ChangeSetPersister.d.ts +11 -9
- package/unit-of-work/ChangeSetPersister.js +100 -65
- package/unit-of-work/CommitOrderCalculator.d.ts +1 -1
- package/unit-of-work/CommitOrderCalculator.js +6 -10
- package/unit-of-work/IdentityMap.d.ts +1 -1
- package/unit-of-work/IdentityMap.js +1 -5
- package/unit-of-work/UnitOfWork.d.ts +16 -8
- package/unit-of-work/UnitOfWork.js +266 -209
- package/unit-of-work/index.d.ts +6 -6
- package/unit-of-work/index.js +6 -22
- package/utils/AbstractSchemaGenerator.d.ts +11 -11
- package/utils/AbstractSchemaGenerator.js +21 -20
- package/utils/Configuration.d.ts +774 -224
- package/utils/Configuration.js +166 -216
- package/utils/ConfigurationLoader.d.ts +1 -53
- package/utils/ConfigurationLoader.js +1 -367
- package/utils/Cursor.d.ts +6 -9
- package/utils/Cursor.js +25 -25
- package/utils/DataloaderUtils.d.ts +18 -8
- package/utils/DataloaderUtils.js +63 -21
- package/utils/EntityComparator.d.ts +9 -5
- package/utils/EntityComparator.js +155 -108
- package/utils/NullHighlighter.d.ts +1 -1
- package/utils/NullHighlighter.js +1 -5
- package/utils/QueryHelper.d.ts +12 -4
- package/utils/QueryHelper.js +110 -53
- package/utils/RawQueryFragment.d.ts +37 -14
- package/utils/RawQueryFragment.js +50 -33
- package/utils/RequestContext.d.ts +2 -2
- package/utils/RequestContext.js +3 -7
- package/utils/TransactionContext.d.ts +1 -1
- package/utils/TransactionContext.js +4 -8
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +223 -0
- package/utils/Utils.d.ts +16 -100
- package/utils/Utils.js +114 -332
- package/utils/clone.js +7 -11
- package/utils/env-vars.d.ts +3 -0
- package/utils/env-vars.js +87 -0
- package/utils/fs-utils.d.ts +12 -0
- package/utils/fs-utils.js +97 -0
- package/utils/index.d.ts +14 -13
- package/utils/index.js +14 -29
- package/utils/upsert-utils.d.ts +8 -3
- package/utils/upsert-utils.js +57 -10
- package/decorators/Check.d.ts +0 -3
- package/decorators/Check.js +0 -16
- package/decorators/CreateRequestContext.d.ts +0 -3
- package/decorators/CreateRequestContext.js +0 -33
- package/decorators/Embeddable.d.ts +0 -8
- package/decorators/Embeddable.js +0 -14
- package/decorators/Embedded.d.ts +0 -18
- package/decorators/Embedded.js +0 -20
- package/decorators/Entity.d.ts +0 -18
- package/decorators/Entity.js +0 -16
- package/decorators/Enum.d.ts +0 -9
- package/decorators/Enum.js +0 -19
- package/decorators/Filter.d.ts +0 -2
- package/decorators/Filter.js +0 -11
- package/decorators/Formula.d.ts +0 -5
- package/decorators/Formula.js +0 -18
- package/decorators/Indexed.d.ts +0 -17
- package/decorators/Indexed.js +0 -24
- package/decorators/ManyToMany.d.ts +0 -40
- package/decorators/ManyToMany.js +0 -16
- package/decorators/ManyToOne.d.ts +0 -30
- package/decorators/ManyToOne.js +0 -16
- package/decorators/OneToMany.d.ts +0 -28
- package/decorators/OneToMany.js +0 -20
- package/decorators/OneToOne.d.ts +0 -24
- package/decorators/OneToOne.js +0 -10
- package/decorators/PrimaryKey.d.ts +0 -9
- package/decorators/PrimaryKey.js +0 -23
- package/decorators/Property.d.ts +0 -250
- package/decorators/Property.js +0 -34
- package/decorators/Transactional.d.ts +0 -13
- package/decorators/Transactional.js +0 -31
- package/decorators/hooks.d.ts +0 -16
- package/decorators/hooks.js +0 -59
- package/decorators/index.d.ts +0 -17
- package/decorators/index.js +0 -36
- package/entity/ArrayCollection.d.ts +0 -116
- package/entity/ArrayCollection.js +0 -399
- package/entity/EntityValidator.d.ts +0 -19
- package/entity/EntityValidator.js +0 -154
- package/index.mjs +0 -199
- package/metadata/ReflectMetadataProvider.d.ts +0 -8
- package/metadata/ReflectMetadataProvider.js +0 -48
- package/utils/resolveContextProvider.d.ts +0 -10
- package/utils/resolveContextProvider.js +0 -31
package/EntityManager.js
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
import { getOnConflictReturningFields, getWhereCondition } from './utils/upsert-utils.js';
|
|
2
|
+
import { Utils } from './utils/Utils.js';
|
|
3
|
+
import { Cursor } from './utils/Cursor.js';
|
|
4
|
+
import { DataloaderUtils } from './utils/DataloaderUtils.js';
|
|
5
|
+
import { QueryHelper } from './utils/QueryHelper.js';
|
|
6
|
+
import { TransactionContext } from './utils/TransactionContext.js';
|
|
7
|
+
import { isRaw, RawQueryFragment } from './utils/RawQueryFragment.js';
|
|
8
|
+
import { EntityFactory } from './entity/EntityFactory.js';
|
|
9
|
+
import { EntityAssigner } from './entity/EntityAssigner.js';
|
|
10
|
+
import { validateEmptyWhere, validateParams, validatePrimaryKey, validateProperty } from './entity/validators.js';
|
|
11
|
+
import { EntityLoader } from './entity/EntityLoader.js';
|
|
12
|
+
import { Reference } from './entity/Reference.js';
|
|
13
|
+
import { helper } from './entity/wrap.js';
|
|
14
|
+
import { ChangeSet, ChangeSetType } from './unit-of-work/ChangeSet.js';
|
|
15
|
+
import { UnitOfWork } from './unit-of-work/UnitOfWork.js';
|
|
16
|
+
import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePath, QueryFlag, ReferenceKind, SCALAR_TYPES, } from './enums.js';
|
|
17
|
+
import { EventManager } from './events/EventManager.js';
|
|
18
|
+
import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
|
|
19
|
+
import { OptimisticLockError, ValidationError } from './errors.js';
|
|
20
|
+
import { getLoadingStrategy } from './entity/utils.js';
|
|
21
|
+
import { TransactionManager } from './utils/TransactionManager.js';
|
|
21
22
|
/**
|
|
22
23
|
* The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
|
|
23
24
|
* such as UnitOfWork, Query Language, and Repository API.
|
|
24
25
|
* @template {IDatabaseDriver} Driver current driver type
|
|
25
26
|
*/
|
|
26
|
-
class EntityManager {
|
|
27
|
+
export class EntityManager {
|
|
27
28
|
config;
|
|
28
29
|
driver;
|
|
29
30
|
metadata;
|
|
@@ -33,9 +34,7 @@ class EntityManager {
|
|
|
33
34
|
_id = EntityManager.counter++;
|
|
34
35
|
global = false;
|
|
35
36
|
name;
|
|
36
|
-
|
|
37
|
-
colLoader = new dataloader_1.default(DataloaderUtils_1.DataloaderUtils.getColBatchLoadFn(this));
|
|
38
|
-
validator;
|
|
37
|
+
loaders = {};
|
|
39
38
|
repositoryMap = {};
|
|
40
39
|
entityLoader;
|
|
41
40
|
comparator;
|
|
@@ -52,20 +51,19 @@ class EntityManager {
|
|
|
52
51
|
/**
|
|
53
52
|
* @internal
|
|
54
53
|
*/
|
|
55
|
-
constructor(config, driver, metadata, useContext = true, eventManager = new
|
|
54
|
+
constructor(config, driver, metadata, useContext = true, eventManager = new EventManager(config.get('subscribers'))) {
|
|
56
55
|
this.config = config;
|
|
57
56
|
this.driver = driver;
|
|
58
57
|
this.metadata = metadata;
|
|
59
58
|
this.useContext = useContext;
|
|
60
59
|
this.eventManager = eventManager;
|
|
61
|
-
this.entityLoader = new
|
|
60
|
+
this.entityLoader = new EntityLoader(this);
|
|
62
61
|
this.name = this.config.get('contextName');
|
|
63
|
-
this.validator = new entity_1.EntityValidator(this.config.get('strict'));
|
|
64
62
|
this.comparator = this.config.getComparator(this.metadata);
|
|
65
63
|
this.resultCache = this.config.getResultCacheAdapter();
|
|
66
64
|
this.disableTransactions = this.config.get('disableTransactions');
|
|
67
|
-
this.entityFactory = new
|
|
68
|
-
this.unitOfWork = new
|
|
65
|
+
this.entityFactory = new EntityFactory(this);
|
|
66
|
+
this.unitOfWork = new UnitOfWork(this);
|
|
69
67
|
}
|
|
70
68
|
/**
|
|
71
69
|
* Gets the Driver instance used by this EntityManager.
|
|
@@ -90,7 +88,7 @@ class EntityManager {
|
|
|
90
88
|
* Gets repository for given entity. You can pass either string name or entity class reference.
|
|
91
89
|
*/
|
|
92
90
|
getRepository(entityName) {
|
|
93
|
-
entityName =
|
|
91
|
+
entityName = Utils.className(entityName);
|
|
94
92
|
if (!this.repositoryMap[entityName]) {
|
|
95
93
|
const meta = this.metadata.get(entityName);
|
|
96
94
|
const RepositoryClass = this.config.getRepositoryClass(meta.repository);
|
|
@@ -104,12 +102,6 @@ class EntityManager {
|
|
|
104
102
|
repo(entityName) {
|
|
105
103
|
return this.getRepository(entityName);
|
|
106
104
|
}
|
|
107
|
-
/**
|
|
108
|
-
* Gets EntityValidator instance
|
|
109
|
-
*/
|
|
110
|
-
getValidator() {
|
|
111
|
-
return this.validator;
|
|
112
|
-
}
|
|
113
105
|
/**
|
|
114
106
|
* Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
|
|
115
107
|
*/
|
|
@@ -124,9 +116,9 @@ class EntityManager {
|
|
|
124
116
|
const em = this.getContext();
|
|
125
117
|
em.prepareOptions(options);
|
|
126
118
|
await em.tryFlush(entityName, options);
|
|
127
|
-
entityName =
|
|
119
|
+
entityName = Utils.className(entityName);
|
|
128
120
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
129
|
-
|
|
121
|
+
validateParams(where);
|
|
130
122
|
options.orderBy = options.orderBy || {};
|
|
131
123
|
options.populate = await em.preparePopulate(entityName, options);
|
|
132
124
|
const populate = options.populate;
|
|
@@ -136,7 +128,6 @@ class EntityManager {
|
|
|
136
128
|
await em.entityLoader.populate(entityName, cached.data, populate, {
|
|
137
129
|
...options,
|
|
138
130
|
...em.getPopulateWhere(where, options),
|
|
139
|
-
convertCustomTypes: false,
|
|
140
131
|
ignoreLazyScalarProperties: true,
|
|
141
132
|
lookup: false,
|
|
142
133
|
});
|
|
@@ -147,8 +138,8 @@ class EntityManager {
|
|
|
147
138
|
// save the original hint value so we know it was infer/all
|
|
148
139
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
149
140
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
150
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
151
|
-
const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, ...options });
|
|
141
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
142
|
+
const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
152
143
|
if (results.length === 0) {
|
|
153
144
|
await em.storeCache(options.cache, cached, []);
|
|
154
145
|
return [];
|
|
@@ -163,11 +154,10 @@ class EntityManager {
|
|
|
163
154
|
});
|
|
164
155
|
ret.push(entity);
|
|
165
156
|
}
|
|
166
|
-
const unique =
|
|
157
|
+
const unique = Utils.unique(ret);
|
|
167
158
|
await em.entityLoader.populate(entityName, unique, populate, {
|
|
168
159
|
...options,
|
|
169
160
|
...em.getPopulateWhere(where, options),
|
|
170
|
-
convertCustomTypes: false,
|
|
171
161
|
ignoreLazyScalarProperties: true,
|
|
172
162
|
lookup: false,
|
|
173
163
|
});
|
|
@@ -176,10 +166,65 @@ class EntityManager {
|
|
|
176
166
|
await em.storeCache(options.cache, cached, () => ret);
|
|
177
167
|
}
|
|
178
168
|
else {
|
|
179
|
-
await em.storeCache(options.cache, cached, () => unique.map(e =>
|
|
169
|
+
await em.storeCache(options.cache, cached, () => unique.map(e => helper(e).toPOJO()));
|
|
180
170
|
}
|
|
181
171
|
return unique;
|
|
182
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Finds all entities and returns an async iterable (async generator) that yields results one by one.
|
|
175
|
+
* The results are merged and mapped to entity instances, without adding them to the identity map.
|
|
176
|
+
* You can disable merging by passing the options `{ mergeResults: false }`.
|
|
177
|
+
* With `mergeResults` disabled, to-many collections will contain at most one item, and you will get duplicate
|
|
178
|
+
* root entities when there are multiple items in the populated collection.
|
|
179
|
+
* This is useful for processing large datasets without loading everything into memory at once.
|
|
180
|
+
*
|
|
181
|
+
* ```ts
|
|
182
|
+
* const stream = em.stream(Book, { populate: ['author'] });
|
|
183
|
+
*
|
|
184
|
+
* for await (const book of stream) {
|
|
185
|
+
* // book is an instance of Book entity
|
|
186
|
+
* console.log(book.title, book.author.name);
|
|
187
|
+
* }
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
async *stream(entityName, options = {}) {
|
|
191
|
+
const em = this.getContext();
|
|
192
|
+
em.prepareOptions(options);
|
|
193
|
+
options.strategy = 'joined';
|
|
194
|
+
await em.tryFlush(entityName, options);
|
|
195
|
+
entityName = Utils.className(entityName);
|
|
196
|
+
const where = await em.processWhere(entityName, options.where ?? {}, options, 'read');
|
|
197
|
+
validateParams(where);
|
|
198
|
+
options.orderBy = options.orderBy || {};
|
|
199
|
+
options.populate = await em.preparePopulate(entityName, options);
|
|
200
|
+
const meta = this.metadata.get(entityName);
|
|
201
|
+
options = { ...options };
|
|
202
|
+
// save the original hint value so we know it was infer/all
|
|
203
|
+
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
204
|
+
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
205
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
206
|
+
const stream = em.driver.stream(entityName, where, {
|
|
207
|
+
ctx: em.transactionContext,
|
|
208
|
+
mapResults: false,
|
|
209
|
+
...options,
|
|
210
|
+
});
|
|
211
|
+
for await (const data of stream) {
|
|
212
|
+
const fork = em.fork();
|
|
213
|
+
const entity = fork.entityFactory.create(entityName, data, {
|
|
214
|
+
refresh: options.refresh,
|
|
215
|
+
schema: options.schema,
|
|
216
|
+
convertCustomTypes: true,
|
|
217
|
+
});
|
|
218
|
+
helper(entity).setSerializationContext({
|
|
219
|
+
populate: options.populate,
|
|
220
|
+
fields: options.fields,
|
|
221
|
+
exclude: options.exclude,
|
|
222
|
+
});
|
|
223
|
+
await fork.unitOfWork.dispatchOnLoadEvent();
|
|
224
|
+
fork.clear();
|
|
225
|
+
yield entity;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
183
228
|
/**
|
|
184
229
|
* Finds all entities of given type, optionally matching the `where` condition provided in the `options` parameter.
|
|
185
230
|
*/
|
|
@@ -190,11 +235,11 @@ class EntityManager {
|
|
|
190
235
|
if (options.populateWhere === undefined) {
|
|
191
236
|
options.populateWhere = this.config.get('populateWhere');
|
|
192
237
|
}
|
|
193
|
-
if (options.populateWhere ===
|
|
238
|
+
if (options.populateWhere === PopulateHint.ALL) {
|
|
194
239
|
return { where: {}, populateWhere: options.populateWhere };
|
|
195
240
|
}
|
|
196
|
-
/*
|
|
197
|
-
if (options.populateWhere ===
|
|
241
|
+
/* v8 ignore next */
|
|
242
|
+
if (options.populateWhere === PopulateHint.INFER) {
|
|
198
243
|
return { where, populateWhere: options.populateWhere };
|
|
199
244
|
}
|
|
200
245
|
return { where: options.populateWhere };
|
|
@@ -202,12 +247,12 @@ class EntityManager {
|
|
|
202
247
|
/**
|
|
203
248
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
204
249
|
*/
|
|
205
|
-
addFilter(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
options.entity = Utils_1.Utils.asArray(entityName).map(n => Utils_1.Utils.className(n));
|
|
250
|
+
addFilter(options) {
|
|
251
|
+
if (options.entity) {
|
|
252
|
+
options.entity = Utils.asArray(options.entity).map(n => Utils.className(n));
|
|
209
253
|
}
|
|
210
|
-
|
|
254
|
+
options.default ??= true;
|
|
255
|
+
this.getContext(false).filters[options.name] = options;
|
|
211
256
|
}
|
|
212
257
|
/**
|
|
213
258
|
* Sets filter parameter values globally inside context defined by this entity manager.
|
|
@@ -231,8 +276,8 @@ class EntityManager {
|
|
|
231
276
|
/**
|
|
232
277
|
* Gets logger context for this entity manager.
|
|
233
278
|
*/
|
|
234
|
-
getLoggerContext() {
|
|
235
|
-
const em = this.getContext();
|
|
279
|
+
getLoggerContext(options) {
|
|
280
|
+
const em = options?.disableContextResolution ? this : this.getContext();
|
|
236
281
|
em.loggerContext ??= {};
|
|
237
282
|
return em.loggerContext;
|
|
238
283
|
}
|
|
@@ -240,7 +285,7 @@ class EntityManager {
|
|
|
240
285
|
this.getContext(false).flushMode = flushMode;
|
|
241
286
|
}
|
|
242
287
|
async processWhere(entityName, where, options, type) {
|
|
243
|
-
where =
|
|
288
|
+
where = QueryHelper.processWhere({
|
|
244
289
|
where,
|
|
245
290
|
entityName,
|
|
246
291
|
metadata: this.metadata,
|
|
@@ -249,7 +294,7 @@ class EntityManager {
|
|
|
249
294
|
aliased: type === 'read',
|
|
250
295
|
});
|
|
251
296
|
where = (await this.applyFilters(entityName, where, options.filters ?? {}, type, options));
|
|
252
|
-
where =
|
|
297
|
+
where = this.applyDiscriminatorCondition(entityName, where);
|
|
253
298
|
return where;
|
|
254
299
|
}
|
|
255
300
|
// this method only handles the problem for mongo driver, SQL drivers have their implementation inside QueryBuilder
|
|
@@ -267,43 +312,54 @@ class EntityManager {
|
|
|
267
312
|
return children;
|
|
268
313
|
};
|
|
269
314
|
lookUpChildren(children, meta.className);
|
|
270
|
-
/*
|
|
315
|
+
/* v8 ignore next */
|
|
271
316
|
where[meta.root.discriminatorColumn] = children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue;
|
|
272
317
|
return where;
|
|
273
318
|
}
|
|
274
319
|
createPopulateWhere(cond, options) {
|
|
275
320
|
const ret = {};
|
|
276
321
|
const populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
277
|
-
if (populateWhere ===
|
|
278
|
-
|
|
322
|
+
if (populateWhere === PopulateHint.INFER) {
|
|
323
|
+
Utils.merge(ret, cond);
|
|
279
324
|
}
|
|
280
325
|
else if (typeof populateWhere === 'object') {
|
|
281
|
-
|
|
326
|
+
Utils.merge(ret, populateWhere);
|
|
282
327
|
}
|
|
283
328
|
return ret;
|
|
284
329
|
}
|
|
285
|
-
async getJoinedFilters(meta,
|
|
330
|
+
async getJoinedFilters(meta, options) {
|
|
331
|
+
if (!this.config.get('filtersOnRelations') || !options.populate) {
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
286
334
|
const ret = {};
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
335
|
+
for (const hint of options.populate) {
|
|
336
|
+
const field = hint.field.split(':')[0];
|
|
337
|
+
const prop = meta.properties[field];
|
|
338
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
339
|
+
const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
|
|
340
|
+
if (!joined && !hint.filter) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const filters = QueryHelper.mergePropertyFilters(prop.filters, options.filters);
|
|
344
|
+
const where = await this.applyFilters(prop.type, {}, filters, 'read', {
|
|
345
|
+
...options,
|
|
346
|
+
populate: hint.children,
|
|
347
|
+
});
|
|
348
|
+
const where2 = await this.getJoinedFilters(prop.targetMeta, {
|
|
349
|
+
...options,
|
|
350
|
+
filters,
|
|
351
|
+
populate: hint.children,
|
|
352
|
+
populateWhere: PopulateHint.ALL,
|
|
353
|
+
});
|
|
354
|
+
if (Utils.hasObjectKeys(where)) {
|
|
355
|
+
ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
|
|
356
|
+
}
|
|
357
|
+
if (where2 && Utils.hasObjectKeys(where2)) {
|
|
358
|
+
if (ret[field]) {
|
|
359
|
+
Utils.merge(ret[field], where2);
|
|
299
360
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
Utils_1.Utils.merge(ret[field], where2);
|
|
303
|
-
}
|
|
304
|
-
else {
|
|
305
|
-
ret[field] = where2;
|
|
306
|
-
}
|
|
361
|
+
else {
|
|
362
|
+
ret[field] = where2;
|
|
307
363
|
}
|
|
308
364
|
}
|
|
309
365
|
}
|
|
@@ -312,27 +368,45 @@ class EntityManager {
|
|
|
312
368
|
/**
|
|
313
369
|
* 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.
|
|
314
370
|
*/
|
|
315
|
-
async autoJoinRefsForFilters(meta, options) {
|
|
316
|
-
if (!meta || !this.config.get('autoJoinRefsForFilters')) {
|
|
371
|
+
async autoJoinRefsForFilters(meta, options, parent) {
|
|
372
|
+
if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
|
|
317
373
|
return;
|
|
318
374
|
}
|
|
319
|
-
const props = meta.relations.filter(prop => {
|
|
320
|
-
return !prop.object && [enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
321
|
-
&& ((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)));
|
|
322
|
-
});
|
|
323
375
|
const ret = options.populate;
|
|
324
|
-
for (const prop of
|
|
325
|
-
|
|
326
|
-
|
|
376
|
+
for (const prop of meta.relations) {
|
|
377
|
+
if (prop.object
|
|
378
|
+
|| ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
379
|
+
|| !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
380
|
+
|| (parent?.className === prop.targetMeta.root.className && parent.propName === prop.inversedBy)) {
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
|
|
384
|
+
const cond = await this.applyFilters(prop.type, {}, options.filters, 'read', options);
|
|
385
|
+
if (!Utils.isEmpty(cond)) {
|
|
327
386
|
const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
|
|
328
|
-
|
|
329
|
-
|
|
387
|
+
let found = false;
|
|
388
|
+
for (const hint of populated) {
|
|
389
|
+
if (!hint.all) {
|
|
390
|
+
hint.filter = true;
|
|
391
|
+
}
|
|
392
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
393
|
+
if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === LoadStrategy.JOINED)) {
|
|
394
|
+
found = true;
|
|
395
|
+
}
|
|
330
396
|
}
|
|
331
|
-
|
|
332
|
-
ret.push({ field: `${prop.name}:ref`, strategy:
|
|
397
|
+
if (!found) {
|
|
398
|
+
ret.push({ field: `${prop.name}:ref`, strategy: LoadStrategy.JOINED, filter: true });
|
|
333
399
|
}
|
|
334
400
|
}
|
|
335
401
|
}
|
|
402
|
+
for (const hint of ret) {
|
|
403
|
+
const [field, ref] = hint.field.split(':');
|
|
404
|
+
const prop = meta?.properties[field];
|
|
405
|
+
if (prop && !ref) {
|
|
406
|
+
hint.children ??= [];
|
|
407
|
+
await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { className: meta.root.className, propName: prop.name });
|
|
408
|
+
}
|
|
409
|
+
}
|
|
336
410
|
}
|
|
337
411
|
/**
|
|
338
412
|
* @internal
|
|
@@ -346,7 +420,7 @@ class EntityManager {
|
|
|
346
420
|
}
|
|
347
421
|
const active = new Set();
|
|
348
422
|
const push = (source) => {
|
|
349
|
-
const activeFilters =
|
|
423
|
+
const activeFilters = QueryHelper
|
|
350
424
|
.getActiveFilters(entityName, options, source)
|
|
351
425
|
.filter(f => !active.has(f.name));
|
|
352
426
|
filters.push(...activeFilters);
|
|
@@ -362,24 +436,28 @@ class EntityManager {
|
|
|
362
436
|
let cond;
|
|
363
437
|
if (filter.cond instanceof Function) {
|
|
364
438
|
// @ts-ignore
|
|
365
|
-
const args =
|
|
439
|
+
const args = Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
|
|
366
440
|
if (!args && filter.cond.length > 0 && filter.args !== false) {
|
|
367
441
|
throw new Error(`No arguments provided for filter '${filter.name}'`);
|
|
368
442
|
}
|
|
369
|
-
cond = await filter.cond(args, type, this, findOptions);
|
|
443
|
+
cond = await filter.cond(args, type, this, findOptions, entityName);
|
|
370
444
|
}
|
|
371
445
|
else {
|
|
372
446
|
cond = filter.cond;
|
|
373
447
|
}
|
|
374
|
-
|
|
448
|
+
cond = QueryHelper.processWhere({
|
|
375
449
|
where: cond,
|
|
376
450
|
entityName,
|
|
377
451
|
metadata: this.metadata,
|
|
378
452
|
platform: this.driver.getPlatform(),
|
|
379
453
|
aliased: type === 'read',
|
|
380
|
-
})
|
|
454
|
+
});
|
|
455
|
+
if (filter.strict) {
|
|
456
|
+
Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
|
|
457
|
+
}
|
|
458
|
+
ret.push(cond);
|
|
381
459
|
}
|
|
382
|
-
const conds = [...ret, where].filter(c =>
|
|
460
|
+
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
|
|
383
461
|
return conds.length > 1 ? { $and: conds } : conds[0];
|
|
384
462
|
}
|
|
385
463
|
/**
|
|
@@ -390,7 +468,7 @@ class EntityManager {
|
|
|
390
468
|
const em = this.getContext(false);
|
|
391
469
|
await em.tryFlush(entityName, options);
|
|
392
470
|
options.flushMode = 'commit'; // do not try to auto flush again
|
|
393
|
-
return
|
|
471
|
+
return RawQueryFragment.run(async () => {
|
|
394
472
|
return Promise.all([
|
|
395
473
|
em.find(entityName, where, options),
|
|
396
474
|
em.count(entityName, where, options),
|
|
@@ -432,6 +510,10 @@ class EntityManager {
|
|
|
432
510
|
* });
|
|
433
511
|
* ```
|
|
434
512
|
*
|
|
513
|
+
* The options also support an `includeCount` (true by default) option. If set to false, the `totalCount` is not
|
|
514
|
+
* returned as part of the cursor. This is useful for performance reason, when you don't care about the total number
|
|
515
|
+
* of pages.
|
|
516
|
+
*
|
|
435
517
|
* The `Cursor` object provides the following interface:
|
|
436
518
|
*
|
|
437
519
|
* ```ts
|
|
@@ -441,7 +523,7 @@ class EntityManager {
|
|
|
441
523
|
* User { ... },
|
|
442
524
|
* User { ... },
|
|
443
525
|
* ],
|
|
444
|
-
* totalCount: 50,
|
|
526
|
+
* totalCount: 50, // not included if `includeCount: false`
|
|
445
527
|
* startCursor: 'WzRd',
|
|
446
528
|
* endCursor: 'WzZd',
|
|
447
529
|
* hasPrevPage: true,
|
|
@@ -451,13 +533,15 @@ class EntityManager {
|
|
|
451
533
|
*/
|
|
452
534
|
async findByCursor(entityName, where, options) {
|
|
453
535
|
const em = this.getContext(false);
|
|
454
|
-
entityName =
|
|
536
|
+
entityName = Utils.className(entityName);
|
|
455
537
|
options.overfetch ??= true;
|
|
456
|
-
if (
|
|
538
|
+
if (Utils.isEmpty(options.orderBy)) {
|
|
457
539
|
throw new Error('Explicit `orderBy` option required');
|
|
458
540
|
}
|
|
459
|
-
const [entities, count] =
|
|
460
|
-
|
|
541
|
+
const [entities, count] = options.includeCount !== false
|
|
542
|
+
? await em.findAndCount(entityName, where, options)
|
|
543
|
+
: [await em.find(entityName, where, options)];
|
|
544
|
+
return new Cursor(entities, count, options, this.metadata.get(entityName));
|
|
461
545
|
}
|
|
462
546
|
/**
|
|
463
547
|
* Refreshes the persistent state of an entity from the database, overriding any local changes that have not yet been
|
|
@@ -469,7 +553,7 @@ class EntityManager {
|
|
|
469
553
|
if (!ret) {
|
|
470
554
|
options.failHandler ??= this.config.get('findOneOrFailHandler');
|
|
471
555
|
const entityName = entity.constructor.name;
|
|
472
|
-
const where =
|
|
556
|
+
const where = helper(entity).getPrimaryKey();
|
|
473
557
|
throw options.failHandler(entityName, where);
|
|
474
558
|
}
|
|
475
559
|
return ret;
|
|
@@ -482,18 +566,31 @@ class EntityManager {
|
|
|
482
566
|
async refresh(entity, options = {}) {
|
|
483
567
|
const fork = this.fork({ keepTransactionContext: true });
|
|
484
568
|
const entityName = entity.constructor.name;
|
|
569
|
+
const wrapped = helper(entity);
|
|
485
570
|
const reloaded = await fork.findOne(entityName, entity, {
|
|
486
|
-
schema:
|
|
571
|
+
schema: wrapped.__schema,
|
|
487
572
|
...options,
|
|
488
|
-
flushMode:
|
|
573
|
+
flushMode: FlushMode.COMMIT,
|
|
489
574
|
});
|
|
490
|
-
|
|
491
|
-
|
|
575
|
+
const em = this.getContext();
|
|
576
|
+
if (!reloaded) {
|
|
577
|
+
em.unitOfWork.unsetIdentity(entity);
|
|
578
|
+
return null;
|
|
492
579
|
}
|
|
493
|
-
|
|
494
|
-
|
|
580
|
+
let found = false;
|
|
581
|
+
for (const e of fork.unitOfWork.getIdentityMap()) {
|
|
582
|
+
const ref = em.getReference(e.constructor.name, helper(e).getPrimaryKey());
|
|
583
|
+
const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true });
|
|
584
|
+
em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, true);
|
|
585
|
+
Utils.merge(helper(ref).__originalEntityData, this.comparator.prepareEntity(e));
|
|
586
|
+
found ||= ref === entity;
|
|
587
|
+
}
|
|
588
|
+
if (!found) {
|
|
589
|
+
const data = helper(reloaded).serialize({ ignoreSerializers: true, includeHidden: true });
|
|
590
|
+
em.config.getHydrator(this.metadata).hydrate(entity, wrapped.__meta, data, em.entityFactory, 'full', false, true);
|
|
591
|
+
Utils.merge(wrapped.__originalEntityData, this.comparator.prepareEntity(reloaded));
|
|
495
592
|
}
|
|
496
|
-
return
|
|
593
|
+
return entity;
|
|
497
594
|
}
|
|
498
595
|
/**
|
|
499
596
|
* Finds first entity matching your `where` query.
|
|
@@ -507,45 +604,47 @@ class EntityManager {
|
|
|
507
604
|
return ret;
|
|
508
605
|
}
|
|
509
606
|
const em = this.getContext();
|
|
510
|
-
entityName =
|
|
607
|
+
entityName = Utils.className(entityName);
|
|
511
608
|
em.prepareOptions(options);
|
|
512
609
|
let entity = em.unitOfWork.tryGetById(entityName, where, options.schema);
|
|
513
610
|
// query for a not managed entity which is already in the identity map as it
|
|
514
611
|
// was provided with a PK this entity does not exist in the db, there can't
|
|
515
612
|
// be any relations to it, so no need to deal with the populate hint
|
|
516
|
-
if (entity && !
|
|
613
|
+
if (entity && !helper(entity).__managed) {
|
|
517
614
|
return entity;
|
|
518
615
|
}
|
|
519
616
|
await em.tryFlush(entityName, options);
|
|
520
617
|
const meta = em.metadata.get(entityName);
|
|
521
618
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
522
|
-
|
|
619
|
+
validateEmptyWhere(where);
|
|
523
620
|
em.checkLockRequirements(options.lockMode, meta);
|
|
524
|
-
const isOptimisticLocking =
|
|
621
|
+
const isOptimisticLocking = options.lockMode == null || options.lockMode === LockMode.OPTIMISTIC;
|
|
525
622
|
if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
|
|
526
623
|
return em.lockAndPopulate(meta, entity, where, options);
|
|
527
624
|
}
|
|
528
|
-
|
|
625
|
+
validateParams(where);
|
|
529
626
|
options.populate = await em.preparePopulate(entityName, options);
|
|
530
627
|
const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
|
|
531
628
|
const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
|
|
532
|
-
if (cached?.data) {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
629
|
+
if (cached?.data !== undefined) {
|
|
630
|
+
if (cached.data) {
|
|
631
|
+
await em.entityLoader.populate(entityName, [cached.data], options.populate, {
|
|
632
|
+
...options,
|
|
633
|
+
...em.getPopulateWhere(where, options),
|
|
634
|
+
ignoreLazyScalarProperties: true,
|
|
635
|
+
lookup: false,
|
|
636
|
+
});
|
|
637
|
+
}
|
|
540
638
|
return cached.data;
|
|
541
639
|
}
|
|
542
640
|
options = { ...options };
|
|
543
641
|
// save the original hint value so we know it was infer/all
|
|
544
642
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
545
643
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
546
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
644
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
547
645
|
const data = await em.driver.findOne(entityName, where, {
|
|
548
646
|
ctx: em.transactionContext,
|
|
647
|
+
em,
|
|
549
648
|
...options,
|
|
550
649
|
});
|
|
551
650
|
if (!data) {
|
|
@@ -560,7 +659,7 @@ class EntityManager {
|
|
|
560
659
|
});
|
|
561
660
|
await em.lockAndPopulate(meta, entity, where, options);
|
|
562
661
|
await em.unitOfWork.dispatchOnLoadEvent();
|
|
563
|
-
await em.storeCache(options.cache, cached, () =>
|
|
662
|
+
await em.storeCache(options.cache, cached, () => helper(entity).toPOJO());
|
|
564
663
|
return entity;
|
|
565
664
|
}
|
|
566
665
|
/**
|
|
@@ -583,9 +682,9 @@ class EntityManager {
|
|
|
583
682
|
if (!entity || isStrictViolation) {
|
|
584
683
|
const key = options.strict ? 'findExactlyOneOrFailHandler' : 'findOneOrFailHandler';
|
|
585
684
|
options.failHandler ??= this.config.get(key);
|
|
586
|
-
entityName =
|
|
587
|
-
/*
|
|
588
|
-
where =
|
|
685
|
+
entityName = Utils.className(entityName);
|
|
686
|
+
/* v8 ignore next */
|
|
687
|
+
where = Utils.isEntity(where) ? helper(where).getPrimaryKey() : where;
|
|
589
688
|
throw options.failHandler(entityName, where);
|
|
590
689
|
}
|
|
591
690
|
return entity;
|
|
@@ -630,22 +729,22 @@ class EntityManager {
|
|
|
630
729
|
data = entityNameOrEntity;
|
|
631
730
|
}
|
|
632
731
|
else {
|
|
633
|
-
entityName =
|
|
732
|
+
entityName = Utils.className(entityNameOrEntity);
|
|
634
733
|
}
|
|
635
734
|
const meta = this.metadata.get(entityName);
|
|
636
|
-
const convertCustomTypes = !
|
|
637
|
-
if (
|
|
735
|
+
const convertCustomTypes = !Utils.isEntity(data);
|
|
736
|
+
if (Utils.isEntity(data)) {
|
|
638
737
|
entity = data;
|
|
639
|
-
if (
|
|
738
|
+
if (helper(entity).__managed && helper(entity).__em === em && !this.config.get('upsertManaged')) {
|
|
640
739
|
em.entityFactory.mergeData(meta, entity, data, { initialized: true });
|
|
641
740
|
return entity;
|
|
642
741
|
}
|
|
643
|
-
where =
|
|
742
|
+
where = helper(entity).getPrimaryKey();
|
|
644
743
|
data = em.comparator.prepareEntity(entity);
|
|
645
744
|
}
|
|
646
745
|
else {
|
|
647
|
-
data =
|
|
648
|
-
where =
|
|
746
|
+
data = Utils.copy(QueryHelper.processParams(data));
|
|
747
|
+
where = Utils.extractPK(data, meta);
|
|
649
748
|
if (where && !this.config.get('upsertManaged')) {
|
|
650
749
|
const exists = em.unitOfWork.getById(entityName, where, options.schema);
|
|
651
750
|
if (exists) {
|
|
@@ -653,28 +752,11 @@ class EntityManager {
|
|
|
653
752
|
}
|
|
654
753
|
}
|
|
655
754
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
662
|
-
else if (meta.uniques.length > 0) {
|
|
663
|
-
for (const u of meta.uniques) {
|
|
664
|
-
if (Utils_1.Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
665
|
-
where = Utils_1.Utils.asArray(u.properties).reduce((o, key) => {
|
|
666
|
-
o[key] = data[key];
|
|
667
|
-
return o;
|
|
668
|
-
}, {});
|
|
669
|
-
break;
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
data = QueryHelper_1.QueryHelper.processObjectParams(data);
|
|
675
|
-
em.validator.validateParams(data, 'insert data');
|
|
676
|
-
if (em.eventManager.hasListeners(enums_1.EventType.beforeUpsert, meta)) {
|
|
677
|
-
await em.eventManager.dispatchEvent(enums_1.EventType.beforeUpsert, { entity: data, em, meta }, meta);
|
|
755
|
+
where = getWhereCondition(meta, options.onConflictFields, data, where).where;
|
|
756
|
+
data = QueryHelper.processObjectParams(data);
|
|
757
|
+
validateParams(data, 'insert data');
|
|
758
|
+
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
759
|
+
await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
|
|
678
760
|
}
|
|
679
761
|
const ret = await em.driver.nativeUpdate(entityName, where, data, {
|
|
680
762
|
ctx: em.transactionContext,
|
|
@@ -688,9 +770,9 @@ class EntityManager {
|
|
|
688
770
|
initialized: true,
|
|
689
771
|
schema: options.schema,
|
|
690
772
|
});
|
|
691
|
-
const uniqueFields = options.onConflictFields ?? (
|
|
692
|
-
const returning =
|
|
693
|
-
if (options.onConflictAction === 'ignore' || !
|
|
773
|
+
const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys);
|
|
774
|
+
const returning = getOnConflictReturningFields(meta, data, uniqueFields, options);
|
|
775
|
+
if (options.onConflictAction === 'ignore' || !helper(entity).hasPrimaryKey() || (returning.length > 0 && !(this.getPlatform().usesReturningStatement() && ret.row))) {
|
|
694
776
|
const where = {};
|
|
695
777
|
if (Array.isArray(uniqueFields)) {
|
|
696
778
|
for (const prop of uniqueFields) {
|
|
@@ -715,14 +797,15 @@ class EntityManager {
|
|
|
715
797
|
ctx: em.transactionContext,
|
|
716
798
|
convertCustomTypes: true,
|
|
717
799
|
connectionType: 'write',
|
|
800
|
+
schema: options.schema,
|
|
718
801
|
});
|
|
719
|
-
em.getHydrator().hydrate(entity, meta, data2, em.entityFactory, 'full');
|
|
802
|
+
em.getHydrator().hydrate(entity, meta, data2, em.entityFactory, 'full', false, true);
|
|
720
803
|
}
|
|
721
804
|
// recompute the data as there might be some values missing (e.g. those with db column defaults)
|
|
722
805
|
const snapshot = this.comparator.prepareEntity(entity);
|
|
723
806
|
em.unitOfWork.register(entity, snapshot, { refresh: true });
|
|
724
|
-
if (em.eventManager.hasListeners(
|
|
725
|
-
await em.eventManager.dispatchEvent(
|
|
807
|
+
if (em.eventManager.hasListeners(EventType.afterUpsert, meta)) {
|
|
808
|
+
await em.eventManager.dispatchEvent(EventType.afterUpsert, { entity, em, meta }, meta);
|
|
726
809
|
}
|
|
727
810
|
return entity;
|
|
728
811
|
}
|
|
@@ -768,19 +851,19 @@ class EntityManager {
|
|
|
768
851
|
data = entityNameOrEntity;
|
|
769
852
|
}
|
|
770
853
|
else {
|
|
771
|
-
entityName =
|
|
854
|
+
entityName = Utils.className(entityNameOrEntity);
|
|
772
855
|
}
|
|
773
856
|
const batchSize = options.batchSize ?? this.config.get('batchSize');
|
|
774
857
|
if (data.length > batchSize) {
|
|
775
858
|
const ret = [];
|
|
776
859
|
for (let i = 0; i < data.length; i += batchSize) {
|
|
777
860
|
const chunk = data.slice(i, i + batchSize);
|
|
778
|
-
ret.push(...await this.upsertMany(entityName, chunk, options));
|
|
861
|
+
ret.push(...(await this.upsertMany(entityName, chunk, options)));
|
|
779
862
|
}
|
|
780
863
|
return ret;
|
|
781
864
|
}
|
|
782
865
|
const meta = this.metadata.get(entityName);
|
|
783
|
-
const convertCustomTypes = !
|
|
866
|
+
const convertCustomTypes = !Utils.isEntity(data[0]);
|
|
784
867
|
const allData = [];
|
|
785
868
|
const allWhere = [];
|
|
786
869
|
const entities = new Map();
|
|
@@ -788,20 +871,20 @@ class EntityManager {
|
|
|
788
871
|
for (let i = 0; i < data.length; i++) {
|
|
789
872
|
let row = data[i];
|
|
790
873
|
let where;
|
|
791
|
-
if (
|
|
874
|
+
if (Utils.isEntity(row)) {
|
|
792
875
|
const entity = row;
|
|
793
|
-
if (
|
|
876
|
+
if (helper(entity).__managed && helper(entity).__em === em && !this.config.get('upsertManaged')) {
|
|
794
877
|
em.entityFactory.mergeData(meta, entity, row, { initialized: true });
|
|
795
878
|
entities.set(entity, row);
|
|
796
879
|
entitiesByData.set(row, entity);
|
|
797
880
|
continue;
|
|
798
881
|
}
|
|
799
|
-
where =
|
|
882
|
+
where = helper(entity).getPrimaryKey();
|
|
800
883
|
row = em.comparator.prepareEntity(entity);
|
|
801
884
|
}
|
|
802
885
|
else {
|
|
803
|
-
row = data[i] =
|
|
804
|
-
where =
|
|
886
|
+
row = data[i] = Utils.copy(QueryHelper.processParams(row));
|
|
887
|
+
where = Utils.extractPK(row, meta);
|
|
805
888
|
if (where && !this.config.get('upsertManaged')) {
|
|
806
889
|
const exists = em.unitOfWork.getById(entityName, where, options.schema);
|
|
807
890
|
if (exists) {
|
|
@@ -812,42 +895,28 @@ class EntityManager {
|
|
|
812
895
|
}
|
|
813
896
|
}
|
|
814
897
|
}
|
|
815
|
-
const unique = meta.props.filter(p => p.unique).map(p => p.name);
|
|
816
|
-
propIndex = unique.findIndex(p =>
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
else if (meta.uniques.length > 0) {
|
|
822
|
-
for (const u of meta.uniques) {
|
|
823
|
-
if (Utils_1.Utils.asArray(u.properties).every(p => row[p] != null)) {
|
|
824
|
-
where = Utils_1.Utils.asArray(u.properties).reduce((o, key) => {
|
|
825
|
-
o[key] = row[key];
|
|
826
|
-
return o;
|
|
827
|
-
}, {});
|
|
828
|
-
break;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
row = QueryHelper_1.QueryHelper.processObjectParams(row);
|
|
834
|
-
where = QueryHelper_1.QueryHelper.processWhere({
|
|
835
|
-
where,
|
|
898
|
+
const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
|
|
899
|
+
propIndex = !isRaw(unique) && unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
|
|
900
|
+
const tmp = getWhereCondition(meta, options.onConflictFields, row, where);
|
|
901
|
+
propIndex = tmp.propIndex;
|
|
902
|
+
where = QueryHelper.processWhere({
|
|
903
|
+
where: tmp.where,
|
|
836
904
|
entityName,
|
|
837
905
|
metadata: this.metadata,
|
|
838
906
|
platform: this.getPlatform(),
|
|
839
907
|
});
|
|
840
|
-
|
|
908
|
+
row = QueryHelper.processObjectParams(row);
|
|
909
|
+
validateParams(row, 'insert data');
|
|
841
910
|
allData.push(row);
|
|
842
911
|
allWhere.push(where);
|
|
843
912
|
}
|
|
844
913
|
if (entities.size === data.length) {
|
|
845
914
|
return [...entities.keys()];
|
|
846
915
|
}
|
|
847
|
-
if (em.eventManager.hasListeners(
|
|
916
|
+
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
848
917
|
for (const dto of data) {
|
|
849
918
|
const entity = entitiesByData.get(dto) ?? dto;
|
|
850
|
-
await em.eventManager.dispatchEvent(
|
|
919
|
+
await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity, em, meta }, meta);
|
|
851
920
|
}
|
|
852
921
|
}
|
|
853
922
|
const res = await em.driver.nativeUpdateMany(entityName, allWhere, allData, {
|
|
@@ -860,27 +929,27 @@ class EntityManager {
|
|
|
860
929
|
entitiesByData.clear();
|
|
861
930
|
const loadPK = new Map();
|
|
862
931
|
allData.forEach((row, i) => {
|
|
863
|
-
em.unitOfWork.getChangeSetPersister().mapReturnedValues(
|
|
864
|
-
const entity =
|
|
932
|
+
em.unitOfWork.getChangeSetPersister().mapReturnedValues(Utils.isEntity(data[i]) ? data[i] : null, Utils.isEntity(data[i]) ? {} : data[i], res.rows?.[i], meta, true);
|
|
933
|
+
const entity = Utils.isEntity(data[i]) ? data[i] : em.entityFactory.create(entityName, row, {
|
|
865
934
|
refresh: true,
|
|
866
935
|
initialized: true,
|
|
867
936
|
schema: options.schema,
|
|
868
937
|
});
|
|
869
|
-
if (!
|
|
938
|
+
if (!helper(entity).hasPrimaryKey()) {
|
|
870
939
|
loadPK.set(entity, allWhere[i]);
|
|
871
940
|
}
|
|
872
941
|
entities.set(entity, row);
|
|
873
942
|
entitiesByData.set(row, entity);
|
|
874
943
|
});
|
|
875
944
|
// skip if we got the PKs via returning statement (`rows`)
|
|
876
|
-
const uniqueFields = options.onConflictFields ?? (
|
|
877
|
-
const returning =
|
|
945
|
+
const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(allWhere[0]) ? Object.keys(allWhere[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
|
|
946
|
+
const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
|
|
878
947
|
const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
|
|
879
948
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
880
949
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
881
|
-
const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
|
|
950
|
+
const add = new Set(propIndex !== false && propIndex >= 0 ? [unique[propIndex]] : []);
|
|
882
951
|
for (const cond of loadPK.values()) {
|
|
883
|
-
|
|
952
|
+
Utils.keys(cond).forEach(key => add.add(key));
|
|
884
953
|
}
|
|
885
954
|
const where = { $or: [] };
|
|
886
955
|
data.forEach((item, idx) => {
|
|
@@ -895,6 +964,7 @@ class EntityManager {
|
|
|
895
964
|
ctx: em.transactionContext,
|
|
896
965
|
convertCustomTypes: true,
|
|
897
966
|
connectionType: 'write',
|
|
967
|
+
schema: options.schema,
|
|
898
968
|
});
|
|
899
969
|
for (const [entity, cond] of loadPK.entries()) {
|
|
900
970
|
const row = data2.find(row => {
|
|
@@ -906,11 +976,11 @@ class EntityManager {
|
|
|
906
976
|
});
|
|
907
977
|
return this.comparator.matching(entityName, cond, tmp);
|
|
908
978
|
});
|
|
909
|
-
/*
|
|
979
|
+
/* v8 ignore next */
|
|
910
980
|
if (!row) {
|
|
911
981
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
912
982
|
}
|
|
913
|
-
em.getHydrator().hydrate(entity, meta, row, em.entityFactory, 'full');
|
|
983
|
+
em.getHydrator().hydrate(entity, meta, row, em.entityFactory, 'full', false, true);
|
|
914
984
|
}
|
|
915
985
|
if (loadPK.size !== data2.length && Array.isArray(uniqueFields)) {
|
|
916
986
|
for (let i = 0; i < allData.length; i++) {
|
|
@@ -929,7 +999,7 @@ class EntityManager {
|
|
|
929
999
|
}, {});
|
|
930
1000
|
return this.comparator.matching(entityName, cond, pk);
|
|
931
1001
|
});
|
|
932
|
-
/*
|
|
1002
|
+
/* v8 ignore next */
|
|
933
1003
|
if (!row) {
|
|
934
1004
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
935
1005
|
}
|
|
@@ -942,54 +1012,46 @@ class EntityManager {
|
|
|
942
1012
|
const snapshot = this.comparator.prepareEntity(entity);
|
|
943
1013
|
em.unitOfWork.register(entity, snapshot, { refresh: true });
|
|
944
1014
|
}
|
|
945
|
-
if (em.eventManager.hasListeners(
|
|
1015
|
+
if (em.eventManager.hasListeners(EventType.afterUpsert, meta)) {
|
|
946
1016
|
for (const [entity] of entities) {
|
|
947
|
-
await em.eventManager.dispatchEvent(
|
|
1017
|
+
await em.eventManager.dispatchEvent(EventType.afterUpsert, { entity, em, meta }, meta);
|
|
948
1018
|
}
|
|
949
1019
|
}
|
|
950
1020
|
return [...entities.keys()];
|
|
951
1021
|
}
|
|
952
1022
|
/**
|
|
953
1023
|
* Runs your callback wrapped inside a database transaction.
|
|
1024
|
+
*
|
|
1025
|
+
* If a transaction is already active, a new savepoint (nested transaction) will be created by default. This behavior
|
|
1026
|
+
* can be controlled via the `propagation` option. Use the provided EntityManager instance for all operations that
|
|
1027
|
+
* should be part of the transaction. You can safely use a global EntityManager instance from a DI container, as this
|
|
1028
|
+
* method automatically creates an async context for the transaction.
|
|
1029
|
+
*
|
|
1030
|
+
* **Concurrency note:** When running multiple transactions concurrently (e.g. in parallel requests or jobs), use the
|
|
1031
|
+
* `clear: true` option. This ensures the callback runs in a clear fork of the EntityManager, providing full isolation
|
|
1032
|
+
* between concurrent transactional handlers. Using `clear: true` is an alternative to forking explicitly and calling
|
|
1033
|
+
* the method on the new fork – it already provides the necessary isolation for safe concurrent usage.
|
|
1034
|
+
*
|
|
1035
|
+
* **Propagation note:** Changes made within a transaction (whether top-level or nested) are always propagated to the
|
|
1036
|
+
* parent context, unless the parent context is a global one. If you want to avoid that, fork the EntityManager first
|
|
1037
|
+
* and then call this method on the fork.
|
|
1038
|
+
*
|
|
1039
|
+
* **Example:**
|
|
1040
|
+
* ```ts
|
|
1041
|
+
* await em.transactional(async (em) => {
|
|
1042
|
+
* const author = new Author('Jon');
|
|
1043
|
+
* em.persist(author);
|
|
1044
|
+
* // flush is called automatically at the end of the callback
|
|
1045
|
+
* });
|
|
1046
|
+
* ```
|
|
954
1047
|
*/
|
|
955
1048
|
async transactional(cb, options = {}) {
|
|
956
1049
|
const em = this.getContext(false);
|
|
957
1050
|
if (this.disableTransactions || em.disableTransactions) {
|
|
958
1051
|
return cb(em);
|
|
959
1052
|
}
|
|
960
|
-
const
|
|
961
|
-
|
|
962
|
-
flushMode: options.flushMode,
|
|
963
|
-
cloneEventManager: true,
|
|
964
|
-
disableTransactions: options.ignoreNestedTransactions,
|
|
965
|
-
loggerContext: options.loggerContext,
|
|
966
|
-
});
|
|
967
|
-
options.ctx ??= em.transactionContext;
|
|
968
|
-
const propagateToUpperContext = !em.global || this.config.get('allowGlobalContext');
|
|
969
|
-
return TransactionContext_1.TransactionContext.create(fork, async () => {
|
|
970
|
-
return fork.getConnection().transactional(async (trx) => {
|
|
971
|
-
fork.transactionContext = trx;
|
|
972
|
-
if (propagateToUpperContext) {
|
|
973
|
-
fork.eventManager.registerSubscriber({
|
|
974
|
-
afterFlush(args) {
|
|
975
|
-
args.uow.getChangeSets()
|
|
976
|
-
.filter(cs => [unit_of_work_1.ChangeSetType.DELETE, unit_of_work_1.ChangeSetType.DELETE_EARLY].includes(cs.type))
|
|
977
|
-
.forEach(cs => em.unitOfWork.unsetIdentity(cs.entity));
|
|
978
|
-
},
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
const ret = await cb(fork);
|
|
982
|
-
await fork.flush();
|
|
983
|
-
if (propagateToUpperContext) {
|
|
984
|
-
// ensure all entities from inner context are merged to the upper one
|
|
985
|
-
for (const entity of fork.unitOfWork.getIdentityMap()) {
|
|
986
|
-
em.unitOfWork.register(entity);
|
|
987
|
-
entity.__helper.__em = em;
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
return ret;
|
|
991
|
-
}, { ...options, eventBroadcaster: new events_1.TransactionEventBroadcaster(fork, { topLevelTransaction: !options.ctx }) });
|
|
992
|
-
});
|
|
1053
|
+
const manager = new TransactionManager(this);
|
|
1054
|
+
return manager.handle(cb, options);
|
|
993
1055
|
}
|
|
994
1056
|
/**
|
|
995
1057
|
* Starts new transaction bound to this EntityManager. Use `ctx` parameter to provide the parent when nesting transactions.
|
|
@@ -1001,7 +1063,7 @@ class EntityManager {
|
|
|
1001
1063
|
const em = this.getContext(false);
|
|
1002
1064
|
em.transactionContext = await em.getConnection('write').begin({
|
|
1003
1065
|
...options,
|
|
1004
|
-
eventBroadcaster: new
|
|
1066
|
+
eventBroadcaster: new TransactionEventBroadcaster(em, { topLevelTransaction: !options.ctx }),
|
|
1005
1067
|
});
|
|
1006
1068
|
}
|
|
1007
1069
|
/**
|
|
@@ -1014,10 +1076,10 @@ class EntityManager {
|
|
|
1014
1076
|
return;
|
|
1015
1077
|
}
|
|
1016
1078
|
if (!em.transactionContext) {
|
|
1017
|
-
throw
|
|
1079
|
+
throw ValidationError.transactionRequired();
|
|
1018
1080
|
}
|
|
1019
1081
|
await em.flush();
|
|
1020
|
-
await em.getConnection('write').commit(em.transactionContext, new
|
|
1082
|
+
await em.getConnection('write').commit(em.transactionContext, new TransactionEventBroadcaster(em));
|
|
1021
1083
|
delete em.transactionContext;
|
|
1022
1084
|
}
|
|
1023
1085
|
/**
|
|
@@ -1029,9 +1091,9 @@ class EntityManager {
|
|
|
1029
1091
|
}
|
|
1030
1092
|
const em = this.getContext(false);
|
|
1031
1093
|
if (!em.transactionContext) {
|
|
1032
|
-
throw
|
|
1094
|
+
throw ValidationError.transactionRequired();
|
|
1033
1095
|
}
|
|
1034
|
-
await em.getConnection('write').rollback(em.transactionContext, new
|
|
1096
|
+
await em.getConnection('write').rollback(em.transactionContext, new TransactionEventBroadcaster(em));
|
|
1035
1097
|
delete em.transactionContext;
|
|
1036
1098
|
em.unitOfWork.clearActionsQueue();
|
|
1037
1099
|
}
|
|
@@ -1039,7 +1101,7 @@ class EntityManager {
|
|
|
1039
1101
|
* Runs your callback wrapped inside a database transaction.
|
|
1040
1102
|
*/
|
|
1041
1103
|
async lock(entity, lockMode, options = {}) {
|
|
1042
|
-
options =
|
|
1104
|
+
options = Utils.isPlainObject(options) ? options : { lockVersion: options };
|
|
1043
1105
|
await this.getUnitOfWork().lock(entity, { lockMode, ...options });
|
|
1044
1106
|
}
|
|
1045
1107
|
/**
|
|
@@ -1054,26 +1116,26 @@ class EntityManager {
|
|
|
1054
1116
|
data = entityNameOrEntity;
|
|
1055
1117
|
}
|
|
1056
1118
|
else {
|
|
1057
|
-
entityName =
|
|
1119
|
+
entityName = Utils.className(entityNameOrEntity);
|
|
1058
1120
|
}
|
|
1059
|
-
if (
|
|
1060
|
-
if (options.schema &&
|
|
1061
|
-
|
|
1121
|
+
if (Utils.isEntity(data)) {
|
|
1122
|
+
if (options.schema && helper(data).getSchema() == null) {
|
|
1123
|
+
helper(data).setSchema(options.schema);
|
|
1062
1124
|
}
|
|
1063
|
-
if (!
|
|
1125
|
+
if (!helper(data).__managed) {
|
|
1064
1126
|
// the entity might have been created via `em.create()`, which adds it to the persist stack automatically
|
|
1065
1127
|
em.unitOfWork.getPersistStack().delete(data);
|
|
1066
1128
|
// it can be also in the identity map if it had a PK value already
|
|
1067
1129
|
em.unitOfWork.unsetIdentity(data);
|
|
1068
1130
|
}
|
|
1069
|
-
const meta =
|
|
1131
|
+
const meta = helper(data).__meta;
|
|
1070
1132
|
const payload = em.comparator.prepareEntity(data);
|
|
1071
|
-
const cs = new
|
|
1133
|
+
const cs = new ChangeSet(data, ChangeSetType.CREATE, payload, meta);
|
|
1072
1134
|
await em.unitOfWork.getChangeSetPersister().executeInserts([cs], { ctx: em.transactionContext, ...options });
|
|
1073
1135
|
return cs.getPrimaryKey();
|
|
1074
1136
|
}
|
|
1075
|
-
data =
|
|
1076
|
-
|
|
1137
|
+
data = QueryHelper.processObjectParams(data);
|
|
1138
|
+
validateParams(data, 'insert data');
|
|
1077
1139
|
const res = await em.driver.nativeInsert(entityName, data, { ctx: em.transactionContext, ...options });
|
|
1078
1140
|
return res.insertId;
|
|
1079
1141
|
}
|
|
@@ -1089,31 +1151,31 @@ class EntityManager {
|
|
|
1089
1151
|
data = entityNameOrEntities;
|
|
1090
1152
|
}
|
|
1091
1153
|
else {
|
|
1092
|
-
entityName =
|
|
1154
|
+
entityName = Utils.className(entityNameOrEntities);
|
|
1093
1155
|
}
|
|
1094
1156
|
if (data.length === 0) {
|
|
1095
1157
|
return [];
|
|
1096
1158
|
}
|
|
1097
|
-
if (
|
|
1098
|
-
const meta =
|
|
1159
|
+
if (Utils.isEntity(data[0])) {
|
|
1160
|
+
const meta = helper(data[0]).__meta;
|
|
1099
1161
|
const css = data.map(row => {
|
|
1100
|
-
if (options.schema &&
|
|
1101
|
-
|
|
1162
|
+
if (options.schema && helper(row).getSchema() == null) {
|
|
1163
|
+
helper(row).setSchema(options.schema);
|
|
1102
1164
|
}
|
|
1103
|
-
if (!
|
|
1165
|
+
if (!helper(row).__managed) {
|
|
1104
1166
|
// the entity might have been created via `em.create()`, which adds it to the persist stack automatically
|
|
1105
1167
|
em.unitOfWork.getPersistStack().delete(row);
|
|
1106
1168
|
// it can be also in the identity map if it had a PK value already
|
|
1107
1169
|
em.unitOfWork.unsetIdentity(row);
|
|
1108
1170
|
}
|
|
1109
1171
|
const payload = em.comparator.prepareEntity(row);
|
|
1110
|
-
return new
|
|
1172
|
+
return new ChangeSet(row, ChangeSetType.CREATE, payload, meta);
|
|
1111
1173
|
});
|
|
1112
1174
|
await em.unitOfWork.getChangeSetPersister().executeInserts(css, { ctx: em.transactionContext, ...options });
|
|
1113
1175
|
return css.map(cs => cs.getPrimaryKey());
|
|
1114
1176
|
}
|
|
1115
|
-
data = data.map(row =>
|
|
1116
|
-
data.forEach(row =>
|
|
1177
|
+
data = data.map(row => QueryHelper.processObjectParams(row));
|
|
1178
|
+
data.forEach(row => validateParams(row, 'insert data'));
|
|
1117
1179
|
const res = await em.driver.nativeInsertMany(entityName, data, { ctx: em.transactionContext, ...options });
|
|
1118
1180
|
if (res.insertedIds) {
|
|
1119
1181
|
return res.insertedIds;
|
|
@@ -1126,11 +1188,11 @@ class EntityManager {
|
|
|
1126
1188
|
async nativeUpdate(entityName, where, data, options = {}) {
|
|
1127
1189
|
const em = this.getContext(false);
|
|
1128
1190
|
em.prepareOptions(options);
|
|
1129
|
-
entityName =
|
|
1130
|
-
data =
|
|
1191
|
+
entityName = Utils.className(entityName);
|
|
1192
|
+
data = QueryHelper.processObjectParams(data);
|
|
1131
1193
|
where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
|
|
1132
|
-
|
|
1133
|
-
|
|
1194
|
+
validateParams(data, 'update data');
|
|
1195
|
+
validateParams(where, 'update condition');
|
|
1134
1196
|
const res = await em.driver.nativeUpdate(entityName, where, data, { ctx: em.transactionContext, ...options });
|
|
1135
1197
|
return res.affectedRows;
|
|
1136
1198
|
}
|
|
@@ -1140,9 +1202,9 @@ class EntityManager {
|
|
|
1140
1202
|
async nativeDelete(entityName, where, options = {}) {
|
|
1141
1203
|
const em = this.getContext(false);
|
|
1142
1204
|
em.prepareOptions(options);
|
|
1143
|
-
entityName =
|
|
1205
|
+
entityName = Utils.className(entityName);
|
|
1144
1206
|
where = await em.processWhere(entityName, where, options, 'delete');
|
|
1145
|
-
|
|
1207
|
+
validateParams(where, 'delete condition');
|
|
1146
1208
|
const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, ...options });
|
|
1147
1209
|
return res.affectedRows;
|
|
1148
1210
|
}
|
|
@@ -1150,18 +1212,20 @@ class EntityManager {
|
|
|
1150
1212
|
* Maps raw database result to an entity and merges it to this EntityManager.
|
|
1151
1213
|
*/
|
|
1152
1214
|
map(entityName, result, options = {}) {
|
|
1153
|
-
entityName =
|
|
1215
|
+
entityName = Utils.className(entityName);
|
|
1154
1216
|
const meta = this.metadata.get(entityName);
|
|
1155
1217
|
const data = this.driver.mapResult(result, meta);
|
|
1156
|
-
Object.keys(data)
|
|
1218
|
+
for (const k of Object.keys(data)) {
|
|
1157
1219
|
const prop = meta.properties[k];
|
|
1158
|
-
if (prop
|
|
1159
|
-
|
|
1220
|
+
if (prop?.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
|
|
1221
|
+
validateProperty(prop, data[k], data);
|
|
1160
1222
|
}
|
|
1161
|
-
}
|
|
1223
|
+
}
|
|
1162
1224
|
return this.merge(entityName, data, {
|
|
1163
1225
|
convertCustomTypes: true,
|
|
1164
|
-
refresh: true,
|
|
1226
|
+
refresh: true,
|
|
1227
|
+
validate: false,
|
|
1228
|
+
...options,
|
|
1165
1229
|
});
|
|
1166
1230
|
}
|
|
1167
1231
|
/**
|
|
@@ -1169,22 +1233,23 @@ class EntityManager {
|
|
|
1169
1233
|
* via second parameter. By default, it will return already loaded entities without modifying them.
|
|
1170
1234
|
*/
|
|
1171
1235
|
merge(entityName, data, options = {}) {
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
return em.merge(entityName.constructor.name, entityName, data);
|
|
1236
|
+
if (Utils.isEntity(entityName)) {
|
|
1237
|
+
return this.merge(entityName.constructor.name, entityName, data);
|
|
1175
1238
|
}
|
|
1239
|
+
const em = options.disableContextResolution ? this : this.getContext();
|
|
1176
1240
|
options.schema ??= em._schema;
|
|
1177
|
-
|
|
1178
|
-
|
|
1241
|
+
options.validate ??= true;
|
|
1242
|
+
options.cascade ??= true;
|
|
1243
|
+
entityName = Utils.className(entityName);
|
|
1244
|
+
validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1179
1245
|
let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
|
|
1180
|
-
if (entity &&
|
|
1246
|
+
if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
|
|
1181
1247
|
return entity;
|
|
1182
1248
|
}
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
em.
|
|
1187
|
-
em.unitOfWork.merge(entity);
|
|
1249
|
+
const dataIsEntity = Utils.isEntity(data);
|
|
1250
|
+
entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1251
|
+
const visited = options.cascade ? undefined : new Set([entity]);
|
|
1252
|
+
em.unitOfWork.merge(entity, visited);
|
|
1188
1253
|
return entity;
|
|
1189
1254
|
}
|
|
1190
1255
|
/**
|
|
@@ -1209,6 +1274,7 @@ class EntityManager {
|
|
|
1209
1274
|
...options,
|
|
1210
1275
|
newEntity: !options.managed,
|
|
1211
1276
|
merge: options.managed,
|
|
1277
|
+
normalizeAccessors: true,
|
|
1212
1278
|
});
|
|
1213
1279
|
options.persist ??= em.config.get('persistOnCreate');
|
|
1214
1280
|
if (options.persist && !this.getMetadata(entityName).embeddable) {
|
|
@@ -1220,7 +1286,7 @@ class EntityManager {
|
|
|
1220
1286
|
* Shortcut for `wrap(entity).assign(data, { em })`
|
|
1221
1287
|
*/
|
|
1222
1288
|
assign(entity, data, options = {}) {
|
|
1223
|
-
return
|
|
1289
|
+
return EntityAssigner.assign(entity, data, { em: this.getContext(), ...options });
|
|
1224
1290
|
}
|
|
1225
1291
|
/**
|
|
1226
1292
|
* Gets a reference to the entity identified by the given type and identifier without actually loading it, if the entity is not yet loaded
|
|
@@ -1228,16 +1294,16 @@ class EntityManager {
|
|
|
1228
1294
|
getReference(entityName, id, options = {}) {
|
|
1229
1295
|
options.schema ??= this.schema;
|
|
1230
1296
|
options.convertCustomTypes ??= false;
|
|
1231
|
-
const meta = this.metadata.get(
|
|
1232
|
-
if (
|
|
1297
|
+
const meta = this.metadata.get(Utils.className(entityName));
|
|
1298
|
+
if (Utils.isPrimaryKey(id)) {
|
|
1233
1299
|
if (meta.compositePK) {
|
|
1234
|
-
throw
|
|
1300
|
+
throw ValidationError.invalidCompositeIdentifier(meta);
|
|
1235
1301
|
}
|
|
1236
1302
|
id = [id];
|
|
1237
1303
|
}
|
|
1238
1304
|
const entity = this.getEntityFactory().createReference(entityName, id, { merge: true, ...options });
|
|
1239
1305
|
if (options.wrapped) {
|
|
1240
|
-
return
|
|
1306
|
+
return Reference.create(entity);
|
|
1241
1307
|
}
|
|
1242
1308
|
return entity;
|
|
1243
1309
|
}
|
|
@@ -1247,11 +1313,9 @@ class EntityManager {
|
|
|
1247
1313
|
async count(entityName, where = {}, options = {}) {
|
|
1248
1314
|
const em = this.getContext(false);
|
|
1249
1315
|
// Shallow copy options since the object will be modified when deleting orderBy
|
|
1250
|
-
options = {
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
};
|
|
1254
|
-
entityName = Utils_1.Utils.className(entityName);
|
|
1316
|
+
options = { ...options };
|
|
1317
|
+
em.prepareOptions(options);
|
|
1318
|
+
entityName = Utils.className(entityName);
|
|
1255
1319
|
await em.tryFlush(entityName, options);
|
|
1256
1320
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
1257
1321
|
options.populate = await em.preparePopulate(entityName, options);
|
|
@@ -1260,15 +1324,15 @@ class EntityManager {
|
|
|
1260
1324
|
const meta = em.metadata.find(entityName);
|
|
1261
1325
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
1262
1326
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
1263
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
1264
|
-
|
|
1327
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
1328
|
+
validateParams(where);
|
|
1265
1329
|
delete options.orderBy;
|
|
1266
1330
|
const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
|
|
1267
1331
|
const cached = await em.tryCache(entityName, options.cache, cacheKey);
|
|
1268
|
-
if (cached?.data) {
|
|
1332
|
+
if (cached?.data !== undefined) {
|
|
1269
1333
|
return cached.data;
|
|
1270
1334
|
}
|
|
1271
|
-
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, ...options });
|
|
1335
|
+
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
1272
1336
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1273
1337
|
return +count;
|
|
1274
1338
|
}
|
|
@@ -1278,30 +1342,23 @@ class EntityManager {
|
|
|
1278
1342
|
*/
|
|
1279
1343
|
persist(entity) {
|
|
1280
1344
|
const em = this.getContext();
|
|
1281
|
-
if (
|
|
1345
|
+
if (Utils.isEntity(entity)) {
|
|
1282
1346
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
1283
1347
|
em.unitOfWork.persist(entity, undefined, { cascade: false });
|
|
1284
1348
|
return em;
|
|
1285
1349
|
}
|
|
1286
|
-
const entities =
|
|
1350
|
+
const entities = Utils.asArray(entity);
|
|
1287
1351
|
for (const ent of entities) {
|
|
1288
|
-
if (!
|
|
1289
|
-
/*
|
|
1352
|
+
if (!Utils.isEntity(ent, true)) {
|
|
1353
|
+
/* v8 ignore next */
|
|
1290
1354
|
const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor.name) : undefined;
|
|
1291
|
-
throw
|
|
1355
|
+
throw ValidationError.notDiscoveredEntity(ent, meta);
|
|
1292
1356
|
}
|
|
1293
1357
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
1294
|
-
em.unitOfWork.persist(
|
|
1358
|
+
em.unitOfWork.persist(Reference.unwrapReference(ent), undefined, { cascade: false });
|
|
1295
1359
|
}
|
|
1296
1360
|
return this;
|
|
1297
1361
|
}
|
|
1298
|
-
/**
|
|
1299
|
-
* Persists your entity immediately, flushing all not yet persisted changes to the database too.
|
|
1300
|
-
* Equivalent to `em.persist(e).flush()`.
|
|
1301
|
-
*/
|
|
1302
|
-
async persistAndFlush(entity) {
|
|
1303
|
-
await this.persist(entity).flush();
|
|
1304
|
-
}
|
|
1305
1362
|
/**
|
|
1306
1363
|
* Marks entity for removal.
|
|
1307
1364
|
* A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
|
|
@@ -1310,28 +1367,21 @@ class EntityManager {
|
|
|
1310
1367
|
*/
|
|
1311
1368
|
remove(entity) {
|
|
1312
1369
|
const em = this.getContext();
|
|
1313
|
-
if (
|
|
1370
|
+
if (Utils.isEntity(entity)) {
|
|
1314
1371
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
1315
1372
|
em.unitOfWork.remove(entity, undefined, { cascade: false });
|
|
1316
1373
|
return em;
|
|
1317
1374
|
}
|
|
1318
|
-
const entities =
|
|
1375
|
+
const entities = Utils.asArray(entity, true);
|
|
1319
1376
|
for (const ent of entities) {
|
|
1320
|
-
if (!
|
|
1377
|
+
if (!Utils.isEntity(ent, true)) {
|
|
1321
1378
|
throw new Error(`You need to pass entity instance or reference to 'em.remove()'. To remove entities by condition, use 'em.nativeDelete()'.`);
|
|
1322
1379
|
}
|
|
1323
1380
|
// do not cascade just yet, cascading of entities in remove stack is done when flushing
|
|
1324
|
-
em.unitOfWork.remove(
|
|
1381
|
+
em.unitOfWork.remove(Reference.unwrapReference(ent), undefined, { cascade: false });
|
|
1325
1382
|
}
|
|
1326
1383
|
return em;
|
|
1327
1384
|
}
|
|
1328
|
-
/**
|
|
1329
|
-
* Removes an entity instance immediately, flushing all not yet persisted changes to the database too.
|
|
1330
|
-
* Equivalent to `em.remove(e).flush()`
|
|
1331
|
-
*/
|
|
1332
|
-
async removeAndFlush(entity) {
|
|
1333
|
-
await this.remove(entity).flush();
|
|
1334
|
-
}
|
|
1335
1385
|
/**
|
|
1336
1386
|
* Flushes all changes to objects that have been queued up to now to the database.
|
|
1337
1387
|
* This effectively synchronizes the in-memory state of managed objects with the database.
|
|
@@ -1345,12 +1395,12 @@ class EntityManager {
|
|
|
1345
1395
|
async tryFlush(entityName, options) {
|
|
1346
1396
|
const em = this.getContext();
|
|
1347
1397
|
const flushMode = options.flushMode ?? em.flushMode ?? em.config.get('flushMode');
|
|
1348
|
-
entityName =
|
|
1398
|
+
entityName = Utils.className(entityName);
|
|
1349
1399
|
const meta = em.metadata.get(entityName);
|
|
1350
|
-
if (flushMode ===
|
|
1400
|
+
if (flushMode === FlushMode.COMMIT) {
|
|
1351
1401
|
return;
|
|
1352
1402
|
}
|
|
1353
|
-
if (flushMode ===
|
|
1403
|
+
if (flushMode === FlushMode.ALWAYS || em.getUnitOfWork().shouldAutoFlush(meta)) {
|
|
1354
1404
|
await em.flush();
|
|
1355
1405
|
}
|
|
1356
1406
|
}
|
|
@@ -1364,7 +1414,7 @@ class EntityManager {
|
|
|
1364
1414
|
* Checks whether given property can be populated on the entity.
|
|
1365
1415
|
*/
|
|
1366
1416
|
canPopulate(entityName, property) {
|
|
1367
|
-
entityName =
|
|
1417
|
+
entityName = Utils.className(entityName);
|
|
1368
1418
|
// eslint-disable-next-line prefer-const
|
|
1369
1419
|
let [p, ...parts] = property.split('.');
|
|
1370
1420
|
const meta = this.metadata.find(entityName);
|
|
@@ -1387,14 +1437,14 @@ class EntityManager {
|
|
|
1387
1437
|
* Loads specified relations in batch. This will execute one query for each relation, that will populate it on all the specified entities.
|
|
1388
1438
|
*/
|
|
1389
1439
|
async populate(entities, populate, options = {}) {
|
|
1390
|
-
const arr =
|
|
1440
|
+
const arr = Utils.asArray(entities);
|
|
1391
1441
|
if (arr.length === 0) {
|
|
1392
1442
|
return entities;
|
|
1393
1443
|
}
|
|
1394
1444
|
const em = this.getContext();
|
|
1395
1445
|
em.prepareOptions(options);
|
|
1396
1446
|
const entityName = arr[0].constructor.name;
|
|
1397
|
-
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
|
|
1447
|
+
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1398
1448
|
await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1399
1449
|
return entities;
|
|
1400
1450
|
}
|
|
@@ -1408,7 +1458,7 @@ class EntityManager {
|
|
|
1408
1458
|
options.freshEventManager ??= false;
|
|
1409
1459
|
options.cloneEventManager ??= false;
|
|
1410
1460
|
const eventManager = options.freshEventManager
|
|
1411
|
-
? new
|
|
1461
|
+
? new EventManager(em.config.get('subscribers'))
|
|
1412
1462
|
: options.cloneEventManager
|
|
1413
1463
|
? em.eventManager.clone()
|
|
1414
1464
|
: em.eventManager;
|
|
@@ -1423,13 +1473,16 @@ class EntityManager {
|
|
|
1423
1473
|
fork.transactionContext = em.transactionContext;
|
|
1424
1474
|
}
|
|
1425
1475
|
fork.filters = { ...em.filters };
|
|
1426
|
-
fork.filterParams =
|
|
1427
|
-
fork.loggerContext =
|
|
1476
|
+
fork.filterParams = Utils.copy(em.filterParams);
|
|
1477
|
+
fork.loggerContext = Utils.merge({}, em.loggerContext, options.loggerContext);
|
|
1428
1478
|
fork._schema = options.schema ?? em._schema;
|
|
1429
1479
|
if (!options.clear) {
|
|
1430
1480
|
for (const entity of em.unitOfWork.getIdentityMap()) {
|
|
1431
1481
|
fork.unitOfWork.register(entity);
|
|
1432
1482
|
}
|
|
1483
|
+
for (const entity of em.unitOfWork.getPersistStack()) {
|
|
1484
|
+
fork.unitOfWork.persist(entity);
|
|
1485
|
+
}
|
|
1433
1486
|
for (const entity of em.unitOfWork.getOrphanRemoveStack()) {
|
|
1434
1487
|
fork.unitOfWork.getOrphanRemoveStack().add(entity);
|
|
1435
1488
|
}
|
|
@@ -1451,6 +1504,12 @@ class EntityManager {
|
|
|
1451
1504
|
getEntityFactory() {
|
|
1452
1505
|
return this.getContext().entityFactory;
|
|
1453
1506
|
}
|
|
1507
|
+
/**
|
|
1508
|
+
* @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
|
|
1509
|
+
*/
|
|
1510
|
+
getEntityLoader() {
|
|
1511
|
+
return this.getContext().entityLoader;
|
|
1512
|
+
}
|
|
1454
1513
|
/**
|
|
1455
1514
|
* Gets the Hydrator used by the EntityManager.
|
|
1456
1515
|
*/
|
|
@@ -1465,14 +1524,14 @@ class EntityManager {
|
|
|
1465
1524
|
if (!this.useContext) {
|
|
1466
1525
|
return this;
|
|
1467
1526
|
}
|
|
1468
|
-
let em =
|
|
1527
|
+
let em = TransactionContext.getEntityManager(this.name); // prefer the tx context
|
|
1469
1528
|
if (em) {
|
|
1470
1529
|
return em;
|
|
1471
1530
|
}
|
|
1472
1531
|
// no explicit tx started
|
|
1473
1532
|
em = this.config.get('context')(this.name) ?? this;
|
|
1474
1533
|
if (validate && !this.config.get('allowGlobalContext') && em.global) {
|
|
1475
|
-
throw
|
|
1534
|
+
throw ValidationError.cannotUseGlobalContext();
|
|
1476
1535
|
}
|
|
1477
1536
|
return em;
|
|
1478
1537
|
}
|
|
@@ -1513,7 +1572,7 @@ class EntityManager {
|
|
|
1513
1572
|
*/
|
|
1514
1573
|
getMetadata(entityName) {
|
|
1515
1574
|
if (entityName) {
|
|
1516
|
-
entityName =
|
|
1575
|
+
entityName = Utils.className(entityName);
|
|
1517
1576
|
return this.metadata.get(entityName);
|
|
1518
1577
|
}
|
|
1519
1578
|
return this.metadata;
|
|
@@ -1528,15 +1587,15 @@ class EntityManager {
|
|
|
1528
1587
|
if (!mode) {
|
|
1529
1588
|
return;
|
|
1530
1589
|
}
|
|
1531
|
-
if (mode ===
|
|
1532
|
-
throw
|
|
1590
|
+
if (mode === LockMode.OPTIMISTIC && !meta.versionProperty) {
|
|
1591
|
+
throw OptimisticLockError.notVersioned(meta);
|
|
1533
1592
|
}
|
|
1534
|
-
if ([
|
|
1535
|
-
throw
|
|
1593
|
+
if ([LockMode.PESSIMISTIC_READ, LockMode.PESSIMISTIC_WRITE].includes(mode) && !this.isInTransaction()) {
|
|
1594
|
+
throw ValidationError.transactionRequired();
|
|
1536
1595
|
}
|
|
1537
1596
|
}
|
|
1538
1597
|
async lockAndPopulate(meta, entity, where, options) {
|
|
1539
|
-
if (!meta.virtual && options.lockMode ===
|
|
1598
|
+
if (!meta.virtual && options.lockMode === LockMode.OPTIMISTIC) {
|
|
1540
1599
|
await this.lock(entity, options.lockMode, {
|
|
1541
1600
|
lockVersion: options.lockVersion,
|
|
1542
1601
|
lockTableAliases: options.lockTableAliases,
|
|
@@ -1547,7 +1606,6 @@ class EntityManager {
|
|
|
1547
1606
|
...options,
|
|
1548
1607
|
...this.getPopulateWhere(where, options),
|
|
1549
1608
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
1550
|
-
convertCustomTypes: false,
|
|
1551
1609
|
ignoreLazyScalarProperties: true,
|
|
1552
1610
|
lookup: false,
|
|
1553
1611
|
});
|
|
@@ -1555,8 +1613,8 @@ class EntityManager {
|
|
|
1555
1613
|
}
|
|
1556
1614
|
buildFields(fields) {
|
|
1557
1615
|
return fields.reduce((ret, f) => {
|
|
1558
|
-
if (
|
|
1559
|
-
|
|
1616
|
+
if (Utils.isPlainObject(f)) {
|
|
1617
|
+
Utils.keys(f).forEach(ff => ret.push(...this.buildFields(f[ff]).map(field => `${ff}.${field}`)));
|
|
1560
1618
|
}
|
|
1561
1619
|
else {
|
|
1562
1620
|
ret.push(f);
|
|
@@ -1575,12 +1633,12 @@ class EntityManager {
|
|
|
1575
1633
|
const pruneToOneRelations = (meta, fields) => {
|
|
1576
1634
|
const ret = [];
|
|
1577
1635
|
for (let field of fields) {
|
|
1578
|
-
if (field ===
|
|
1579
|
-
ret.push(...meta.props.filter(prop => prop.lazy || [
|
|
1636
|
+
if (field === PopulatePath.ALL || field.startsWith(`${PopulatePath.ALL}.`)) {
|
|
1637
|
+
ret.push(...meta.props.filter(prop => prop.lazy || [ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind)).map(prop => prop.name));
|
|
1580
1638
|
continue;
|
|
1581
1639
|
}
|
|
1582
1640
|
field = field.split(':')[0];
|
|
1583
|
-
if (!field.includes('.') && ![
|
|
1641
|
+
if (!field.includes('.') && ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(meta.properties[field].kind)) {
|
|
1584
1642
|
ret.push(field);
|
|
1585
1643
|
continue;
|
|
1586
1644
|
}
|
|
@@ -1599,7 +1657,7 @@ class EntityManager {
|
|
|
1599
1657
|
ret.push(...inner.map(c => `${key}.${c}`));
|
|
1600
1658
|
}
|
|
1601
1659
|
}
|
|
1602
|
-
return
|
|
1660
|
+
return Utils.unique(ret);
|
|
1603
1661
|
};
|
|
1604
1662
|
options.populate = pruneToOneRelations(meta, this.buildFields(options.fields));
|
|
1605
1663
|
}
|
|
@@ -1609,18 +1667,18 @@ class EntityManager {
|
|
|
1609
1667
|
return populate;
|
|
1610
1668
|
}
|
|
1611
1669
|
if (typeof options.populate !== 'boolean') {
|
|
1612
|
-
options.populate =
|
|
1613
|
-
/*
|
|
1614
|
-
if (typeof field === 'boolean' || field ===
|
|
1670
|
+
options.populate = Utils.asArray(options.populate).map(field => {
|
|
1671
|
+
/* v8 ignore next */
|
|
1672
|
+
if (typeof field === 'boolean' || field === PopulatePath.ALL) {
|
|
1615
1673
|
return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
|
|
1616
1674
|
}
|
|
1617
1675
|
// will be handled in QueryBuilder when processing the where condition via CriteriaNode
|
|
1618
|
-
if (field ===
|
|
1676
|
+
if (field === PopulatePath.INFER) {
|
|
1619
1677
|
options.flags ??= [];
|
|
1620
|
-
options.flags.push(
|
|
1678
|
+
options.flags.push(QueryFlag.INFER_POPULATE);
|
|
1621
1679
|
return [];
|
|
1622
1680
|
}
|
|
1623
|
-
if (
|
|
1681
|
+
if (typeof field === 'string') {
|
|
1624
1682
|
return [{ field, strategy: options.strategy }];
|
|
1625
1683
|
}
|
|
1626
1684
|
return [field];
|
|
@@ -1629,13 +1687,13 @@ class EntityManager {
|
|
|
1629
1687
|
const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy);
|
|
1630
1688
|
const invalid = populate.find(({ field }) => !this.canPopulate(entityName, field));
|
|
1631
1689
|
if (validate && invalid) {
|
|
1632
|
-
throw
|
|
1690
|
+
throw ValidationError.invalidPropertyName(entityName, invalid.field);
|
|
1633
1691
|
}
|
|
1634
1692
|
await this.autoJoinRefsForFilters(meta, { ...options, populate });
|
|
1635
1693
|
return populate.map(field => {
|
|
1636
1694
|
// force select-in strategy when populating all relations as otherwise we could cause infinite loops when self-referencing
|
|
1637
1695
|
const all = field.all ?? (Array.isArray(options.populate) && options.populate.includes('*'));
|
|
1638
|
-
field.strategy = all ?
|
|
1696
|
+
field.strategy = all ? LoadStrategy.SELECT_IN : (options.strategy ?? field.strategy);
|
|
1639
1697
|
return field;
|
|
1640
1698
|
});
|
|
1641
1699
|
}
|
|
@@ -1644,33 +1702,33 @@ class EntityManager {
|
|
|
1644
1702
|
* some additional lazy properties, if so, we reload and merge the data from database
|
|
1645
1703
|
*/
|
|
1646
1704
|
shouldRefresh(meta, entity, options) {
|
|
1647
|
-
if (!
|
|
1705
|
+
if (!helper(entity).__initialized || options.refresh) {
|
|
1648
1706
|
return true;
|
|
1649
1707
|
}
|
|
1650
1708
|
let autoRefresh;
|
|
1651
1709
|
if (options.fields) {
|
|
1652
|
-
autoRefresh = options.fields.some(field => !
|
|
1710
|
+
autoRefresh = options.fields.some(field => !helper(entity).__loadedProperties.has(field));
|
|
1653
1711
|
}
|
|
1654
1712
|
else {
|
|
1655
1713
|
autoRefresh = meta.comparableProps.some(prop => {
|
|
1656
|
-
const inlineEmbedded = prop.kind ===
|
|
1657
|
-
return !inlineEmbedded && !prop.lazy && !
|
|
1714
|
+
const inlineEmbedded = prop.kind === ReferenceKind.EMBEDDED && !prop.object;
|
|
1715
|
+
return !inlineEmbedded && !prop.lazy && !helper(entity).__loadedProperties.has(prop.name);
|
|
1658
1716
|
});
|
|
1659
1717
|
}
|
|
1660
1718
|
if (autoRefresh) {
|
|
1661
1719
|
return true;
|
|
1662
1720
|
}
|
|
1663
1721
|
if (Array.isArray(options.populate)) {
|
|
1664
|
-
return options.populate.some(field => !
|
|
1722
|
+
return options.populate.some(field => !helper(entity).__loadedProperties.has(field));
|
|
1665
1723
|
}
|
|
1666
1724
|
return !!options.populate;
|
|
1667
1725
|
}
|
|
1668
1726
|
prepareOptions(options) {
|
|
1669
|
-
if (!
|
|
1670
|
-
throw new
|
|
1727
|
+
if (!Utils.isEmpty(options.fields) && !Utils.isEmpty(options.exclude)) {
|
|
1728
|
+
throw new ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
|
|
1671
1729
|
}
|
|
1672
1730
|
options.schema ??= this._schema;
|
|
1673
|
-
options.logging =
|
|
1731
|
+
options.logging = options.loggerContext = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1674
1732
|
}
|
|
1675
1733
|
/**
|
|
1676
1734
|
* @internal
|
|
@@ -1694,31 +1752,31 @@ class EntityManager {
|
|
|
1694
1752
|
const em = this.getContext();
|
|
1695
1753
|
const cacheKey = Array.isArray(config) ? config[0] : JSON.stringify(key);
|
|
1696
1754
|
const cached = await em.resultCache.get(cacheKey);
|
|
1697
|
-
if (cached) {
|
|
1698
|
-
|
|
1699
|
-
if (Array.isArray(cached) && merge) {
|
|
1700
|
-
data = cached.map(item => em.entityFactory.create(entityName, item, {
|
|
1701
|
-
merge: true,
|
|
1702
|
-
convertCustomTypes: true,
|
|
1703
|
-
refresh,
|
|
1704
|
-
recomputeSnapshot: true,
|
|
1705
|
-
}));
|
|
1706
|
-
}
|
|
1707
|
-
else if (Utils_1.Utils.isObject(cached) && merge) {
|
|
1708
|
-
data = em.entityFactory.create(entityName, cached, {
|
|
1709
|
-
merge: true,
|
|
1710
|
-
convertCustomTypes: true,
|
|
1711
|
-
refresh,
|
|
1712
|
-
recomputeSnapshot: true,
|
|
1713
|
-
});
|
|
1714
|
-
}
|
|
1715
|
-
else {
|
|
1716
|
-
data = cached;
|
|
1717
|
-
}
|
|
1718
|
-
await em.unitOfWork.dispatchOnLoadEvent();
|
|
1719
|
-
return { key: cacheKey, data };
|
|
1755
|
+
if (!cached) {
|
|
1756
|
+
return { key: cacheKey, data: cached };
|
|
1720
1757
|
}
|
|
1721
|
-
|
|
1758
|
+
let data;
|
|
1759
|
+
if (Array.isArray(cached) && merge) {
|
|
1760
|
+
data = cached.map(item => em.entityFactory.create(entityName, item, {
|
|
1761
|
+
merge: true,
|
|
1762
|
+
convertCustomTypes: true,
|
|
1763
|
+
refresh,
|
|
1764
|
+
recomputeSnapshot: true,
|
|
1765
|
+
}));
|
|
1766
|
+
}
|
|
1767
|
+
else if (Utils.isObject(cached) && merge) {
|
|
1768
|
+
data = em.entityFactory.create(entityName, cached, {
|
|
1769
|
+
merge: true,
|
|
1770
|
+
convertCustomTypes: true,
|
|
1771
|
+
refresh,
|
|
1772
|
+
recomputeSnapshot: true,
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
else {
|
|
1776
|
+
data = cached;
|
|
1777
|
+
}
|
|
1778
|
+
await em.unitOfWork.dispatchOnLoadEvent();
|
|
1779
|
+
return { key: cacheKey, data };
|
|
1722
1780
|
}
|
|
1723
1781
|
/**
|
|
1724
1782
|
* @internal
|
|
@@ -1727,7 +1785,7 @@ class EntityManager {
|
|
|
1727
1785
|
config ??= this.config.get('resultCache').global;
|
|
1728
1786
|
if (config) {
|
|
1729
1787
|
const em = this.getContext();
|
|
1730
|
-
const expiration = Array.isArray(config) ? config[1] : (
|
|
1788
|
+
const expiration = Array.isArray(config) ? config[1] : (typeof config === 'number' ? config : undefined);
|
|
1731
1789
|
await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
|
|
1732
1790
|
}
|
|
1733
1791
|
}
|
|
@@ -1760,6 +1818,19 @@ class EntityManager {
|
|
|
1760
1818
|
set schema(schema) {
|
|
1761
1819
|
this.getContext(false)._schema = schema ?? undefined;
|
|
1762
1820
|
}
|
|
1821
|
+
/** @internal */
|
|
1822
|
+
async getDataLoader(type) {
|
|
1823
|
+
const em = this.getContext();
|
|
1824
|
+
if (em.loaders[type]) {
|
|
1825
|
+
return em.loaders[type];
|
|
1826
|
+
}
|
|
1827
|
+
const DataLoader = await DataloaderUtils.getDataLoader();
|
|
1828
|
+
switch (type) {
|
|
1829
|
+
case 'ref': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
|
|
1830
|
+
case '1:m': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
|
|
1831
|
+
case 'm:n': return (em.loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1763
1834
|
/**
|
|
1764
1835
|
* Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
|
|
1765
1836
|
* if executed inside request context handler.
|
|
@@ -1768,8 +1839,7 @@ class EntityManager {
|
|
|
1768
1839
|
return this.getContext(false)._id;
|
|
1769
1840
|
}
|
|
1770
1841
|
/** @ignore */
|
|
1771
|
-
[
|
|
1842
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
1772
1843
|
return `[EntityManager<${this.id}>]`;
|
|
1773
1844
|
}
|
|
1774
1845
|
}
|
|
1775
|
-
exports.EntityManager = EntityManager;
|