@mikro-orm/core 7.0.0-dev.31 → 7.0.0-dev.311

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 (214) hide show
  1. package/EntityManager.d.ts +69 -61
  2. package/EntityManager.js +365 -283
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +109 -142
  5. package/README.md +5 -3
  6. package/cache/FileCacheAdapter.d.ts +1 -2
  7. package/cache/FileCacheAdapter.js +19 -14
  8. package/cache/GeneratedCacheAdapter.d.ts +0 -1
  9. package/cache/GeneratedCacheAdapter.js +0 -2
  10. package/cache/index.d.ts +1 -2
  11. package/cache/index.js +0 -2
  12. package/connections/Connection.d.ts +12 -5
  13. package/connections/Connection.js +37 -15
  14. package/drivers/DatabaseDriver.d.ts +25 -16
  15. package/drivers/DatabaseDriver.js +144 -43
  16. package/drivers/IDatabaseDriver.d.ts +118 -23
  17. package/entity/BaseEntity.d.ts +63 -4
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +101 -29
  20. package/entity/Collection.js +473 -115
  21. package/entity/EntityAssigner.js +37 -25
  22. package/entity/EntityFactory.d.ts +7 -1
  23. package/entity/EntityFactory.js +116 -64
  24. package/entity/EntityHelper.d.ts +2 -2
  25. package/entity/EntityHelper.js +69 -27
  26. package/entity/EntityLoader.d.ts +11 -10
  27. package/entity/EntityLoader.js +262 -98
  28. package/entity/EntityRepository.d.ts +28 -8
  29. package/entity/EntityRepository.js +8 -2
  30. package/entity/PolymorphicRef.d.ts +12 -0
  31. package/entity/PolymorphicRef.js +18 -0
  32. package/entity/Reference.d.ts +2 -6
  33. package/entity/Reference.js +52 -19
  34. package/entity/WrappedEntity.d.ts +3 -8
  35. package/entity/WrappedEntity.js +6 -7
  36. package/entity/defineEntity.d.ts +525 -311
  37. package/entity/defineEntity.js +134 -290
  38. package/entity/index.d.ts +2 -2
  39. package/entity/index.js +2 -2
  40. package/entity/utils.d.ts +6 -1
  41. package/entity/utils.js +46 -11
  42. package/entity/validators.d.ts +11 -0
  43. package/entity/validators.js +66 -0
  44. package/enums.d.ts +8 -6
  45. package/enums.js +13 -17
  46. package/errors.d.ts +20 -10
  47. package/errors.js +63 -31
  48. package/events/EventManager.d.ts +2 -1
  49. package/events/EventManager.js +24 -13
  50. package/events/index.d.ts +1 -1
  51. package/events/index.js +0 -1
  52. package/exceptions.js +7 -2
  53. package/hydration/Hydrator.js +1 -2
  54. package/hydration/ObjectHydrator.d.ts +4 -4
  55. package/hydration/ObjectHydrator.js +105 -46
  56. package/index.d.ts +2 -2
  57. package/index.js +1 -2
  58. package/logging/DefaultLogger.d.ts +1 -1
  59. package/logging/DefaultLogger.js +3 -4
  60. package/logging/SimpleLogger.d.ts +1 -1
  61. package/logging/colors.d.ts +1 -1
  62. package/logging/colors.js +5 -7
  63. package/logging/index.d.ts +2 -1
  64. package/logging/index.js +1 -1
  65. package/logging/inspect.d.ts +2 -0
  66. package/logging/inspect.js +11 -0
  67. package/metadata/EntitySchema.d.ts +47 -23
  68. package/metadata/EntitySchema.js +103 -34
  69. package/metadata/MetadataDiscovery.d.ts +64 -9
  70. package/metadata/MetadataDiscovery.js +866 -354
  71. package/metadata/MetadataProvider.d.ts +11 -2
  72. package/metadata/MetadataProvider.js +71 -2
  73. package/metadata/MetadataStorage.d.ts +13 -11
  74. package/metadata/MetadataStorage.js +72 -41
  75. package/metadata/MetadataValidator.d.ts +32 -9
  76. package/metadata/MetadataValidator.js +214 -44
  77. package/metadata/discover-entities.d.ts +5 -0
  78. package/metadata/discover-entities.js +40 -0
  79. package/metadata/index.d.ts +1 -1
  80. package/metadata/index.js +0 -1
  81. package/metadata/types.d.ts +577 -0
  82. package/metadata/types.js +1 -0
  83. package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
  84. package/naming-strategy/AbstractNamingStrategy.js +26 -5
  85. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  86. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  87. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  88. package/naming-strategy/MongoNamingStrategy.js +6 -6
  89. package/naming-strategy/NamingStrategy.d.ts +28 -4
  90. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  91. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  92. package/naming-strategy/index.d.ts +1 -1
  93. package/naming-strategy/index.js +0 -1
  94. package/not-supported.d.ts +2 -0
  95. package/not-supported.js +8 -0
  96. package/package.json +47 -36
  97. package/platforms/ExceptionConverter.js +1 -1
  98. package/platforms/Platform.d.ts +11 -15
  99. package/platforms/Platform.js +72 -69
  100. package/serialization/EntitySerializer.d.ts +6 -3
  101. package/serialization/EntitySerializer.js +53 -29
  102. package/serialization/EntityTransformer.js +33 -21
  103. package/serialization/SerializationContext.d.ts +6 -6
  104. package/serialization/SerializationContext.js +4 -4
  105. package/types/ArrayType.d.ts +1 -1
  106. package/types/ArrayType.js +2 -3
  107. package/types/BigIntType.js +1 -1
  108. package/types/BlobType.d.ts +0 -1
  109. package/types/BlobType.js +0 -3
  110. package/types/BooleanType.d.ts +1 -0
  111. package/types/BooleanType.js +3 -0
  112. package/types/DecimalType.js +2 -2
  113. package/types/DoubleType.js +1 -1
  114. package/types/EnumArrayType.js +1 -2
  115. package/types/JsonType.d.ts +1 -1
  116. package/types/JsonType.js +7 -2
  117. package/types/TinyIntType.js +1 -1
  118. package/types/Type.d.ts +2 -4
  119. package/types/Type.js +3 -3
  120. package/types/Uint8ArrayType.d.ts +0 -1
  121. package/types/Uint8ArrayType.js +1 -4
  122. package/types/index.d.ts +3 -2
  123. package/typings.d.ts +427 -170
  124. package/typings.js +100 -45
  125. package/unit-of-work/ChangeSet.d.ts +4 -6
  126. package/unit-of-work/ChangeSet.js +8 -9
  127. package/unit-of-work/ChangeSetComputer.d.ts +3 -8
  128. package/unit-of-work/ChangeSetComputer.js +49 -26
  129. package/unit-of-work/ChangeSetPersister.d.ts +13 -12
  130. package/unit-of-work/ChangeSetPersister.js +106 -43
  131. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  132. package/unit-of-work/CommitOrderCalculator.js +17 -15
  133. package/unit-of-work/IdentityMap.d.ts +12 -0
  134. package/unit-of-work/IdentityMap.js +39 -1
  135. package/unit-of-work/UnitOfWork.d.ts +34 -4
  136. package/unit-of-work/UnitOfWork.js +294 -107
  137. package/utils/AbstractMigrator.d.ts +101 -0
  138. package/utils/AbstractMigrator.js +303 -0
  139. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  140. package/utils/AbstractSchemaGenerator.js +30 -18
  141. package/utils/AsyncContext.d.ts +6 -0
  142. package/utils/AsyncContext.js +42 -0
  143. package/utils/Configuration.d.ts +795 -211
  144. package/utils/Configuration.js +160 -197
  145. package/utils/ConfigurationLoader.d.ts +1 -52
  146. package/utils/ConfigurationLoader.js +1 -330
  147. package/utils/Cursor.d.ts +0 -3
  148. package/utils/Cursor.js +29 -14
  149. package/utils/DataloaderUtils.d.ts +10 -5
  150. package/utils/DataloaderUtils.js +42 -22
  151. package/utils/EntityComparator.d.ts +16 -9
  152. package/utils/EntityComparator.js +202 -96
  153. package/utils/QueryHelper.d.ts +34 -6
  154. package/utils/QueryHelper.js +181 -48
  155. package/utils/RawQueryFragment.d.ts +28 -34
  156. package/utils/RawQueryFragment.js +37 -72
  157. package/utils/RequestContext.js +2 -2
  158. package/utils/TransactionContext.js +2 -2
  159. package/utils/TransactionManager.js +11 -7
  160. package/utils/Utils.d.ts +16 -127
  161. package/utils/Utils.js +106 -401
  162. package/utils/clone.js +8 -23
  163. package/utils/env-vars.d.ts +7 -0
  164. package/utils/env-vars.js +98 -0
  165. package/utils/fs-utils.d.ts +34 -0
  166. package/utils/fs-utils.js +193 -0
  167. package/utils/index.d.ts +1 -3
  168. package/utils/index.js +1 -3
  169. package/utils/upsert-utils.d.ts +9 -4
  170. package/utils/upsert-utils.js +51 -5
  171. package/decorators/Check.d.ts +0 -3
  172. package/decorators/Check.js +0 -13
  173. package/decorators/CreateRequestContext.d.ts +0 -3
  174. package/decorators/CreateRequestContext.js +0 -32
  175. package/decorators/Embeddable.d.ts +0 -8
  176. package/decorators/Embeddable.js +0 -11
  177. package/decorators/Embedded.d.ts +0 -12
  178. package/decorators/Embedded.js +0 -18
  179. package/decorators/Entity.d.ts +0 -33
  180. package/decorators/Entity.js +0 -12
  181. package/decorators/Enum.d.ts +0 -9
  182. package/decorators/Enum.js +0 -16
  183. package/decorators/Filter.d.ts +0 -2
  184. package/decorators/Filter.js +0 -8
  185. package/decorators/Formula.d.ts +0 -4
  186. package/decorators/Formula.js +0 -15
  187. package/decorators/Indexed.d.ts +0 -19
  188. package/decorators/Indexed.js +0 -20
  189. package/decorators/ManyToMany.d.ts +0 -42
  190. package/decorators/ManyToMany.js +0 -14
  191. package/decorators/ManyToOne.d.ts +0 -34
  192. package/decorators/ManyToOne.js +0 -14
  193. package/decorators/OneToMany.d.ts +0 -28
  194. package/decorators/OneToMany.js +0 -17
  195. package/decorators/OneToOne.d.ts +0 -28
  196. package/decorators/OneToOne.js +0 -7
  197. package/decorators/PrimaryKey.d.ts +0 -8
  198. package/decorators/PrimaryKey.js +0 -20
  199. package/decorators/Property.d.ts +0 -250
  200. package/decorators/Property.js +0 -32
  201. package/decorators/Transactional.d.ts +0 -14
  202. package/decorators/Transactional.js +0 -28
  203. package/decorators/hooks.d.ts +0 -16
  204. package/decorators/hooks.js +0 -47
  205. package/decorators/index.d.ts +0 -17
  206. package/decorators/index.js +0 -17
  207. package/entity/ArrayCollection.d.ts +0 -118
  208. package/entity/ArrayCollection.js +0 -407
  209. package/entity/EntityValidator.d.ts +0 -19
  210. package/entity/EntityValidator.js +0 -150
  211. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  212. package/metadata/ReflectMetadataProvider.js +0 -44
  213. package/utils/resolveContextProvider.d.ts +0 -10
  214. package/utils/resolveContextProvider.js +0 -28
@@ -1,4 +1,3 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
2
1
  import { Collection } from '../entity/Collection.js';
3
2
  import { EntityHelper } from '../entity/EntityHelper.js';
4
3
  import { helper } from '../entity/wrap.js';
@@ -13,8 +12,9 @@ import { Cascade, DeferMode, EventType, LockMode, ReferenceKind } from '../enums
13
12
  import { OptimisticLockError, ValidationError } from '../errors.js';
14
13
  import { TransactionEventBroadcaster } from '../events/TransactionEventBroadcaster.js';
15
14
  import { IdentityMap } from './IdentityMap.js';
15
+ import { createAsyncContext } from '../utils/AsyncContext.js';
16
16
  // to deal with validation for flush inside flush hooks and `Promise.all`
17
- const insideFlush = new AsyncLocalStorage();
17
+ const insideFlush = createAsyncContext();
18
18
  export class UnitOfWork {
19
19
  em;
20
20
  /** map of references to managed entities */
@@ -42,8 +42,8 @@ export class UnitOfWork {
42
42
  this.identityMap = new IdentityMap(this.platform.getDefaultSchemaName());
43
43
  this.eventManager = this.em.getEventManager();
44
44
  this.comparator = this.em.getComparator();
45
- this.changeSetComputer = new ChangeSetComputer(this.em.getValidator(), this.collectionUpdates, this.metadata, this.platform, this.em.config, this.em);
46
- this.changeSetPersister = new ChangeSetPersister(this.em.getDriver(), this.metadata, this.em.config.getHydrator(this.metadata), this.em.getEntityFactory(), this.em.getValidator(), this.em.config, this.em);
45
+ this.changeSetComputer = new ChangeSetComputer(this.em, this.collectionUpdates);
46
+ this.changeSetPersister = new ChangeSetPersister(this.em);
47
47
  }
48
48
  merge(entity, visited) {
49
49
  const wrapped = helper(entity);
@@ -61,10 +61,51 @@ export class UnitOfWork {
61
61
  // as there can be some entity with already changed state that is not yet flushed
62
62
  if (wrapped.__initialized && (!visited || !wrapped.__originalEntityData)) {
63
63
  wrapped.__originalEntityData = this.comparator.prepareEntity(entity);
64
- wrapped.__touched = false;
65
64
  }
66
65
  this.cascade(entity, Cascade.MERGE, visited ?? new Set());
67
66
  }
67
+ /**
68
+ * Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
69
+ * we need to normalize the shape, so relation values are only raw FKs. This method handles that.
70
+ * @internal
71
+ */
72
+ normalizeEntityData(meta, data) {
73
+ const forceUndefined = this.em.config.get('forceUndefined');
74
+ for (const key of Utils.keys(data)) {
75
+ const prop = meta.properties[key];
76
+ if (!prop) {
77
+ continue;
78
+ }
79
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) &&
80
+ Utils.isPlainObject(data[prop.name])) {
81
+ // Skip polymorphic relations - they use PolymorphicRef wrapper
82
+ if (!prop.polymorphic) {
83
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
84
+ }
85
+ }
86
+ else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
87
+ for (const p of prop.targetMeta.props) {
88
+ /* v8 ignore next */
89
+ const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
90
+ data[(prefix + p.name)] = data[prop.name][p.name];
91
+ }
92
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
93
+ }
94
+ if (prop.hydrate === false && prop.customType?.ensureComparable(meta, prop)) {
95
+ const converted = prop.customType.convertToJSValue(data[key], this.platform, {
96
+ key,
97
+ mode: 'hydration',
98
+ force: true,
99
+ });
100
+ data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
101
+ }
102
+ if (forceUndefined) {
103
+ if (data[key] === null) {
104
+ data[key] = undefined;
105
+ }
106
+ }
107
+ }
108
+ }
68
109
  /**
69
110
  * @internal
70
111
  */
@@ -82,31 +123,14 @@ export class UnitOfWork {
82
123
  wrapped.__em ??= this.em;
83
124
  wrapped.__managed = true;
84
125
  if (data && (options?.refresh || !wrapped.__originalEntityData)) {
126
+ this.normalizeEntityData(wrapped.__meta, data);
85
127
  for (const key of Utils.keys(data)) {
86
128
  const prop = wrapped.__meta.properties[key];
87
- if (!prop) {
88
- continue;
89
- }
90
- wrapped.__loadedProperties.add(key);
91
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
92
- data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
93
- }
94
- else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
95
- for (const p of prop.targetMeta.props) {
96
- /* v8 ignore next */
97
- const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
98
- data[prefix + p.name] = data[prop.name][p.name];
99
- }
100
- data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
101
- }
102
- if (forceUndefined) {
103
- if (data[key] === null) {
104
- data[key] = undefined;
105
- }
129
+ if (prop) {
130
+ wrapped.__loadedProperties.add(key);
106
131
  }
107
132
  }
108
133
  wrapped.__originalEntityData = data;
109
- wrapped.__touched = false;
110
134
  }
111
135
  return entity;
112
136
  }
@@ -153,6 +177,40 @@ export class UnitOfWork {
153
177
  }
154
178
  return this.identityMap.getByHash(meta, hash);
155
179
  }
180
+ /**
181
+ * Returns entity from the identity map by an alternate key (non-PK property).
182
+ * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format for lookup.
183
+ * If false (default), the value is assumed to be in JS format already.
184
+ */
185
+ getByKey(entityName, key, value, schema, convertCustomTypes) {
186
+ const meta = this.metadata.find(entityName).root;
187
+ schema ??= meta.schema ?? this.em.config.getSchema();
188
+ const prop = meta.properties[key];
189
+ // Convert from DB format to JS format if needed
190
+ if (convertCustomTypes && prop?.customType) {
191
+ value = prop.customType.convertToJSValue(value, this.platform, { mode: 'hydration' });
192
+ }
193
+ const hash = this.identityMap.getKeyHash(key, '' + value, schema);
194
+ return this.identityMap.getByHash(meta, hash);
195
+ }
196
+ /**
197
+ * Stores an entity in the identity map under an alternate key (non-PK property).
198
+ * Also sets the property value on the entity.
199
+ * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format.
200
+ * If false (default), the value is assumed to be in JS format already.
201
+ */
202
+ storeByKey(entity, key, value, schema, convertCustomTypes) {
203
+ const meta = entity.__meta.root;
204
+ schema ??= meta.schema ?? this.em.config.getSchema();
205
+ const prop = meta.properties[key];
206
+ // Convert from DB format to JS format if needed
207
+ if (convertCustomTypes && prop?.customType) {
208
+ value = prop.customType.convertToJSValue(value, this.platform, { mode: 'hydration' });
209
+ }
210
+ // Set the property on the entity
211
+ entity[key] = value;
212
+ this.identityMap.storeByKey(entity, key, '' + value, schema);
213
+ }
156
214
  tryGetById(entityName, where, schema, strict = true) {
157
215
  const pk = Utils.extractPK(where, this.metadata.find(entityName), strict);
158
216
  if (!pk) {
@@ -191,13 +249,11 @@ export class UnitOfWork {
191
249
  if (insideFlush.getStore()) {
192
250
  return false;
193
251
  }
194
- if (this.queuedActions.has(meta.className) || this.queuedActions.has(meta.root.className)) {
252
+ if (this.queuedActions.has(meta.class) || this.queuedActions.has(meta.root.class)) {
195
253
  return true;
196
254
  }
197
- for (const entity of this.identityMap.getStore(meta).values()) {
198
- if (helper(entity).__initialized && helper(entity).isTouched()) {
199
- return true;
200
- }
255
+ if (meta.discriminatorMap && Object.values(meta.discriminatorMap).some(v => this.queuedActions.has(v))) {
256
+ return true;
201
257
  }
202
258
  return false;
203
259
  }
@@ -206,7 +262,7 @@ export class UnitOfWork {
206
262
  }
207
263
  computeChangeSet(entity, type) {
208
264
  const wrapped = helper(entity);
209
- if (type) {
265
+ if (type === ChangeSetType.DELETE || type === ChangeSetType.DELETE_EARLY) {
210
266
  this.changeSets.set(entity, new ChangeSet(entity, type, {}, wrapped.__meta));
211
267
  return;
212
268
  }
@@ -214,11 +270,14 @@ export class UnitOfWork {
214
270
  if (!cs || this.checkUniqueProps(cs)) {
215
271
  return;
216
272
  }
273
+ /* v8 ignore next */
274
+ if (type) {
275
+ cs.type = type;
276
+ }
217
277
  this.initIdentifier(entity);
218
278
  this.changeSets.set(entity, cs);
219
279
  this.persistStack.delete(entity);
220
280
  wrapped.__originalEntityData = this.comparator.prepareEntity(entity);
221
- wrapped.__touched = false;
222
281
  }
223
282
  recomputeSingleChangeSet(entity) {
224
283
  const changeSet = this.changeSets.get(entity);
@@ -229,7 +288,6 @@ export class UnitOfWork {
229
288
  if (cs && !this.checkUniqueProps(cs)) {
230
289
  Object.assign(changeSet.payload, cs.payload);
231
290
  helper(entity).__originalEntityData = this.comparator.prepareEntity(entity);
232
- helper(entity).__touched = false;
233
291
  }
234
292
  }
235
293
  persist(entity, visited, options = {}) {
@@ -239,7 +297,7 @@ export class UnitOfWork {
239
297
  }
240
298
  const wrapped = helper(entity);
241
299
  this.persistStack.add(entity);
242
- this.queuedActions.add(wrapped.__meta.className);
300
+ this.queuedActions.add(wrapped.__meta.class);
243
301
  this.removeStack.delete(entity);
244
302
  if (!wrapped.__managed && wrapped.hasPrimaryKey()) {
245
303
  this.identityMap.store(entity);
@@ -252,7 +310,7 @@ export class UnitOfWork {
252
310
  // allow removing not managed entities if they are not part of the persist stack
253
311
  if (helper(entity).__managed || !this.persistStack.has(entity)) {
254
312
  this.removeStack.add(entity);
255
- this.queuedActions.add(helper(entity).__meta.className);
313
+ this.queuedActions.add(helper(entity).__meta.class);
256
314
  }
257
315
  else {
258
316
  this.persistStack.delete(entity);
@@ -269,7 +327,7 @@ export class UnitOfWork {
269
327
  }
270
328
  continue;
271
329
  }
272
- const target = relation && relation[inverseProp];
330
+ const target = relation?.[inverseProp];
273
331
  if (relation && Utils.isCollection(target)) {
274
332
  target.removeWithoutPropagation(entity);
275
333
  }
@@ -315,7 +373,8 @@ export class UnitOfWork {
315
373
  this.filterCollectionUpdates();
316
374
  // nothing to do, do not start transaction
317
375
  if (this.changeSets.size === 0 && this.collectionUpdates.size === 0 && this.extraUpdates.size === 0) {
318
- return void (await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this }));
376
+ await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this });
377
+ return;
319
378
  }
320
379
  const groups = this.getChangeSetGroups();
321
380
  const platform = this.em.getPlatform();
@@ -342,10 +401,10 @@ export class UnitOfWork {
342
401
  }
343
402
  }
344
403
  async lock(entity, options) {
345
- if (!this.getById(entity.constructor.name, helper(entity).__primaryKeys, helper(entity).__schema)) {
404
+ if (!this.getById(entity.constructor, helper(entity).__primaryKeys, helper(entity).__schema)) {
346
405
  throw ValidationError.entityNotManaged(entity);
347
406
  }
348
- const meta = this.metadata.find(entity.constructor.name);
407
+ const meta = this.metadata.find(entity.constructor);
349
408
  if (options.lockMode === LockMode.OPTIMISTIC) {
350
409
  await this.lockOptimistic(entity, meta, options.lockVersion);
351
410
  }
@@ -369,7 +428,10 @@ export class UnitOfWork {
369
428
  if (Utils.isCollection(rel)) {
370
429
  rel.removeWithoutPropagation(entity);
371
430
  }
372
- else if (rel && (prop.mapToPk ? helper(this.em.getReference(prop.type, rel)).getSerializedPrimaryKey() === serializedPK : rel === entity)) {
431
+ else if (rel &&
432
+ (prop.mapToPk
433
+ ? helper(this.em.getReference(prop.targetMeta.class, rel)).getSerializedPrimaryKey() === serializedPK
434
+ : rel === entity)) {
373
435
  if (prop.formula) {
374
436
  delete referrer[prop.name];
375
437
  }
@@ -381,7 +443,6 @@ export class UnitOfWork {
381
443
  }
382
444
  delete wrapped.__identifier;
383
445
  delete wrapped.__originalEntityData;
384
- wrapped.__touched = false;
385
446
  wrapped.__managed = false;
386
447
  }
387
448
  computeChangeSets() {
@@ -391,14 +452,14 @@ export class UnitOfWork {
391
452
  this.cascade(entity, Cascade.REMOVE, visited);
392
453
  }
393
454
  visited.clear();
394
- for (const entity of this.persistStack) {
395
- this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
396
- }
397
455
  for (const entity of this.identityMap) {
398
456
  if (!this.removeStack.has(entity) && !this.persistStack.has(entity) && !this.orphanRemoveStack.has(entity)) {
399
457
  this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
400
458
  }
401
459
  }
460
+ for (const entity of this.persistStack) {
461
+ this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
462
+ }
402
463
  visited.clear();
403
464
  for (const entity of this.persistStack) {
404
465
  this.findNewEntities(entity, visited);
@@ -412,24 +473,24 @@ export class UnitOfWork {
412
473
  const inserts = {};
413
474
  for (const cs of this.changeSets.values()) {
414
475
  if (cs.type === ChangeSetType.CREATE) {
415
- inserts[cs.meta.className] ??= [];
416
- inserts[cs.meta.className].push(cs);
476
+ inserts[cs.meta.uniqueName] ??= [];
477
+ inserts[cs.meta.uniqueName].push(cs);
417
478
  }
418
479
  }
419
480
  for (const cs of this.changeSets.values()) {
420
481
  if (cs.type === ChangeSetType.UPDATE) {
421
- this.findEarlyUpdates(cs, inserts[cs.meta.className]);
482
+ this.findEarlyUpdates(cs, inserts[cs.meta.uniqueName]);
422
483
  }
423
484
  }
424
485
  for (const entity of this.removeStack) {
425
486
  const wrapped = helper(entity);
426
- /* v8 ignore next 3 */
487
+ /* v8 ignore next */
427
488
  if (wrapped.__processing) {
428
489
  continue;
429
490
  }
430
491
  const deletePkHash = [wrapped.getSerializedPrimaryKey(), ...this.expandUniqueProps(entity)];
431
492
  let type = ChangeSetType.DELETE;
432
- for (const cs of inserts[wrapped.__meta.className] ?? []) {
493
+ for (const cs of inserts[wrapped.__meta.uniqueName] ?? []) {
433
494
  if (deletePkHash.some(hash => hash === cs.getSerializedPrimaryKey() || this.expandUniqueProps(cs.entity).find(child => hash === child))) {
434
495
  type = ChangeSetType.DELETE_EARLY;
435
496
  }
@@ -448,7 +509,7 @@ export class UnitOfWork {
448
509
  }
449
510
  for (const cs of this.changeSets.values()) {
450
511
  for (const prop of props) {
451
- if (prop.name in cs.payload && cs.rootName === changeSet.rootName && cs.type === changeSet.type) {
512
+ if (prop.name in cs.payload && cs.rootMeta === changeSet.rootMeta && cs.type === changeSet.type) {
452
513
  conflicts = true;
453
514
  if (changeSet.payload[prop.name] == null) {
454
515
  type = ChangeSetType.UPDATE_EARLY;
@@ -459,7 +520,13 @@ export class UnitOfWork {
459
520
  if (!conflicts) {
460
521
  return;
461
522
  }
462
- this.extraUpdates.add([changeSet.entity, props.map(p => p.name), props.map(p => changeSet.entity[p.name]), changeSet, type]);
523
+ this.extraUpdates.add([
524
+ changeSet.entity,
525
+ props.map(p => p.name),
526
+ props.map(p => changeSet.entity[p.name]),
527
+ changeSet,
528
+ type,
529
+ ]);
463
530
  for (const p of props) {
464
531
  delete changeSet.entity[p.name];
465
532
  delete changeSet.payload[p.name];
@@ -467,9 +534,10 @@ export class UnitOfWork {
467
534
  }
468
535
  scheduleOrphanRemoval(entity, visited) {
469
536
  if (entity) {
470
- helper(entity).__em = this.em;
537
+ const wrapped = helper(entity);
538
+ wrapped.__em = this.em;
471
539
  this.orphanRemoveStack.add(entity);
472
- this.queuedActions.add(entity.__meta.className);
540
+ this.queuedActions.add(wrapped.__meta.class);
473
541
  this.cascade(entity, Cascade.SCHEDULE_ORPHAN_REMOVAL, visited);
474
542
  }
475
543
  }
@@ -505,7 +573,68 @@ export class UnitOfWork {
505
573
  }
506
574
  const changeSet = this.changeSetComputer.computeChangeSet(entity);
507
575
  if (changeSet && !this.checkUniqueProps(changeSet)) {
508
- this.changeSets.set(entity, changeSet);
576
+ // For TPT child entities, create changesets for each table in hierarchy
577
+ if (wrapped.__meta.inheritanceType === 'tpt' && wrapped.__meta.tptParent) {
578
+ this.createTPTChangeSets(entity, changeSet);
579
+ }
580
+ else {
581
+ this.changeSets.set(entity, changeSet);
582
+ }
583
+ }
584
+ }
585
+ /**
586
+ * For TPT inheritance, creates separate changesets for each table in the hierarchy.
587
+ * Uses the same entity instance for all changesets - only the metadata and payload differ.
588
+ */
589
+ createTPTChangeSets(entity, originalChangeSet) {
590
+ const meta = helper(entity).__meta;
591
+ const isCreate = originalChangeSet.type === ChangeSetType.CREATE;
592
+ let current = meta;
593
+ let leafCs;
594
+ const parentChangeSets = [];
595
+ while (current) {
596
+ const isRoot = !current.tptParent;
597
+ const payload = {};
598
+ for (const prop of current.ownProps) {
599
+ if (prop.name in originalChangeSet.payload) {
600
+ payload[prop.name] = originalChangeSet.payload[prop.name];
601
+ }
602
+ }
603
+ // For CREATE on non-root tables, include the PK (EntityIdentifier for deferred resolution)
604
+ if (isCreate && !isRoot) {
605
+ const wrapped = helper(entity);
606
+ const identifier = wrapped.__identifier;
607
+ const identifiers = Array.isArray(identifier) ? identifier : [identifier];
608
+ for (let i = 0; i < current.primaryKeys.length; i++) {
609
+ const pk = current.primaryKeys[i];
610
+ payload[pk] = identifiers[i] ?? originalChangeSet.payload[pk];
611
+ }
612
+ }
613
+ if (!isCreate && Object.keys(payload).length === 0) {
614
+ current = current.tptParent;
615
+ continue;
616
+ }
617
+ const cs = new ChangeSet(entity, originalChangeSet.type, payload, current);
618
+ if (current === meta) {
619
+ cs.originalEntity = originalChangeSet.originalEntity;
620
+ leafCs = cs;
621
+ }
622
+ else {
623
+ parentChangeSets.push(cs);
624
+ }
625
+ current = current.tptParent;
626
+ }
627
+ // When only parent properties changed (UPDATE), leaf payload is empty—create a stub anchor
628
+ if (!leafCs && parentChangeSets.length > 0) {
629
+ leafCs = new ChangeSet(entity, originalChangeSet.type, {}, meta);
630
+ leafCs.originalEntity = originalChangeSet.originalEntity;
631
+ }
632
+ // Store the leaf changeset in the main map (entity as key), with parent CSs attached
633
+ if (leafCs) {
634
+ if (parentChangeSets.length > 0) {
635
+ leafCs.tptChangeSets = parentChangeSets;
636
+ }
637
+ this.changeSets.set(entity, leafCs);
509
638
  }
510
639
  }
511
640
  /**
@@ -518,7 +647,7 @@ export class UnitOfWork {
518
647
  // when changing a unique nullable property (or a 1:1 relation), we can't do it in a single
519
648
  // query as it would cause unique constraint violations
520
649
  const uniqueProps = changeSet.meta.uniqueProps.filter(prop => {
521
- return (prop.nullable || changeSet.type !== ChangeSetType.CREATE);
650
+ return prop.nullable || changeSet.type !== ChangeSetType.CREATE;
522
651
  });
523
652
  this.scheduleExtraUpdate(changeSet, uniqueProps);
524
653
  return changeSet.type === ChangeSetType.UPDATE && !Utils.hasObjectKeys(changeSet.payload);
@@ -528,25 +657,33 @@ export class UnitOfWork {
528
657
  if (!wrapped.__meta.hasUniqueProps) {
529
658
  return [];
530
659
  }
531
- const simpleUniqueHashes = wrapped.__meta.uniqueProps.map(prop => {
660
+ const simpleUniqueHashes = wrapped.__meta.uniqueProps
661
+ .map(prop => {
532
662
  if (entity[prop.name] != null) {
533
- return prop.kind === ReferenceKind.SCALAR || prop.mapToPk ? entity[prop.name] : helper(entity[prop.name]).getSerializedPrimaryKey();
663
+ return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
664
+ ? entity[prop.name]
665
+ : helper(entity[prop.name]).getSerializedPrimaryKey();
534
666
  }
535
667
  if (wrapped.__originalEntityData?.[prop.name] != null) {
536
668
  return Utils.getPrimaryKeyHash(Utils.asArray(wrapped.__originalEntityData[prop.name]));
537
669
  }
538
670
  return undefined;
539
- }).filter(i => i);
540
- const compoundUniqueHashes = wrapped.__meta.uniques.map(unique => {
671
+ })
672
+ .filter(i => i);
673
+ const compoundUniqueHashes = wrapped.__meta.uniques
674
+ .map(unique => {
541
675
  const props = Utils.asArray(unique.properties);
542
676
  if (props.every(prop => entity[prop] != null)) {
543
677
  return Utils.getPrimaryKeyHash(props.map(p => {
544
678
  const prop = wrapped.__meta.properties[p];
545
- return prop.kind === ReferenceKind.SCALAR || prop.mapToPk ? entity[prop.name] : helper(entity[prop.name]).getSerializedPrimaryKey();
679
+ return prop.kind === ReferenceKind.SCALAR || prop.mapToPk
680
+ ? entity[prop.name]
681
+ : helper(entity[prop.name]).getSerializedPrimaryKey();
546
682
  }));
547
683
  }
548
684
  return undefined;
549
- }).filter(i => i);
685
+ })
686
+ .filter(i => i);
550
687
  return simpleUniqueHashes.concat(compoundUniqueHashes);
551
688
  }
552
689
  initIdentifier(entity) {
@@ -578,7 +715,8 @@ export class UnitOfWork {
578
715
  return this.processToOneReference(kind, visited, processed, idx);
579
716
  }
580
717
  if (Utils.isCollection(kind)) {
581
- kind.getItems(false)
718
+ kind
719
+ .getItems(false)
582
720
  .filter(item => !item.__helper.__originalEntityData)
583
721
  .forEach(item => {
584
722
  // propagate schema from parent
@@ -602,7 +740,8 @@ export class UnitOfWork {
602
740
  parent[prop.name] = coll;
603
741
  return;
604
742
  }
605
- collection.getItems(false)
743
+ collection
744
+ .getItems(false)
606
745
  .filter(item => !item.__helper.__originalEntityData)
607
746
  .forEach(item => this.findNewEntities(item, visited, 0, processed));
608
747
  }
@@ -618,7 +757,7 @@ export class UnitOfWork {
618
757
  const copy = this.comparator.prepareEntity(changeSet.entity);
619
758
  await this.eventManager.dispatchEvent(type, { entity: changeSet.entity, meta, em: this.em, changeSet });
620
759
  const current = this.comparator.prepareEntity(changeSet.entity);
621
- const diff = this.comparator.diffEntities(changeSet.name, copy, current);
760
+ const diff = this.comparator.diffEntities(changeSet.meta.class, copy, current);
622
761
  Object.assign(changeSet.payload, diff);
623
762
  const wrapped = helper(changeSet.entity);
624
763
  if (wrapped.__identifier) {
@@ -694,7 +833,8 @@ export class UnitOfWork {
694
833
  return filtered.some(items => processed.has(items));
695
834
  }
696
835
  shouldCascade(prop, type) {
697
- if ([Cascade.REMOVE, Cascade.SCHEDULE_ORPHAN_REMOVAL, Cascade.CANCEL_ORPHAN_REMOVAL, Cascade.ALL].includes(type) && prop.orphanRemoval) {
836
+ if ([Cascade.REMOVE, Cascade.SCHEDULE_ORPHAN_REMOVAL, Cascade.CANCEL_ORPHAN_REMOVAL, Cascade.ALL].includes(type) &&
837
+ prop.orphanRemoval) {
698
838
  return true;
699
839
  }
700
840
  // ignore user settings for merge, it is kept only for back compatibility, this should have never been configurable
@@ -713,7 +853,7 @@ export class UnitOfWork {
713
853
  if (!meta.versionProperty) {
714
854
  throw OptimisticLockError.notVersioned(meta);
715
855
  }
716
- if (!Utils.isDefined(version)) {
856
+ if (typeof version === 'undefined') {
717
857
  return;
718
858
  }
719
859
  const wrapped = helper(entity);
@@ -727,26 +867,28 @@ export class UnitOfWork {
727
867
  }
728
868
  fixMissingReference(entity, prop) {
729
869
  const reference = entity[prop.name];
730
- const kind = Reference.unwrapReference(reference);
731
- if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && kind && !prop.mapToPk) {
732
- if (!Utils.isEntity(kind)) {
733
- entity[prop.name] = this.em.getReference(prop.type, kind, { wrapped: !!prop.ref });
870
+ const target = Reference.unwrapReference(reference);
871
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && target && !prop.mapToPk) {
872
+ if (!Utils.isEntity(target)) {
873
+ entity[prop.name] = this.em.getReference(prop.targetMeta.class, target, {
874
+ wrapped: !!prop.ref,
875
+ });
734
876
  }
735
- else if (!helper(kind).__initialized && !helper(kind).__em) {
736
- const pk = helper(kind).getPrimaryKey();
737
- entity[prop.name] = this.em.getReference(prop.type, pk, { wrapped: !!prop.ref });
877
+ else if (!helper(target).__initialized && !helper(target).__em) {
878
+ const pk = helper(target).getPrimaryKey();
879
+ entity[prop.name] = this.em.getReference(prop.targetMeta.class, pk, { wrapped: !!prop.ref });
738
880
  }
739
881
  }
740
- // perf: set the `Collection._property` to skip the getter, as it can be slow when there is a lot of relations
741
- if (Utils.isCollection(kind)) {
742
- kind.property = prop;
882
+ // perf: set the `Collection._property` to skip the getter, as it can be slow when there are a lot of relations
883
+ if (Utils.isCollection(target)) {
884
+ target.property = prop;
743
885
  }
744
886
  const isCollection = [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind);
745
- if (isCollection && Array.isArray(kind)) {
887
+ if (isCollection && Array.isArray(target)) {
746
888
  const collection = new Collection(entity);
747
889
  collection.property = prop;
748
890
  entity[prop.name] = collection;
749
- collection.set(kind);
891
+ collection.set(target);
750
892
  }
751
893
  }
752
894
  async persistToDatabase(groups, ctx) {
@@ -756,30 +898,30 @@ export class UnitOfWork {
756
898
  const commitOrder = this.getCommitOrder();
757
899
  const commitOrderReversed = [...commitOrder].reverse();
758
900
  // early delete - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
759
- for (const name of commitOrderReversed) {
760
- await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(name) ?? [], ctx);
901
+ for (const meta of commitOrderReversed) {
902
+ await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE_EARLY].get(meta) ?? [], ctx);
761
903
  }
762
904
  // early update - when we recreate entity in the same UoW, we need to issue those delete queries before inserts
763
- for (const name of commitOrder) {
764
- await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(name) ?? [], ctx);
905
+ for (const meta of commitOrder) {
906
+ await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE_EARLY].get(meta) ?? [], ctx);
765
907
  }
766
908
  // extra updates
767
909
  await this.commitExtraUpdates(ChangeSetType.UPDATE_EARLY, ctx);
768
910
  // create
769
- for (const name of commitOrder) {
770
- await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(name) ?? [], ctx);
911
+ for (const meta of commitOrder) {
912
+ await this.commitCreateChangeSets(groups[ChangeSetType.CREATE].get(meta) ?? [], ctx);
771
913
  }
772
914
  // update
773
- for (const name of commitOrder) {
774
- await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(name) ?? [], ctx);
915
+ for (const meta of commitOrder) {
916
+ await this.commitUpdateChangeSets(groups[ChangeSetType.UPDATE].get(meta) ?? [], ctx);
775
917
  }
776
918
  // extra updates
777
919
  await this.commitExtraUpdates(ChangeSetType.UPDATE, ctx);
778
920
  // collection updates
779
921
  await this.commitCollectionUpdates(ctx);
780
922
  // delete - entity deletions need to be in reverse commit order
781
- for (const name of commitOrderReversed) {
782
- await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(name) ?? [], ctx);
923
+ for (const meta of commitOrderReversed) {
924
+ await this.commitDeleteChangeSets(groups[ChangeSetType.DELETE].get(meta) ?? [], ctx);
783
925
  }
784
926
  // take snapshots of all persisted collections
785
927
  const visited = new Set();
@@ -792,9 +934,9 @@ export class UnitOfWork {
792
934
  return;
793
935
  }
794
936
  const props = changeSets[0].meta.root.relations.filter(prop => {
795
- return (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner)
796
- || prop.kind === ReferenceKind.MANY_TO_ONE
797
- || (prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && !this.platform.usesPivotTable());
937
+ return ((prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner) ||
938
+ prop.kind === ReferenceKind.MANY_TO_ONE ||
939
+ (prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && !this.platform.usesPivotTable()));
798
940
  });
799
941
  for (const changeSet of changeSets) {
800
942
  this.findExtraUpdates(changeSet, props);
@@ -815,16 +957,28 @@ export class UnitOfWork {
815
957
  if (Utils.isCollection(ref)) {
816
958
  ref.getItems(false).some(item => {
817
959
  const cs = this.changeSets.get(Reference.unwrapReference(item));
818
- const isScheduledForInsert = cs && cs.type === ChangeSetType.CREATE && !cs.persisted;
960
+ const isScheduledForInsert = cs?.type === ChangeSetType.CREATE && !cs.persisted;
819
961
  if (isScheduledForInsert) {
820
962
  this.scheduleExtraUpdate(changeSet, [prop]);
821
963
  return true;
822
964
  }
823
965
  return false;
824
966
  });
967
+ continue;
968
+ }
969
+ const refEntity = Reference.unwrapReference(ref);
970
+ // For mapToPk properties, the value is a primitive (string/array), not an entity
971
+ if (!Utils.isEntity(refEntity)) {
972
+ continue;
825
973
  }
826
- const cs = this.changeSets.get(Reference.unwrapReference(ref));
827
- const isScheduledForInsert = cs && cs.type === ChangeSetType.CREATE && !cs.persisted;
974
+ // For TPT entities, check if the ROOT table's changeset has been persisted
975
+ // (since the FK is to the root table, not the concrete entity's table)
976
+ let cs = this.changeSets.get(refEntity);
977
+ if (cs?.tptChangeSets?.length) {
978
+ // Root table changeset is the last one (ordered immediate parent → root)
979
+ cs = cs.tptChangeSets[cs.tptChangeSets.length - 1];
980
+ }
981
+ const isScheduledForInsert = cs?.type === ChangeSetType.CREATE && !cs.persisted;
828
982
  if (isScheduledForInsert) {
829
983
  this.scheduleExtraUpdate(changeSet, [prop]);
830
984
  }
@@ -855,10 +1009,10 @@ export class UnitOfWork {
855
1009
  for (const changeSet of changeSets) {
856
1010
  const wrapped = helper(changeSet.entity);
857
1011
  wrapped.__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
858
- wrapped.__touched = false;
859
1012
  if (!wrapped.__initialized) {
860
1013
  for (const prop of changeSet.meta.relations) {
861
- if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) && changeSet.entity[prop.name] == null) {
1014
+ if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) &&
1015
+ changeSet.entity[prop.name] == null) {
862
1016
  changeSet.entity[prop.name] = Collection.create(changeSet.entity, prop.name, undefined, wrapped.isInitialized());
863
1017
  }
864
1018
  }
@@ -887,7 +1041,7 @@ export class UnitOfWork {
887
1041
  continue;
888
1042
  }
889
1043
  if (Array.isArray(extraUpdate[1])) {
890
- extraUpdate[1].forEach((p, i) => extraUpdate[0][p] = extraUpdate[2][i]);
1044
+ extraUpdate[1].forEach((p, i) => (extraUpdate[0][p] = extraUpdate[2][i]));
891
1045
  }
892
1046
  else {
893
1047
  extraUpdate[0][extraUpdate[1]] = extraUpdate[2];
@@ -947,12 +1101,24 @@ export class UnitOfWork {
947
1101
  [ChangeSetType.UPDATE_EARLY]: new Map(),
948
1102
  [ChangeSetType.DELETE_EARLY]: new Map(),
949
1103
  };
950
- for (const cs of this.changeSets.values()) {
1104
+ const addToGroup = (cs) => {
1105
+ // Skip stub TPT changesets with empty payload (e.g. leaf with no own-property changes on UPDATE)
1106
+ if ((cs.type === ChangeSetType.UPDATE || cs.type === ChangeSetType.UPDATE_EARLY) &&
1107
+ !Utils.hasObjectKeys(cs.payload)) {
1108
+ return;
1109
+ }
951
1110
  const group = groups[cs.type];
952
- const classGroup = group.get(cs.rootName) ?? [];
1111
+ const groupKey = cs.meta.inheritanceType === 'tpt' ? cs.meta : cs.rootMeta;
1112
+ const classGroup = group.get(groupKey) ?? [];
953
1113
  classGroup.push(cs);
954
- if (!group.has(cs.rootName)) {
955
- group.set(cs.rootName, classGroup);
1114
+ if (!group.has(groupKey)) {
1115
+ group.set(groupKey, classGroup);
1116
+ }
1117
+ };
1118
+ for (const cs of this.changeSets.values()) {
1119
+ addToGroup(cs);
1120
+ for (const parentCs of cs.tptChangeSets ?? []) {
1121
+ addToGroup(parentCs);
956
1122
  }
957
1123
  }
958
1124
  return groups;
@@ -960,14 +1126,35 @@ export class UnitOfWork {
960
1126
  getCommitOrder() {
961
1127
  const calc = new CommitOrderCalculator();
962
1128
  const set = new Set();
963
- this.changeSets.forEach(cs => set.add(cs.rootName));
964
- set.forEach(entityName => calc.addNode(entityName));
965
- for (const entityName of set) {
966
- for (const prop of this.metadata.find(entityName).props) {
967
- calc.discoverProperty(prop, entityName);
1129
+ this.changeSets.forEach(cs => {
1130
+ if (cs.meta.inheritanceType === 'tpt') {
1131
+ set.add(cs.meta);
1132
+ for (const parentCs of cs.tptChangeSets ?? []) {
1133
+ set.add(parentCs.meta);
1134
+ }
1135
+ }
1136
+ else {
1137
+ set.add(cs.rootMeta);
1138
+ }
1139
+ });
1140
+ set.forEach(meta => calc.addNode(meta._id));
1141
+ for (const meta of set) {
1142
+ for (const prop of meta.relations) {
1143
+ if (prop.polymorphTargets) {
1144
+ for (const targetMeta of prop.polymorphTargets) {
1145
+ calc.discoverProperty({ ...prop, targetMeta }, meta._id);
1146
+ }
1147
+ }
1148
+ else {
1149
+ calc.discoverProperty(prop, meta._id);
1150
+ }
1151
+ }
1152
+ // For TPT, parent table must be inserted BEFORE child tables
1153
+ if (meta.inheritanceType === 'tpt' && meta.tptParent && set.has(meta.tptParent)) {
1154
+ calc.addDependency(meta.tptParent._id, meta._id, 1);
968
1155
  }
969
1156
  }
970
- return calc.sort();
1157
+ return calc.sort().map(id => this.metadata.getById(id));
971
1158
  }
972
1159
  resetTransaction(oldTx) {
973
1160
  if (oldTx) {