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