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