@mikro-orm/core 7.0.0-dev.33 → 7.0.0-dev.331
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 +70 -75
- package/EntityManager.js +487 -402
- package/MikroORM.d.ts +45 -38
- package/MikroORM.js +123 -156
- package/README.md +7 -4
- package/cache/FileCacheAdapter.d.ts +2 -7
- package/cache/FileCacheAdapter.js +35 -30
- package/cache/GeneratedCacheAdapter.d.ts +1 -2
- package/cache/GeneratedCacheAdapter.js +6 -8
- package/cache/MemoryCacheAdapter.d.ts +1 -2
- package/cache/MemoryCacheAdapter.js +8 -8
- package/cache/index.d.ts +1 -2
- package/cache/index.js +0 -2
- package/connections/Connection.d.ts +12 -5
- package/connections/Connection.js +37 -15
- package/drivers/DatabaseDriver.d.ts +25 -18
- package/drivers/DatabaseDriver.js +144 -45
- package/drivers/IDatabaseDriver.d.ts +118 -23
- package/entity/BaseEntity.d.ts +63 -4
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +95 -31
- package/entity/Collection.js +487 -139
- package/entity/EntityAssigner.js +37 -25
- package/entity/EntityFactory.d.ts +8 -9
- package/entity/EntityFactory.js +152 -100
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +69 -27
- package/entity/EntityLoader.d.ts +12 -13
- package/entity/EntityLoader.js +286 -125
- package/entity/EntityRepository.d.ts +28 -8
- package/entity/EntityRepository.js +8 -2
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/Reference.d.ts +3 -8
- package/entity/Reference.js +62 -29
- package/entity/WrappedEntity.d.ts +7 -10
- package/entity/WrappedEntity.js +6 -7
- package/entity/defineEntity.d.ts +472 -313
- package/entity/defineEntity.js +134 -290
- package/entity/index.d.ts +2 -2
- package/entity/index.js +2 -2
- package/entity/utils.d.ts +6 -1
- package/entity/utils.js +46 -11
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +66 -0
- package/enums.d.ts +8 -6
- package/enums.js +13 -17
- package/errors.d.ts +26 -16
- package/errors.js +63 -31
- package/events/EventManager.d.ts +3 -5
- package/events/EventManager.js +37 -26
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/exceptions.js +9 -2
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +5 -6
- package/hydration/ObjectHydrator.js +109 -50
- package/index.d.ts +2 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/DefaultLogger.js +3 -4
- package/logging/SimpleLogger.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +4 -6
- package/logging/index.d.ts +2 -1
- package/logging/index.js +1 -1
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +11 -0
- package/metadata/EntitySchema.d.ts +47 -23
- package/metadata/EntitySchema.js +103 -34
- package/metadata/MetadataDiscovery.d.ts +65 -18
- package/metadata/MetadataDiscovery.js +940 -424
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +71 -2
- package/metadata/MetadataStorage.d.ts +11 -13
- package/metadata/MetadataStorage.js +79 -48
- package/metadata/MetadataValidator.d.ts +32 -9
- package/metadata/MetadataValidator.js +214 -44
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +40 -0
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +0 -1
- package/metadata/types.d.ts +577 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
- package/naming-strategy/AbstractNamingStrategy.js +26 -5
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +7 -6
- package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
- package/naming-strategy/MongoNamingStrategy.js +6 -6
- package/naming-strategy/NamingStrategy.d.ts +28 -4
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/naming-strategy/index.d.ts +1 -1
- package/naming-strategy/index.js +0 -1
- package/not-supported.d.ts +2 -0
- package/not-supported.js +8 -0
- package/package.json +47 -36
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +33 -15
- package/platforms/Platform.js +125 -69
- package/serialization/EntitySerializer.d.ts +6 -3
- package/serialization/EntitySerializer.js +54 -30
- package/serialization/EntityTransformer.js +37 -22
- package/serialization/SerializationContext.d.ts +10 -14
- package/serialization/SerializationContext.js +24 -19
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.js +1 -1
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +1 -0
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.js +2 -2
- package/types/DoubleType.js +1 -1
- package/types/EnumArrayType.js +1 -2
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/TinyIntType.js +1 -1
- package/types/Type.d.ts +2 -4
- package/types/Type.js +3 -3
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +1 -4
- package/types/UuidType.d.ts +2 -0
- package/types/UuidType.js +14 -2
- package/types/index.d.ts +3 -2
- package/typings.d.ts +427 -170
- package/typings.js +100 -45
- package/unit-of-work/ChangeSet.d.ts +4 -6
- package/unit-of-work/ChangeSet.js +8 -9
- package/unit-of-work/ChangeSetComputer.d.ts +2 -12
- package/unit-of-work/ChangeSetComputer.js +61 -38
- package/unit-of-work/ChangeSetPersister.d.ts +10 -17
- package/unit-of-work/ChangeSetPersister.js +136 -73
- package/unit-of-work/CommitOrderCalculator.d.ts +13 -14
- package/unit-of-work/CommitOrderCalculator.js +22 -20
- package/unit-of-work/IdentityMap.d.ts +12 -3
- package/unit-of-work/IdentityMap.js +51 -13
- package/unit-of-work/UnitOfWork.d.ts +39 -23
- package/unit-of-work/UnitOfWork.js +441 -246
- package/utils/AbstractMigrator.d.ts +101 -0
- package/utils/AbstractMigrator.js +303 -0
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +30 -18
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +647 -185
- package/utils/Configuration.js +215 -252
- package/utils/ConfigurationLoader.d.ts +1 -52
- package/utils/ConfigurationLoader.js +1 -330
- package/utils/Cursor.d.ts +3 -6
- package/utils/Cursor.js +32 -17
- package/utils/DataloaderUtils.d.ts +10 -5
- package/utils/DataloaderUtils.js +42 -22
- package/utils/EntityComparator.d.ts +21 -21
- package/utils/EntityComparator.js +224 -118
- package/utils/QueryHelper.d.ts +34 -7
- package/utils/QueryHelper.js +183 -72
- package/utils/RawQueryFragment.d.ts +28 -34
- package/utils/RawQueryFragment.js +37 -72
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +11 -8
- package/utils/Utils.d.ts +16 -127
- package/utils/Utils.js +104 -402
- package/utils/clone.js +13 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +98 -0
- package/utils/fs-utils.d.ts +20 -0
- package/utils/fs-utils.js +193 -0
- package/utils/index.d.ts +1 -3
- package/utils/index.js +1 -3
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +51 -5
- 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 -12
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -33
- 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 -42
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -34
- 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 -28
- 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 -14
- 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 -118
- package/entity/ArrayCollection.js +0 -407
- package/entity/EntityValidator.d.ts +0 -19
- package/entity/EntityValidator.js +0 -150
- 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,15 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import DataLoader from 'dataloader';
|
|
3
|
-
import { getOnConflictReturningFields } from './utils/upsert-utils.js';
|
|
1
|
+
import { getOnConflictReturningFields, getWhereCondition } from './utils/upsert-utils.js';
|
|
4
2
|
import { Utils } from './utils/Utils.js';
|
|
5
3
|
import { Cursor } from './utils/Cursor.js';
|
|
6
|
-
import { DataloaderUtils } from './utils/DataloaderUtils.js';
|
|
7
4
|
import { QueryHelper } from './utils/QueryHelper.js';
|
|
8
5
|
import { TransactionContext } from './utils/TransactionContext.js';
|
|
9
|
-
import { isRaw,
|
|
6
|
+
import { isRaw, Raw } from './utils/RawQueryFragment.js';
|
|
10
7
|
import { EntityFactory } from './entity/EntityFactory.js';
|
|
11
8
|
import { EntityAssigner } from './entity/EntityAssigner.js';
|
|
12
|
-
import {
|
|
9
|
+
import { validateEmptyWhere, validateParams, validatePrimaryKey, validateProperty } from './entity/validators.js';
|
|
13
10
|
import { EntityLoader } from './entity/EntityLoader.js';
|
|
14
11
|
import { Reference } from './entity/Reference.js';
|
|
15
12
|
import { helper } from './entity/wrap.js';
|
|
@@ -19,7 +16,7 @@ import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePat
|
|
|
19
16
|
import { EventManager } from './events/EventManager.js';
|
|
20
17
|
import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
|
|
21
18
|
import { OptimisticLockError, ValidationError } from './errors.js';
|
|
22
|
-
import { getLoadingStrategy } from './entity/utils.js';
|
|
19
|
+
import { applyPopulateHints, getLoadingStrategy } from './entity/utils.js';
|
|
23
20
|
import { TransactionManager } from './utils/TransactionManager.js';
|
|
24
21
|
/**
|
|
25
22
|
* The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
|
|
@@ -30,29 +27,27 @@ export class EntityManager {
|
|
|
30
27
|
config;
|
|
31
28
|
driver;
|
|
32
29
|
metadata;
|
|
33
|
-
useContext;
|
|
34
30
|
eventManager;
|
|
35
|
-
static counter = 1;
|
|
36
|
-
|
|
31
|
+
static #counter = 1;
|
|
32
|
+
/** @internal */
|
|
33
|
+
_id = EntityManager.#counter++;
|
|
37
34
|
global = false;
|
|
38
35
|
name;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
resultCache;
|
|
49
|
-
filters = {};
|
|
50
|
-
filterParams = {};
|
|
36
|
+
#loaders = {};
|
|
37
|
+
#repositoryMap = new Map();
|
|
38
|
+
#entityLoader;
|
|
39
|
+
#comparator;
|
|
40
|
+
#entityFactory;
|
|
41
|
+
#unitOfWork;
|
|
42
|
+
#resultCache;
|
|
43
|
+
#filters = {};
|
|
44
|
+
#filterParams = {};
|
|
51
45
|
loggerContext;
|
|
52
|
-
transactionContext;
|
|
53
|
-
disableTransactions;
|
|
54
|
-
flushMode;
|
|
55
|
-
|
|
46
|
+
#transactionContext;
|
|
47
|
+
#disableTransactions;
|
|
48
|
+
#flushMode;
|
|
49
|
+
#schema;
|
|
50
|
+
#useContext;
|
|
56
51
|
/**
|
|
57
52
|
* @internal
|
|
58
53
|
*/
|
|
@@ -60,16 +55,15 @@ export class EntityManager {
|
|
|
60
55
|
this.config = config;
|
|
61
56
|
this.driver = driver;
|
|
62
57
|
this.metadata = metadata;
|
|
63
|
-
this.useContext = useContext;
|
|
64
58
|
this.eventManager = eventManager;
|
|
65
|
-
this
|
|
59
|
+
this.#useContext = useContext;
|
|
60
|
+
this.#entityLoader = new EntityLoader(this);
|
|
66
61
|
this.name = this.config.get('contextName');
|
|
67
|
-
this
|
|
68
|
-
this
|
|
69
|
-
this
|
|
70
|
-
this
|
|
71
|
-
this
|
|
72
|
-
this.unitOfWork = new UnitOfWork(this);
|
|
62
|
+
this.#comparator = this.config.getComparator(this.metadata);
|
|
63
|
+
this.#resultCache = this.config.getResultCacheAdapter();
|
|
64
|
+
this.#disableTransactions = this.config.get('disableTransactions');
|
|
65
|
+
this.#entityFactory = new EntityFactory(this);
|
|
66
|
+
this.#unitOfWork = new UnitOfWork(this);
|
|
73
67
|
}
|
|
74
68
|
/**
|
|
75
69
|
* Gets the Driver instance used by this EntityManager.
|
|
@@ -94,13 +88,12 @@ export class EntityManager {
|
|
|
94
88
|
* Gets repository for given entity. You can pass either string name or entity class reference.
|
|
95
89
|
*/
|
|
96
90
|
getRepository(entityName) {
|
|
97
|
-
|
|
98
|
-
if (!this.
|
|
99
|
-
const meta = this.metadata.get(entityName);
|
|
91
|
+
const meta = this.metadata.get(entityName);
|
|
92
|
+
if (!this.#repositoryMap.has(meta)) {
|
|
100
93
|
const RepositoryClass = this.config.getRepositoryClass(meta.repository);
|
|
101
|
-
this.
|
|
94
|
+
this.#repositoryMap.set(meta, new RepositoryClass(this, entityName));
|
|
102
95
|
}
|
|
103
|
-
return this.
|
|
96
|
+
return this.#repositoryMap.get(meta);
|
|
104
97
|
}
|
|
105
98
|
/**
|
|
106
99
|
* Shortcut for `em.getRepository()`.
|
|
@@ -108,12 +101,6 @@ export class EntityManager {
|
|
|
108
101
|
repo(entityName) {
|
|
109
102
|
return this.getRepository(entityName);
|
|
110
103
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Gets EntityValidator instance
|
|
113
|
-
*/
|
|
114
|
-
getValidator() {
|
|
115
|
-
return this.validator;
|
|
116
|
-
}
|
|
117
104
|
/**
|
|
118
105
|
* Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
|
|
119
106
|
*/
|
|
@@ -128,16 +115,21 @@ export class EntityManager {
|
|
|
128
115
|
const em = this.getContext();
|
|
129
116
|
em.prepareOptions(options);
|
|
130
117
|
await em.tryFlush(entityName, options);
|
|
131
|
-
entityName = Utils.className(entityName);
|
|
132
118
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
119
|
+
validateParams(where);
|
|
120
|
+
const meta = this.metadata.get(entityName);
|
|
121
|
+
if (meta.orderBy) {
|
|
122
|
+
options.orderBy = QueryHelper.mergeOrderBy(options.orderBy, meta.orderBy);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
options.orderBy ??= {};
|
|
126
|
+
}
|
|
127
|
+
options.populate = (await em.preparePopulate(entityName, options));
|
|
136
128
|
const populate = options.populate;
|
|
137
129
|
const cacheKey = em.cacheKey(entityName, options, 'em.find', where);
|
|
138
130
|
const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
|
|
139
131
|
if (cached?.data) {
|
|
140
|
-
await em
|
|
132
|
+
await em.#entityLoader.populate(entityName, cached.data, populate, {
|
|
141
133
|
...options,
|
|
142
134
|
...em.getPopulateWhere(where, options),
|
|
143
135
|
ignoreLazyScalarProperties: true,
|
|
@@ -145,20 +137,20 @@ export class EntityManager {
|
|
|
145
137
|
});
|
|
146
138
|
return cached.data;
|
|
147
139
|
}
|
|
148
|
-
const meta = this.metadata.get(entityName);
|
|
149
140
|
options = { ...options };
|
|
150
141
|
// save the original hint value so we know it was infer/all
|
|
151
142
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
152
143
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
153
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
154
|
-
|
|
144
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
145
|
+
await em.processUnionWhere(entityName, options, 'read');
|
|
146
|
+
const results = await em.driver.find(entityName, where, { ctx: em.#transactionContext, em, ...options });
|
|
155
147
|
if (results.length === 0) {
|
|
156
148
|
await em.storeCache(options.cache, cached, []);
|
|
157
149
|
return [];
|
|
158
150
|
}
|
|
159
151
|
const ret = [];
|
|
160
152
|
for (const data of results) {
|
|
161
|
-
const entity = em
|
|
153
|
+
const entity = em.#entityFactory.create(entityName, data, {
|
|
162
154
|
merge: true,
|
|
163
155
|
refresh: options.refresh,
|
|
164
156
|
schema: options.schema,
|
|
@@ -167,13 +159,13 @@ export class EntityManager {
|
|
|
167
159
|
ret.push(entity);
|
|
168
160
|
}
|
|
169
161
|
const unique = Utils.unique(ret);
|
|
170
|
-
await em
|
|
162
|
+
await em.#entityLoader.populate(entityName, unique, populate, {
|
|
171
163
|
...options,
|
|
172
164
|
...em.getPopulateWhere(where, options),
|
|
173
165
|
ignoreLazyScalarProperties: true,
|
|
174
166
|
lookup: false,
|
|
175
167
|
});
|
|
176
|
-
await em
|
|
168
|
+
await em.#unitOfWork.dispatchOnLoadEvent();
|
|
177
169
|
if (meta.virtual) {
|
|
178
170
|
await em.storeCache(options.cache, cached, () => ret);
|
|
179
171
|
}
|
|
@@ -182,6 +174,60 @@ export class EntityManager {
|
|
|
182
174
|
}
|
|
183
175
|
return unique;
|
|
184
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Finds all entities and returns an async iterable (async generator) that yields results one by one.
|
|
179
|
+
* The results are merged and mapped to entity instances, without adding them to the identity map.
|
|
180
|
+
* You can disable merging by passing the options `{ mergeResults: false }`.
|
|
181
|
+
* With `mergeResults` disabled, to-many collections will contain at most one item, and you will get duplicate
|
|
182
|
+
* root entities when there are multiple items in the populated collection.
|
|
183
|
+
* This is useful for processing large datasets without loading everything into memory at once.
|
|
184
|
+
*
|
|
185
|
+
* ```ts
|
|
186
|
+
* const stream = em.stream(Book, { populate: ['author'] });
|
|
187
|
+
*
|
|
188
|
+
* for await (const book of stream) {
|
|
189
|
+
* // book is an instance of Book entity
|
|
190
|
+
* console.log(book.title, book.author.name);
|
|
191
|
+
* }
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
async *stream(entityName, options = {}) {
|
|
195
|
+
const em = this.getContext();
|
|
196
|
+
em.prepareOptions(options);
|
|
197
|
+
options.strategy = 'joined';
|
|
198
|
+
await em.tryFlush(entityName, options);
|
|
199
|
+
const where = (await em.processWhere(entityName, options.where ?? {}, options, 'read'));
|
|
200
|
+
validateParams(where);
|
|
201
|
+
options.orderBy = options.orderBy || {};
|
|
202
|
+
options.populate = (await em.preparePopulate(entityName, options));
|
|
203
|
+
const meta = this.metadata.get(entityName);
|
|
204
|
+
options = { ...options };
|
|
205
|
+
// save the original hint value so we know it was infer/all
|
|
206
|
+
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
207
|
+
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
208
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
209
|
+
const stream = em.driver.stream(entityName, where, {
|
|
210
|
+
ctx: em.#transactionContext,
|
|
211
|
+
mapResults: false,
|
|
212
|
+
...options,
|
|
213
|
+
});
|
|
214
|
+
for await (const data of stream) {
|
|
215
|
+
const fork = em.fork();
|
|
216
|
+
const entity = fork.#entityFactory.create(entityName, data, {
|
|
217
|
+
refresh: options.refresh,
|
|
218
|
+
schema: options.schema,
|
|
219
|
+
convertCustomTypes: true,
|
|
220
|
+
});
|
|
221
|
+
helper(entity).setSerializationContext({
|
|
222
|
+
populate: options.populate,
|
|
223
|
+
fields: options.fields,
|
|
224
|
+
exclude: options.exclude,
|
|
225
|
+
});
|
|
226
|
+
await fork.#unitOfWork.dispatchOnLoadEvent();
|
|
227
|
+
fork.clear();
|
|
228
|
+
yield entity;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
185
231
|
/**
|
|
186
232
|
* Finds all entities of given type, optionally matching the `where` condition provided in the `options` parameter.
|
|
187
233
|
*/
|
|
@@ -195,7 +241,7 @@ export class EntityManager {
|
|
|
195
241
|
if (options.populateWhere === PopulateHint.ALL) {
|
|
196
242
|
return { where: {}, populateWhere: options.populateWhere };
|
|
197
243
|
}
|
|
198
|
-
/* v8 ignore next
|
|
244
|
+
/* v8 ignore next */
|
|
199
245
|
if (options.populateWhere === PopulateHint.INFER) {
|
|
200
246
|
return { where, populateWhere: options.populateWhere };
|
|
201
247
|
}
|
|
@@ -204,25 +250,25 @@ export class EntityManager {
|
|
|
204
250
|
/**
|
|
205
251
|
* Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
|
|
206
252
|
*/
|
|
207
|
-
addFilter(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
options.entity = Utils.asArray(entityName).map(n => Utils.className(n));
|
|
253
|
+
addFilter(options) {
|
|
254
|
+
if (options.entity) {
|
|
255
|
+
options.entity = Utils.asArray(options.entity).map(n => Utils.className(n));
|
|
211
256
|
}
|
|
212
|
-
|
|
257
|
+
options.default ??= true;
|
|
258
|
+
this.getContext(false).#filters[options.name] = options;
|
|
213
259
|
}
|
|
214
260
|
/**
|
|
215
261
|
* Sets filter parameter values globally inside context defined by this entity manager.
|
|
216
262
|
* If you want to set shared value for all contexts, be sure to use the root entity manager.
|
|
217
263
|
*/
|
|
218
264
|
setFilterParams(name, args) {
|
|
219
|
-
this.getContext()
|
|
265
|
+
this.getContext().#filterParams[name] = args;
|
|
220
266
|
}
|
|
221
267
|
/**
|
|
222
268
|
* Returns filter parameters for given filter set in this context.
|
|
223
269
|
*/
|
|
224
270
|
getFilterParams(name) {
|
|
225
|
-
return this.getContext()
|
|
271
|
+
return this.getContext().#filterParams[name];
|
|
226
272
|
}
|
|
227
273
|
/**
|
|
228
274
|
* Sets logger context for this entity manager.
|
|
@@ -239,7 +285,7 @@ export class EntityManager {
|
|
|
239
285
|
return em.loggerContext;
|
|
240
286
|
}
|
|
241
287
|
setFlushMode(flushMode) {
|
|
242
|
-
this.getContext(false)
|
|
288
|
+
this.getContext(false).#flushMode = flushMode;
|
|
243
289
|
}
|
|
244
290
|
async processWhere(entityName, where, options, type) {
|
|
245
291
|
where = QueryHelper.processWhere({
|
|
@@ -254,23 +300,34 @@ export class EntityManager {
|
|
|
254
300
|
where = this.applyDiscriminatorCondition(entityName, where);
|
|
255
301
|
return where;
|
|
256
302
|
}
|
|
303
|
+
async processUnionWhere(entityName, options, type) {
|
|
304
|
+
if (options.unionWhere?.length) {
|
|
305
|
+
if (!this.driver.getPlatform().supportsUnionWhere()) {
|
|
306
|
+
throw new Error(`unionWhere is only supported on SQL drivers`);
|
|
307
|
+
}
|
|
308
|
+
options.unionWhere = (await Promise.all(options.unionWhere.map(branch => this.processWhere(entityName, branch, options, type))));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
257
311
|
// this method only handles the problem for mongo driver, SQL drivers have their implementation inside QueryBuilder
|
|
258
312
|
applyDiscriminatorCondition(entityName, where) {
|
|
259
313
|
const meta = this.metadata.find(entityName);
|
|
260
|
-
if (!meta?.discriminatorValue) {
|
|
314
|
+
if (meta?.root.inheritanceType !== 'sti' || !meta?.discriminatorValue) {
|
|
261
315
|
return where;
|
|
262
316
|
}
|
|
263
|
-
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.
|
|
317
|
+
const types = Object.values(meta.root.discriminatorMap).map(cls => this.metadata.get(cls));
|
|
264
318
|
const children = [];
|
|
265
319
|
const lookUpChildren = (ret, type) => {
|
|
266
320
|
const children = types.filter(meta2 => meta2.extends === type);
|
|
267
|
-
children.forEach(m => lookUpChildren(ret, m.
|
|
321
|
+
children.forEach(m => lookUpChildren(ret, m.class));
|
|
268
322
|
ret.push(...children.filter(c => c.discriminatorValue));
|
|
269
323
|
return children;
|
|
270
324
|
};
|
|
271
|
-
lookUpChildren(children, meta.
|
|
325
|
+
lookUpChildren(children, meta.class);
|
|
272
326
|
/* v8 ignore next */
|
|
273
|
-
where[meta.root.discriminatorColumn] =
|
|
327
|
+
where[meta.root.discriminatorColumn] =
|
|
328
|
+
children.length > 0
|
|
329
|
+
? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] }
|
|
330
|
+
: meta.discriminatorValue;
|
|
274
331
|
return where;
|
|
275
332
|
}
|
|
276
333
|
createPopulateWhere(cond, options) {
|
|
@@ -284,60 +341,78 @@ export class EntityManager {
|
|
|
284
341
|
}
|
|
285
342
|
return ret;
|
|
286
343
|
}
|
|
287
|
-
async getJoinedFilters(meta,
|
|
344
|
+
async getJoinedFilters(meta, options) {
|
|
345
|
+
// If user provided populateFilter, merge it with computed filters
|
|
346
|
+
const userFilter = options.populateFilter;
|
|
347
|
+
if (!this.config.get('filtersOnRelations') || !options.populate) {
|
|
348
|
+
return userFilter;
|
|
349
|
+
}
|
|
288
350
|
const ret = {};
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
351
|
+
for (const hint of options.populate) {
|
|
352
|
+
const field = hint.field.split(':')[0];
|
|
353
|
+
const prop = meta.properties[field];
|
|
354
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
355
|
+
const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
|
|
356
|
+
if (!joined && !hint.filter) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
const filters = QueryHelper.mergePropertyFilters(prop.filters, options.filters);
|
|
360
|
+
const where = await this.applyFilters(prop.targetMeta.class, {}, filters, 'read', {
|
|
361
|
+
...options,
|
|
362
|
+
populate: hint.children,
|
|
363
|
+
});
|
|
364
|
+
const where2 = await this.getJoinedFilters(prop.targetMeta, {
|
|
365
|
+
...options,
|
|
366
|
+
filters,
|
|
367
|
+
populate: hint.children,
|
|
368
|
+
populateWhere: PopulateHint.ALL,
|
|
369
|
+
});
|
|
370
|
+
if (Utils.hasObjectKeys(where)) {
|
|
371
|
+
ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
|
|
372
|
+
}
|
|
373
|
+
if (where2 && Utils.hasObjectKeys(where2)) {
|
|
374
|
+
if (ret[field]) {
|
|
375
|
+
Utils.merge(ret[field], where2);
|
|
297
376
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (Utils.hasObjectKeys(where)) {
|
|
301
|
-
ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
|
|
302
|
-
}
|
|
303
|
-
if (Utils.hasObjectKeys(where2)) {
|
|
304
|
-
if (ret[field]) {
|
|
305
|
-
Utils.merge(ret[field], where2);
|
|
306
|
-
}
|
|
307
|
-
else {
|
|
308
|
-
ret[field] = where2;
|
|
309
|
-
}
|
|
377
|
+
else {
|
|
378
|
+
ret[field] = where2;
|
|
310
379
|
}
|
|
311
380
|
}
|
|
312
381
|
}
|
|
313
|
-
|
|
382
|
+
// Merge user-provided populateFilter with computed filters
|
|
383
|
+
if (userFilter) {
|
|
384
|
+
Utils.merge(ret, userFilter);
|
|
385
|
+
}
|
|
386
|
+
return Utils.hasObjectKeys(ret) ? ret : undefined;
|
|
314
387
|
}
|
|
315
388
|
/**
|
|
316
389
|
* 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.
|
|
317
390
|
*/
|
|
318
|
-
async autoJoinRefsForFilters(meta, options) {
|
|
319
|
-
if (!meta || !this.config.get('autoJoinRefsForFilters')) {
|
|
391
|
+
async autoJoinRefsForFilters(meta, options, parent) {
|
|
392
|
+
if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
|
|
320
393
|
return;
|
|
321
394
|
}
|
|
322
|
-
const props = meta.relations.filter(prop => {
|
|
323
|
-
return !prop.object && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)
|
|
324
|
-
&& ((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)));
|
|
325
|
-
});
|
|
326
395
|
const ret = options.populate;
|
|
327
|
-
for (const prop of
|
|
328
|
-
|
|
396
|
+
for (const prop of meta.relations) {
|
|
397
|
+
if (prop.object ||
|
|
398
|
+
![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) ||
|
|
399
|
+
!((options.fields?.length ?? 0) === 0 ||
|
|
400
|
+
options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`))) ||
|
|
401
|
+
(parent?.class === prop.targetMeta.root.class && parent.propName === prop.inversedBy)) {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
options = { ...options, filters: QueryHelper.mergePropertyFilters(prop.filters, options.filters) };
|
|
405
|
+
const cond = await this.applyFilters(prop.targetMeta.class, {}, options.filters, 'read', options);
|
|
329
406
|
if (!Utils.isEmpty(cond)) {
|
|
330
407
|
const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
|
|
331
408
|
let found = false;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
found = true;
|
|
340
|
-
}
|
|
409
|
+
for (const hint of populated) {
|
|
410
|
+
if (!hint.all) {
|
|
411
|
+
hint.filter = true;
|
|
412
|
+
}
|
|
413
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
414
|
+
if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === LoadStrategy.JOINED)) {
|
|
415
|
+
found = true;
|
|
341
416
|
}
|
|
342
417
|
}
|
|
343
418
|
if (!found) {
|
|
@@ -345,27 +420,30 @@ export class EntityManager {
|
|
|
345
420
|
}
|
|
346
421
|
}
|
|
347
422
|
}
|
|
423
|
+
for (const hint of ret) {
|
|
424
|
+
const [field, ref] = hint.field.split(':');
|
|
425
|
+
const prop = meta?.properties[field];
|
|
426
|
+
if (prop && !ref) {
|
|
427
|
+
hint.children ??= [];
|
|
428
|
+
await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { class: meta.root.class, propName: prop.name });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
348
431
|
}
|
|
349
432
|
/**
|
|
350
433
|
* @internal
|
|
351
434
|
*/
|
|
352
435
|
async applyFilters(entityName, where, options, type, findOptions) {
|
|
353
|
-
const meta = this.metadata.
|
|
436
|
+
const meta = this.metadata.get(entityName);
|
|
354
437
|
const filters = [];
|
|
355
438
|
const ret = [];
|
|
356
|
-
if (!meta) {
|
|
357
|
-
return where;
|
|
358
|
-
}
|
|
359
439
|
const active = new Set();
|
|
360
440
|
const push = (source) => {
|
|
361
|
-
const activeFilters = QueryHelper
|
|
362
|
-
.getActiveFilters(entityName, options, source)
|
|
363
|
-
.filter(f => !active.has(f.name));
|
|
441
|
+
const activeFilters = QueryHelper.getActiveFilters(meta, options, source).filter(f => !active.has(f.name));
|
|
364
442
|
filters.push(...activeFilters);
|
|
365
443
|
activeFilters.forEach(f => active.add(f.name));
|
|
366
444
|
};
|
|
367
445
|
push(this.config.get('filters'));
|
|
368
|
-
push(this
|
|
446
|
+
push(this.#filters);
|
|
369
447
|
push(meta.filters);
|
|
370
448
|
if (filters.length === 0) {
|
|
371
449
|
return where;
|
|
@@ -374,24 +452,29 @@ export class EntityManager {
|
|
|
374
452
|
let cond;
|
|
375
453
|
if (filter.cond instanceof Function) {
|
|
376
454
|
// @ts-ignore
|
|
377
|
-
|
|
455
|
+
// oxfmt-ignore
|
|
456
|
+
const args = Utils.isPlainObject(options?.[filter.name]) ? options[filter.name] : this.getContext().#filterParams[filter.name];
|
|
378
457
|
if (!args && filter.cond.length > 0 && filter.args !== false) {
|
|
379
458
|
throw new Error(`No arguments provided for filter '${filter.name}'`);
|
|
380
459
|
}
|
|
381
|
-
cond = await filter.cond(args, type, this, findOptions, entityName);
|
|
460
|
+
cond = await filter.cond(args, type, this, findOptions, Utils.className(entityName));
|
|
382
461
|
}
|
|
383
462
|
else {
|
|
384
463
|
cond = filter.cond;
|
|
385
464
|
}
|
|
386
|
-
|
|
465
|
+
cond = QueryHelper.processWhere({
|
|
387
466
|
where: cond,
|
|
388
467
|
entityName,
|
|
389
468
|
metadata: this.metadata,
|
|
390
469
|
platform: this.driver.getPlatform(),
|
|
391
470
|
aliased: type === 'read',
|
|
392
|
-
})
|
|
471
|
+
});
|
|
472
|
+
if (filter.strict) {
|
|
473
|
+
Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
|
|
474
|
+
}
|
|
475
|
+
ret.push(cond);
|
|
393
476
|
}
|
|
394
|
-
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c));
|
|
477
|
+
const conds = [...ret, where].filter(c => Utils.hasObjectKeys(c) || Raw.hasObjectFragments(c));
|
|
395
478
|
return conds.length > 1 ? { $and: conds } : conds[0];
|
|
396
479
|
}
|
|
397
480
|
/**
|
|
@@ -402,12 +485,10 @@ export class EntityManager {
|
|
|
402
485
|
const em = this.getContext(false);
|
|
403
486
|
await em.tryFlush(entityName, options);
|
|
404
487
|
options.flushMode = 'commit'; // do not try to auto flush again
|
|
405
|
-
return
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
]);
|
|
410
|
-
});
|
|
488
|
+
return Promise.all([
|
|
489
|
+
em.find(entityName, where, options),
|
|
490
|
+
em.count(entityName, where, options),
|
|
491
|
+
]);
|
|
411
492
|
}
|
|
412
493
|
/**
|
|
413
494
|
* Calls `em.find()` and `em.count()` with the same arguments (where applicable) and returns the results as {@apilink Cursor} object.
|
|
@@ -423,21 +504,21 @@ export class EntityManager {
|
|
|
423
504
|
* - POJO/entity instance
|
|
424
505
|
*
|
|
425
506
|
* ```ts
|
|
426
|
-
* const currentCursor = await em.findByCursor(User, {
|
|
507
|
+
* const currentCursor = await em.findByCursor(User, {
|
|
427
508
|
* first: 10,
|
|
428
509
|
* after: previousCursor, // cursor instance
|
|
429
510
|
* orderBy: { id: 'desc' },
|
|
430
511
|
* });
|
|
431
512
|
*
|
|
432
513
|
* // to fetch next page
|
|
433
|
-
* const nextCursor = await em.findByCursor(User, {
|
|
514
|
+
* const nextCursor = await em.findByCursor(User, {
|
|
434
515
|
* first: 10,
|
|
435
516
|
* after: currentCursor.endCursor, // opaque string
|
|
436
517
|
* orderBy: { id: 'desc' },
|
|
437
518
|
* });
|
|
438
519
|
*
|
|
439
520
|
* // to fetch next page
|
|
440
|
-
* const nextCursor2 = await em.findByCursor(User, {
|
|
521
|
+
* const nextCursor2 = await em.findByCursor(User, {
|
|
441
522
|
* first: 10,
|
|
442
523
|
* after: { id: lastSeenId }, // entity-like POJO
|
|
443
524
|
* orderBy: { id: 'desc' },
|
|
@@ -465,16 +546,16 @@ export class EntityManager {
|
|
|
465
546
|
* }
|
|
466
547
|
* ```
|
|
467
548
|
*/
|
|
468
|
-
async findByCursor(entityName,
|
|
549
|
+
async findByCursor(entityName, options) {
|
|
469
550
|
const em = this.getContext(false);
|
|
470
|
-
entityName = Utils.className(entityName);
|
|
471
551
|
options.overfetch ??= true;
|
|
472
|
-
|
|
552
|
+
options.where ??= {};
|
|
553
|
+
if (Utils.isEmpty(options.orderBy) && !Raw.hasObjectFragments(options.orderBy)) {
|
|
473
554
|
throw new Error('Explicit `orderBy` option required');
|
|
474
555
|
}
|
|
475
556
|
const [entities, count] = options.includeCount !== false
|
|
476
|
-
? await em.findAndCount(entityName, where, options)
|
|
477
|
-
: [await em.find(entityName, where, options)];
|
|
557
|
+
? await em.findAndCount(entityName, options.where, options)
|
|
558
|
+
: [await em.find(entityName, options.where, options)];
|
|
478
559
|
return new Cursor(entities, count, options, this.metadata.get(entityName));
|
|
479
560
|
}
|
|
480
561
|
/**
|
|
@@ -486,9 +567,9 @@ export class EntityManager {
|
|
|
486
567
|
const ret = await this.refresh(entity, options);
|
|
487
568
|
if (!ret) {
|
|
488
569
|
options.failHandler ??= this.config.get('findOneOrFailHandler');
|
|
489
|
-
const
|
|
490
|
-
const where =
|
|
491
|
-
throw options.failHandler(
|
|
570
|
+
const wrapped = helper(entity);
|
|
571
|
+
const where = wrapped.getPrimaryKey();
|
|
572
|
+
throw options.failHandler(wrapped.__meta.className, where);
|
|
492
573
|
}
|
|
493
574
|
return ret;
|
|
494
575
|
}
|
|
@@ -499,29 +580,37 @@ export class EntityManager {
|
|
|
499
580
|
*/
|
|
500
581
|
async refresh(entity, options = {}) {
|
|
501
582
|
const fork = this.fork({ keepTransactionContext: true });
|
|
502
|
-
const
|
|
503
|
-
const reloaded = await fork.findOne(
|
|
504
|
-
schema:
|
|
583
|
+
const wrapped = helper(entity);
|
|
584
|
+
const reloaded = await fork.findOne(wrapped.__meta.class, entity, {
|
|
585
|
+
schema: wrapped.__schema,
|
|
505
586
|
...options,
|
|
506
587
|
flushMode: FlushMode.COMMIT,
|
|
507
588
|
});
|
|
508
589
|
const em = this.getContext();
|
|
509
590
|
if (!reloaded) {
|
|
510
|
-
em
|
|
591
|
+
em.#unitOfWork.unsetIdentity(entity);
|
|
511
592
|
return null;
|
|
512
593
|
}
|
|
513
594
|
let found = false;
|
|
514
|
-
for (const e of fork
|
|
515
|
-
const ref = em.getReference(e.constructor
|
|
516
|
-
const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true });
|
|
517
|
-
em.config
|
|
518
|
-
|
|
595
|
+
for (const e of fork.#unitOfWork.getIdentityMap()) {
|
|
596
|
+
const ref = em.getReference(e.constructor, helper(e).getPrimaryKey());
|
|
597
|
+
const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true, convertCustomTypes: false });
|
|
598
|
+
em.config
|
|
599
|
+
.getHydrator(this.metadata)
|
|
600
|
+
.hydrate(ref, helper(ref).__meta, data, em.#entityFactory, 'full', false, false);
|
|
601
|
+
Utils.merge(helper(ref).__originalEntityData, this.#comparator.prepareEntity(e));
|
|
519
602
|
found ||= ref === entity;
|
|
520
603
|
}
|
|
521
604
|
if (!found) {
|
|
522
|
-
const data = helper(reloaded).serialize({
|
|
523
|
-
|
|
524
|
-
|
|
605
|
+
const data = helper(reloaded).serialize({
|
|
606
|
+
ignoreSerializers: true,
|
|
607
|
+
includeHidden: true,
|
|
608
|
+
convertCustomTypes: true,
|
|
609
|
+
});
|
|
610
|
+
em.config
|
|
611
|
+
.getHydrator(this.metadata)
|
|
612
|
+
.hydrate(entity, wrapped.__meta, data, em.#entityFactory, 'full', false, true);
|
|
613
|
+
Utils.merge(wrapped.__originalEntityData, this.#comparator.prepareEntity(reloaded));
|
|
525
614
|
}
|
|
526
615
|
return entity;
|
|
527
616
|
}
|
|
@@ -537,9 +626,8 @@ export class EntityManager {
|
|
|
537
626
|
return ret;
|
|
538
627
|
}
|
|
539
628
|
const em = this.getContext();
|
|
540
|
-
entityName = Utils.className(entityName);
|
|
541
629
|
em.prepareOptions(options);
|
|
542
|
-
let entity = em
|
|
630
|
+
let entity = em.#unitOfWork.tryGetById(entityName, where, options.schema);
|
|
543
631
|
// query for a not managed entity which is already in the identity map as it
|
|
544
632
|
// was provided with a PK this entity does not exist in the db, there can't
|
|
545
633
|
// be any relations to it, so no need to deal with the populate hint
|
|
@@ -549,19 +637,19 @@ export class EntityManager {
|
|
|
549
637
|
await em.tryFlush(entityName, options);
|
|
550
638
|
const meta = em.metadata.get(entityName);
|
|
551
639
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
552
|
-
|
|
640
|
+
validateEmptyWhere(where);
|
|
553
641
|
em.checkLockRequirements(options.lockMode, meta);
|
|
554
|
-
const isOptimisticLocking =
|
|
642
|
+
const isOptimisticLocking = options.lockMode == null || options.lockMode === LockMode.OPTIMISTIC;
|
|
555
643
|
if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
|
|
556
644
|
return em.lockAndPopulate(meta, entity, where, options);
|
|
557
645
|
}
|
|
558
|
-
|
|
559
|
-
options.populate = await em.preparePopulate(entityName, options);
|
|
646
|
+
validateParams(where);
|
|
647
|
+
options.populate = (await em.preparePopulate(entityName, options));
|
|
560
648
|
const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
|
|
561
649
|
const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
|
|
562
650
|
if (cached?.data !== undefined) {
|
|
563
651
|
if (cached.data) {
|
|
564
|
-
await em
|
|
652
|
+
await em.#entityLoader.populate(entityName, [cached.data], options.populate, {
|
|
565
653
|
...options,
|
|
566
654
|
...em.getPopulateWhere(where, options),
|
|
567
655
|
ignoreLazyScalarProperties: true,
|
|
@@ -574,9 +662,10 @@ export class EntityManager {
|
|
|
574
662
|
// save the original hint value so we know it was infer/all
|
|
575
663
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
576
664
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
577
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
665
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
666
|
+
await em.processUnionWhere(entityName, options, 'read');
|
|
578
667
|
const data = await em.driver.findOne(entityName, where, {
|
|
579
|
-
ctx: em
|
|
668
|
+
ctx: em.#transactionContext,
|
|
580
669
|
em,
|
|
581
670
|
...options,
|
|
582
671
|
});
|
|
@@ -584,14 +673,14 @@ export class EntityManager {
|
|
|
584
673
|
await em.storeCache(options.cache, cached, null);
|
|
585
674
|
return null;
|
|
586
675
|
}
|
|
587
|
-
entity = em
|
|
676
|
+
entity = em.#entityFactory.create(entityName, data, {
|
|
588
677
|
merge: true,
|
|
589
678
|
refresh: options.refresh,
|
|
590
679
|
schema: options.schema,
|
|
591
680
|
convertCustomTypes: true,
|
|
592
681
|
});
|
|
593
682
|
await em.lockAndPopulate(meta, entity, where, options);
|
|
594
|
-
await em
|
|
683
|
+
await em.#unitOfWork.dispatchOnLoadEvent();
|
|
595
684
|
await em.storeCache(options.cache, cached, () => helper(entity).toPOJO());
|
|
596
685
|
return entity;
|
|
597
686
|
}
|
|
@@ -615,10 +704,10 @@ export class EntityManager {
|
|
|
615
704
|
if (!entity || isStrictViolation) {
|
|
616
705
|
const key = options.strict ? 'findExactlyOneOrFailHandler' : 'findOneOrFailHandler';
|
|
617
706
|
options.failHandler ??= this.config.get(key);
|
|
618
|
-
|
|
707
|
+
const name = Utils.className(entityName);
|
|
619
708
|
/* v8 ignore next */
|
|
620
709
|
where = Utils.isEntity(where) ? helper(where).getPrimaryKey() : where;
|
|
621
|
-
throw options.failHandler(
|
|
710
|
+
throw options.failHandler(name, where);
|
|
622
711
|
}
|
|
623
712
|
return entity;
|
|
624
713
|
}
|
|
@@ -658,71 +747,57 @@ export class EntityManager {
|
|
|
658
747
|
let where;
|
|
659
748
|
let entity = null;
|
|
660
749
|
if (data === undefined) {
|
|
661
|
-
entityName = entityNameOrEntity.constructor
|
|
750
|
+
entityName = entityNameOrEntity.constructor;
|
|
662
751
|
data = entityNameOrEntity;
|
|
663
752
|
}
|
|
664
753
|
else {
|
|
665
|
-
entityName =
|
|
754
|
+
entityName = entityNameOrEntity;
|
|
666
755
|
}
|
|
667
756
|
const meta = this.metadata.get(entityName);
|
|
668
757
|
const convertCustomTypes = !Utils.isEntity(data);
|
|
669
758
|
if (Utils.isEntity(data)) {
|
|
670
759
|
entity = data;
|
|
671
760
|
if (helper(entity).__managed && helper(entity).__em === em && !this.config.get('upsertManaged')) {
|
|
672
|
-
em
|
|
761
|
+
em.#entityFactory.mergeData(meta, entity, data, { initialized: true });
|
|
673
762
|
return entity;
|
|
674
763
|
}
|
|
675
764
|
where = helper(entity).getPrimaryKey();
|
|
676
|
-
data = em
|
|
765
|
+
data = em.#comparator.prepareEntity(entity);
|
|
677
766
|
}
|
|
678
767
|
else {
|
|
679
768
|
data = Utils.copy(QueryHelper.processParams(data));
|
|
680
769
|
where = Utils.extractPK(data, meta);
|
|
681
770
|
if (where && !this.config.get('upsertManaged')) {
|
|
682
|
-
const exists = em
|
|
771
|
+
const exists = em.#unitOfWork.getById(entityName, where, options.schema);
|
|
683
772
|
if (exists) {
|
|
684
773
|
return em.assign(exists, data);
|
|
685
774
|
}
|
|
686
775
|
}
|
|
687
776
|
}
|
|
688
|
-
|
|
689
|
-
const propIndex = !isRaw(unique) && unique.findIndex(p => data[p] != null);
|
|
690
|
-
if (options.onConflictFields || where == null) {
|
|
691
|
-
if (propIndex !== false && propIndex >= 0) {
|
|
692
|
-
where = { [unique[propIndex]]: data[unique[propIndex]] };
|
|
693
|
-
}
|
|
694
|
-
else if (meta.uniques.length > 0) {
|
|
695
|
-
for (const u of meta.uniques) {
|
|
696
|
-
if (Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
697
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
698
|
-
o[key] = data[key];
|
|
699
|
-
return o;
|
|
700
|
-
}, {});
|
|
701
|
-
break;
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
}
|
|
777
|
+
where = getWhereCondition(meta, options.onConflictFields, data, where).where;
|
|
706
778
|
data = QueryHelper.processObjectParams(data);
|
|
707
|
-
|
|
779
|
+
validateParams(data, 'insert data');
|
|
708
780
|
if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
|
|
709
781
|
await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
|
|
710
782
|
}
|
|
711
783
|
const ret = await em.driver.nativeUpdate(entityName, where, data, {
|
|
712
|
-
ctx: em
|
|
784
|
+
ctx: em.#transactionContext,
|
|
713
785
|
upsert: true,
|
|
714
786
|
convertCustomTypes,
|
|
715
787
|
...options,
|
|
716
788
|
});
|
|
717
|
-
em
|
|
718
|
-
entity ??= em
|
|
789
|
+
em.#unitOfWork.getChangeSetPersister().mapReturnedValues(entity, data, ret.row, meta, true);
|
|
790
|
+
entity ??= em.#entityFactory.create(entityName, data, {
|
|
719
791
|
refresh: true,
|
|
720
792
|
initialized: true,
|
|
721
793
|
schema: options.schema,
|
|
722
794
|
});
|
|
723
|
-
const uniqueFields = options.onConflictFields ??
|
|
795
|
+
const uniqueFields = options.onConflictFields ??
|
|
796
|
+
(Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys);
|
|
724
797
|
const returning = getOnConflictReturningFields(meta, data, uniqueFields, options);
|
|
725
|
-
if (options.onConflictAction === 'ignore' ||
|
|
798
|
+
if (options.onConflictAction === 'ignore' ||
|
|
799
|
+
!helper(entity).hasPrimaryKey() ||
|
|
800
|
+
(returning.length > 0 && !(this.getPlatform().usesReturningStatement() && ret.row))) {
|
|
726
801
|
const where = {};
|
|
727
802
|
if (Array.isArray(uniqueFields)) {
|
|
728
803
|
for (const prop of uniqueFields) {
|
|
@@ -742,18 +817,18 @@ export class EntityManager {
|
|
|
742
817
|
where[meta.primaryKeys[0]] = ret.insertId;
|
|
743
818
|
}
|
|
744
819
|
}
|
|
745
|
-
const data2 = await this.driver.findOne(meta.
|
|
820
|
+
const data2 = await this.driver.findOne(meta.class, where, {
|
|
746
821
|
fields: returning,
|
|
747
|
-
ctx: em
|
|
822
|
+
ctx: em.#transactionContext,
|
|
748
823
|
convertCustomTypes: true,
|
|
749
824
|
connectionType: 'write',
|
|
750
825
|
schema: options.schema,
|
|
751
826
|
});
|
|
752
|
-
em.getHydrator().hydrate(entity, meta, data2, em
|
|
827
|
+
em.getHydrator().hydrate(entity, meta, data2, em.#entityFactory, 'full', false, true);
|
|
753
828
|
}
|
|
754
829
|
// recompute the data as there might be some values missing (e.g. those with db column defaults)
|
|
755
|
-
const snapshot = this
|
|
756
|
-
em
|
|
830
|
+
const snapshot = this.#comparator.prepareEntity(entity);
|
|
831
|
+
em.#unitOfWork.register(entity, snapshot, { refresh: true });
|
|
757
832
|
if (em.eventManager.hasListeners(EventType.afterUpsert, meta)) {
|
|
758
833
|
await em.eventManager.dispatchEvent(EventType.afterUpsert, { entity, em, meta }, meta);
|
|
759
834
|
}
|
|
@@ -797,11 +872,11 @@ export class EntityManager {
|
|
|
797
872
|
let entityName;
|
|
798
873
|
let propIndex;
|
|
799
874
|
if (data === undefined) {
|
|
800
|
-
entityName = entityNameOrEntity[0].constructor
|
|
875
|
+
entityName = entityNameOrEntity[0].constructor;
|
|
801
876
|
data = entityNameOrEntity;
|
|
802
877
|
}
|
|
803
878
|
else {
|
|
804
|
-
entityName =
|
|
879
|
+
entityName = entityNameOrEntity;
|
|
805
880
|
}
|
|
806
881
|
const batchSize = options.batchSize ?? this.config.get('batchSize');
|
|
807
882
|
if (data.length > batchSize) {
|
|
@@ -824,19 +899,19 @@ export class EntityManager {
|
|
|
824
899
|
if (Utils.isEntity(row)) {
|
|
825
900
|
const entity = row;
|
|
826
901
|
if (helper(entity).__managed && helper(entity).__em === em && !this.config.get('upsertManaged')) {
|
|
827
|
-
em
|
|
902
|
+
em.#entityFactory.mergeData(meta, entity, row, { initialized: true });
|
|
828
903
|
entities.set(entity, row);
|
|
829
904
|
entitiesByData.set(row, entity);
|
|
830
905
|
continue;
|
|
831
906
|
}
|
|
832
907
|
where = helper(entity).getPrimaryKey();
|
|
833
|
-
row = em
|
|
908
|
+
row = em.#comparator.prepareEntity(entity);
|
|
834
909
|
}
|
|
835
910
|
else {
|
|
836
911
|
row = data[i] = Utils.copy(QueryHelper.processParams(row));
|
|
837
912
|
where = Utils.extractPK(row, meta);
|
|
838
913
|
if (where && !this.config.get('upsertManaged')) {
|
|
839
|
-
const exists = em
|
|
914
|
+
const exists = em.#unitOfWork.getById(entityName, where, options.schema);
|
|
840
915
|
if (exists) {
|
|
841
916
|
em.assign(exists, row);
|
|
842
917
|
entities.set(exists, row);
|
|
@@ -845,32 +920,20 @@ export class EntityManager {
|
|
|
845
920
|
}
|
|
846
921
|
}
|
|
847
922
|
}
|
|
848
|
-
const unique = meta.props.filter(p => p.unique).map(p => p.name);
|
|
849
|
-
propIndex =
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
else if (meta.uniques.length > 0) {
|
|
855
|
-
for (const u of meta.uniques) {
|
|
856
|
-
if (Utils.asArray(u.properties).every(p => row[p] != null)) {
|
|
857
|
-
where = Utils.asArray(u.properties).reduce((o, key) => {
|
|
858
|
-
o[key] = row[key];
|
|
859
|
-
return o;
|
|
860
|
-
}, {});
|
|
861
|
-
break;
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
row = QueryHelper.processObjectParams(row);
|
|
923
|
+
const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
|
|
924
|
+
propIndex =
|
|
925
|
+
!isRaw(unique) &&
|
|
926
|
+
unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
|
|
927
|
+
const tmp = getWhereCondition(meta, options.onConflictFields, row, where);
|
|
928
|
+
propIndex = tmp.propIndex;
|
|
867
929
|
where = QueryHelper.processWhere({
|
|
868
|
-
where,
|
|
930
|
+
where: tmp.where,
|
|
869
931
|
entityName,
|
|
870
932
|
metadata: this.metadata,
|
|
871
933
|
platform: this.getPlatform(),
|
|
872
934
|
});
|
|
873
|
-
|
|
935
|
+
row = QueryHelper.processObjectParams(row);
|
|
936
|
+
validateParams(row, 'insert data');
|
|
874
937
|
allData.push(row);
|
|
875
938
|
allWhere.push(where);
|
|
876
939
|
}
|
|
@@ -884,7 +947,7 @@ export class EntityManager {
|
|
|
884
947
|
}
|
|
885
948
|
}
|
|
886
949
|
const res = await em.driver.nativeUpdateMany(entityName, allWhere, allData, {
|
|
887
|
-
ctx: em
|
|
950
|
+
ctx: em.#transactionContext,
|
|
888
951
|
upsert: true,
|
|
889
952
|
convertCustomTypes,
|
|
890
953
|
...options,
|
|
@@ -893,12 +956,16 @@ export class EntityManager {
|
|
|
893
956
|
entitiesByData.clear();
|
|
894
957
|
const loadPK = new Map();
|
|
895
958
|
allData.forEach((row, i) => {
|
|
896
|
-
em
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
959
|
+
em.#unitOfWork
|
|
960
|
+
.getChangeSetPersister()
|
|
961
|
+
.mapReturnedValues(Utils.isEntity(data[i]) ? data[i] : null, Utils.isEntity(data[i]) ? {} : data[i], res.rows?.[i], meta, true);
|
|
962
|
+
const entity = Utils.isEntity(data[i])
|
|
963
|
+
? data[i]
|
|
964
|
+
: em.#entityFactory.create(entityName, row, {
|
|
965
|
+
refresh: true,
|
|
966
|
+
initialized: true,
|
|
967
|
+
schema: options.schema,
|
|
968
|
+
});
|
|
902
969
|
if (!helper(entity).hasPrimaryKey()) {
|
|
903
970
|
loadPK.set(entity, allWhere[i]);
|
|
904
971
|
}
|
|
@@ -906,12 +973,15 @@ export class EntityManager {
|
|
|
906
973
|
entitiesByData.set(row, entity);
|
|
907
974
|
});
|
|
908
975
|
// skip if we got the PKs via returning statement (`rows`)
|
|
976
|
+
// oxfmt-ignore
|
|
909
977
|
const uniqueFields = options.onConflictFields ?? (Utils.isPlainObject(allWhere[0]) ? Object.keys(allWhere[0]).flatMap(key => Utils.splitPrimaryKeys(key)) : meta.primaryKeys);
|
|
910
978
|
const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
|
|
911
979
|
const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
|
|
912
980
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
913
981
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
914
|
-
const add = new Set(propIndex
|
|
982
|
+
const add = new Set(propIndex !== false && propIndex >= 0
|
|
983
|
+
? [unique[propIndex]]
|
|
984
|
+
: []);
|
|
915
985
|
for (const cond of loadPK.values()) {
|
|
916
986
|
Utils.keys(cond).forEach(key => add.add(key));
|
|
917
987
|
}
|
|
@@ -923,9 +993,11 @@ export class EntityManager {
|
|
|
923
993
|
where.$or[idx][prop] = item[prop];
|
|
924
994
|
});
|
|
925
995
|
});
|
|
926
|
-
const data2 = await this.driver.find(meta.
|
|
927
|
-
fields: returning
|
|
928
|
-
|
|
996
|
+
const data2 = await this.driver.find(meta.class, where, {
|
|
997
|
+
fields: returning
|
|
998
|
+
.concat(...add)
|
|
999
|
+
.concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
|
|
1000
|
+
ctx: em.#transactionContext,
|
|
929
1001
|
convertCustomTypes: true,
|
|
930
1002
|
connectionType: 'write',
|
|
931
1003
|
schema: options.schema,
|
|
@@ -938,13 +1010,13 @@ export class EntityManager {
|
|
|
938
1010
|
tmp[k] = row[k];
|
|
939
1011
|
}
|
|
940
1012
|
});
|
|
941
|
-
return this
|
|
1013
|
+
return this.#comparator.matching(entityName, cond, tmp);
|
|
942
1014
|
});
|
|
943
|
-
/* v8 ignore next
|
|
1015
|
+
/* v8 ignore next */
|
|
944
1016
|
if (!row) {
|
|
945
1017
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
946
1018
|
}
|
|
947
|
-
em.getHydrator().hydrate(entity, meta, row, em
|
|
1019
|
+
em.getHydrator().hydrate(entity, meta, row, em.#entityFactory, 'full', false, true);
|
|
948
1020
|
}
|
|
949
1021
|
if (loadPK.size !== data2.length && Array.isArray(uniqueFields)) {
|
|
950
1022
|
for (let i = 0; i < allData.length; i++) {
|
|
@@ -961,20 +1033,20 @@ export class EntityManager {
|
|
|
961
1033
|
a[b] = item[b];
|
|
962
1034
|
return a;
|
|
963
1035
|
}, {});
|
|
964
|
-
return this
|
|
1036
|
+
return this.#comparator.matching(entityName, cond, pk);
|
|
965
1037
|
});
|
|
966
|
-
/* v8 ignore next
|
|
1038
|
+
/* v8 ignore next */
|
|
967
1039
|
if (!row) {
|
|
968
1040
|
throw new Error(`Cannot find matching entity for condition ${JSON.stringify(cond)}`);
|
|
969
1041
|
}
|
|
970
|
-
em.getHydrator().hydrate(entity, meta, row, em
|
|
1042
|
+
em.getHydrator().hydrate(entity, meta, row, em.#entityFactory, 'full');
|
|
971
1043
|
}
|
|
972
1044
|
}
|
|
973
1045
|
}
|
|
974
1046
|
for (const [entity] of entities) {
|
|
975
1047
|
// recompute the data as there might be some values missing (e.g. those with db column defaults)
|
|
976
|
-
const snapshot = this
|
|
977
|
-
em
|
|
1048
|
+
const snapshot = this.#comparator.prepareEntity(entity);
|
|
1049
|
+
em.#unitOfWork.register(entity, snapshot, { refresh: true });
|
|
978
1050
|
}
|
|
979
1051
|
if (em.eventManager.hasListeners(EventType.afterUpsert, meta)) {
|
|
980
1052
|
for (const [entity] of entities) {
|
|
@@ -1011,7 +1083,7 @@ export class EntityManager {
|
|
|
1011
1083
|
*/
|
|
1012
1084
|
async transactional(cb, options = {}) {
|
|
1013
1085
|
const em = this.getContext(false);
|
|
1014
|
-
if (this
|
|
1086
|
+
if (this.#disableTransactions || em.#disableTransactions) {
|
|
1015
1087
|
return cb(em);
|
|
1016
1088
|
}
|
|
1017
1089
|
const manager = new TransactionManager(this);
|
|
@@ -1021,11 +1093,11 @@ export class EntityManager {
|
|
|
1021
1093
|
* Starts new transaction bound to this EntityManager. Use `ctx` parameter to provide the parent when nesting transactions.
|
|
1022
1094
|
*/
|
|
1023
1095
|
async begin(options = {}) {
|
|
1024
|
-
if (this
|
|
1096
|
+
if (this.#disableTransactions) {
|
|
1025
1097
|
return;
|
|
1026
1098
|
}
|
|
1027
1099
|
const em = this.getContext(false);
|
|
1028
|
-
em
|
|
1100
|
+
em.#transactionContext = await em.getConnection('write').begin({
|
|
1029
1101
|
...options,
|
|
1030
1102
|
eventBroadcaster: new TransactionEventBroadcaster(em, { topLevelTransaction: !options.ctx }),
|
|
1031
1103
|
});
|
|
@@ -1035,31 +1107,31 @@ export class EntityManager {
|
|
|
1035
1107
|
*/
|
|
1036
1108
|
async commit() {
|
|
1037
1109
|
const em = this.getContext(false);
|
|
1038
|
-
if (this
|
|
1110
|
+
if (this.#disableTransactions) {
|
|
1039
1111
|
await em.flush();
|
|
1040
1112
|
return;
|
|
1041
1113
|
}
|
|
1042
|
-
if (!em
|
|
1114
|
+
if (!em.#transactionContext) {
|
|
1043
1115
|
throw ValidationError.transactionRequired();
|
|
1044
1116
|
}
|
|
1045
1117
|
await em.flush();
|
|
1046
|
-
await em.getConnection('write').commit(em
|
|
1047
|
-
|
|
1118
|
+
await em.getConnection('write').commit(em.#transactionContext, new TransactionEventBroadcaster(em));
|
|
1119
|
+
em.#transactionContext = undefined;
|
|
1048
1120
|
}
|
|
1049
1121
|
/**
|
|
1050
1122
|
* Rollbacks the transaction bound to this EntityManager.
|
|
1051
1123
|
*/
|
|
1052
1124
|
async rollback() {
|
|
1053
|
-
if (this
|
|
1125
|
+
if (this.#disableTransactions) {
|
|
1054
1126
|
return;
|
|
1055
1127
|
}
|
|
1056
1128
|
const em = this.getContext(false);
|
|
1057
|
-
if (!em
|
|
1129
|
+
if (!em.#transactionContext) {
|
|
1058
1130
|
throw ValidationError.transactionRequired();
|
|
1059
1131
|
}
|
|
1060
|
-
await em.getConnection('write').rollback(em
|
|
1061
|
-
|
|
1062
|
-
em
|
|
1132
|
+
await em.getConnection('write').rollback(em.#transactionContext, new TransactionEventBroadcaster(em));
|
|
1133
|
+
em.#transactionContext = undefined;
|
|
1134
|
+
em.#unitOfWork.clearActionsQueue();
|
|
1063
1135
|
}
|
|
1064
1136
|
/**
|
|
1065
1137
|
* Runs your callback wrapped inside a database transaction.
|
|
@@ -1076,11 +1148,11 @@ export class EntityManager {
|
|
|
1076
1148
|
em.prepareOptions(options);
|
|
1077
1149
|
let entityName;
|
|
1078
1150
|
if (data === undefined) {
|
|
1079
|
-
entityName = entityNameOrEntity.constructor
|
|
1151
|
+
entityName = entityNameOrEntity.constructor;
|
|
1080
1152
|
data = entityNameOrEntity;
|
|
1081
1153
|
}
|
|
1082
1154
|
else {
|
|
1083
|
-
entityName =
|
|
1155
|
+
entityName = entityNameOrEntity;
|
|
1084
1156
|
}
|
|
1085
1157
|
if (Utils.isEntity(data)) {
|
|
1086
1158
|
if (options.schema && helper(data).getSchema() == null) {
|
|
@@ -1088,19 +1160,22 @@ export class EntityManager {
|
|
|
1088
1160
|
}
|
|
1089
1161
|
if (!helper(data).__managed) {
|
|
1090
1162
|
// the entity might have been created via `em.create()`, which adds it to the persist stack automatically
|
|
1091
|
-
em
|
|
1163
|
+
em.#unitOfWork.getPersistStack().delete(data);
|
|
1092
1164
|
// it can be also in the identity map if it had a PK value already
|
|
1093
|
-
em
|
|
1165
|
+
em.#unitOfWork.unsetIdentity(data);
|
|
1094
1166
|
}
|
|
1095
1167
|
const meta = helper(data).__meta;
|
|
1096
|
-
const payload = em
|
|
1168
|
+
const payload = em.#comparator.prepareEntity(data);
|
|
1097
1169
|
const cs = new ChangeSet(data, ChangeSetType.CREATE, payload, meta);
|
|
1098
|
-
await em
|
|
1170
|
+
await em.#unitOfWork.getChangeSetPersister().executeInserts([cs], { ctx: em.#transactionContext, ...options });
|
|
1099
1171
|
return cs.getPrimaryKey();
|
|
1100
1172
|
}
|
|
1101
1173
|
data = QueryHelper.processObjectParams(data);
|
|
1102
|
-
|
|
1103
|
-
const res = await em.driver.nativeInsert(entityName, data, {
|
|
1174
|
+
validateParams(data, 'insert data');
|
|
1175
|
+
const res = await em.driver.nativeInsert(entityName, data, {
|
|
1176
|
+
ctx: em.#transactionContext,
|
|
1177
|
+
...options,
|
|
1178
|
+
});
|
|
1104
1179
|
return res.insertId;
|
|
1105
1180
|
}
|
|
1106
1181
|
/**
|
|
@@ -1111,11 +1186,11 @@ export class EntityManager {
|
|
|
1111
1186
|
em.prepareOptions(options);
|
|
1112
1187
|
let entityName;
|
|
1113
1188
|
if (data === undefined) {
|
|
1114
|
-
entityName = entityNameOrEntities[0].constructor
|
|
1189
|
+
entityName = entityNameOrEntities[0].constructor;
|
|
1115
1190
|
data = entityNameOrEntities;
|
|
1116
1191
|
}
|
|
1117
1192
|
else {
|
|
1118
|
-
entityName =
|
|
1193
|
+
entityName = entityNameOrEntities;
|
|
1119
1194
|
}
|
|
1120
1195
|
if (data.length === 0) {
|
|
1121
1196
|
return [];
|
|
@@ -1128,19 +1203,22 @@ export class EntityManager {
|
|
|
1128
1203
|
}
|
|
1129
1204
|
if (!helper(row).__managed) {
|
|
1130
1205
|
// the entity might have been created via `em.create()`, which adds it to the persist stack automatically
|
|
1131
|
-
em
|
|
1206
|
+
em.#unitOfWork.getPersistStack().delete(row);
|
|
1132
1207
|
// it can be also in the identity map if it had a PK value already
|
|
1133
|
-
em
|
|
1208
|
+
em.#unitOfWork.unsetIdentity(row);
|
|
1134
1209
|
}
|
|
1135
|
-
const payload = em
|
|
1210
|
+
const payload = em.#comparator.prepareEntity(row);
|
|
1136
1211
|
return new ChangeSet(row, ChangeSetType.CREATE, payload, meta);
|
|
1137
1212
|
});
|
|
1138
|
-
await em
|
|
1213
|
+
await em.#unitOfWork.getChangeSetPersister().executeInserts(css, { ctx: em.#transactionContext, ...options });
|
|
1139
1214
|
return css.map(cs => cs.getPrimaryKey());
|
|
1140
1215
|
}
|
|
1141
1216
|
data = data.map(row => QueryHelper.processObjectParams(row));
|
|
1142
|
-
data.forEach(row =>
|
|
1143
|
-
const res = await em.driver.nativeInsertMany(entityName, data, {
|
|
1217
|
+
data.forEach(row => validateParams(row, 'insert data'));
|
|
1218
|
+
const res = await em.driver.nativeInsertMany(entityName, data, {
|
|
1219
|
+
ctx: em.#transactionContext,
|
|
1220
|
+
...options,
|
|
1221
|
+
});
|
|
1144
1222
|
if (res.insertedIds) {
|
|
1145
1223
|
return res.insertedIds;
|
|
1146
1224
|
}
|
|
@@ -1152,12 +1230,16 @@ export class EntityManager {
|
|
|
1152
1230
|
async nativeUpdate(entityName, where, data, options = {}) {
|
|
1153
1231
|
const em = this.getContext(false);
|
|
1154
1232
|
em.prepareOptions(options);
|
|
1155
|
-
|
|
1233
|
+
await em.processUnionWhere(entityName, options, 'update');
|
|
1156
1234
|
data = QueryHelper.processObjectParams(data);
|
|
1157
1235
|
where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
const res = await em.driver.nativeUpdate(entityName, where, data, {
|
|
1236
|
+
validateParams(data, 'update data');
|
|
1237
|
+
validateParams(where, 'update condition');
|
|
1238
|
+
const res = await em.driver.nativeUpdate(entityName, where, data, {
|
|
1239
|
+
ctx: em.#transactionContext,
|
|
1240
|
+
em,
|
|
1241
|
+
...options,
|
|
1242
|
+
});
|
|
1161
1243
|
return res.affectedRows;
|
|
1162
1244
|
}
|
|
1163
1245
|
/**
|
|
@@ -1166,28 +1248,36 @@ export class EntityManager {
|
|
|
1166
1248
|
async nativeDelete(entityName, where, options = {}) {
|
|
1167
1249
|
const em = this.getContext(false);
|
|
1168
1250
|
em.prepareOptions(options);
|
|
1169
|
-
|
|
1170
|
-
where = await em.processWhere(entityName, where, options, 'delete');
|
|
1171
|
-
|
|
1172
|
-
const res = await em.driver.nativeDelete(entityName, where, {
|
|
1251
|
+
await em.processUnionWhere(entityName, options, 'delete');
|
|
1252
|
+
where = (await em.processWhere(entityName, where, options, 'delete'));
|
|
1253
|
+
validateParams(where, 'delete condition');
|
|
1254
|
+
const res = await em.driver.nativeDelete(entityName, where, {
|
|
1255
|
+
ctx: em.#transactionContext,
|
|
1256
|
+
em,
|
|
1257
|
+
...options,
|
|
1258
|
+
});
|
|
1173
1259
|
return res.affectedRows;
|
|
1174
1260
|
}
|
|
1175
1261
|
/**
|
|
1176
1262
|
* Maps raw database result to an entity and merges it to this EntityManager.
|
|
1177
1263
|
*/
|
|
1178
1264
|
map(entityName, result, options = {}) {
|
|
1179
|
-
entityName = Utils.className(entityName);
|
|
1180
1265
|
const meta = this.metadata.get(entityName);
|
|
1181
1266
|
const data = this.driver.mapResult(result, meta);
|
|
1182
|
-
Object.keys(data)
|
|
1267
|
+
for (const k of Object.keys(data)) {
|
|
1183
1268
|
const prop = meta.properties[k];
|
|
1184
|
-
if (prop
|
|
1185
|
-
|
|
1269
|
+
if (prop?.kind === ReferenceKind.SCALAR &&
|
|
1270
|
+
SCALAR_TYPES.has(prop.runtimeType) &&
|
|
1271
|
+
!prop.customType &&
|
|
1272
|
+
(prop.setter || !prop.getter)) {
|
|
1273
|
+
validateProperty(prop, data[k], data);
|
|
1186
1274
|
}
|
|
1187
|
-
}
|
|
1275
|
+
}
|
|
1188
1276
|
return this.merge(entityName, data, {
|
|
1189
1277
|
convertCustomTypes: true,
|
|
1190
|
-
refresh: true,
|
|
1278
|
+
refresh: true,
|
|
1279
|
+
validate: false,
|
|
1280
|
+
...options,
|
|
1191
1281
|
});
|
|
1192
1282
|
}
|
|
1193
1283
|
/**
|
|
@@ -1196,34 +1286,23 @@ export class EntityManager {
|
|
|
1196
1286
|
*/
|
|
1197
1287
|
merge(entityName, data, options = {}) {
|
|
1198
1288
|
if (Utils.isEntity(entityName)) {
|
|
1199
|
-
return this.merge(entityName.constructor
|
|
1289
|
+
return this.merge(entityName.constructor, entityName, data);
|
|
1200
1290
|
}
|
|
1201
1291
|
const em = options.disableContextResolution ? this : this.getContext();
|
|
1202
|
-
options.schema ??= em
|
|
1292
|
+
options.schema ??= em.#schema;
|
|
1203
1293
|
options.validate ??= true;
|
|
1204
1294
|
options.cascade ??= true;
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1208
|
-
}
|
|
1209
|
-
let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
|
|
1295
|
+
validatePrimaryKey(data, em.metadata.get(entityName));
|
|
1296
|
+
let entity = em.#unitOfWork.tryGetById(entityName, data, options.schema, false);
|
|
1210
1297
|
if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
|
|
1211
1298
|
return entity;
|
|
1212
1299
|
}
|
|
1213
|
-
const meta = em.metadata.find(entityName);
|
|
1214
|
-
const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
|
|
1215
1300
|
const dataIsEntity = Utils.isEntity(data);
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
return entity;
|
|
1220
|
-
}
|
|
1221
|
-
entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1222
|
-
if (options.validate) {
|
|
1223
|
-
em.validator.validate(entity, data, childMeta ?? meta);
|
|
1224
|
-
}
|
|
1301
|
+
entity = dataIsEntity
|
|
1302
|
+
? data
|
|
1303
|
+
: em.#entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1225
1304
|
const visited = options.cascade ? undefined : new Set([entity]);
|
|
1226
|
-
em
|
|
1305
|
+
em.#unitOfWork.merge(entity, visited);
|
|
1227
1306
|
return entity;
|
|
1228
1307
|
}
|
|
1229
1308
|
/**
|
|
@@ -1243,11 +1322,12 @@ export class EntityManager {
|
|
|
1243
1322
|
*/
|
|
1244
1323
|
create(entityName, data, options = {}) {
|
|
1245
1324
|
const em = this.getContext();
|
|
1246
|
-
options.schema ??= em
|
|
1247
|
-
const entity = em
|
|
1325
|
+
options.schema ??= em.#schema;
|
|
1326
|
+
const entity = em.#entityFactory.create(entityName, data, {
|
|
1248
1327
|
...options,
|
|
1249
1328
|
newEntity: !options.managed,
|
|
1250
1329
|
merge: options.managed,
|
|
1330
|
+
normalizeAccessors: true,
|
|
1251
1331
|
});
|
|
1252
1332
|
options.persist ??= em.config.get('persistOnCreate');
|
|
1253
1333
|
if (options.persist && !this.getMetadata(entityName).embeddable) {
|
|
@@ -1267,7 +1347,7 @@ export class EntityManager {
|
|
|
1267
1347
|
getReference(entityName, id, options = {}) {
|
|
1268
1348
|
options.schema ??= this.schema;
|
|
1269
1349
|
options.convertCustomTypes ??= false;
|
|
1270
|
-
const meta = this.metadata.get(
|
|
1350
|
+
const meta = this.metadata.get(entityName);
|
|
1271
1351
|
if (Utils.isPrimaryKey(id)) {
|
|
1272
1352
|
if (meta.compositePK) {
|
|
1273
1353
|
throw ValidationError.invalidCompositeIdentifier(meta);
|
|
@@ -1288,24 +1368,24 @@ export class EntityManager {
|
|
|
1288
1368
|
// Shallow copy options since the object will be modified when deleting orderBy
|
|
1289
1369
|
options = { ...options };
|
|
1290
1370
|
em.prepareOptions(options);
|
|
1291
|
-
entityName = Utils.className(entityName);
|
|
1292
1371
|
await em.tryFlush(entityName, options);
|
|
1293
1372
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
1294
|
-
options.populate = await em.preparePopulate(entityName, options);
|
|
1373
|
+
options.populate = (await em.preparePopulate(entityName, options));
|
|
1295
1374
|
options = { ...options };
|
|
1296
1375
|
// save the original hint value so we know it was infer/all
|
|
1297
1376
|
const meta = em.metadata.find(entityName);
|
|
1298
1377
|
options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
|
|
1299
1378
|
options.populateWhere = this.createPopulateWhere({ ...where }, options);
|
|
1300
|
-
options.populateFilter = await this.getJoinedFilters(meta,
|
|
1301
|
-
|
|
1379
|
+
options.populateFilter = await this.getJoinedFilters(meta, options);
|
|
1380
|
+
validateParams(where);
|
|
1302
1381
|
delete options.orderBy;
|
|
1382
|
+
await em.processUnionWhere(entityName, options, 'read');
|
|
1303
1383
|
const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
|
|
1304
1384
|
const cached = await em.tryCache(entityName, options.cache, cacheKey);
|
|
1305
1385
|
if (cached?.data !== undefined) {
|
|
1306
1386
|
return cached.data;
|
|
1307
1387
|
}
|
|
1308
|
-
const count = await em.driver.count(entityName, where, { ctx: em
|
|
1388
|
+
const count = await em.driver.count(entityName, where, { ctx: em.#transactionContext, em, ...options });
|
|
1309
1389
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1310
1390
|
return +count;
|
|
1311
1391
|
}
|
|
@@ -1317,28 +1397,21 @@ export class EntityManager {
|
|
|
1317
1397
|
const em = this.getContext();
|
|
1318
1398
|
if (Utils.isEntity(entity)) {
|
|
1319
1399
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
1320
|
-
em
|
|
1400
|
+
em.#unitOfWork.persist(entity, undefined, { cascade: false });
|
|
1321
1401
|
return em;
|
|
1322
1402
|
}
|
|
1323
1403
|
const entities = Utils.asArray(entity);
|
|
1324
1404
|
for (const ent of entities) {
|
|
1325
1405
|
if (!Utils.isEntity(ent, true)) {
|
|
1326
1406
|
/* v8 ignore next */
|
|
1327
|
-
const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor
|
|
1407
|
+
const meta = typeof ent === 'object' ? em.metadata.find(ent.constructor) : undefined;
|
|
1328
1408
|
throw ValidationError.notDiscoveredEntity(ent, meta);
|
|
1329
1409
|
}
|
|
1330
1410
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
1331
|
-
em
|
|
1411
|
+
em.#unitOfWork.persist(Reference.unwrapReference(ent), undefined, { cascade: false });
|
|
1332
1412
|
}
|
|
1333
1413
|
return this;
|
|
1334
1414
|
}
|
|
1335
|
-
/**
|
|
1336
|
-
* Persists your entity immediately, flushing all not yet persisted changes to the database too.
|
|
1337
|
-
* Equivalent to `em.persist(e).flush()`.
|
|
1338
|
-
*/
|
|
1339
|
-
async persistAndFlush(entity) {
|
|
1340
|
-
await this.persist(entity).flush();
|
|
1341
|
-
}
|
|
1342
1415
|
/**
|
|
1343
1416
|
* Marks entity for removal.
|
|
1344
1417
|
* A removed entity will be removed from the database at or before transaction commit or as a result of the flush operation.
|
|
@@ -1349,7 +1422,7 @@ export class EntityManager {
|
|
|
1349
1422
|
const em = this.getContext();
|
|
1350
1423
|
if (Utils.isEntity(entity)) {
|
|
1351
1424
|
// do not cascade just yet, cascading of entities in persist stack is done when flushing
|
|
1352
|
-
em
|
|
1425
|
+
em.#unitOfWork.remove(entity, undefined, { cascade: false });
|
|
1353
1426
|
return em;
|
|
1354
1427
|
}
|
|
1355
1428
|
const entities = Utils.asArray(entity, true);
|
|
@@ -1358,17 +1431,10 @@ export class EntityManager {
|
|
|
1358
1431
|
throw new Error(`You need to pass entity instance or reference to 'em.remove()'. To remove entities by condition, use 'em.nativeDelete()'.`);
|
|
1359
1432
|
}
|
|
1360
1433
|
// do not cascade just yet, cascading of entities in remove stack is done when flushing
|
|
1361
|
-
em
|
|
1434
|
+
em.#unitOfWork.remove(Reference.unwrapReference(ent), undefined, { cascade: false });
|
|
1362
1435
|
}
|
|
1363
1436
|
return em;
|
|
1364
1437
|
}
|
|
1365
|
-
/**
|
|
1366
|
-
* Removes an entity instance immediately, flushing all not yet persisted changes to the database too.
|
|
1367
|
-
* Equivalent to `em.remove(e).flush()`
|
|
1368
|
-
*/
|
|
1369
|
-
async removeAndFlush(entity) {
|
|
1370
|
-
await this.remove(entity).flush();
|
|
1371
|
-
}
|
|
1372
1438
|
/**
|
|
1373
1439
|
* Flushes all changes to objects that have been queued up to now to the database.
|
|
1374
1440
|
* This effectively synchronizes the in-memory state of managed objects with the database.
|
|
@@ -1381,8 +1447,7 @@ export class EntityManager {
|
|
|
1381
1447
|
*/
|
|
1382
1448
|
async tryFlush(entityName, options) {
|
|
1383
1449
|
const em = this.getContext();
|
|
1384
|
-
const flushMode = options.flushMode ?? em
|
|
1385
|
-
entityName = Utils.className(entityName);
|
|
1450
|
+
const flushMode = options.flushMode ?? em.#flushMode ?? em.config.get('flushMode');
|
|
1386
1451
|
const meta = em.metadata.get(entityName);
|
|
1387
1452
|
if (flushMode === FlushMode.COMMIT) {
|
|
1388
1453
|
return;
|
|
@@ -1395,13 +1460,12 @@ export class EntityManager {
|
|
|
1395
1460
|
* Clears the EntityManager. All entities that are currently managed by this EntityManager become detached.
|
|
1396
1461
|
*/
|
|
1397
1462
|
clear() {
|
|
1398
|
-
this.getContext()
|
|
1463
|
+
this.getContext().#unitOfWork.clear();
|
|
1399
1464
|
}
|
|
1400
1465
|
/**
|
|
1401
1466
|
* Checks whether given property can be populated on the entity.
|
|
1402
1467
|
*/
|
|
1403
1468
|
canPopulate(entityName, property) {
|
|
1404
|
-
entityName = Utils.className(entityName);
|
|
1405
1469
|
// eslint-disable-next-line prefer-const
|
|
1406
1470
|
let [p, ...parts] = property.split('.');
|
|
1407
1471
|
const meta = this.metadata.find(entityName);
|
|
@@ -1411,12 +1475,11 @@ export class EntityManager {
|
|
|
1411
1475
|
if (p.includes(':')) {
|
|
1412
1476
|
p = p.split(':', 2)[0];
|
|
1413
1477
|
}
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
}
|
|
1478
|
+
// For TPT inheritance, check the entity's own properties, not just the root's
|
|
1479
|
+
// For STI, meta.properties includes all properties anyway
|
|
1480
|
+
const ret = p in meta.properties;
|
|
1418
1481
|
if (parts.length > 0) {
|
|
1419
|
-
return this.canPopulate(
|
|
1482
|
+
return this.canPopulate(meta.properties[p].targetMeta.class, parts.join('.'));
|
|
1420
1483
|
}
|
|
1421
1484
|
return ret;
|
|
1422
1485
|
}
|
|
@@ -1430,9 +1493,9 @@ export class EntityManager {
|
|
|
1430
1493
|
}
|
|
1431
1494
|
const em = this.getContext();
|
|
1432
1495
|
em.prepareOptions(options);
|
|
1433
|
-
const entityName = arr[0].constructor
|
|
1434
|
-
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
|
|
1435
|
-
await em
|
|
1496
|
+
const entityName = arr[0].constructor;
|
|
1497
|
+
const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
|
|
1498
|
+
await em.#entityLoader.populate(entityName, arr, preparedPopulate, options);
|
|
1436
1499
|
return entities;
|
|
1437
1500
|
}
|
|
1438
1501
|
/**
|
|
@@ -1453,25 +1516,26 @@ export class EntityManager {
|
|
|
1453
1516
|
const allowGlobalContext = em.config.get('allowGlobalContext');
|
|
1454
1517
|
em.config.set('allowGlobalContext', true);
|
|
1455
1518
|
const fork = new em.constructor(em.config, em.driver, em.metadata, options.useContext, eventManager);
|
|
1456
|
-
fork.setFlushMode(options.flushMode ?? em
|
|
1457
|
-
fork
|
|
1519
|
+
fork.setFlushMode(options.flushMode ?? em.#flushMode);
|
|
1520
|
+
fork.#disableTransactions =
|
|
1521
|
+
options.disableTransactions ?? this.#disableTransactions ?? this.config.get('disableTransactions');
|
|
1458
1522
|
em.config.set('allowGlobalContext', allowGlobalContext);
|
|
1459
1523
|
if (options.keepTransactionContext) {
|
|
1460
|
-
fork
|
|
1524
|
+
fork.#transactionContext = em.#transactionContext;
|
|
1461
1525
|
}
|
|
1462
|
-
fork
|
|
1463
|
-
fork
|
|
1526
|
+
fork.#filters = { ...em.#filters };
|
|
1527
|
+
fork.#filterParams = Utils.copy(em.#filterParams);
|
|
1464
1528
|
fork.loggerContext = Utils.merge({}, em.loggerContext, options.loggerContext);
|
|
1465
|
-
fork
|
|
1529
|
+
fork.#schema = options.schema ?? em.#schema;
|
|
1466
1530
|
if (!options.clear) {
|
|
1467
|
-
for (const entity of em
|
|
1468
|
-
fork
|
|
1531
|
+
for (const entity of em.#unitOfWork.getIdentityMap()) {
|
|
1532
|
+
fork.#unitOfWork.register(entity);
|
|
1469
1533
|
}
|
|
1470
|
-
for (const entity of em
|
|
1471
|
-
fork
|
|
1534
|
+
for (const entity of em.#unitOfWork.getPersistStack()) {
|
|
1535
|
+
fork.#unitOfWork.persist(entity);
|
|
1472
1536
|
}
|
|
1473
|
-
for (const entity of em
|
|
1474
|
-
fork
|
|
1537
|
+
for (const entity of em.#unitOfWork.getOrphanRemoveStack()) {
|
|
1538
|
+
fork.#unitOfWork.getOrphanRemoveStack().add(entity);
|
|
1475
1539
|
}
|
|
1476
1540
|
}
|
|
1477
1541
|
return fork;
|
|
@@ -1481,21 +1545,21 @@ export class EntityManager {
|
|
|
1481
1545
|
*/
|
|
1482
1546
|
getUnitOfWork(useContext = true) {
|
|
1483
1547
|
if (!useContext) {
|
|
1484
|
-
return this
|
|
1548
|
+
return this.#unitOfWork;
|
|
1485
1549
|
}
|
|
1486
|
-
return this.getContext()
|
|
1550
|
+
return this.getContext().#unitOfWork;
|
|
1487
1551
|
}
|
|
1488
1552
|
/**
|
|
1489
1553
|
* Gets the EntityFactory used by the EntityManager.
|
|
1490
1554
|
*/
|
|
1491
1555
|
getEntityFactory() {
|
|
1492
|
-
return this.getContext()
|
|
1556
|
+
return this.getContext().#entityFactory;
|
|
1493
1557
|
}
|
|
1494
1558
|
/**
|
|
1495
1559
|
* @internal use `em.populate()` as the user facing API, this is exposed only for internal usage
|
|
1496
1560
|
*/
|
|
1497
1561
|
getEntityLoader() {
|
|
1498
|
-
return this.getContext()
|
|
1562
|
+
return this.getContext().#entityLoader;
|
|
1499
1563
|
}
|
|
1500
1564
|
/**
|
|
1501
1565
|
* Gets the Hydrator used by the EntityManager.
|
|
@@ -1508,7 +1572,7 @@ export class EntityManager {
|
|
|
1508
1572
|
* @internal
|
|
1509
1573
|
*/
|
|
1510
1574
|
getContext(validate = true) {
|
|
1511
|
-
if (!this
|
|
1575
|
+
if (!this.#useContext) {
|
|
1512
1576
|
return this;
|
|
1513
1577
|
}
|
|
1514
1578
|
let em = TransactionContext.getEntityManager(this.name); // prefer the tx context
|
|
@@ -1529,13 +1593,13 @@ export class EntityManager {
|
|
|
1529
1593
|
* Checks whether this EntityManager is currently operating inside a database transaction.
|
|
1530
1594
|
*/
|
|
1531
1595
|
isInTransaction() {
|
|
1532
|
-
return !!this.getContext(false)
|
|
1596
|
+
return !!this.getContext(false).#transactionContext;
|
|
1533
1597
|
}
|
|
1534
1598
|
/**
|
|
1535
1599
|
* Gets the transaction context (driver dependent object used to make sure queries are executed on same connection).
|
|
1536
1600
|
*/
|
|
1537
1601
|
getTransactionContext() {
|
|
1538
|
-
return this.getContext(false)
|
|
1602
|
+
return this.getContext(false).#transactionContext;
|
|
1539
1603
|
}
|
|
1540
1604
|
/**
|
|
1541
1605
|
* Sets the transaction context.
|
|
@@ -1545,21 +1609,20 @@ export class EntityManager {
|
|
|
1545
1609
|
this.resetTransactionContext();
|
|
1546
1610
|
}
|
|
1547
1611
|
else {
|
|
1548
|
-
this.getContext(false)
|
|
1612
|
+
this.getContext(false).#transactionContext = ctx;
|
|
1549
1613
|
}
|
|
1550
1614
|
}
|
|
1551
1615
|
/**
|
|
1552
1616
|
* Resets the transaction context.
|
|
1553
1617
|
*/
|
|
1554
1618
|
resetTransactionContext() {
|
|
1555
|
-
|
|
1619
|
+
this.getContext(false).#transactionContext = undefined;
|
|
1556
1620
|
}
|
|
1557
1621
|
/**
|
|
1558
1622
|
* Gets the `MetadataStorage` (without parameters) or `EntityMetadata` instance when provided with the `entityName` parameter.
|
|
1559
1623
|
*/
|
|
1560
1624
|
getMetadata(entityName) {
|
|
1561
1625
|
if (entityName) {
|
|
1562
|
-
entityName = Utils.className(entityName);
|
|
1563
1626
|
return this.metadata.get(entityName);
|
|
1564
1627
|
}
|
|
1565
1628
|
return this.metadata;
|
|
@@ -1568,7 +1631,7 @@ export class EntityManager {
|
|
|
1568
1631
|
* Gets the EntityComparator.
|
|
1569
1632
|
*/
|
|
1570
1633
|
getComparator() {
|
|
1571
|
-
return this
|
|
1634
|
+
return this.#comparator;
|
|
1572
1635
|
}
|
|
1573
1636
|
checkLockRequirements(mode, meta) {
|
|
1574
1637
|
if (!mode) {
|
|
@@ -1588,8 +1651,8 @@ export class EntityManager {
|
|
|
1588
1651
|
lockTableAliases: options.lockTableAliases,
|
|
1589
1652
|
});
|
|
1590
1653
|
}
|
|
1591
|
-
const preparedPopulate = await this.preparePopulate(meta.
|
|
1592
|
-
await this
|
|
1654
|
+
const preparedPopulate = await this.preparePopulate(meta.class, options);
|
|
1655
|
+
await this.#entityLoader.populate(meta.class, [entity], preparedPopulate, {
|
|
1593
1656
|
...options,
|
|
1594
1657
|
...this.getPopulateWhere(where, options),
|
|
1595
1658
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
@@ -1609,6 +1672,7 @@ export class EntityManager {
|
|
|
1609
1672
|
return ret;
|
|
1610
1673
|
}, []);
|
|
1611
1674
|
}
|
|
1675
|
+
/** @internal */
|
|
1612
1676
|
async preparePopulate(entityName, options, validate = true) {
|
|
1613
1677
|
if (options.populate === false) {
|
|
1614
1678
|
return [];
|
|
@@ -1621,11 +1685,14 @@ export class EntityManager {
|
|
|
1621
1685
|
const ret = [];
|
|
1622
1686
|
for (let field of fields) {
|
|
1623
1687
|
if (field === PopulatePath.ALL || field.startsWith(`${PopulatePath.ALL}.`)) {
|
|
1624
|
-
ret.push(...meta.props
|
|
1688
|
+
ret.push(...meta.props
|
|
1689
|
+
.filter(prop => prop.lazy || [ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind))
|
|
1690
|
+
.map(prop => prop.name));
|
|
1625
1691
|
continue;
|
|
1626
1692
|
}
|
|
1627
1693
|
field = field.split(':')[0];
|
|
1628
|
-
if (!field.includes('.') &&
|
|
1694
|
+
if (!field.includes('.') &&
|
|
1695
|
+
![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(meta.properties[field].kind)) {
|
|
1629
1696
|
ret.push(field);
|
|
1630
1697
|
continue;
|
|
1631
1698
|
}
|
|
@@ -1649,13 +1716,14 @@ export class EntityManager {
|
|
|
1649
1716
|
options.populate = pruneToOneRelations(meta, this.buildFields(options.fields));
|
|
1650
1717
|
}
|
|
1651
1718
|
if (!options.populate) {
|
|
1652
|
-
const populate = this
|
|
1719
|
+
const populate = this.#entityLoader.normalizePopulate(entityName, [], options.strategy, true, options.exclude);
|
|
1653
1720
|
await this.autoJoinRefsForFilters(meta, { ...options, populate });
|
|
1654
1721
|
return populate;
|
|
1655
1722
|
}
|
|
1656
1723
|
if (typeof options.populate !== 'boolean') {
|
|
1657
|
-
options.populate = Utils.asArray(options.populate)
|
|
1658
|
-
|
|
1724
|
+
options.populate = Utils.asArray(options.populate)
|
|
1725
|
+
.map(field => {
|
|
1726
|
+
/* v8 ignore next */
|
|
1659
1727
|
if (typeof field === 'boolean' || field === PopulatePath.ALL) {
|
|
1660
1728
|
return [{ field: meta.primaryKeys[0], strategy: options.strategy, all: !!field }]; //
|
|
1661
1729
|
}
|
|
@@ -1665,24 +1733,28 @@ export class EntityManager {
|
|
|
1665
1733
|
options.flags.push(QueryFlag.INFER_POPULATE);
|
|
1666
1734
|
return [];
|
|
1667
1735
|
}
|
|
1668
|
-
if (
|
|
1736
|
+
if (typeof field === 'string') {
|
|
1669
1737
|
return [{ field, strategy: options.strategy }];
|
|
1670
1738
|
}
|
|
1671
1739
|
return [field];
|
|
1672
|
-
})
|
|
1740
|
+
})
|
|
1741
|
+
.flat();
|
|
1673
1742
|
}
|
|
1674
|
-
const populate = this
|
|
1743
|
+
const populate = this.#entityLoader.normalizePopulate(entityName, options.populate, options.strategy, true, options.exclude);
|
|
1675
1744
|
const invalid = populate.find(({ field }) => !this.canPopulate(entityName, field));
|
|
1676
1745
|
if (validate && invalid) {
|
|
1677
1746
|
throw ValidationError.invalidPropertyName(entityName, invalid.field);
|
|
1678
1747
|
}
|
|
1679
1748
|
await this.autoJoinRefsForFilters(meta, { ...options, populate });
|
|
1680
|
-
|
|
1749
|
+
for (const field of populate) {
|
|
1681
1750
|
// force select-in strategy when populating all relations as otherwise we could cause infinite loops when self-referencing
|
|
1682
1751
|
const all = field.all ?? (Array.isArray(options.populate) && options.populate.includes('*'));
|
|
1683
1752
|
field.strategy = all ? LoadStrategy.SELECT_IN : (options.strategy ?? field.strategy);
|
|
1684
|
-
|
|
1685
|
-
|
|
1753
|
+
}
|
|
1754
|
+
if (options.populateHints) {
|
|
1755
|
+
applyPopulateHints(populate, options.populateHints);
|
|
1756
|
+
}
|
|
1757
|
+
return populate;
|
|
1686
1758
|
}
|
|
1687
1759
|
/**
|
|
1688
1760
|
* when the entity is found in identity map, we check if it was partially loaded or we are trying to populate
|
|
@@ -1702,7 +1774,7 @@ export class EntityManager {
|
|
|
1702
1774
|
return !inlineEmbedded && !prop.lazy && !helper(entity).__loadedProperties.has(prop.name);
|
|
1703
1775
|
});
|
|
1704
1776
|
}
|
|
1705
|
-
if (autoRefresh) {
|
|
1777
|
+
if (autoRefresh || options.filters) {
|
|
1706
1778
|
return true;
|
|
1707
1779
|
}
|
|
1708
1780
|
if (Array.isArray(options.populate)) {
|
|
@@ -1714,7 +1786,7 @@ export class EntityManager {
|
|
|
1714
1786
|
if (!Utils.isEmpty(options.fields) && !Utils.isEmpty(options.exclude)) {
|
|
1715
1787
|
throw new ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
|
|
1716
1788
|
}
|
|
1717
|
-
options.schema ??= this
|
|
1789
|
+
options.schema ??= this.#schema;
|
|
1718
1790
|
options.logging = options.loggerContext = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1719
1791
|
}
|
|
1720
1792
|
/**
|
|
@@ -1726,7 +1798,7 @@ export class EntityManager {
|
|
|
1726
1798
|
for (const k of ['ctx', 'strategy', 'flushMode', 'logging', 'loggerContext']) {
|
|
1727
1799
|
delete opts[k];
|
|
1728
1800
|
}
|
|
1729
|
-
return [entityName, method, opts, where];
|
|
1801
|
+
return [Utils.className(entityName), method, opts, where];
|
|
1730
1802
|
}
|
|
1731
1803
|
/**
|
|
1732
1804
|
* @internal
|
|
@@ -1738,31 +1810,27 @@ export class EntityManager {
|
|
|
1738
1810
|
}
|
|
1739
1811
|
const em = this.getContext();
|
|
1740
1812
|
const cacheKey = Array.isArray(config) ? config[0] : JSON.stringify(key);
|
|
1741
|
-
const cached = await em
|
|
1813
|
+
const cached = await em.#resultCache.get(cacheKey);
|
|
1742
1814
|
if (!cached) {
|
|
1743
1815
|
return { key: cacheKey, data: cached };
|
|
1744
1816
|
}
|
|
1745
1817
|
let data;
|
|
1818
|
+
const createOptions = {
|
|
1819
|
+
merge: true,
|
|
1820
|
+
convertCustomTypes: false,
|
|
1821
|
+
refresh,
|
|
1822
|
+
recomputeSnapshot: true,
|
|
1823
|
+
};
|
|
1746
1824
|
if (Array.isArray(cached) && merge) {
|
|
1747
|
-
data = cached.map(item => em
|
|
1748
|
-
merge: true,
|
|
1749
|
-
convertCustomTypes: true,
|
|
1750
|
-
refresh,
|
|
1751
|
-
recomputeSnapshot: true,
|
|
1752
|
-
}));
|
|
1825
|
+
data = cached.map(item => em.#entityFactory.create(entityName, item, createOptions));
|
|
1753
1826
|
}
|
|
1754
1827
|
else if (Utils.isObject(cached) && merge) {
|
|
1755
|
-
data = em
|
|
1756
|
-
merge: true,
|
|
1757
|
-
convertCustomTypes: true,
|
|
1758
|
-
refresh,
|
|
1759
|
-
recomputeSnapshot: true,
|
|
1760
|
-
});
|
|
1828
|
+
data = em.#entityFactory.create(entityName, cached, createOptions);
|
|
1761
1829
|
}
|
|
1762
1830
|
else {
|
|
1763
1831
|
data = cached;
|
|
1764
1832
|
}
|
|
1765
|
-
await em
|
|
1833
|
+
await em.#unitOfWork.dispatchOnLoadEvent();
|
|
1766
1834
|
return { key: cacheKey, data };
|
|
1767
1835
|
}
|
|
1768
1836
|
/**
|
|
@@ -1772,8 +1840,8 @@ export class EntityManager {
|
|
|
1772
1840
|
config ??= this.config.get('resultCache').global;
|
|
1773
1841
|
if (config) {
|
|
1774
1842
|
const em = this.getContext();
|
|
1775
|
-
const expiration = Array.isArray(config) ? config[1] :
|
|
1776
|
-
await em
|
|
1843
|
+
const expiration = Array.isArray(config) ? config[1] : typeof config === 'number' ? config : undefined;
|
|
1844
|
+
await em.#resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
|
|
1777
1845
|
}
|
|
1778
1846
|
}
|
|
1779
1847
|
/**
|
|
@@ -1789,21 +1857,38 @@ export class EntityManager {
|
|
|
1789
1857
|
* ```
|
|
1790
1858
|
*/
|
|
1791
1859
|
async clearCache(cacheKey) {
|
|
1792
|
-
await this.getContext()
|
|
1860
|
+
await this.getContext().#resultCache.remove(cacheKey);
|
|
1793
1861
|
}
|
|
1794
1862
|
/**
|
|
1795
1863
|
* Returns the default schema of this EntityManager. Respects the context, so global EM will give you the contextual schema
|
|
1796
1864
|
* if executed inside request context handler.
|
|
1797
1865
|
*/
|
|
1798
1866
|
get schema() {
|
|
1799
|
-
return this.getContext(false)
|
|
1867
|
+
return this.getContext(false).#schema;
|
|
1800
1868
|
}
|
|
1801
1869
|
/**
|
|
1802
1870
|
* Sets the default schema of this EntityManager. Respects the context, so global EM will set the contextual schema
|
|
1803
1871
|
* if executed inside request context handler.
|
|
1804
1872
|
*/
|
|
1805
1873
|
set schema(schema) {
|
|
1806
|
-
this.getContext(false)
|
|
1874
|
+
this.getContext(false).#schema = schema ?? undefined;
|
|
1875
|
+
}
|
|
1876
|
+
/** @internal */
|
|
1877
|
+
async getDataLoader(type) {
|
|
1878
|
+
const em = this.getContext();
|
|
1879
|
+
if (em.#loaders[type]) {
|
|
1880
|
+
return em.#loaders[type];
|
|
1881
|
+
}
|
|
1882
|
+
const { DataloaderUtils } = await import('@mikro-orm/core/dataloader');
|
|
1883
|
+
const DataLoader = await DataloaderUtils.getDataLoader();
|
|
1884
|
+
switch (type) {
|
|
1885
|
+
case 'ref':
|
|
1886
|
+
return (em.#loaders[type] ??= new DataLoader(DataloaderUtils.getRefBatchLoadFn(em)));
|
|
1887
|
+
case '1:m':
|
|
1888
|
+
return (em.#loaders[type] ??= new DataLoader(DataloaderUtils.getColBatchLoadFn(em)));
|
|
1889
|
+
case 'm:n':
|
|
1890
|
+
return (em.#loaders[type] ??= new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(em)));
|
|
1891
|
+
}
|
|
1807
1892
|
}
|
|
1808
1893
|
/**
|
|
1809
1894
|
* Returns the ID of this EntityManager. Respects the context, so global EM will give you the contextual ID
|
|
@@ -1813,7 +1898,7 @@ export class EntityManager {
|
|
|
1813
1898
|
return this.getContext(false)._id;
|
|
1814
1899
|
}
|
|
1815
1900
|
/** @ignore */
|
|
1816
|
-
[inspect.custom]() {
|
|
1901
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
1817
1902
|
return `[EntityManager<${this.id}>]`;
|
|
1818
1903
|
}
|
|
1819
1904
|
}
|