@mikro-orm/core 7.0.0-dev.6 → 7.0.0-dev.61

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 (123) hide show
  1. package/EntityManager.d.ts +85 -32
  2. package/EntityManager.js +281 -178
  3. package/MikroORM.d.ts +8 -8
  4. package/MikroORM.js +31 -74
  5. package/README.md +3 -2
  6. package/cache/FileCacheAdapter.d.ts +2 -1
  7. package/cache/FileCacheAdapter.js +5 -4
  8. package/connections/Connection.d.ts +11 -7
  9. package/connections/Connection.js +16 -13
  10. package/decorators/Embeddable.d.ts +2 -0
  11. package/decorators/Embedded.d.ts +5 -11
  12. package/decorators/Entity.d.ts +20 -3
  13. package/decorators/Indexed.d.ts +2 -2
  14. package/decorators/ManyToMany.d.ts +2 -0
  15. package/decorators/ManyToOne.d.ts +4 -0
  16. package/decorators/OneToOne.d.ts +4 -0
  17. package/decorators/Property.d.ts +53 -9
  18. package/decorators/Transactional.d.ts +3 -1
  19. package/decorators/Transactional.js +6 -3
  20. package/decorators/index.d.ts +1 -1
  21. package/drivers/DatabaseDriver.d.ts +11 -5
  22. package/drivers/DatabaseDriver.js +13 -4
  23. package/drivers/IDatabaseDriver.d.ts +29 -5
  24. package/entity/ArrayCollection.d.ts +6 -4
  25. package/entity/ArrayCollection.js +27 -12
  26. package/entity/BaseEntity.d.ts +0 -1
  27. package/entity/BaseEntity.js +0 -3
  28. package/entity/Collection.d.ts +3 -4
  29. package/entity/Collection.js +34 -17
  30. package/entity/EntityAssigner.d.ts +1 -1
  31. package/entity/EntityAssigner.js +9 -1
  32. package/entity/EntityFactory.d.ts +7 -0
  33. package/entity/EntityFactory.js +63 -40
  34. package/entity/EntityHelper.js +26 -9
  35. package/entity/EntityLoader.d.ts +5 -4
  36. package/entity/EntityLoader.js +69 -36
  37. package/entity/EntityRepository.d.ts +1 -1
  38. package/entity/EntityValidator.js +2 -2
  39. package/entity/Reference.d.ts +9 -7
  40. package/entity/Reference.js +32 -5
  41. package/entity/WrappedEntity.d.ts +0 -2
  42. package/entity/WrappedEntity.js +1 -5
  43. package/entity/defineEntity.d.ts +555 -0
  44. package/entity/defineEntity.js +529 -0
  45. package/entity/index.d.ts +2 -0
  46. package/entity/index.js +2 -0
  47. package/entity/utils.d.ts +7 -0
  48. package/entity/utils.js +15 -3
  49. package/enums.d.ts +18 -5
  50. package/enums.js +13 -0
  51. package/errors.d.ts +6 -1
  52. package/errors.js +14 -4
  53. package/events/EventSubscriber.d.ts +3 -1
  54. package/hydration/ObjectHydrator.d.ts +4 -4
  55. package/hydration/ObjectHydrator.js +35 -24
  56. package/index.d.ts +2 -1
  57. package/index.js +1 -1
  58. package/logging/DefaultLogger.d.ts +1 -1
  59. package/logging/SimpleLogger.d.ts +1 -1
  60. package/metadata/EntitySchema.d.ts +8 -4
  61. package/metadata/EntitySchema.js +41 -23
  62. package/metadata/MetadataDiscovery.d.ts +5 -7
  63. package/metadata/MetadataDiscovery.js +151 -159
  64. package/metadata/MetadataStorage.js +1 -1
  65. package/metadata/MetadataValidator.js +4 -3
  66. package/metadata/discover-entities.d.ts +5 -0
  67. package/metadata/discover-entities.js +39 -0
  68. package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
  69. package/naming-strategy/AbstractNamingStrategy.js +7 -1
  70. package/naming-strategy/NamingStrategy.d.ts +11 -1
  71. package/package.json +14 -8
  72. package/platforms/Platform.d.ts +5 -8
  73. package/platforms/Platform.js +4 -17
  74. package/serialization/EntitySerializer.d.ts +2 -0
  75. package/serialization/EntitySerializer.js +29 -11
  76. package/serialization/EntityTransformer.js +22 -12
  77. package/serialization/SerializationContext.js +14 -11
  78. package/types/BigIntType.d.ts +9 -6
  79. package/types/BigIntType.js +3 -0
  80. package/types/BlobType.d.ts +0 -1
  81. package/types/BlobType.js +0 -3
  82. package/types/BooleanType.d.ts +2 -1
  83. package/types/BooleanType.js +3 -0
  84. package/types/DecimalType.d.ts +6 -4
  85. package/types/DecimalType.js +1 -1
  86. package/types/DoubleType.js +1 -1
  87. package/types/JsonType.d.ts +1 -1
  88. package/types/JsonType.js +7 -2
  89. package/types/Type.d.ts +2 -1
  90. package/types/Type.js +1 -1
  91. package/types/Uint8ArrayType.d.ts +0 -1
  92. package/types/Uint8ArrayType.js +0 -3
  93. package/types/index.d.ts +1 -1
  94. package/typings.d.ts +95 -52
  95. package/typings.js +31 -31
  96. package/unit-of-work/ChangeSetComputer.js +8 -3
  97. package/unit-of-work/ChangeSetPersister.d.ts +4 -2
  98. package/unit-of-work/ChangeSetPersister.js +37 -16
  99. package/unit-of-work/UnitOfWork.d.ts +8 -1
  100. package/unit-of-work/UnitOfWork.js +110 -53
  101. package/utils/AbstractSchemaGenerator.js +3 -1
  102. package/utils/Configuration.d.ts +201 -184
  103. package/utils/Configuration.js +143 -151
  104. package/utils/ConfigurationLoader.d.ts +9 -22
  105. package/utils/ConfigurationLoader.js +53 -76
  106. package/utils/Cursor.d.ts +3 -3
  107. package/utils/Cursor.js +3 -0
  108. package/utils/DataloaderUtils.d.ts +15 -5
  109. package/utils/DataloaderUtils.js +53 -7
  110. package/utils/EntityComparator.d.ts +8 -4
  111. package/utils/EntityComparator.js +105 -58
  112. package/utils/QueryHelper.d.ts +9 -1
  113. package/utils/QueryHelper.js +66 -5
  114. package/utils/RawQueryFragment.d.ts +36 -4
  115. package/utils/RawQueryFragment.js +34 -13
  116. package/utils/TransactionManager.d.ts +65 -0
  117. package/utils/TransactionManager.js +223 -0
  118. package/utils/Utils.d.ts +16 -31
  119. package/utils/Utils.js +129 -107
  120. package/utils/index.d.ts +1 -0
  121. package/utils/index.js +1 -0
  122. package/utils/upsert-utils.d.ts +7 -2
  123. package/utils/upsert-utils.js +52 -1
@@ -12,16 +12,18 @@ export class ChangeSetPersister {
12
12
  factory;
13
13
  validator;
14
14
  config;
15
+ em;
15
16
  platform;
16
17
  comparator;
17
18
  usesReturningStatement;
18
- constructor(driver, metadata, hydrator, factory, validator, config) {
19
+ constructor(driver, metadata, hydrator, factory, validator, config, em) {
19
20
  this.driver = driver;
20
21
  this.metadata = metadata;
21
22
  this.hydrator = hydrator;
22
23
  this.factory = factory;
23
24
  this.validator = validator;
24
25
  this.config = config;
26
+ this.em = em;
25
27
  this.platform = this.driver.getPlatform();
26
28
  this.comparator = this.config.getComparator(this.metadata);
27
29
  this.usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
@@ -62,7 +64,7 @@ export class ChangeSetPersister {
62
64
  for (let i = 0; i < changeSets.length; i += size) {
63
65
  const chunk = changeSets.slice(i, i + size);
64
66
  const pks = chunk.map(cs => cs.getPrimaryKey());
65
- options = this.propagateSchemaFromMetadata(meta, options);
67
+ options = this.prepareOptions(meta, options);
66
68
  await this.driver.nativeDelete(meta.root.className, { [pk]: { $in: pks } }, options);
67
69
  }
68
70
  }
@@ -90,7 +92,7 @@ export class ChangeSetPersister {
90
92
  }
91
93
  async persistNewEntity(meta, changeSet, options) {
92
94
  const wrapped = helper(changeSet.entity);
93
- options = this.propagateSchemaFromMetadata(meta, options, {
95
+ options = this.prepareOptions(meta, options, {
94
96
  convertCustomTypes: false,
95
97
  });
96
98
  const res = await this.driver.nativeInsertMany(meta.className, [changeSet.payload], options);
@@ -116,15 +118,17 @@ export class ChangeSetPersister {
116
118
  }
117
119
  }
118
120
  }
119
- propagateSchemaFromMetadata(meta, options, additionalOptions) {
121
+ prepareOptions(meta, options, additionalOptions) {
122
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
120
123
  return {
121
124
  ...options,
122
125
  ...additionalOptions,
123
126
  schema: options?.schema ?? meta.schema,
127
+ loggerContext,
124
128
  };
125
129
  }
126
130
  async persistNewEntitiesBatch(meta, changeSets, options) {
127
- options = this.propagateSchemaFromMetadata(meta, options, {
131
+ options = this.prepareOptions(meta, options, {
128
132
  convertCustomTypes: false,
129
133
  processCollections: false,
130
134
  });
@@ -175,7 +179,7 @@ export class ChangeSetPersister {
175
179
  }
176
180
  async persistManagedEntitiesBatch(meta, changeSets, options) {
177
181
  await this.checkOptimisticLocks(meta, changeSets, options);
178
- options = this.propagateSchemaFromMetadata(meta, options, {
182
+ options = this.prepareOptions(meta, options, {
179
183
  convertCustomTypes: false,
180
184
  processCollections: false,
181
185
  });
@@ -210,7 +214,9 @@ export class ChangeSetPersister {
210
214
  // of using the raw value from db, we convert it back to the db value explicitly
211
215
  value = prop.customType ? prop.customType.convertToDatabaseValue(insertId, this.platform, { mode: 'serialization' }) : value;
212
216
  changeSet.payload[wrapped.__meta.primaryKeys[0]] = value;
213
- wrapped.__identifier?.setValue(value);
217
+ if (wrapped.__identifier && !Array.isArray(wrapped.__identifier)) {
218
+ wrapped.__identifier.setValue(value);
219
+ }
214
220
  }
215
221
  /**
216
222
  * Sets populate flag to new entities so they are serialized like if they were loaded from the db
@@ -233,7 +239,7 @@ export class ChangeSetPersister {
233
239
  }
234
240
  async updateEntity(meta, changeSet, options) {
235
241
  const cond = changeSet.getPrimaryKey(true);
236
- options = this.propagateSchemaFromMetadata(meta, options, {
242
+ options = this.prepareOptions(meta, options, {
237
243
  convertCustomTypes: false,
238
244
  });
239
245
  if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)) {
@@ -260,7 +266,7 @@ export class ChangeSetPersister {
260
266
  return cond;
261
267
  });
262
268
  const primaryKeys = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
263
- options = this.propagateSchemaFromMetadata(meta, options, {
269
+ options = this.prepareOptions(meta, options, {
264
270
  fields: primaryKeys,
265
271
  });
266
272
  const res = await this.driver.find(meta.root.className, { $or }, options);
@@ -284,11 +290,22 @@ export class ChangeSetPersister {
284
290
  async reloadVersionValues(meta, changeSets, options) {
285
291
  const reloadProps = meta.versionProperty && !this.usesReturningStatement ? [meta.properties[meta.versionProperty]] : [];
286
292
  if (changeSets[0].type === ChangeSetType.CREATE) {
287
- // do not reload things that already had a runtime value
288
- meta.props
289
- .filter(prop => prop.persist !== false && (prop.autoincrement || prop.generated || prop.defaultRaw))
290
- .filter(prop => (changeSets[0].entity[prop.name] == null && prop.defaultRaw !== 'null') || isRaw(changeSets[0].entity[prop.name]))
291
- .forEach(prop => reloadProps.push(prop));
293
+ for (const prop of meta.props) {
294
+ if (prop.persist === false) {
295
+ continue;
296
+ }
297
+ if (isRaw(changeSets[0].entity[prop.name])) {
298
+ reloadProps.push(prop);
299
+ continue;
300
+ }
301
+ // do not reload things that already had a runtime value
302
+ if (changeSets[0].entity[prop.name] != null || prop.defaultRaw === 'null') {
303
+ continue;
304
+ }
305
+ if (prop.autoincrement || prop.generated || prop.defaultRaw) {
306
+ reloadProps.push(prop);
307
+ }
308
+ }
292
309
  }
293
310
  if (changeSets[0].type === ChangeSetType.UPDATE) {
294
311
  const returning = new Set();
@@ -319,12 +336,12 @@ export class ChangeSetPersister {
319
336
  }
320
337
  return val;
321
338
  });
322
- options = this.propagateSchemaFromMetadata(meta, options, {
339
+ options = this.prepareOptions(meta, options, {
323
340
  fields: Utils.unique(reloadProps.map(prop => prop.name)),
324
341
  });
325
342
  const data = await this.driver.find(meta.className, { [pk]: { $in: pks } }, options);
326
343
  const map = new Map();
327
- data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));
344
+ data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.platform, true), item));
328
345
  for (const changeSet of changeSets) {
329
346
  const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
330
347
  this.hydrator.hydrate(changeSet.entity, meta, data, this.factory, 'full', false, true);
@@ -338,6 +355,10 @@ export class ChangeSetPersister {
338
355
  changeSet.payload[prop.name] = value.getValue();
339
356
  return;
340
357
  }
358
+ if (Array.isArray(value) && value.every(item => item instanceof EntityIdentifier)) {
359
+ changeSet.payload[prop.name] = value.map(item => item.getValue());
360
+ return;
361
+ }
341
362
  if (prop.kind === ReferenceKind.MANY_TO_MANY && Array.isArray(value)) {
342
363
  changeSet.payload[prop.name] = value.map(val => val instanceof EntityIdentifier ? val.getValue() : val);
343
364
  return;
@@ -28,6 +28,12 @@ export declare class UnitOfWork {
28
28
  private working;
29
29
  constructor(em: EntityManager);
30
30
  merge<T extends object>(entity: T, visited?: Set<AnyEntity>): void;
31
+ /**
32
+ * Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
33
+ * we need to normalize the shape, so relation values are only raw FKs. This method handles that.
34
+ * @internal
35
+ */
36
+ normalizeEntityData<T extends object>(meta: EntityMetadata<T>, data: EntityData<T>): void;
31
37
  /**
32
38
  * @internal
33
39
  */
@@ -39,7 +45,7 @@ export declare class UnitOfWork {
39
45
  /**
40
46
  * Returns entity from the identity map. For composite keys, you need to pass an array of PKs in the same order as they are defined in `meta.primaryKeys`.
41
47
  */
42
- getById<T extends object>(entityName: string, id: Primary<T> | Primary<T>[], schema?: string): T | undefined;
48
+ getById<T extends object>(entityName: string, id: Primary<T> | Primary<T>[], schema?: string, convertCustomTypes?: boolean): T | undefined;
43
49
  tryGetById<T extends object>(entityName: string, where: FilterQuery<T>, schema?: string, strict?: boolean): T | null;
44
50
  /**
45
51
  * Returns map of all managed entities.
@@ -103,6 +109,7 @@ export declare class UnitOfWork {
103
109
  private commitDeleteChangeSets;
104
110
  private commitExtraUpdates;
105
111
  private commitCollectionUpdates;
112
+ private filterCollectionUpdates;
106
113
  /**
107
114
  * Orders change sets so FK constrains are maintained, ensures stable order (needed for node < 11)
108
115
  */
@@ -43,7 +43,7 @@ export class UnitOfWork {
43
43
  this.eventManager = this.em.getEventManager();
44
44
  this.comparator = this.em.getComparator();
45
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);
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);
47
47
  }
48
48
  merge(entity, visited) {
49
49
  const wrapped = helper(entity);
@@ -61,10 +61,43 @@ 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) && Utils.isPlainObject(data[prop.name])) {
80
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
81
+ }
82
+ else if (prop.kind === ReferenceKind.EMBEDDED && !prop.object && Utils.isPlainObject(data[prop.name])) {
83
+ for (const p of prop.targetMeta.props) {
84
+ /* v8 ignore next */
85
+ const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
86
+ data[prefix + p.name] = data[prop.name][p.name];
87
+ }
88
+ data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
89
+ }
90
+ if (prop.hydrate === false && prop.customType?.ensureComparable(meta, prop)) {
91
+ const converted = prop.customType.convertToJSValue(data[key], this.platform, { key, mode: 'hydration', force: true });
92
+ data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
93
+ }
94
+ if (forceUndefined) {
95
+ if (data[key] === null) {
96
+ data[key] = undefined;
97
+ }
98
+ }
99
+ }
100
+ }
68
101
  /**
69
102
  * @internal
70
103
  */
@@ -82,31 +115,14 @@ export class UnitOfWork {
82
115
  wrapped.__em ??= this.em;
83
116
  wrapped.__managed = true;
84
117
  if (data && (options?.refresh || !wrapped.__originalEntityData)) {
118
+ this.normalizeEntityData(wrapped.__meta, data);
85
119
  for (const key of Utils.keys(data)) {
86
120
  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.primaryKeys, 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.primaryKeys, true);
101
- }
102
- if (forceUndefined) {
103
- if (data[key] === null) {
104
- data[key] = undefined;
105
- }
121
+ if (prop) {
122
+ wrapped.__loadedProperties.add(key);
106
123
  }
107
124
  }
108
125
  wrapped.__originalEntityData = data;
109
- wrapped.__touched = false;
110
126
  }
111
127
  return entity;
112
128
  }
@@ -125,7 +141,7 @@ export class UnitOfWork {
125
141
  /**
126
142
  * Returns entity from the identity map. For composite keys, you need to pass an array of PKs in the same order as they are defined in `meta.primaryKeys`.
127
143
  */
128
- getById(entityName, id, schema) {
144
+ getById(entityName, id, schema, convertCustomTypes) {
129
145
  if (id == null || (Array.isArray(id) && id.length === 0)) {
130
146
  return undefined;
131
147
  }
@@ -135,7 +151,16 @@ export class UnitOfWork {
135
151
  hash = '' + id;
136
152
  }
137
153
  else {
138
- const keys = Array.isArray(id) ? Utils.flatten(id) : [id];
154
+ let keys = Array.isArray(id) ? Utils.flatten(id) : [id];
155
+ keys = meta.getPrimaryProps(true).map((p, i) => {
156
+ if (!convertCustomTypes && p.customType) {
157
+ return p.customType.convertToDatabaseValue(keys[i], this.platform, {
158
+ key: p.name,
159
+ mode: 'hydration',
160
+ });
161
+ }
162
+ return keys[i];
163
+ });
139
164
  hash = Utils.getPrimaryKeyHash(keys);
140
165
  }
141
166
  schema ??= meta.schema ?? this.em.config.getSchema();
@@ -185,10 +210,8 @@ export class UnitOfWork {
185
210
  if (this.queuedActions.has(meta.className) || this.queuedActions.has(meta.root.className)) {
186
211
  return true;
187
212
  }
188
- for (const entity of this.identityMap.getStore(meta).values()) {
189
- if (helper(entity).__initialized && helper(entity).isTouched()) {
190
- return true;
191
- }
213
+ if (meta.discriminatorMap && Object.values(meta.discriminatorMap).some(v => this.queuedActions.has(v))) {
214
+ return true;
192
215
  }
193
216
  return false;
194
217
  }
@@ -209,7 +232,6 @@ export class UnitOfWork {
209
232
  this.changeSets.set(entity, cs);
210
233
  this.persistStack.delete(entity);
211
234
  wrapped.__originalEntityData = this.comparator.prepareEntity(entity);
212
- wrapped.__touched = false;
213
235
  }
214
236
  recomputeSingleChangeSet(entity) {
215
237
  const changeSet = this.changeSets.get(entity);
@@ -220,7 +242,6 @@ export class UnitOfWork {
220
242
  if (cs && !this.checkUniqueProps(cs)) {
221
243
  Object.assign(changeSet.payload, cs.payload);
222
244
  helper(entity).__originalEntityData = this.comparator.prepareEntity(entity);
223
- helper(entity).__touched = false;
224
245
  }
225
246
  }
226
247
  persist(entity, visited, options = {}) {
@@ -303,6 +324,7 @@ export class UnitOfWork {
303
324
  cs.entity.__helper.__processing = true;
304
325
  }
305
326
  await this.eventManager.dispatchEvent(EventType.onFlush, { em: this.em, uow: this });
327
+ this.filterCollectionUpdates();
306
328
  // nothing to do, do not start transaction
307
329
  if (this.changeSets.size === 0 && this.collectionUpdates.size === 0 && this.extraUpdates.size === 0) {
308
330
  return void (await this.eventManager.dispatchEvent(EventType.afterFlush, { em: this.em, uow: this }));
@@ -311,9 +333,11 @@ export class UnitOfWork {
311
333
  const platform = this.em.getPlatform();
312
334
  const runInTransaction = !this.em.isInTransaction() && platform.supportsTransactions() && this.em.config.get('implicitTransactions');
313
335
  if (runInTransaction) {
336
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
314
337
  await this.em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
315
338
  ctx: oldTx,
316
339
  eventBroadcaster: new TransactionEventBroadcaster(this.em),
340
+ loggerContext,
317
341
  });
318
342
  }
319
343
  else {
@@ -369,7 +393,6 @@ export class UnitOfWork {
369
393
  }
370
394
  delete wrapped.__identifier;
371
395
  delete wrapped.__originalEntityData;
372
- wrapped.__touched = false;
373
396
  wrapped.__managed = false;
374
397
  }
375
398
  computeChangeSets() {
@@ -379,14 +402,14 @@ export class UnitOfWork {
379
402
  this.cascade(entity, Cascade.REMOVE, visited);
380
403
  }
381
404
  visited.clear();
382
- for (const entity of this.persistStack) {
383
- this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
384
- }
385
405
  for (const entity of this.identityMap) {
386
406
  if (!this.removeStack.has(entity) && !this.persistStack.has(entity) && !this.orphanRemoveStack.has(entity)) {
387
407
  this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
388
408
  }
389
409
  }
410
+ for (const entity of this.persistStack) {
411
+ this.cascade(entity, Cascade.PERSIST, visited, { checkRemoveStack: true });
412
+ }
390
413
  visited.clear();
391
414
  for (const entity of this.persistStack) {
392
415
  this.findNewEntities(entity, visited);
@@ -542,13 +565,22 @@ export class UnitOfWork {
542
565
  if (!wrapped || wrapped.__identifier || wrapped.hasPrimaryKey()) {
543
566
  return;
544
567
  }
545
- const pk = wrapped.__meta.getPrimaryProps()[0];
546
- if (pk.kind === ReferenceKind.SCALAR) {
547
- wrapped.__identifier = new EntityIdentifier();
568
+ const pks = wrapped.__meta.getPrimaryProps();
569
+ const idents = [];
570
+ for (const pk of pks) {
571
+ if (pk.kind === ReferenceKind.SCALAR) {
572
+ idents.push(new EntityIdentifier(entity[pk.name]));
573
+ }
574
+ else if (entity[pk.name]) {
575
+ this.initIdentifier(entity[pk.name]);
576
+ idents.push(helper(entity[pk.name])?.__identifier);
577
+ }
578
+ }
579
+ if (pks.length === 1) {
580
+ wrapped.__identifier = idents[0];
548
581
  }
549
- else if (entity[pk.name]) {
550
- this.initIdentifier(entity[pk.name]);
551
- wrapped.__identifier = helper(entity[pk.name])?.__identifier;
582
+ else {
583
+ wrapped.__identifier = idents;
552
584
  }
553
585
  }
554
586
  processReference(parent, prop, kind, visited, processed, idx) {
@@ -600,8 +632,15 @@ export class UnitOfWork {
600
632
  const diff = this.comparator.diffEntities(changeSet.name, copy, current);
601
633
  Object.assign(changeSet.payload, diff);
602
634
  const wrapped = helper(changeSet.entity);
603
- if (wrapped.__identifier && diff[wrapped.__meta.primaryKeys[0]]) {
604
- wrapped.__identifier.setValue(diff[wrapped.__meta.primaryKeys[0]]);
635
+ if (wrapped.__identifier) {
636
+ const idents = Utils.asArray(wrapped.__identifier);
637
+ let i = 0;
638
+ for (const pk of wrapped.__meta.primaryKeys) {
639
+ if (diff[pk]) {
640
+ idents[i].setValue(diff[pk]);
641
+ }
642
+ i++;
643
+ }
605
644
  }
606
645
  }
607
646
  postCommitCleanup() {
@@ -825,9 +864,16 @@ export class UnitOfWork {
825
864
  }
826
865
  await this.changeSetPersister.executeUpdates(changeSets, batched, { ctx });
827
866
  for (const changeSet of changeSets) {
828
- helper(changeSet.entity).__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
829
- helper(changeSet.entity).__touched = false;
830
- helper(changeSet.entity).__initialized = true;
867
+ const wrapped = helper(changeSet.entity);
868
+ wrapped.__originalEntityData = this.comparator.prepareEntity(changeSet.entity);
869
+ if (!wrapped.__initialized) {
870
+ for (const prop of changeSet.meta.relations) {
871
+ if ([ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(prop.kind) && changeSet.entity[prop.name] == null) {
872
+ changeSet.entity[prop.name] = Collection.create(changeSet.entity, prop.name, undefined, wrapped.isInitialized());
873
+ }
874
+ }
875
+ wrapped.__initialized = true;
876
+ }
831
877
  await this.runHooks(EventType.afterUpdate, changeSet);
832
878
  }
833
879
  }
@@ -870,23 +916,34 @@ export class UnitOfWork {
870
916
  }
871
917
  }
872
918
  async commitCollectionUpdates(ctx) {
873
- const collectionUpdates = [];
919
+ this.filterCollectionUpdates();
920
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
921
+ await this.em.getDriver().syncCollections(this.collectionUpdates, {
922
+ ctx,
923
+ schema: this.em.schema,
924
+ loggerContext,
925
+ });
926
+ for (const coll of this.collectionUpdates) {
927
+ coll.takeSnapshot();
928
+ }
929
+ }
930
+ filterCollectionUpdates() {
874
931
  for (const coll of this.collectionUpdates) {
932
+ let skip = true;
875
933
  if (coll.property.owner || coll.getItems(false).filter(item => !item.__helper.__initialized).length > 0) {
876
934
  if (this.platform.usesPivotTable()) {
877
- collectionUpdates.push(coll);
935
+ skip = false;
878
936
  }
879
937
  }
880
938
  else if (coll.property.kind === ReferenceKind.ONE_TO_MANY && coll.getSnapshot() === undefined) {
881
- collectionUpdates.push(coll);
939
+ skip = false;
882
940
  }
883
941
  else if (coll.property.kind === ReferenceKind.MANY_TO_MANY && !coll.property.owner) {
884
- collectionUpdates.push(coll);
942
+ skip = false;
943
+ }
944
+ if (skip) {
945
+ this.collectionUpdates.delete(coll);
885
946
  }
886
- }
887
- await this.em.getDriver().syncCollections(collectionUpdates, { ctx, schema: this.em.schema });
888
- for (const coll of this.collectionUpdates) {
889
- coll.takeSnapshot();
890
947
  }
891
948
  }
892
949
  /**
@@ -42,7 +42,9 @@ export class AbstractSchemaGenerator {
42
42
  for (const meta of this.getOrderedMetadata(options?.schema).reverse()) {
43
43
  await this.driver.nativeDelete(meta.className, {}, options);
44
44
  }
45
- this.clearIdentityMap();
45
+ if (options?.clearIdentityMap ?? true) {
46
+ this.clearIdentityMap();
47
+ }
46
48
  }
47
49
  clearIdentityMap() {
48
50
  /* v8 ignore next 3 */