@mikro-orm/core 7.0.9 → 7.0.10-dev.0

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 (206) hide show
  1. package/EntityManager.d.ts +583 -884
  2. package/EntityManager.js +1899 -1926
  3. package/MikroORM.d.ts +74 -103
  4. package/MikroORM.js +178 -177
  5. package/README.md +1 -1
  6. package/cache/CacheAdapter.d.ts +36 -36
  7. package/cache/FileCacheAdapter.d.ts +24 -30
  8. package/cache/FileCacheAdapter.js +78 -80
  9. package/cache/GeneratedCacheAdapter.d.ts +20 -18
  10. package/cache/GeneratedCacheAdapter.js +30 -30
  11. package/cache/MemoryCacheAdapter.d.ts +20 -18
  12. package/cache/MemoryCacheAdapter.js +36 -35
  13. package/cache/NullCacheAdapter.d.ts +16 -16
  14. package/cache/NullCacheAdapter.js +24 -24
  15. package/connections/Connection.d.ts +84 -95
  16. package/connections/Connection.js +168 -165
  17. package/drivers/DatabaseDriver.d.ts +80 -186
  18. package/drivers/DatabaseDriver.js +443 -450
  19. package/drivers/IDatabaseDriver.d.ts +301 -440
  20. package/entity/BaseEntity.d.ts +83 -120
  21. package/entity/BaseEntity.js +43 -43
  22. package/entity/Collection.d.ts +181 -215
  23. package/entity/Collection.js +724 -730
  24. package/entity/EntityAssigner.d.ts +77 -88
  25. package/entity/EntityAssigner.js +230 -231
  26. package/entity/EntityFactory.d.ts +55 -67
  27. package/entity/EntityFactory.js +416 -457
  28. package/entity/EntityHelper.d.ts +23 -35
  29. package/entity/EntityHelper.js +279 -291
  30. package/entity/EntityIdentifier.d.ts +4 -4
  31. package/entity/EntityIdentifier.js +10 -10
  32. package/entity/EntityLoader.d.ts +72 -98
  33. package/entity/EntityLoader.js +761 -792
  34. package/entity/EntityRepository.d.ts +201 -316
  35. package/entity/EntityRepository.js +213 -213
  36. package/entity/PolymorphicRef.d.ts +5 -5
  37. package/entity/PolymorphicRef.js +10 -10
  38. package/entity/Reference.d.ts +83 -127
  39. package/entity/Reference.js +277 -281
  40. package/entity/WrappedEntity.d.ts +72 -115
  41. package/entity/WrappedEntity.js +166 -168
  42. package/entity/defineEntity.d.ts +654 -1359
  43. package/entity/defineEntity.js +518 -527
  44. package/entity/utils.d.ts +3 -13
  45. package/entity/utils.js +73 -71
  46. package/entity/validators.js +43 -43
  47. package/entity/wrap.js +8 -8
  48. package/enums.d.ts +253 -258
  49. package/enums.js +252 -251
  50. package/errors.d.ts +72 -114
  51. package/errors.js +253 -350
  52. package/events/EventManager.d.ts +14 -26
  53. package/events/EventManager.js +77 -79
  54. package/events/EventSubscriber.d.ts +29 -29
  55. package/events/TransactionEventBroadcaster.d.ts +8 -15
  56. package/events/TransactionEventBroadcaster.js +14 -14
  57. package/exceptions.d.ts +40 -23
  58. package/exceptions.js +52 -35
  59. package/hydration/Hydrator.d.ts +17 -42
  60. package/hydration/Hydrator.js +43 -43
  61. package/hydration/ObjectHydrator.d.ts +17 -50
  62. package/hydration/ObjectHydrator.js +418 -483
  63. package/index.d.ts +2 -116
  64. package/index.js +1 -10
  65. package/logging/DefaultLogger.d.ts +32 -34
  66. package/logging/DefaultLogger.js +86 -86
  67. package/logging/Logger.d.ts +41 -41
  68. package/logging/SimpleLogger.d.ts +11 -13
  69. package/logging/SimpleLogger.js +22 -22
  70. package/logging/colors.d.ts +6 -6
  71. package/logging/colors.js +10 -11
  72. package/logging/inspect.js +7 -7
  73. package/metadata/EntitySchema.d.ts +130 -214
  74. package/metadata/EntitySchema.js +412 -411
  75. package/metadata/MetadataDiscovery.d.ts +114 -114
  76. package/metadata/MetadataDiscovery.js +1879 -1957
  77. package/metadata/MetadataProvider.d.ts +26 -29
  78. package/metadata/MetadataProvider.js +97 -95
  79. package/metadata/MetadataStorage.d.ts +32 -38
  80. package/metadata/MetadataStorage.js +118 -118
  81. package/metadata/MetadataValidator.d.ts +39 -39
  82. package/metadata/MetadataValidator.js +338 -381
  83. package/metadata/discover-entities.d.ts +2 -5
  84. package/metadata/discover-entities.js +37 -35
  85. package/metadata/types.d.ts +531 -615
  86. package/naming-strategy/AbstractNamingStrategy.d.ts +39 -54
  87. package/naming-strategy/AbstractNamingStrategy.js +85 -90
  88. package/naming-strategy/EntityCaseNamingStrategy.d.ts +6 -6
  89. package/naming-strategy/EntityCaseNamingStrategy.js +22 -22
  90. package/naming-strategy/MongoNamingStrategy.d.ts +6 -6
  91. package/naming-strategy/MongoNamingStrategy.js +18 -18
  92. package/naming-strategy/NamingStrategy.d.ts +99 -109
  93. package/naming-strategy/UnderscoreNamingStrategy.d.ts +7 -7
  94. package/naming-strategy/UnderscoreNamingStrategy.js +21 -21
  95. package/not-supported.js +4 -7
  96. package/package.json +1 -1
  97. package/platforms/ExceptionConverter.d.ts +1 -1
  98. package/platforms/ExceptionConverter.js +4 -4
  99. package/platforms/Platform.d.ts +303 -312
  100. package/platforms/Platform.js +675 -695
  101. package/serialization/EntitySerializer.d.ts +26 -49
  102. package/serialization/EntitySerializer.js +218 -224
  103. package/serialization/EntityTransformer.d.ts +6 -10
  104. package/serialization/EntityTransformer.js +217 -219
  105. package/serialization/SerializationContext.d.ts +23 -27
  106. package/serialization/SerializationContext.js +105 -105
  107. package/types/ArrayType.d.ts +8 -8
  108. package/types/ArrayType.js +33 -33
  109. package/types/BigIntType.d.ts +10 -17
  110. package/types/BigIntType.js +37 -37
  111. package/types/BlobType.d.ts +3 -3
  112. package/types/BlobType.js +13 -13
  113. package/types/BooleanType.d.ts +4 -4
  114. package/types/BooleanType.js +12 -12
  115. package/types/CharacterType.d.ts +2 -2
  116. package/types/CharacterType.js +6 -6
  117. package/types/DateTimeType.d.ts +5 -5
  118. package/types/DateTimeType.js +15 -15
  119. package/types/DateType.d.ts +5 -5
  120. package/types/DateType.js +15 -15
  121. package/types/DecimalType.d.ts +7 -7
  122. package/types/DecimalType.js +26 -26
  123. package/types/DoubleType.d.ts +3 -3
  124. package/types/DoubleType.js +12 -12
  125. package/types/EnumArrayType.d.ts +5 -5
  126. package/types/EnumArrayType.js +24 -24
  127. package/types/EnumType.d.ts +3 -3
  128. package/types/EnumType.js +11 -11
  129. package/types/FloatType.d.ts +3 -3
  130. package/types/FloatType.js +9 -9
  131. package/types/IntegerType.d.ts +3 -3
  132. package/types/IntegerType.js +9 -9
  133. package/types/IntervalType.d.ts +4 -4
  134. package/types/IntervalType.js +12 -12
  135. package/types/JsonType.d.ts +8 -8
  136. package/types/JsonType.js +32 -32
  137. package/types/MediumIntType.d.ts +1 -1
  138. package/types/MediumIntType.js +3 -3
  139. package/types/SmallIntType.d.ts +3 -3
  140. package/types/SmallIntType.js +9 -9
  141. package/types/StringType.d.ts +4 -4
  142. package/types/StringType.js +12 -12
  143. package/types/TextType.d.ts +3 -3
  144. package/types/TextType.js +9 -9
  145. package/types/TimeType.d.ts +5 -5
  146. package/types/TimeType.js +17 -17
  147. package/types/TinyIntType.d.ts +3 -3
  148. package/types/TinyIntType.js +10 -10
  149. package/types/Type.d.ts +79 -83
  150. package/types/Type.js +82 -82
  151. package/types/Uint8ArrayType.d.ts +4 -4
  152. package/types/Uint8ArrayType.js +21 -21
  153. package/types/UnknownType.d.ts +4 -4
  154. package/types/UnknownType.js +12 -12
  155. package/types/UuidType.d.ts +5 -5
  156. package/types/UuidType.js +19 -19
  157. package/types/index.d.ts +49 -75
  158. package/types/index.js +26 -52
  159. package/typings.d.ts +741 -1254
  160. package/typings.js +233 -244
  161. package/unit-of-work/ChangeSet.d.ts +26 -26
  162. package/unit-of-work/ChangeSet.js +56 -56
  163. package/unit-of-work/ChangeSetComputer.d.ts +12 -12
  164. package/unit-of-work/ChangeSetComputer.js +179 -187
  165. package/unit-of-work/ChangeSetPersister.d.ts +50 -69
  166. package/unit-of-work/ChangeSetPersister.js +442 -465
  167. package/unit-of-work/CommitOrderCalculator.d.ts +40 -40
  168. package/unit-of-work/CommitOrderCalculator.js +88 -89
  169. package/unit-of-work/IdentityMap.d.ts +31 -31
  170. package/unit-of-work/IdentityMap.js +105 -105
  171. package/unit-of-work/UnitOfWork.d.ts +141 -181
  172. package/unit-of-work/UnitOfWork.js +1222 -1236
  173. package/utils/AbstractMigrator.d.ts +91 -111
  174. package/utils/AbstractMigrator.js +275 -275
  175. package/utils/AbstractSchemaGenerator.d.ts +34 -43
  176. package/utils/AbstractSchemaGenerator.js +122 -121
  177. package/utils/AsyncContext.d.ts +3 -3
  178. package/utils/AsyncContext.js +35 -34
  179. package/utils/Configuration.d.ts +808 -852
  180. package/utils/Configuration.js +344 -359
  181. package/utils/Cursor.d.ts +22 -40
  182. package/utils/Cursor.js +127 -135
  183. package/utils/DataloaderUtils.d.ts +43 -58
  184. package/utils/DataloaderUtils.js +198 -203
  185. package/utils/EntityComparator.d.ts +82 -99
  186. package/utils/EntityComparator.js +737 -829
  187. package/utils/NullHighlighter.d.ts +1 -1
  188. package/utils/NullHighlighter.js +3 -3
  189. package/utils/QueryHelper.d.ts +51 -79
  190. package/utils/QueryHelper.js +361 -372
  191. package/utils/RawQueryFragment.d.ts +34 -50
  192. package/utils/RawQueryFragment.js +105 -107
  193. package/utils/RequestContext.d.ts +32 -32
  194. package/utils/RequestContext.js +53 -52
  195. package/utils/TransactionContext.d.ts +16 -16
  196. package/utils/TransactionContext.js +27 -27
  197. package/utils/TransactionManager.d.ts +58 -58
  198. package/utils/TransactionManager.js +197 -199
  199. package/utils/Utils.d.ts +145 -204
  200. package/utils/Utils.js +815 -815
  201. package/utils/clone.js +114 -105
  202. package/utils/env-vars.js +88 -90
  203. package/utils/fs-utils.d.ts +15 -15
  204. package/utils/fs-utils.js +181 -180
  205. package/utils/upsert-utils.d.ts +5 -20
  206. package/utils/upsert-utils.js +116 -114
@@ -8,492 +8,469 @@ import { OptimisticLockError, ValidationError } from '../errors.js';
8
8
  import { ReferenceKind } from '../enums.js';
9
9
  /** @internal Executes change sets against the database, handling inserts, updates, and deletes. */
10
10
  export class ChangeSetPersister {
11
- #platform;
12
- #comparator;
13
- #usesReturningStatement;
14
- #driver;
15
- #metadata;
16
- #hydrator;
17
- #factory;
18
- #config;
19
- #em;
20
- constructor(em) {
21
- this.#em = em;
22
- this.#driver = this.#em.getDriver();
23
- this.#config = this.#em.config;
24
- this.#metadata = this.#em.getMetadata();
25
- this.#factory = this.#em.getEntityFactory();
26
- this.#platform = this.#driver.getPlatform();
27
- this.#hydrator = this.#config.getHydrator(this.#metadata);
28
- this.#comparator = this.#config.getComparator(this.#metadata);
29
- this.#usesReturningStatement = this.#platform.usesReturningStatement() || this.#platform.usesOutputStatement();
30
- }
31
- /** Executes all pending INSERT change sets, using batch inserts when possible. */
32
- async executeInserts(changeSets, options, withSchema) {
33
- if (!withSchema) {
34
- return this.runForEachSchema(changeSets, 'executeInserts', options);
35
- }
36
- const meta = changeSets[0].meta;
37
- changeSets.forEach(changeSet => this.processProperties(changeSet));
38
- if (changeSets.length > 1 && this.#config.get('useBatchInserts', this.#platform.usesBatchInserts())) {
39
- return this.persistNewEntities(meta, changeSets, options);
40
- }
41
- for (const changeSet of changeSets) {
42
- await this.persistNewEntity(meta, changeSet, options);
43
- }
44
- }
45
- /** Executes all pending UPDATE change sets, using batch updates when possible. */
46
- async executeUpdates(changeSets, batched, options, withSchema) {
47
- if (!withSchema) {
48
- return this.runForEachSchema(changeSets, 'executeUpdates', options, batched);
49
- }
50
- const meta = changeSets[0].meta;
51
- changeSets.forEach(changeSet => this.processProperties(changeSet));
52
- // For STI with conflicting fieldNames (renamedFrom properties), we can't batch mixed child types
53
- const hasSTIConflicts = meta.root.props.some(p => p.renamedFrom);
54
- const hasMixedTypes = hasSTIConflicts && changeSets.some(cs => cs.meta.class !== meta.class);
55
- if (
56
- batched &&
57
- changeSets.length > 1 &&
58
- !hasMixedTypes &&
59
- this.#config.get('useBatchUpdates', this.#platform.usesBatchUpdates())
60
- ) {
61
- return this.persistManagedEntities(meta, changeSets, options);
62
- }
63
- for (const changeSet of changeSets) {
64
- await this.persistManagedEntity(changeSet, options);
65
- }
66
- }
67
- /** Executes all pending DELETE change sets in batches. */
68
- async executeDeletes(changeSets, options, withSchema) {
69
- if (!withSchema) {
70
- return this.runForEachSchema(changeSets, 'executeDeletes', options);
71
- }
72
- const size = this.#config.get('batchSize');
73
- const meta = changeSets[0].meta;
74
- const pk = Utils.getPrimaryKeyHash(meta.primaryKeys);
75
- for (let i = 0; i < changeSets.length; i += size) {
76
- const chunk = changeSets.slice(i, i + size);
77
- const pks = chunk.map(cs => cs.getPrimaryKey());
78
- options = this.prepareOptions(meta, options);
79
- await this.#driver.nativeDelete(meta.root.class, { [pk]: { $in: pks } }, options);
80
- }
81
- }
82
- async runForEachSchema(changeSets, method, options, ...args) {
83
- const groups = new Map();
84
- changeSets.forEach(cs => {
85
- const group = groups.get(cs.schema) ?? [];
86
- group.push(cs);
87
- groups.set(cs.schema, group);
88
- });
89
- for (const [key, group] of groups.entries()) {
90
- options = { ...options, schema: key };
91
- // @ts-ignore
92
- await this[method](group, ...args, options, true);
93
- }
94
- }
95
- validateRequired(entity) {
96
- const wrapped = helper(entity);
97
- for (const prop of wrapped.__meta.props) {
98
- if (
99
- !prop.nullable &&
100
- !prop.autoincrement &&
101
- !prop.default &&
102
- !prop.defaultRaw &&
103
- !prop.onCreate &&
104
- !prop.generated &&
105
- !prop.embedded &&
106
- ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) &&
107
- !(
108
- prop.kind === ReferenceKind.EMBEDDED &&
109
- prop.targetMeta?.props.every(p => p.formula || p.persist === false || p.primary)
110
- ) &&
111
- prop.name !== wrapped.__meta.root.discriminatorColumn &&
112
- prop.type !== 'ObjectId' &&
113
- prop.persist !== false &&
114
- entity[prop.name] == null
115
- ) {
116
- throw ValidationError.propertyRequired(entity, prop);
117
- }
118
- }
119
- }
120
- processProperties(changeSet) {
121
- const meta = changeSet.meta;
122
- for (const prop of meta.relations) {
123
- this.processProperty(changeSet, prop);
124
- }
125
- if (changeSet.type === ChangeSetType.CREATE && this.#config.get('validateRequired')) {
126
- this.validateRequired(changeSet.entity);
127
- }
128
- }
129
- async persistNewEntity(meta, changeSet, options) {
130
- const wrapped = helper(changeSet.entity);
131
- options = this.prepareOptions(meta, options, {
132
- convertCustomTypes: false,
133
- });
134
- this.resolveTPTIdentifiers(changeSet);
135
- // Use changeSet's own meta for STI entities to get correct field mappings
136
- const res = await this.#driver.nativeInsertMany(changeSet.meta.class, [changeSet.payload], options);
137
- if (!wrapped.hasPrimaryKey()) {
138
- this.mapPrimaryKey(meta, res.insertId ?? res.row?.[meta.primaryKeys[0]], changeSet);
139
- }
140
- this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
141
- this.syncCompositeIdentifiers(changeSet);
142
- this.markAsPopulated(changeSet, meta);
143
- wrapped.__initialized = true;
144
- wrapped.__managed = true;
145
- if (!this.#usesReturningStatement) {
146
- await this.reloadVersionValues(meta, [changeSet], options);
147
- }
148
- changeSet.persisted = true;
149
- }
150
- async persistNewEntities(meta, changeSets, options) {
151
- const size = this.#config.get('batchSize');
152
- for (let i = 0; i < changeSets.length; i += size) {
153
- const chunk = changeSets.slice(i, i + size);
154
- await this.persistNewEntitiesBatch(meta, chunk, options);
155
- if (!this.#usesReturningStatement) {
156
- await this.reloadVersionValues(meta, chunk, options);
157
- }
158
- }
159
- }
160
- prepareOptions(meta, options, additionalOptions) {
161
- const loggerContext = Utils.merge(
162
- { id: this.#em._id },
163
- this.#em.getLoggerContext({ disableContextResolution: true }),
164
- );
165
- return {
166
- ...options,
167
- ...additionalOptions,
168
- schema: options?.schema ?? meta.schema,
169
- loggerContext,
170
- };
171
- }
172
- async persistNewEntitiesBatch(meta, changeSets, options) {
173
- options = this.prepareOptions(meta, options, {
174
- convertCustomTypes: false,
175
- processCollections: false,
176
- });
177
- for (const changeSet of changeSets) {
178
- this.resolveTPTIdentifiers(changeSet);
179
- }
180
- const res = await this.#driver.nativeInsertMany(
181
- meta.class,
182
- changeSets.map(cs => cs.payload),
183
- options,
184
- );
185
- for (let i = 0; i < changeSets.length; i++) {
186
- const changeSet = changeSets[i];
187
- const wrapped = helper(changeSet.entity);
188
- if (!wrapped.hasPrimaryKey()) {
189
- const field = meta.getPrimaryProps()[0].fieldNames[0];
190
- this.mapPrimaryKey(meta, res.rows[i][field], changeSet);
191
- }
192
- if (res.rows) {
193
- this.mapReturnedValues(changeSet.entity, changeSet.payload, res.rows[i], meta);
194
- }
195
- this.syncCompositeIdentifiers(changeSet);
196
- this.markAsPopulated(changeSet, meta);
197
- wrapped.__initialized = true;
198
- wrapped.__managed = true;
199
- changeSet.persisted = true;
200
- }
201
- }
202
- async persistManagedEntity(changeSet, options) {
203
- const meta = changeSet.meta;
204
- const res = await this.updateEntity(meta, changeSet, options);
205
- this.checkOptimisticLock(meta, changeSet, res);
206
- this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
207
- await this.reloadVersionValues(meta, [changeSet], options);
208
- changeSet.persisted = true;
209
- }
210
- async persistManagedEntities(meta, changeSets, options) {
211
- const size = this.#config.get('batchSize');
212
- for (let i = 0; i < changeSets.length; i += size) {
213
- const chunk = changeSets.slice(i, i + size);
214
- await this.persistManagedEntitiesBatch(meta, chunk, options);
215
- await this.reloadVersionValues(meta, chunk, options);
216
- }
217
- }
218
- checkConcurrencyKeys(meta, changeSet, cond) {
219
- const tmp = [];
220
- for (const key of meta.concurrencyCheckKeys) {
221
- cond[key] = changeSet.originalEntity[key];
222
- if (changeSet.payload[key]) {
223
- tmp.push(key);
224
- }
225
- }
226
- if (tmp.length === 0 && meta.concurrencyCheckKeys.size > 0) {
227
- throw OptimisticLockError.lockFailed(changeSet.entity);
228
- }
229
- }
230
- async persistManagedEntitiesBatch(meta, changeSets, options) {
231
- await this.checkOptimisticLocks(meta, changeSets, options);
232
- options = this.prepareOptions(meta, options, {
233
- convertCustomTypes: false,
234
- processCollections: false,
235
- });
236
- const cond = [];
237
- const payload = [];
238
- for (const changeSet of changeSets) {
239
- const where = changeSet.getPrimaryKey(true);
240
- this.checkConcurrencyKeys(meta, changeSet, where);
241
- cond.push(where);
242
- payload.push(changeSet.payload);
243
- }
244
- const res = await this.#driver.nativeUpdateMany(meta.class, cond, payload, options);
245
- const map = new Map();
246
- res.rows?.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.#platform, true), item));
247
- for (const changeSet of changeSets) {
248
- if (res.rows) {
249
- const row = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
250
- this.mapReturnedValues(changeSet.entity, changeSet.payload, row, meta);
251
- }
252
- changeSet.persisted = true;
253
- }
254
- }
255
- mapPrimaryKey(meta, value, changeSet) {
256
- const prop = meta.properties[meta.primaryKeys[0]];
257
- const insertId = prop.customType ? prop.customType.convertToJSValue(value, this.#platform) : value;
258
- const wrapped = helper(changeSet.entity);
259
- if (!wrapped.hasPrimaryKey()) {
260
- wrapped.setPrimaryKey(insertId);
261
- }
262
- // some drivers might be returning bigint PKs as numbers when the number is small enough,
263
- // but we need to have it as string so comparison works in change set tracking, so instead
264
- // of using the raw value from db, we convert it back to the db value explicitly
265
- value = prop.customType
266
- ? prop.customType.convertToDatabaseValue(insertId, this.#platform, { mode: 'serialization' })
267
- : value;
268
- changeSet.payload[wrapped.__meta.primaryKeys[0]] = value;
269
- if (wrapped.__identifier && !Array.isArray(wrapped.__identifier)) {
270
- wrapped.__identifier.setValue(value);
11
+ #platform;
12
+ #comparator;
13
+ #usesReturningStatement;
14
+ #driver;
15
+ #metadata;
16
+ #hydrator;
17
+ #factory;
18
+ #config;
19
+ #em;
20
+ constructor(em) {
21
+ this.#em = em;
22
+ this.#driver = this.#em.getDriver();
23
+ this.#config = this.#em.config;
24
+ this.#metadata = this.#em.getMetadata();
25
+ this.#factory = this.#em.getEntityFactory();
26
+ this.#platform = this.#driver.getPlatform();
27
+ this.#hydrator = this.#config.getHydrator(this.#metadata);
28
+ this.#comparator = this.#config.getComparator(this.#metadata);
29
+ this.#usesReturningStatement = this.#platform.usesReturningStatement() || this.#platform.usesOutputStatement();
30
+ }
31
+ /** Executes all pending INSERT change sets, using batch inserts when possible. */
32
+ async executeInserts(changeSets, options, withSchema) {
33
+ if (!withSchema) {
34
+ return this.runForEachSchema(changeSets, 'executeInserts', options);
35
+ }
36
+ const meta = changeSets[0].meta;
37
+ changeSets.forEach(changeSet => this.processProperties(changeSet));
38
+ if (changeSets.length > 1 && this.#config.get('useBatchInserts', this.#platform.usesBatchInserts())) {
39
+ return this.persistNewEntities(meta, changeSets, options);
40
+ }
41
+ for (const changeSet of changeSets) {
42
+ await this.persistNewEntity(meta, changeSet, options);
43
+ }
271
44
  }
272
- }
273
- /**
274
- * After INSERT + hydration, sync all EntityIdentifier placeholders in composite PK arrays
275
- * with the real values now present on the entity. This is needed because `mapPrimaryKey`
276
- * only handles the first PK column, but any scalar PK in a composite key may be auto-generated.
277
- */
278
- syncCompositeIdentifiers(changeSet) {
279
- const wrapped = helper(changeSet.entity);
280
- if (!Array.isArray(wrapped.__identifier)) {
281
- return;
45
+ /** Executes all pending UPDATE change sets, using batch updates when possible. */
46
+ async executeUpdates(changeSets, batched, options, withSchema) {
47
+ if (!withSchema) {
48
+ return this.runForEachSchema(changeSets, 'executeUpdates', options, batched);
49
+ }
50
+ const meta = changeSets[0].meta;
51
+ changeSets.forEach(changeSet => this.processProperties(changeSet));
52
+ // For STI with conflicting fieldNames (renamedFrom properties), we can't batch mixed child types
53
+ const hasSTIConflicts = meta.root.props.some(p => p.renamedFrom);
54
+ const hasMixedTypes = hasSTIConflicts && changeSets.some(cs => cs.meta.class !== meta.class);
55
+ if (batched &&
56
+ changeSets.length > 1 &&
57
+ !hasMixedTypes &&
58
+ this.#config.get('useBatchUpdates', this.#platform.usesBatchUpdates())) {
59
+ return this.persistManagedEntities(meta, changeSets, options);
60
+ }
61
+ for (const changeSet of changeSets) {
62
+ await this.persistManagedEntity(changeSet, options);
63
+ }
282
64
  }
283
- const pks = changeSet.meta.getPrimaryProps();
284
- for (let i = 0; i < pks.length; i++) {
285
- const ident = wrapped.__identifier[i];
286
- if (ident instanceof EntityIdentifier && pks[i].kind === ReferenceKind.SCALAR) {
287
- ident.setValue(changeSet.entity[pks[i].name]);
288
- }
65
+ /** Executes all pending DELETE change sets in batches. */
66
+ async executeDeletes(changeSets, options, withSchema) {
67
+ if (!withSchema) {
68
+ return this.runForEachSchema(changeSets, 'executeDeletes', options);
69
+ }
70
+ const size = this.#config.get('batchSize');
71
+ const meta = changeSets[0].meta;
72
+ const pk = Utils.getPrimaryKeyHash(meta.primaryKeys);
73
+ for (let i = 0; i < changeSets.length; i += size) {
74
+ const chunk = changeSets.slice(i, i + size);
75
+ const pks = chunk.map(cs => cs.getPrimaryKey());
76
+ options = this.prepareOptions(meta, options);
77
+ await this.#driver.nativeDelete(meta.root.class, { [pk]: { $in: pks } }, options);
78
+ }
289
79
  }
290
- }
291
- /**
292
- * Sets populate flag to new entities so they are serialized like if they were loaded from the db
293
- */
294
- markAsPopulated(changeSet, meta) {
295
- helper(changeSet.entity).__schema = this.#driver.getSchemaName(meta, changeSet);
296
- if (!this.#config.get('populateAfterFlush')) {
297
- return;
80
+ async runForEachSchema(changeSets, method, options, ...args) {
81
+ const groups = new Map();
82
+ changeSets.forEach(cs => {
83
+ const group = groups.get(cs.schema) ?? [];
84
+ group.push(cs);
85
+ groups.set(cs.schema, group);
86
+ });
87
+ for (const [key, group] of groups.entries()) {
88
+ options = { ...options, schema: key };
89
+ // @ts-ignore
90
+ await this[method](group, ...args, options, true);
91
+ }
298
92
  }
299
- helper(changeSet.entity).populated();
300
- meta.relations.forEach(prop => {
301
- const value = changeSet.entity[prop.name];
302
- if (Utils.isEntity(value, true)) {
303
- value.__helper.populated();
304
- } else if (Utils.isCollection(value)) {
305
- value.populated();
306
- }
307
- });
308
- }
309
- async updateEntity(meta, changeSet, options) {
310
- const cond = changeSet.getPrimaryKey(true);
311
- options = this.prepareOptions(meta, options, {
312
- convertCustomTypes: false,
313
- });
314
- if (
315
- meta.concurrencyCheckKeys.size === 0 &&
316
- (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)
317
- ) {
318
- return this.#driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
93
+ validateRequired(entity) {
94
+ const wrapped = helper(entity);
95
+ for (const prop of wrapped.__meta.props) {
96
+ if (!prop.nullable &&
97
+ !prop.autoincrement &&
98
+ !prop.default &&
99
+ !prop.defaultRaw &&
100
+ !prop.onCreate &&
101
+ !prop.generated &&
102
+ !prop.embedded &&
103
+ ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) &&
104
+ !(prop.kind === ReferenceKind.EMBEDDED &&
105
+ prop.targetMeta?.props.every(p => p.formula || p.persist === false || p.primary)) &&
106
+ prop.name !== wrapped.__meta.root.discriminatorColumn &&
107
+ prop.type !== 'ObjectId' &&
108
+ prop.persist !== false &&
109
+ entity[prop.name] == null) {
110
+ throw ValidationError.propertyRequired(entity, prop);
111
+ }
112
+ }
319
113
  }
320
- if (meta.versionProperty) {
321
- cond[meta.versionProperty] = this.#platform.convertVersionValue(
322
- changeSet.entity[meta.versionProperty],
323
- meta.properties[meta.versionProperty],
324
- );
114
+ processProperties(changeSet) {
115
+ const meta = changeSet.meta;
116
+ for (const prop of meta.relations) {
117
+ this.processProperty(changeSet, prop);
118
+ }
119
+ if (changeSet.type === ChangeSetType.CREATE && this.#config.get('validateRequired')) {
120
+ this.validateRequired(changeSet.entity);
121
+ }
325
122
  }
326
- this.checkConcurrencyKeys(meta, changeSet, cond);
327
- return this.#driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
328
- }
329
- async checkOptimisticLocks(meta, changeSets, options) {
330
- if (
331
- meta.concurrencyCheckKeys.size === 0 &&
332
- (!meta.versionProperty || changeSets.every(cs => cs.entity[meta.versionProperty] == null))
333
- ) {
334
- return;
123
+ async persistNewEntity(meta, changeSet, options) {
124
+ const wrapped = helper(changeSet.entity);
125
+ options = this.prepareOptions(meta, options, {
126
+ convertCustomTypes: false,
127
+ });
128
+ this.resolveTPTIdentifiers(changeSet);
129
+ // Use changeSet's own meta for STI entities to get correct field mappings
130
+ const res = await this.#driver.nativeInsertMany(changeSet.meta.class, [changeSet.payload], options);
131
+ if (!wrapped.hasPrimaryKey()) {
132
+ this.mapPrimaryKey(meta, res.insertId ?? res.row?.[meta.primaryKeys[0]], changeSet);
133
+ }
134
+ this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
135
+ this.syncCompositeIdentifiers(changeSet);
136
+ this.markAsPopulated(changeSet, meta);
137
+ wrapped.__initialized = true;
138
+ wrapped.__managed = true;
139
+ if (!this.#usesReturningStatement) {
140
+ await this.reloadVersionValues(meta, [changeSet], options);
141
+ }
142
+ changeSet.persisted = true;
143
+ }
144
+ async persistNewEntities(meta, changeSets, options) {
145
+ const size = this.#config.get('batchSize');
146
+ for (let i = 0; i < changeSets.length; i += size) {
147
+ const chunk = changeSets.slice(i, i + size);
148
+ await this.persistNewEntitiesBatch(meta, chunk, options);
149
+ if (!this.#usesReturningStatement) {
150
+ await this.reloadVersionValues(meta, chunk, options);
151
+ }
152
+ }
335
153
  }
336
- // skip entity references as they don't have version values loaded
337
- changeSets = changeSets.filter(cs => helper(cs.entity).__initialized);
338
- const $or = changeSets.map(cs => {
339
- const cond = Utils.getPrimaryKeyCond(cs.originalEntity, meta.primaryKeys.concat(...meta.concurrencyCheckKeys));
340
- if (meta.versionProperty) {
341
- // @ts-ignore
342
- cond[meta.versionProperty] = this.#platform.convertVersionValue(
343
- cs.entity[meta.versionProperty],
344
- meta.properties[meta.versionProperty],
345
- );
346
- }
347
- return cond;
348
- });
349
- const primaryKeys = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
350
- options = this.prepareOptions(meta, options, {
351
- fields: primaryKeys,
352
- });
353
- const res = await this.#driver.find(meta.root.class, { $or }, options);
354
- if (res.length !== changeSets.length) {
355
- const compare = (a, b, keys) => keys.every(k => a[k] === b[k]);
356
- const entity = changeSets.find(cs => {
357
- return !res.some(row => compare(Utils.getPrimaryKeyCond(cs.entity, primaryKeys), row, primaryKeys));
358
- }).entity;
359
- throw OptimisticLockError.lockFailed(entity);
154
+ prepareOptions(meta, options, additionalOptions) {
155
+ const loggerContext = Utils.merge({ id: this.#em._id }, this.#em.getLoggerContext({ disableContextResolution: true }));
156
+ return {
157
+ ...options,
158
+ ...additionalOptions,
159
+ schema: options?.schema ?? meta.schema,
160
+ loggerContext,
161
+ };
162
+ }
163
+ async persistNewEntitiesBatch(meta, changeSets, options) {
164
+ options = this.prepareOptions(meta, options, {
165
+ convertCustomTypes: false,
166
+ processCollections: false,
167
+ });
168
+ for (const changeSet of changeSets) {
169
+ this.resolveTPTIdentifiers(changeSet);
170
+ }
171
+ const res = await this.#driver.nativeInsertMany(meta.class, changeSets.map(cs => cs.payload), options);
172
+ for (let i = 0; i < changeSets.length; i++) {
173
+ const changeSet = changeSets[i];
174
+ const wrapped = helper(changeSet.entity);
175
+ if (!wrapped.hasPrimaryKey()) {
176
+ const field = meta.getPrimaryProps()[0].fieldNames[0];
177
+ this.mapPrimaryKey(meta, res.rows[i][field], changeSet);
178
+ }
179
+ if (res.rows) {
180
+ this.mapReturnedValues(changeSet.entity, changeSet.payload, res.rows[i], meta);
181
+ }
182
+ this.syncCompositeIdentifiers(changeSet);
183
+ this.markAsPopulated(changeSet, meta);
184
+ wrapped.__initialized = true;
185
+ wrapped.__managed = true;
186
+ changeSet.persisted = true;
187
+ }
360
188
  }
361
- }
362
- checkOptimisticLock(meta, changeSet, res) {
363
- if ((meta.versionProperty || meta.concurrencyCheckKeys.size > 0) && res && !res.affectedRows) {
364
- throw OptimisticLockError.lockFailed(changeSet.entity);
189
+ async persistManagedEntity(changeSet, options) {
190
+ const meta = changeSet.meta;
191
+ const res = await this.updateEntity(meta, changeSet, options);
192
+ this.checkOptimisticLock(meta, changeSet, res);
193
+ this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
194
+ await this.reloadVersionValues(meta, [changeSet], options);
195
+ changeSet.persisted = true;
196
+ }
197
+ async persistManagedEntities(meta, changeSets, options) {
198
+ const size = this.#config.get('batchSize');
199
+ for (let i = 0; i < changeSets.length; i += size) {
200
+ const chunk = changeSets.slice(i, i + size);
201
+ await this.persistManagedEntitiesBatch(meta, chunk, options);
202
+ await this.reloadVersionValues(meta, chunk, options);
203
+ }
365
204
  }
366
- }
367
- /**
368
- * This method also handles reloading of database default values for inserts and raw property updates,
369
- * so we use a single query in case of both versioning and default values is used.
370
- */
371
- async reloadVersionValues(meta, changeSets, options) {
372
- const reloadProps =
373
- meta.versionProperty && !this.#usesReturningStatement ? [meta.properties[meta.versionProperty]] : [];
374
- if (changeSets[0].type === ChangeSetType.CREATE) {
375
- for (const prop of meta.props) {
376
- if (prop.persist === false) {
377
- continue;
378
- }
379
- if (isRaw(changeSets[0].entity[prop.name])) {
380
- reloadProps.push(prop);
381
- continue;
382
- }
383
- // do not reload things that already had a runtime value
384
- if (changeSets[0].entity[prop.name] != null || prop.defaultRaw === 'null') {
385
- continue;
386
- }
387
- if (prop.autoincrement || prop.generated || prop.defaultRaw) {
388
- reloadProps.push(prop);
389
- }
390
- }
205
+ checkConcurrencyKeys(meta, changeSet, cond) {
206
+ const tmp = [];
207
+ for (const key of meta.concurrencyCheckKeys) {
208
+ cond[key] = changeSet.originalEntity[key];
209
+ if (changeSet.payload[key]) {
210
+ tmp.push(key);
211
+ }
212
+ }
213
+ if (tmp.length === 0 && meta.concurrencyCheckKeys.size > 0) {
214
+ throw OptimisticLockError.lockFailed(changeSet.entity);
215
+ }
391
216
  }
392
- if (changeSets[0].type === ChangeSetType.UPDATE) {
393
- const returning = new Set();
394
- changeSets.forEach(cs => {
395
- Utils.keys(cs.payload).forEach(k => {
396
- if (isRaw(cs.payload[k]) && isRaw(cs.entity[k])) {
397
- returning.add(meta.properties[k]);
398
- }
217
+ async persistManagedEntitiesBatch(meta, changeSets, options) {
218
+ await this.checkOptimisticLocks(meta, changeSets, options);
219
+ options = this.prepareOptions(meta, options, {
220
+ convertCustomTypes: false,
221
+ processCollections: false,
399
222
  });
400
- });
401
- // reload generated columns
402
- if (!this.#usesReturningStatement) {
403
- meta.props.filter(prop => prop.generated && !prop.primary).forEach(prop => reloadProps.push(prop));
404
- reloadProps.push(...returning);
405
- }
406
- }
407
- if (reloadProps.length === 0) {
408
- return;
409
- }
410
- reloadProps.unshift(...meta.getPrimaryProps());
411
- const pk = Utils.getPrimaryKeyHash(meta.primaryKeys);
412
- const pks = changeSets.map(cs => {
413
- const val = helper(cs.entity).getPrimaryKey(true);
414
- if (Utils.isPlainObject(val)) {
415
- return Utils.getCompositeKeyValue(val, meta, false, this.#platform);
416
- }
417
- return val;
418
- });
419
- options = this.prepareOptions(meta, options, {
420
- fields: Utils.unique(reloadProps.map(prop => prop.name)),
421
- });
422
- const data = await this.#driver.find(meta.class, { [pk]: { $in: pks } }, options);
423
- const map = new Map();
424
- data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.#platform, true), item));
425
- for (const changeSet of changeSets) {
426
- const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
427
- this.#hydrator.hydrate(changeSet.entity, meta, data, this.#factory, 'full', false, true);
428
- Object.assign(changeSet.payload, data); // merge to the changeset payload, so it gets saved to the entity snapshot
223
+ const cond = [];
224
+ const payload = [];
225
+ for (const changeSet of changeSets) {
226
+ const where = changeSet.getPrimaryKey(true);
227
+ this.checkConcurrencyKeys(meta, changeSet, where);
228
+ cond.push(where);
229
+ payload.push(changeSet.payload);
230
+ }
231
+ const res = await this.#driver.nativeUpdateMany(meta.class, cond, payload, options);
232
+ const map = new Map();
233
+ res.rows?.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.#platform, true), item));
234
+ for (const changeSet of changeSets) {
235
+ if (res.rows) {
236
+ const row = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
237
+ this.mapReturnedValues(changeSet.entity, changeSet.payload, row, meta);
238
+ }
239
+ changeSet.persisted = true;
240
+ }
429
241
  }
430
- }
431
- /**
432
- * For TPT child tables, resolve EntityIdentifier values in PK fields.
433
- * The parent table insert assigns the actual PK value, which the child table references.
434
- */
435
- resolveTPTIdentifiers(changeSet) {
436
- if (changeSet.meta.inheritanceType !== 'tpt' || !changeSet.meta.tptParent) {
437
- return;
242
+ mapPrimaryKey(meta, value, changeSet) {
243
+ const prop = meta.properties[meta.primaryKeys[0]];
244
+ const insertId = prop.customType ? prop.customType.convertToJSValue(value, this.#platform) : value;
245
+ const wrapped = helper(changeSet.entity);
246
+ if (!wrapped.hasPrimaryKey()) {
247
+ wrapped.setPrimaryKey(insertId);
248
+ }
249
+ // some drivers might be returning bigint PKs as numbers when the number is small enough,
250
+ // but we need to have it as string so comparison works in change set tracking, so instead
251
+ // of using the raw value from db, we convert it back to the db value explicitly
252
+ value = prop.customType
253
+ ? prop.customType.convertToDatabaseValue(insertId, this.#platform, { mode: 'serialization' })
254
+ : value;
255
+ changeSet.payload[wrapped.__meta.primaryKeys[0]] = value;
256
+ if (wrapped.__identifier && !Array.isArray(wrapped.__identifier)) {
257
+ wrapped.__identifier.setValue(value);
258
+ }
438
259
  }
439
- for (const pk of changeSet.meta.primaryKeys) {
440
- const value = changeSet.payload[pk];
441
- if (value instanceof EntityIdentifier) {
442
- changeSet.payload[pk] = value.getValue();
443
- }
260
+ /**
261
+ * After INSERT + hydration, sync all EntityIdentifier placeholders in composite PK arrays
262
+ * with the real values now present on the entity. This is needed because `mapPrimaryKey`
263
+ * only handles the first PK column, but any scalar PK in a composite key may be auto-generated.
264
+ */
265
+ syncCompositeIdentifiers(changeSet) {
266
+ const wrapped = helper(changeSet.entity);
267
+ if (!Array.isArray(wrapped.__identifier)) {
268
+ return;
269
+ }
270
+ const pks = changeSet.meta.getPrimaryProps();
271
+ for (let i = 0; i < pks.length; i++) {
272
+ const ident = wrapped.__identifier[i];
273
+ if (ident instanceof EntityIdentifier && pks[i].kind === ReferenceKind.SCALAR) {
274
+ ident.setValue(changeSet.entity[pks[i].name]);
275
+ }
276
+ }
444
277
  }
445
- }
446
- processProperty(changeSet, prop) {
447
- const meta = changeSet.meta;
448
- const value = changeSet.payload[prop.name]; // for inline embeddables
449
- if (value instanceof EntityIdentifier) {
450
- changeSet.payload[prop.name] = value.getValue();
451
- return;
278
+ /**
279
+ * Sets populate flag to new entities so they are serialized like if they were loaded from the db
280
+ */
281
+ markAsPopulated(changeSet, meta) {
282
+ helper(changeSet.entity).__schema = this.#driver.getSchemaName(meta, changeSet);
283
+ if (!this.#config.get('populateAfterFlush')) {
284
+ return;
285
+ }
286
+ helper(changeSet.entity).populated();
287
+ meta.relations.forEach(prop => {
288
+ const value = changeSet.entity[prop.name];
289
+ if (Utils.isEntity(value, true)) {
290
+ value.__helper.populated();
291
+ }
292
+ else if (Utils.isCollection(value)) {
293
+ value.populated();
294
+ }
295
+ });
452
296
  }
453
- if (value instanceof PolymorphicRef) {
454
- if (value.id instanceof EntityIdentifier) {
455
- value.id = value.id.getValue();
456
- }
457
- return;
297
+ async updateEntity(meta, changeSet, options) {
298
+ const cond = changeSet.getPrimaryKey(true);
299
+ options = this.prepareOptions(meta, options, {
300
+ convertCustomTypes: false,
301
+ });
302
+ if (meta.concurrencyCheckKeys.size === 0 &&
303
+ (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)) {
304
+ return this.#driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
305
+ }
306
+ if (meta.versionProperty) {
307
+ cond[meta.versionProperty] = this.#platform.convertVersionValue(changeSet.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
308
+ }
309
+ this.checkConcurrencyKeys(meta, changeSet, cond);
310
+ return this.#driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
458
311
  }
459
- if (Array.isArray(value) && value.some(item => item instanceof EntityIdentifier)) {
460
- changeSet.payload[prop.name] = value.map(item => (item instanceof EntityIdentifier ? item.getValue() : item));
461
- return;
312
+ async checkOptimisticLocks(meta, changeSets, options) {
313
+ if (meta.concurrencyCheckKeys.size === 0 &&
314
+ (!meta.versionProperty || changeSets.every(cs => cs.entity[meta.versionProperty] == null))) {
315
+ return;
316
+ }
317
+ // skip entity references as they don't have version values loaded
318
+ changeSets = changeSets.filter(cs => helper(cs.entity).__initialized);
319
+ const $or = changeSets.map(cs => {
320
+ const cond = Utils.getPrimaryKeyCond(cs.originalEntity, meta.primaryKeys.concat(...meta.concurrencyCheckKeys));
321
+ if (meta.versionProperty) {
322
+ // @ts-ignore
323
+ cond[meta.versionProperty] = this.#platform.convertVersionValue(cs.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
324
+ }
325
+ return cond;
326
+ });
327
+ const primaryKeys = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
328
+ options = this.prepareOptions(meta, options, {
329
+ fields: primaryKeys,
330
+ });
331
+ const res = await this.#driver.find(meta.root.class, { $or }, options);
332
+ if (res.length !== changeSets.length) {
333
+ const compare = (a, b, keys) => keys.every(k => a[k] === b[k]);
334
+ const entity = changeSets.find(cs => {
335
+ return !res.some(row => compare(Utils.getPrimaryKeyCond(cs.entity, primaryKeys), row, primaryKeys));
336
+ }).entity;
337
+ throw OptimisticLockError.lockFailed(entity);
338
+ }
462
339
  }
463
- if (prop.kind === ReferenceKind.MANY_TO_MANY && Array.isArray(value)) {
464
- changeSet.payload[prop.name] = value.map(val => (val instanceof EntityIdentifier ? val.getValue() : val));
465
- return;
340
+ checkOptimisticLock(meta, changeSet, res) {
341
+ if ((meta.versionProperty || meta.concurrencyCheckKeys.size > 0) && res && !res.affectedRows) {
342
+ throw OptimisticLockError.lockFailed(changeSet.entity);
343
+ }
466
344
  }
467
- if (prop.name in changeSet.payload) {
468
- return;
345
+ /**
346
+ * This method also handles reloading of database default values for inserts and raw property updates,
347
+ * so we use a single query in case of both versioning and default values is used.
348
+ */
349
+ async reloadVersionValues(meta, changeSets, options) {
350
+ const reloadProps = meta.versionProperty && !this.#usesReturningStatement ? [meta.properties[meta.versionProperty]] : [];
351
+ if (changeSets[0].type === ChangeSetType.CREATE) {
352
+ for (const prop of meta.props) {
353
+ if (prop.persist === false) {
354
+ continue;
355
+ }
356
+ if (isRaw(changeSets[0].entity[prop.name])) {
357
+ reloadProps.push(prop);
358
+ continue;
359
+ }
360
+ // do not reload things that already had a runtime value
361
+ if (changeSets[0].entity[prop.name] != null || prop.defaultRaw === 'null') {
362
+ continue;
363
+ }
364
+ if (prop.autoincrement || prop.generated || prop.defaultRaw) {
365
+ reloadProps.push(prop);
366
+ }
367
+ }
368
+ }
369
+ if (changeSets[0].type === ChangeSetType.UPDATE) {
370
+ const returning = new Set();
371
+ changeSets.forEach(cs => {
372
+ Utils.keys(cs.payload).forEach(k => {
373
+ if (isRaw(cs.payload[k]) && isRaw(cs.entity[k])) {
374
+ returning.add(meta.properties[k]);
375
+ }
376
+ });
377
+ });
378
+ // reload generated columns
379
+ if (!this.#usesReturningStatement) {
380
+ meta.props.filter(prop => prop.generated && !prop.primary).forEach(prop => reloadProps.push(prop));
381
+ reloadProps.push(...returning);
382
+ }
383
+ }
384
+ if (reloadProps.length === 0) {
385
+ return;
386
+ }
387
+ reloadProps.unshift(...meta.getPrimaryProps());
388
+ const pk = Utils.getPrimaryKeyHash(meta.primaryKeys);
389
+ const pks = changeSets.map(cs => {
390
+ const val = helper(cs.entity).getPrimaryKey(true);
391
+ if (Utils.isPlainObject(val)) {
392
+ return Utils.getCompositeKeyValue(val, meta, false, this.#platform);
393
+ }
394
+ return val;
395
+ });
396
+ options = this.prepareOptions(meta, options, {
397
+ fields: Utils.unique(reloadProps.map(prop => prop.name)),
398
+ });
399
+ const data = await this.#driver.find(meta.class, { [pk]: { $in: pks } }, options);
400
+ const map = new Map();
401
+ data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.#platform, true), item));
402
+ for (const changeSet of changeSets) {
403
+ const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
404
+ this.#hydrator.hydrate(changeSet.entity, meta, data, this.#factory, 'full', false, true);
405
+ Object.assign(changeSet.payload, data); // merge to the changeset payload, so it gets saved to the entity snapshot
406
+ }
469
407
  }
470
- const values = Utils.unwrapProperty(changeSet.payload, meta, prop, true); // for object embeddables
471
- values.forEach(([value, indexes]) => {
472
- if (value instanceof EntityIdentifier) {
473
- Utils.setPayloadProperty(changeSet.payload, meta, prop, value.getValue(), indexes);
474
- }
475
- });
476
- }
477
- /**
478
- * Maps values returned via `returning` statement (postgres) or the inserted id (other sql drivers).
479
- * No need to handle composite keys here as they need to be set upfront.
480
- * We do need to map to the change set payload too, as it will be used in the originalEntityData for new entities.
481
- */
482
- mapReturnedValues(entity, payload, row, meta, upsert = false) {
483
- if ((!this.#usesReturningStatement && !upsert) || !row || !Utils.hasObjectKeys(row)) {
484
- return;
408
+ /**
409
+ * For TPT child tables, resolve EntityIdentifier values in PK fields.
410
+ * The parent table insert assigns the actual PK value, which the child table references.
411
+ */
412
+ resolveTPTIdentifiers(changeSet) {
413
+ if (changeSet.meta.inheritanceType !== 'tpt' || !changeSet.meta.tptParent) {
414
+ return;
415
+ }
416
+ for (const pk of changeSet.meta.primaryKeys) {
417
+ const value = changeSet.payload[pk];
418
+ if (value instanceof EntityIdentifier) {
419
+ changeSet.payload[pk] = value.getValue();
420
+ }
421
+ }
485
422
  }
486
- const mapped = this.#comparator.mapResult(meta, row);
487
- if (entity) {
488
- this.#hydrator.hydrate(entity, meta, mapped, this.#factory, 'full', false, true);
423
+ processProperty(changeSet, prop) {
424
+ const meta = changeSet.meta;
425
+ const value = changeSet.payload[prop.name]; // for inline embeddables
426
+ if (value instanceof EntityIdentifier) {
427
+ changeSet.payload[prop.name] = value.getValue();
428
+ return;
429
+ }
430
+ if (value instanceof PolymorphicRef) {
431
+ if (value.id instanceof EntityIdentifier) {
432
+ value.id = value.id.getValue();
433
+ }
434
+ return;
435
+ }
436
+ if (Array.isArray(value) && value.some(item => item instanceof EntityIdentifier)) {
437
+ changeSet.payload[prop.name] = value.map(item => (item instanceof EntityIdentifier ? item.getValue() : item));
438
+ return;
439
+ }
440
+ if (prop.kind === ReferenceKind.MANY_TO_MANY && Array.isArray(value)) {
441
+ changeSet.payload[prop.name] = value.map(val => (val instanceof EntityIdentifier ? val.getValue() : val));
442
+ return;
443
+ }
444
+ if (prop.name in changeSet.payload) {
445
+ return;
446
+ }
447
+ const values = Utils.unwrapProperty(changeSet.payload, meta, prop, true); // for object embeddables
448
+ values.forEach(([value, indexes]) => {
449
+ if (value instanceof EntityIdentifier) {
450
+ Utils.setPayloadProperty(changeSet.payload, meta, prop, value.getValue(), indexes);
451
+ }
452
+ });
489
453
  }
490
- if (upsert) {
491
- for (const prop of meta.props) {
492
- if (prop.customType && prop.name in mapped) {
493
- mapped[prop.name] = prop.customType.convertToJSValue(mapped[prop.name], this.#platform);
454
+ /**
455
+ * Maps values returned via `returning` statement (postgres) or the inserted id (other sql drivers).
456
+ * No need to handle composite keys here as they need to be set upfront.
457
+ * We do need to map to the change set payload too, as it will be used in the originalEntityData for new entities.
458
+ */
459
+ mapReturnedValues(entity, payload, row, meta, upsert = false) {
460
+ if ((!this.#usesReturningStatement && !upsert) || !row || !Utils.hasObjectKeys(row)) {
461
+ return;
462
+ }
463
+ const mapped = this.#comparator.mapResult(meta, row);
464
+ if (entity) {
465
+ this.#hydrator.hydrate(entity, meta, mapped, this.#factory, 'full', false, true);
466
+ }
467
+ if (upsert) {
468
+ for (const prop of meta.props) {
469
+ if (prop.customType && prop.name in mapped) {
470
+ mapped[prop.name] = prop.customType.convertToJSValue(mapped[prop.name], this.#platform);
471
+ }
472
+ }
494
473
  }
495
- }
474
+ Object.assign(payload, mapped);
496
475
  }
497
- Object.assign(payload, mapped);
498
- }
499
476
  }