@mikro-orm/core 7.0.2-dev.9 → 7.0.2

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