@mikro-orm/core 7.0.8-dev.8 → 7.0.8

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