@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.
Files changed (218) hide show
  1. package/EntityManager.d.ts +70 -75
  2. package/EntityManager.js +487 -402
  3. package/MikroORM.d.ts +45 -38
  4. package/MikroORM.js +123 -156
  5. package/README.md +7 -4
  6. package/cache/FileCacheAdapter.d.ts +2 -7
  7. package/cache/FileCacheAdapter.js +35 -30
  8. package/cache/GeneratedCacheAdapter.d.ts +1 -2
  9. package/cache/GeneratedCacheAdapter.js +6 -8
  10. package/cache/MemoryCacheAdapter.d.ts +1 -2
  11. package/cache/MemoryCacheAdapter.js +8 -8
  12. package/cache/index.d.ts +1 -2
  13. package/cache/index.js +0 -2
  14. package/connections/Connection.d.ts +12 -5
  15. package/connections/Connection.js +37 -15
  16. package/drivers/DatabaseDriver.d.ts +25 -18
  17. package/drivers/DatabaseDriver.js +144 -45
  18. package/drivers/IDatabaseDriver.d.ts +118 -23
  19. package/entity/BaseEntity.d.ts +63 -4
  20. package/entity/BaseEntity.js +0 -3
  21. package/entity/Collection.d.ts +95 -31
  22. package/entity/Collection.js +487 -139
  23. package/entity/EntityAssigner.js +37 -25
  24. package/entity/EntityFactory.d.ts +8 -9
  25. package/entity/EntityFactory.js +152 -100
  26. package/entity/EntityHelper.d.ts +2 -2
  27. package/entity/EntityHelper.js +69 -27
  28. package/entity/EntityLoader.d.ts +12 -13
  29. package/entity/EntityLoader.js +286 -125
  30. package/entity/EntityRepository.d.ts +28 -8
  31. package/entity/EntityRepository.js +8 -2
  32. package/entity/PolymorphicRef.d.ts +12 -0
  33. package/entity/PolymorphicRef.js +18 -0
  34. package/entity/Reference.d.ts +3 -8
  35. package/entity/Reference.js +62 -29
  36. package/entity/WrappedEntity.d.ts +7 -10
  37. package/entity/WrappedEntity.js +6 -7
  38. package/entity/defineEntity.d.ts +472 -313
  39. package/entity/defineEntity.js +134 -290
  40. package/entity/index.d.ts +2 -2
  41. package/entity/index.js +2 -2
  42. package/entity/utils.d.ts +6 -1
  43. package/entity/utils.js +46 -11
  44. package/entity/validators.d.ts +11 -0
  45. package/entity/validators.js +66 -0
  46. package/enums.d.ts +8 -6
  47. package/enums.js +13 -17
  48. package/errors.d.ts +26 -16
  49. package/errors.js +63 -31
  50. package/events/EventManager.d.ts +3 -5
  51. package/events/EventManager.js +37 -26
  52. package/events/index.d.ts +1 -1
  53. package/events/index.js +0 -1
  54. package/exceptions.js +9 -2
  55. package/hydration/Hydrator.js +1 -2
  56. package/hydration/ObjectHydrator.d.ts +5 -6
  57. package/hydration/ObjectHydrator.js +109 -50
  58. package/index.d.ts +2 -2
  59. package/index.js +1 -2
  60. package/logging/DefaultLogger.d.ts +1 -1
  61. package/logging/DefaultLogger.js +3 -4
  62. package/logging/SimpleLogger.d.ts +1 -1
  63. package/logging/colors.d.ts +1 -1
  64. package/logging/colors.js +4 -6
  65. package/logging/index.d.ts +2 -1
  66. package/logging/index.js +1 -1
  67. package/logging/inspect.d.ts +2 -0
  68. package/logging/inspect.js +11 -0
  69. package/metadata/EntitySchema.d.ts +47 -23
  70. package/metadata/EntitySchema.js +103 -34
  71. package/metadata/MetadataDiscovery.d.ts +65 -18
  72. package/metadata/MetadataDiscovery.js +940 -424
  73. package/metadata/MetadataProvider.d.ts +11 -2
  74. package/metadata/MetadataProvider.js +71 -2
  75. package/metadata/MetadataStorage.d.ts +11 -13
  76. package/metadata/MetadataStorage.js +79 -48
  77. package/metadata/MetadataValidator.d.ts +32 -9
  78. package/metadata/MetadataValidator.js +214 -44
  79. package/metadata/discover-entities.d.ts +5 -0
  80. package/metadata/discover-entities.js +40 -0
  81. package/metadata/index.d.ts +1 -1
  82. package/metadata/index.js +0 -1
  83. package/metadata/types.d.ts +577 -0
  84. package/metadata/types.js +1 -0
  85. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  86. package/naming-strategy/AbstractNamingStrategy.js +26 -5
  87. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  88. package/naming-strategy/EntityCaseNamingStrategy.js +7 -6
  89. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  90. package/naming-strategy/MongoNamingStrategy.js +6 -6
  91. package/naming-strategy/NamingStrategy.d.ts +28 -4
  92. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  93. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  94. package/naming-strategy/index.d.ts +1 -1
  95. package/naming-strategy/index.js +0 -1
  96. package/not-supported.d.ts +2 -0
  97. package/not-supported.js +8 -0
  98. package/package.json +47 -36
  99. package/platforms/ExceptionConverter.js +1 -1
  100. package/platforms/Platform.d.ts +33 -15
  101. package/platforms/Platform.js +125 -69
  102. package/serialization/EntitySerializer.d.ts +6 -3
  103. package/serialization/EntitySerializer.js +54 -30
  104. package/serialization/EntityTransformer.js +37 -22
  105. package/serialization/SerializationContext.d.ts +10 -14
  106. package/serialization/SerializationContext.js +24 -19
  107. package/types/ArrayType.d.ts +1 -1
  108. package/types/ArrayType.js +2 -3
  109. package/types/BigIntType.js +1 -1
  110. package/types/BlobType.d.ts +0 -1
  111. package/types/BlobType.js +0 -3
  112. package/types/BooleanType.d.ts +1 -0
  113. package/types/BooleanType.js +3 -0
  114. package/types/DecimalType.js +2 -2
  115. package/types/DoubleType.js +1 -1
  116. package/types/EnumArrayType.js +1 -2
  117. package/types/JsonType.d.ts +1 -1
  118. package/types/JsonType.js +7 -2
  119. package/types/TinyIntType.js +1 -1
  120. package/types/Type.d.ts +2 -4
  121. package/types/Type.js +3 -3
  122. package/types/Uint8ArrayType.d.ts +0 -1
  123. package/types/Uint8ArrayType.js +1 -4
  124. package/types/UuidType.d.ts +2 -0
  125. package/types/UuidType.js +14 -2
  126. package/types/index.d.ts +3 -2
  127. package/typings.d.ts +427 -170
  128. package/typings.js +100 -45
  129. package/unit-of-work/ChangeSet.d.ts +4 -6
  130. package/unit-of-work/ChangeSet.js +8 -9
  131. package/unit-of-work/ChangeSetComputer.d.ts +2 -12
  132. package/unit-of-work/ChangeSetComputer.js +61 -38
  133. package/unit-of-work/ChangeSetPersister.d.ts +10 -17
  134. package/unit-of-work/ChangeSetPersister.js +136 -73
  135. package/unit-of-work/CommitOrderCalculator.d.ts +13 -14
  136. package/unit-of-work/CommitOrderCalculator.js +22 -20
  137. package/unit-of-work/IdentityMap.d.ts +12 -3
  138. package/unit-of-work/IdentityMap.js +51 -13
  139. package/unit-of-work/UnitOfWork.d.ts +39 -23
  140. package/unit-of-work/UnitOfWork.js +441 -246
  141. package/utils/AbstractMigrator.d.ts +101 -0
  142. package/utils/AbstractMigrator.js +303 -0
  143. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  144. package/utils/AbstractSchemaGenerator.js +30 -18
  145. package/utils/AsyncContext.d.ts +6 -0
  146. package/utils/AsyncContext.js +42 -0
  147. package/utils/Configuration.d.ts +647 -185
  148. package/utils/Configuration.js +215 -252
  149. package/utils/ConfigurationLoader.d.ts +1 -52
  150. package/utils/ConfigurationLoader.js +1 -330
  151. package/utils/Cursor.d.ts +3 -6
  152. package/utils/Cursor.js +32 -17
  153. package/utils/DataloaderUtils.d.ts +10 -5
  154. package/utils/DataloaderUtils.js +42 -22
  155. package/utils/EntityComparator.d.ts +21 -21
  156. package/utils/EntityComparator.js +224 -118
  157. package/utils/QueryHelper.d.ts +34 -7
  158. package/utils/QueryHelper.js +183 -72
  159. package/utils/RawQueryFragment.d.ts +28 -34
  160. package/utils/RawQueryFragment.js +37 -72
  161. package/utils/RequestContext.js +2 -2
  162. package/utils/TransactionContext.js +2 -2
  163. package/utils/TransactionManager.js +11 -8
  164. package/utils/Utils.d.ts +16 -127
  165. package/utils/Utils.js +104 -402
  166. package/utils/clone.js +13 -23
  167. package/utils/env-vars.d.ts +7 -0
  168. package/utils/env-vars.js +98 -0
  169. package/utils/fs-utils.d.ts +20 -0
  170. package/utils/fs-utils.js +193 -0
  171. package/utils/index.d.ts +1 -3
  172. package/utils/index.js +1 -3
  173. package/utils/upsert-utils.d.ts +9 -4
  174. package/utils/upsert-utils.js +51 -5
  175. package/decorators/Check.d.ts +0 -3
  176. package/decorators/Check.js +0 -13
  177. package/decorators/CreateRequestContext.d.ts +0 -3
  178. package/decorators/CreateRequestContext.js +0 -32
  179. package/decorators/Embeddable.d.ts +0 -8
  180. package/decorators/Embeddable.js +0 -11
  181. package/decorators/Embedded.d.ts +0 -12
  182. package/decorators/Embedded.js +0 -18
  183. package/decorators/Entity.d.ts +0 -33
  184. package/decorators/Entity.js +0 -12
  185. package/decorators/Enum.d.ts +0 -9
  186. package/decorators/Enum.js +0 -16
  187. package/decorators/Filter.d.ts +0 -2
  188. package/decorators/Filter.js +0 -8
  189. package/decorators/Formula.d.ts +0 -4
  190. package/decorators/Formula.js +0 -15
  191. package/decorators/Indexed.d.ts +0 -19
  192. package/decorators/Indexed.js +0 -20
  193. package/decorators/ManyToMany.d.ts +0 -42
  194. package/decorators/ManyToMany.js +0 -14
  195. package/decorators/ManyToOne.d.ts +0 -34
  196. package/decorators/ManyToOne.js +0 -14
  197. package/decorators/OneToMany.d.ts +0 -28
  198. package/decorators/OneToMany.js +0 -17
  199. package/decorators/OneToOne.d.ts +0 -28
  200. package/decorators/OneToOne.js +0 -7
  201. package/decorators/PrimaryKey.d.ts +0 -8
  202. package/decorators/PrimaryKey.js +0 -20
  203. package/decorators/Property.d.ts +0 -250
  204. package/decorators/Property.js +0 -32
  205. package/decorators/Transactional.d.ts +0 -14
  206. package/decorators/Transactional.js +0 -28
  207. package/decorators/hooks.d.ts +0 -16
  208. package/decorators/hooks.js +0 -47
  209. package/decorators/index.d.ts +0 -17
  210. package/decorators/index.js +0 -17
  211. package/entity/ArrayCollection.d.ts +0 -118
  212. package/entity/ArrayCollection.js +0 -407
  213. package/entity/EntityValidator.d.ts +0 -19
  214. package/entity/EntityValidator.js +0 -150
  215. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  216. package/metadata/ReflectMetadataProvider.js +0 -44
  217. package/utils/resolveContextProvider.d.ts +0 -10
  218. package/utils/resolveContextProvider.js +0 -28
package/EntityManager.js CHANGED
@@ -1,15 +1,12 @@
1
- import { inspect } from 'node:util';
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, RawQueryFragment } from './utils/RawQueryFragment.js';
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 { EntityValidator } from './entity/EntityValidator.js';
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
- _id = EntityManager.counter++;
31
+ static #counter = 1;
32
+ /** @internal */
33
+ _id = EntityManager.#counter++;
37
34
  global = false;
38
35
  name;
39
- refLoader = new DataLoader(DataloaderUtils.getRefBatchLoadFn(this));
40
- colLoader = new DataLoader(DataloaderUtils.getColBatchLoadFn(this));
41
- colLoaderMtoN = new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(this));
42
- validator;
43
- repositoryMap = {};
44
- entityLoader;
45
- comparator;
46
- entityFactory;
47
- unitOfWork;
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
- _schema;
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.entityLoader = new EntityLoader(this);
59
+ this.#useContext = useContext;
60
+ this.#entityLoader = new EntityLoader(this);
66
61
  this.name = this.config.get('contextName');
67
- this.validator = new EntityValidator(this.config.get('strict'));
68
- this.comparator = this.config.getComparator(this.metadata);
69
- this.resultCache = this.config.getResultCacheAdapter();
70
- this.disableTransactions = this.config.get('disableTransactions');
71
- this.entityFactory = new EntityFactory(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
- entityName = Utils.className(entityName);
98
- if (!this.repositoryMap[entityName]) {
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.repositoryMap[entityName] = new RepositoryClass(this, entityName);
94
+ this.#repositoryMap.set(meta, new RepositoryClass(this, entityName));
102
95
  }
103
- return this.repositoryMap[entityName];
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
- em.validator.validateParams(where);
134
- options.orderBy = options.orderBy || {};
135
- options.populate = await em.preparePopulate(entityName, options);
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.entityLoader.populate(entityName, cached.data, populate, {
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, { ...where }, options);
154
- const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, em, ...options });
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.entityFactory.create(entityName, data, {
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.entityLoader.populate(entityName, unique, populate, {
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.unitOfWork.dispatchOnLoadEvent();
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 3 */
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(name, cond, entityName, enabled = true) {
208
- const options = { name, cond, default: enabled };
209
- if (entityName) {
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
- this.getContext(false).filters[name] = options;
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().filterParams[name] = args;
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().filterParams[name];
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).flushMode = flushMode;
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.find(cls));
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.className));
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.className);
325
+ lookUpChildren(children, meta.class);
272
326
  /* v8 ignore next */
273
- where[meta.root.discriminatorColumn] = children.length > 0 ? { $in: [meta.discriminatorValue, ...children.map(c => c.discriminatorValue)] } : meta.discriminatorValue;
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, cond, options) {
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
- if (options.populate) {
290
- for (const hint of options.populate) {
291
- const field = hint.field.split(':')[0];
292
- const prop = meta.properties[field];
293
- const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
294
- const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
295
- if (!joined && !hint.filter) {
296
- continue;
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
- const where = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', { ...options, populate: hint.children });
299
- const where2 = await this.getJoinedFilters(prop.targetMeta, {}, { ...options, populate: hint.children, populateWhere: PopulateHint.ALL });
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
- return ret;
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 props) {
328
- const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
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
- if (populated.length > 0) {
333
- for (const hint of populated) {
334
- if (!hint.all) {
335
- hint.filter = true;
336
- found = true;
337
- }
338
- else if (hint.field === `${prop.name}:ref`) {
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.find(entityName);
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.filters);
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
- const args = Utils.isPlainObject(options[filter.name]) ? options[filter.name] : this.getContext().filterParams[filter.name];
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
- ret.push(QueryHelper.processWhere({
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 RawQueryFragment.run(async () => {
406
- return Promise.all([
407
- em.find(entityName, where, options),
408
- em.count(entityName, where, options),
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, where, options) {
549
+ async findByCursor(entityName, options) {
469
550
  const em = this.getContext(false);
470
- entityName = Utils.className(entityName);
471
551
  options.overfetch ??= true;
472
- if (Utils.isEmpty(options.orderBy)) {
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 entityName = entity.constructor.name;
490
- const where = helper(entity).getPrimaryKey();
491
- throw options.failHandler(entityName, where);
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 entityName = entity.constructor.name;
503
- const reloaded = await fork.findOne(entityName, entity, {
504
- schema: helper(entity).__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.unitOfWork.unsetIdentity(entity);
591
+ em.#unitOfWork.unsetIdentity(entity);
511
592
  return null;
512
593
  }
513
594
  let found = false;
514
- for (const e of fork.unitOfWork.getIdentityMap()) {
515
- const ref = em.getReference(e.constructor.name, helper(e).getPrimaryKey());
516
- const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true });
517
- em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, true);
518
- helper(ref).__originalEntityData = this.comparator.prepareEntity(e);
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({ ignoreSerializers: true, includeHidden: true });
523
- em.config.getHydrator(this.metadata).hydrate(entity, helper(entity).__meta, data, em.entityFactory, 'full', false, true);
524
- helper(entity).__originalEntityData = this.comparator.prepareEntity(reloaded);
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.unitOfWork.tryGetById(entityName, where, options.schema);
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
- em.validator.validateEmptyWhere(where);
640
+ validateEmptyWhere(where);
553
641
  em.checkLockRequirements(options.lockMode, meta);
554
- const isOptimisticLocking = !Utils.isDefined(options.lockMode) || options.lockMode === LockMode.OPTIMISTIC;
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
- em.validator.validateParams(where);
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.entityLoader.populate(entityName, [cached.data], options.populate, {
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, { ...where }, options);
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.transactionContext,
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.entityFactory.create(entityName, data, {
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.unitOfWork.dispatchOnLoadEvent();
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
- entityName = Utils.className(entityName);
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(entityName, where);
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.name;
750
+ entityName = entityNameOrEntity.constructor;
662
751
  data = entityNameOrEntity;
663
752
  }
664
753
  else {
665
- entityName = Utils.className(entityNameOrEntity);
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.entityFactory.mergeData(meta, entity, data, { initialized: true });
761
+ em.#entityFactory.mergeData(meta, entity, data, { initialized: true });
673
762
  return entity;
674
763
  }
675
764
  where = helper(entity).getPrimaryKey();
676
- data = em.comparator.prepareEntity(entity);
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.unitOfWork.getById(entityName, where, options.schema);
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
- const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
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
- em.validator.validateParams(data, 'insert data');
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.transactionContext,
784
+ ctx: em.#transactionContext,
713
785
  upsert: true,
714
786
  convertCustomTypes,
715
787
  ...options,
716
788
  });
717
- em.unitOfWork.getChangeSetPersister().mapReturnedValues(entity, data, ret.row, meta, true);
718
- entity ??= em.entityFactory.create(entityName, data, {
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 ?? (Utils.isPlainObject(where) ? Object.keys(where) : meta.primaryKeys);
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' || !helper(entity).hasPrimaryKey() || (returning.length > 0 && !(this.getPlatform().usesReturningStatement() && ret.row))) {
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.className, where, {
820
+ const data2 = await this.driver.findOne(meta.class, where, {
746
821
  fields: returning,
747
- ctx: em.transactionContext,
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.entityFactory, 'full', false, true);
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.comparator.prepareEntity(entity);
756
- em.unitOfWork.register(entity, snapshot, { refresh: true });
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.name;
875
+ entityName = entityNameOrEntity[0].constructor;
801
876
  data = entityNameOrEntity;
802
877
  }
803
878
  else {
804
- entityName = Utils.className(entityNameOrEntity);
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.entityFactory.mergeData(meta, entity, row, { initialized: true });
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.comparator.prepareEntity(entity);
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.unitOfWork.getById(entityName, where, options.schema);
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 = unique.findIndex(p => row[p] != null);
850
- if (options.onConflictFields || where == null) {
851
- if (propIndex >= 0) {
852
- where = { [unique[propIndex]]: row[unique[propIndex]] };
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
- em.validator.validateParams(row, 'insert data');
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.transactionContext,
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.unitOfWork.getChangeSetPersister().mapReturnedValues(Utils.isEntity(data[i]) ? data[i] : null, Utils.isEntity(data[i]) ? {} : data[i], res.rows?.[i], meta, true);
897
- const entity = Utils.isEntity(data[i]) ? data[i] : em.entityFactory.create(entityName, row, {
898
- refresh: true,
899
- initialized: true,
900
- schema: options.schema,
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 >= 0 ? [unique[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.className, where, {
927
- fields: returning.concat(...add).concat(...(Array.isArray(uniqueFields) ? uniqueFields : [])),
928
- ctx: em.transactionContext,
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.comparator.matching(entityName, cond, tmp);
1013
+ return this.#comparator.matching(entityName, cond, tmp);
942
1014
  });
943
- /* v8 ignore next 3 */
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.entityFactory, 'full', false, true);
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.comparator.matching(entityName, cond, pk);
1036
+ return this.#comparator.matching(entityName, cond, pk);
965
1037
  });
966
- /* v8 ignore next 3 */
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.entityFactory, 'full');
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.comparator.prepareEntity(entity);
977
- em.unitOfWork.register(entity, snapshot, { refresh: true });
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.disableTransactions || em.disableTransactions) {
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.disableTransactions) {
1096
+ if (this.#disableTransactions) {
1025
1097
  return;
1026
1098
  }
1027
1099
  const em = this.getContext(false);
1028
- em.transactionContext = await em.getConnection('write').begin({
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.disableTransactions) {
1110
+ if (this.#disableTransactions) {
1039
1111
  await em.flush();
1040
1112
  return;
1041
1113
  }
1042
- if (!em.transactionContext) {
1114
+ if (!em.#transactionContext) {
1043
1115
  throw ValidationError.transactionRequired();
1044
1116
  }
1045
1117
  await em.flush();
1046
- await em.getConnection('write').commit(em.transactionContext, new TransactionEventBroadcaster(em));
1047
- delete em.transactionContext;
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.disableTransactions) {
1125
+ if (this.#disableTransactions) {
1054
1126
  return;
1055
1127
  }
1056
1128
  const em = this.getContext(false);
1057
- if (!em.transactionContext) {
1129
+ if (!em.#transactionContext) {
1058
1130
  throw ValidationError.transactionRequired();
1059
1131
  }
1060
- await em.getConnection('write').rollback(em.transactionContext, new TransactionEventBroadcaster(em));
1061
- delete em.transactionContext;
1062
- em.unitOfWork.clearActionsQueue();
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.name;
1151
+ entityName = entityNameOrEntity.constructor;
1080
1152
  data = entityNameOrEntity;
1081
1153
  }
1082
1154
  else {
1083
- entityName = Utils.className(entityNameOrEntity);
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.unitOfWork.getPersistStack().delete(data);
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.unitOfWork.unsetIdentity(data);
1165
+ em.#unitOfWork.unsetIdentity(data);
1094
1166
  }
1095
1167
  const meta = helper(data).__meta;
1096
- const payload = em.comparator.prepareEntity(data);
1168
+ const payload = em.#comparator.prepareEntity(data);
1097
1169
  const cs = new ChangeSet(data, ChangeSetType.CREATE, payload, meta);
1098
- await em.unitOfWork.getChangeSetPersister().executeInserts([cs], { ctx: em.transactionContext, ...options });
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
- em.validator.validateParams(data, 'insert data');
1103
- const res = await em.driver.nativeInsert(entityName, data, { ctx: em.transactionContext, ...options });
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.name;
1189
+ entityName = entityNameOrEntities[0].constructor;
1115
1190
  data = entityNameOrEntities;
1116
1191
  }
1117
1192
  else {
1118
- entityName = Utils.className(entityNameOrEntities);
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.unitOfWork.getPersistStack().delete(row);
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.unitOfWork.unsetIdentity(row);
1208
+ em.#unitOfWork.unsetIdentity(row);
1134
1209
  }
1135
- const payload = em.comparator.prepareEntity(row);
1210
+ const payload = em.#comparator.prepareEntity(row);
1136
1211
  return new ChangeSet(row, ChangeSetType.CREATE, payload, meta);
1137
1212
  });
1138
- await em.unitOfWork.getChangeSetPersister().executeInserts(css, { ctx: em.transactionContext, ...options });
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 => em.validator.validateParams(row, 'insert data'));
1143
- const res = await em.driver.nativeInsertMany(entityName, data, { ctx: em.transactionContext, ...options });
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
- entityName = Utils.className(entityName);
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
- em.validator.validateParams(data, 'update data');
1159
- em.validator.validateParams(where, 'update condition');
1160
- const res = await em.driver.nativeUpdate(entityName, where, data, { ctx: em.transactionContext, ...options });
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
- entityName = Utils.className(entityName);
1170
- where = await em.processWhere(entityName, where, options, 'delete');
1171
- em.validator.validateParams(where, 'delete condition');
1172
- const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, ...options });
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).forEach(k => {
1267
+ for (const k of Object.keys(data)) {
1183
1268
  const prop = meta.properties[k];
1184
- if (prop && prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.includes(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
1185
- data[k] = this.validator.validateProperty(prop, data[k], data);
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, ...options,
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.name, entityName, data);
1289
+ return this.merge(entityName.constructor, entityName, data);
1200
1290
  }
1201
1291
  const em = options.disableContextResolution ? this : this.getContext();
1202
- options.schema ??= em._schema;
1292
+ options.schema ??= em.#schema;
1203
1293
  options.validate ??= true;
1204
1294
  options.cascade ??= true;
1205
- entityName = Utils.className(entityName);
1206
- if (options.validate) {
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
- if (options.keepIdentity && entity && dataIsEntity && entity !== data) {
1217
- helper(entity).__data = helper(data).__data;
1218
- helper(entity).__originalEntityData = helper(data).__originalEntityData;
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.unitOfWork.merge(entity, visited);
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._schema;
1247
- const entity = em.entityFactory.create(entityName, data, {
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(Utils.className(entityName));
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, { ...where }, options);
1301
- em.validator.validateParams(where);
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.transactionContext, em, ...options });
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.unitOfWork.persist(entity, undefined, { cascade: false });
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.name) : undefined;
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.unitOfWork.persist(Reference.unwrapReference(ent), undefined, { cascade: false });
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.unitOfWork.remove(entity, undefined, { cascade: false });
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.unitOfWork.remove(Reference.unwrapReference(ent), undefined, { cascade: false });
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.flushMode ?? em.config.get('flushMode');
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().unitOfWork.clear();
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
- const ret = p in meta.root.properties;
1415
- if (!ret) {
1416
- return !!this.metadata.find(property)?.pivotTable;
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((meta.root.properties)[p].type, parts.join('.'));
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.name;
1434
- const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
1435
- await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
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.flushMode);
1457
- fork.disableTransactions = options.disableTransactions ?? this.disableTransactions ?? this.config.get('disableTransactions');
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.transactionContext = em.transactionContext;
1524
+ fork.#transactionContext = em.#transactionContext;
1461
1525
  }
1462
- fork.filters = { ...em.filters };
1463
- fork.filterParams = Utils.copy(em.filterParams);
1526
+ fork.#filters = { ...em.#filters };
1527
+ fork.#filterParams = Utils.copy(em.#filterParams);
1464
1528
  fork.loggerContext = Utils.merge({}, em.loggerContext, options.loggerContext);
1465
- fork._schema = options.schema ?? em._schema;
1529
+ fork.#schema = options.schema ?? em.#schema;
1466
1530
  if (!options.clear) {
1467
- for (const entity of em.unitOfWork.getIdentityMap()) {
1468
- fork.unitOfWork.register(entity);
1531
+ for (const entity of em.#unitOfWork.getIdentityMap()) {
1532
+ fork.#unitOfWork.register(entity);
1469
1533
  }
1470
- for (const entity of em.unitOfWork.getPersistStack()) {
1471
- fork.unitOfWork.persist(entity);
1534
+ for (const entity of em.#unitOfWork.getPersistStack()) {
1535
+ fork.#unitOfWork.persist(entity);
1472
1536
  }
1473
- for (const entity of em.unitOfWork.getOrphanRemoveStack()) {
1474
- fork.unitOfWork.getOrphanRemoveStack().add(entity);
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.unitOfWork;
1548
+ return this.#unitOfWork;
1485
1549
  }
1486
- return this.getContext().unitOfWork;
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().entityFactory;
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().entityLoader;
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.useContext) {
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).transactionContext;
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).transactionContext;
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).transactionContext = ctx;
1612
+ this.getContext(false).#transactionContext = ctx;
1549
1613
  }
1550
1614
  }
1551
1615
  /**
1552
1616
  * Resets the transaction context.
1553
1617
  */
1554
1618
  resetTransactionContext() {
1555
- delete this.getContext(false).transactionContext;
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.comparator;
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.className, options);
1592
- await this.entityLoader.populate(meta.className, [entity], preparedPopulate, {
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.filter(prop => prop.lazy || [ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind)).map(prop => prop.name));
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('.') && ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(meta.properties[field].kind)) {
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.entityLoader.normalizePopulate(entityName, [], options.strategy);
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).map(field => {
1658
- /* v8 ignore next 3 */
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 (Utils.isString(field)) {
1736
+ if (typeof field === 'string') {
1669
1737
  return [{ field, strategy: options.strategy }];
1670
1738
  }
1671
1739
  return [field];
1672
- }).flat();
1740
+ })
1741
+ .flat();
1673
1742
  }
1674
- const populate = this.entityLoader.normalizePopulate(entityName, options.populate, options.strategy);
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
- return populate.map(field => {
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
- return field;
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._schema;
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.resultCache.get(cacheKey);
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.entityFactory.create(entityName, item, {
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.entityFactory.create(entityName, cached, {
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.unitOfWork.dispatchOnLoadEvent();
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] : (Utils.isNumber(config) ? config : undefined);
1776
- await em.resultCache.set(key.key, data instanceof Function ? data() : data, '', expiration);
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().resultCache.remove(cacheKey);
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)._schema;
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)._schema = schema ?? undefined;
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
  }