@mikro-orm/core 7.0.0-rc.2 → 7.0.0
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 +4 -16
- package/EntityManager.js +248 -181
- package/MikroORM.d.ts +4 -6
- package/MikroORM.js +24 -24
- package/README.md +5 -4
- package/cache/FileCacheAdapter.d.ts +1 -5
- package/cache/FileCacheAdapter.js +22 -24
- package/cache/GeneratedCacheAdapter.d.ts +1 -1
- package/cache/GeneratedCacheAdapter.js +6 -6
- package/cache/MemoryCacheAdapter.d.ts +1 -2
- package/cache/MemoryCacheAdapter.js +8 -8
- package/cache/index.d.ts +1 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +1 -0
- package/connections/Connection.js +43 -14
- package/drivers/DatabaseDriver.d.ts +0 -2
- package/drivers/DatabaseDriver.js +28 -12
- package/drivers/IDatabaseDriver.d.ts +43 -0
- package/entity/Collection.d.ts +1 -9
- package/entity/Collection.js +124 -108
- package/entity/EntityAssigner.js +23 -11
- package/entity/EntityFactory.d.ts +1 -8
- package/entity/EntityFactory.js +79 -59
- package/entity/EntityHelper.js +25 -16
- package/entity/EntityLoader.d.ts +1 -3
- package/entity/EntityLoader.js +90 -60
- package/entity/Reference.d.ts +2 -3
- package/entity/Reference.js +48 -19
- package/entity/WrappedEntity.d.ts +4 -2
- package/entity/WrappedEntity.js +5 -1
- package/entity/defineEntity.d.ts +42 -85
- package/entity/utils.js +28 -26
- package/entity/validators.js +2 -1
- package/enums.d.ts +2 -1
- package/enums.js +13 -17
- package/errors.d.ts +11 -11
- package/errors.js +8 -8
- package/events/EventManager.d.ts +1 -4
- package/events/EventManager.js +26 -23
- package/events/index.d.ts +1 -1
- package/events/index.js +0 -1
- package/exceptions.js +9 -2
- package/hydration/ObjectHydrator.d.ts +1 -2
- package/hydration/ObjectHydrator.js +41 -27
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.js +6 -7
- package/logging/Logger.d.ts +2 -1
- package/logging/colors.js +2 -5
- package/logging/index.d.ts +1 -1
- package/logging/index.js +0 -1
- package/metadata/EntitySchema.d.ts +3 -3
- package/metadata/EntitySchema.js +12 -2
- package/metadata/MetadataDiscovery.d.ts +1 -9
- package/metadata/MetadataDiscovery.js +251 -179
- package/metadata/MetadataProvider.js +26 -1
- package/metadata/MetadataStorage.d.ts +1 -5
- package/metadata/MetadataStorage.js +37 -39
- package/metadata/MetadataValidator.js +20 -5
- package/metadata/discover-entities.js +1 -1
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +0 -1
- package/metadata/types.d.ts +2 -2
- package/naming-strategy/AbstractNamingStrategy.js +6 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +1 -1
- package/naming-strategy/index.d.ts +1 -1
- package/naming-strategy/index.js +0 -1
- package/not-supported.js +5 -1
- package/package.json +38 -38
- package/platforms/Platform.d.ts +24 -1
- package/platforms/Platform.js +106 -27
- package/serialization/EntitySerializer.js +8 -4
- package/serialization/EntityTransformer.js +4 -1
- package/serialization/SerializationContext.d.ts +4 -8
- package/serialization/SerializationContext.js +21 -16
- package/types/UuidType.d.ts +2 -0
- package/types/UuidType.js +14 -2
- package/types/index.d.ts +2 -1
- package/typings.d.ts +35 -24
- package/typings.js +9 -9
- package/unit-of-work/ChangeSet.js +4 -4
- package/unit-of-work/ChangeSetComputer.d.ts +1 -6
- package/unit-of-work/ChangeSetComputer.js +29 -27
- package/unit-of-work/ChangeSetPersister.d.ts +1 -9
- package/unit-of-work/ChangeSetPersister.js +63 -58
- package/unit-of-work/CommitOrderCalculator.d.ts +1 -4
- package/unit-of-work/CommitOrderCalculator.js +17 -15
- package/unit-of-work/IdentityMap.d.ts +2 -5
- package/unit-of-work/IdentityMap.js +18 -18
- package/unit-of-work/UnitOfWork.d.ts +12 -20
- package/unit-of-work/UnitOfWork.js +228 -191
- package/utils/AbstractMigrator.d.ts +2 -2
- package/utils/AbstractMigrator.js +10 -12
- package/utils/AbstractSchemaGenerator.js +2 -1
- package/utils/AsyncContext.js +1 -1
- package/utils/Configuration.d.ts +90 -189
- package/utils/Configuration.js +97 -77
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +8 -6
- package/utils/DataloaderUtils.js +15 -12
- package/utils/EntityComparator.d.ts +8 -15
- package/utils/EntityComparator.js +100 -92
- package/utils/QueryHelper.d.ts +16 -1
- package/utils/QueryHelper.js +108 -50
- package/utils/RawQueryFragment.d.ts +4 -4
- package/utils/RawQueryFragment.js +3 -2
- package/utils/TransactionManager.js +3 -3
- package/utils/Utils.d.ts +2 -2
- package/utils/Utils.js +39 -32
- package/utils/clone.js +5 -0
- package/utils/env-vars.js +6 -5
- package/utils/fs-utils.d.ts +3 -17
- package/utils/fs-utils.js +2 -5
- package/utils/upsert-utils.js +7 -4
|
@@ -3,12 +3,7 @@ import { ChangeSet } from './ChangeSet.js';
|
|
|
3
3
|
import { type Collection } from '../entity/Collection.js';
|
|
4
4
|
import type { EntityManager } from '../EntityManager.js';
|
|
5
5
|
export declare class ChangeSetComputer {
|
|
6
|
-
private
|
|
7
|
-
private readonly collectionUpdates;
|
|
8
|
-
private readonly comparator;
|
|
9
|
-
private readonly metadata;
|
|
10
|
-
private readonly platform;
|
|
11
|
-
private readonly config;
|
|
6
|
+
#private;
|
|
12
7
|
constructor(em: EntityManager, collectionUpdates: Set<Collection<AnyEntity>>);
|
|
13
8
|
computeChangeSet<T extends object>(entity: T): ChangeSet<T> | null;
|
|
14
9
|
/**
|
|
@@ -7,22 +7,22 @@ import { Reference } from '../entity/Reference.js';
|
|
|
7
7
|
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
8
8
|
import { ReferenceKind } from '../enums.js';
|
|
9
9
|
export class ChangeSetComputer {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
#comparator;
|
|
11
|
+
#metadata;
|
|
12
|
+
#platform;
|
|
13
|
+
#config;
|
|
14
|
+
#em;
|
|
15
|
+
#collectionUpdates;
|
|
16
16
|
constructor(em, collectionUpdates) {
|
|
17
|
-
this
|
|
18
|
-
this
|
|
19
|
-
this
|
|
20
|
-
this
|
|
21
|
-
this
|
|
22
|
-
this
|
|
17
|
+
this.#em = em;
|
|
18
|
+
this.#collectionUpdates = collectionUpdates;
|
|
19
|
+
this.#config = this.#em.config;
|
|
20
|
+
this.#metadata = this.#em.getMetadata();
|
|
21
|
+
this.#platform = this.#em.getPlatform();
|
|
22
|
+
this.#comparator = this.#config.getComparator(this.#metadata);
|
|
23
23
|
}
|
|
24
24
|
computeChangeSet(entity) {
|
|
25
|
-
const meta = this
|
|
25
|
+
const meta = this.#metadata.get(entity.constructor);
|
|
26
26
|
if (meta.readonly) {
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
@@ -31,13 +31,14 @@ export class ChangeSetComputer {
|
|
|
31
31
|
const map = new Map();
|
|
32
32
|
// Execute `onCreate` and `onUpdate` on properties recursively, saves `onUpdate` results
|
|
33
33
|
// to the `map` as we want to apply those only if something else changed.
|
|
34
|
-
if (type === ChangeSetType.CREATE) {
|
|
34
|
+
if (type === ChangeSetType.CREATE) {
|
|
35
|
+
// run update hooks only after we know there are other changes
|
|
35
36
|
for (const prop of meta.hydrateProps) {
|
|
36
37
|
this.processPropertyInitializers(entity, prop, type, map);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
if (type === ChangeSetType.UPDATE && !wrapped.__initialized) {
|
|
40
|
-
const data = this
|
|
41
|
+
const data = this.#comparator.prepareEntity(entity);
|
|
41
42
|
if (Utils.equals(data, wrapped.__originalEntityData)) {
|
|
42
43
|
return null;
|
|
43
44
|
}
|
|
@@ -74,15 +75,15 @@ export class ChangeSetComputer {
|
|
|
74
75
|
* Traverses entity graph and executes `onCreate` and `onUpdate` methods, assigning the values to given properties.
|
|
75
76
|
*/
|
|
76
77
|
processPropertyInitializers(entity, prop, type, map, nested) {
|
|
77
|
-
if (prop.onCreate
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
entity[prop.name] = prop.onCreate(entity, this
|
|
78
|
+
if (prop.onCreate &&
|
|
79
|
+
type === ChangeSetType.CREATE &&
|
|
80
|
+
(entity[prop.name] == null ||
|
|
81
|
+
(Utils.isScalarReference(entity[prop.name]) && entity[prop.name].unwrap() == null))) {
|
|
82
|
+
entity[prop.name] = prop.onCreate(entity, this.#em);
|
|
82
83
|
}
|
|
83
84
|
if (prop.onUpdate && type === ChangeSetType.UPDATE) {
|
|
84
85
|
const pairs = map.get(entity) ?? [];
|
|
85
|
-
pairs.push([prop.name, prop.onUpdate(entity, this
|
|
86
|
+
pairs.push([prop.name, prop.onUpdate(entity, this.#em)]);
|
|
86
87
|
map.set(entity, pairs);
|
|
87
88
|
}
|
|
88
89
|
if (prop.kind === ReferenceKind.EMBEDDED && entity[prop.name]) {
|
|
@@ -92,7 +93,7 @@ export class ChangeSetComputer {
|
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
computePayload(entity, ignoreUndefined = false) {
|
|
95
|
-
const data = this
|
|
96
|
+
const data = this.#comparator.prepareEntity(entity);
|
|
96
97
|
const wrapped = helper(entity);
|
|
97
98
|
const entityName = wrapped.__meta.class;
|
|
98
99
|
const originalEntityData = wrapped.__originalEntityData;
|
|
@@ -103,7 +104,7 @@ export class ChangeSetComputer {
|
|
|
103
104
|
return data;
|
|
104
105
|
}
|
|
105
106
|
if (originalEntityData) {
|
|
106
|
-
const comparator = this
|
|
107
|
+
const comparator = this.#comparator.getEntityComparator(entityName);
|
|
107
108
|
const diff = comparator(originalEntityData, data);
|
|
108
109
|
if (ignoreUndefined) {
|
|
109
110
|
Utils.keys(diff)
|
|
@@ -120,7 +121,8 @@ export class ChangeSetComputer {
|
|
|
120
121
|
targets.forEach(([t]) => this.processProperty(changeSet, prop, t));
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
|
-
if (Utils.isCollection(target)) {
|
|
124
|
+
if (Utils.isCollection(target)) {
|
|
125
|
+
// m:n or 1:m
|
|
124
126
|
this.processToMany(prop, changeSet);
|
|
125
127
|
}
|
|
126
128
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
@@ -142,7 +144,7 @@ export class ChangeSetComputer {
|
|
|
142
144
|
if (prop.targetKey && prop.targetMeta) {
|
|
143
145
|
const targetProp = prop.targetMeta.properties[prop.targetKey];
|
|
144
146
|
if (targetProp?.customType) {
|
|
145
|
-
value = targetProp.customType.convertToDatabaseValue(value, this
|
|
147
|
+
value = targetProp.customType.convertToDatabaseValue(value, this.#platform, { mode: 'serialization' });
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
150
|
if (prop.polymorphic) {
|
|
@@ -161,9 +163,9 @@ export class ChangeSetComputer {
|
|
|
161
163
|
return;
|
|
162
164
|
}
|
|
163
165
|
if (target.isDirty()) {
|
|
164
|
-
this
|
|
166
|
+
this.#collectionUpdates.add(target);
|
|
165
167
|
}
|
|
166
|
-
if (prop.owner && !this
|
|
168
|
+
if (prop.owner && !this.#platform.usesPivotTable()) {
|
|
167
169
|
changeSet.payload[prop.name] = target.getItems(false).map((item) => {
|
|
168
170
|
return item.__helper.__identifier ?? item.__helper.getPrimaryKey();
|
|
169
171
|
});
|
|
@@ -3,15 +3,7 @@ import { type ChangeSet } from './ChangeSet.js';
|
|
|
3
3
|
import type { DriverMethodOptions } from '../drivers/IDatabaseDriver.js';
|
|
4
4
|
import type { EntityManager } from '../EntityManager.js';
|
|
5
5
|
export declare class ChangeSetPersister {
|
|
6
|
-
private
|
|
7
|
-
private readonly platform;
|
|
8
|
-
private readonly comparator;
|
|
9
|
-
private readonly usesReturningStatement;
|
|
10
|
-
private readonly driver;
|
|
11
|
-
private readonly metadata;
|
|
12
|
-
private readonly hydrator;
|
|
13
|
-
private readonly factory;
|
|
14
|
-
private readonly config;
|
|
6
|
+
#private;
|
|
15
7
|
constructor(em: EntityManager);
|
|
16
8
|
executeInserts<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
|
|
17
9
|
executeUpdates<T extends object>(changeSets: ChangeSet<T>[], batched: boolean, options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
|
|
@@ -7,25 +7,25 @@ import { Utils } from '../utils/Utils.js';
|
|
|
7
7
|
import { OptimisticLockError, ValidationError } from '../errors.js';
|
|
8
8
|
import { ReferenceKind } from '../enums.js';
|
|
9
9
|
export class ChangeSetPersister {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
#platform;
|
|
11
|
+
#comparator;
|
|
12
|
+
#usesReturningStatement;
|
|
13
|
+
#driver;
|
|
14
|
+
#metadata;
|
|
15
|
+
#hydrator;
|
|
16
|
+
#factory;
|
|
17
|
+
#config;
|
|
18
|
+
#em;
|
|
19
19
|
constructor(em) {
|
|
20
|
-
this
|
|
21
|
-
this
|
|
22
|
-
this
|
|
23
|
-
this
|
|
24
|
-
this
|
|
25
|
-
this
|
|
26
|
-
this
|
|
27
|
-
this
|
|
28
|
-
this
|
|
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
29
|
}
|
|
30
30
|
async executeInserts(changeSets, options, withSchema) {
|
|
31
31
|
if (!withSchema) {
|
|
@@ -33,7 +33,7 @@ export class ChangeSetPersister {
|
|
|
33
33
|
}
|
|
34
34
|
const meta = changeSets[0].meta;
|
|
35
35
|
changeSets.forEach(changeSet => this.processProperties(changeSet));
|
|
36
|
-
if (changeSets.length > 1 && this
|
|
36
|
+
if (changeSets.length > 1 && this.#config.get('useBatchInserts', this.#platform.usesBatchInserts())) {
|
|
37
37
|
return this.persistNewEntities(meta, changeSets, options);
|
|
38
38
|
}
|
|
39
39
|
for (const changeSet of changeSets) {
|
|
@@ -49,7 +49,10 @@ export class ChangeSetPersister {
|
|
|
49
49
|
// For STI with conflicting fieldNames (renamedFrom properties), we can't batch mixed child types
|
|
50
50
|
const hasSTIConflicts = meta.root.props.some(p => p.renamedFrom);
|
|
51
51
|
const hasMixedTypes = hasSTIConflicts && changeSets.some(cs => cs.meta.class !== meta.class);
|
|
52
|
-
if (batched &&
|
|
52
|
+
if (batched &&
|
|
53
|
+
changeSets.length > 1 &&
|
|
54
|
+
!hasMixedTypes &&
|
|
55
|
+
this.#config.get('useBatchUpdates', this.#platform.usesBatchUpdates())) {
|
|
53
56
|
return this.persistManagedEntities(meta, changeSets, options);
|
|
54
57
|
}
|
|
55
58
|
for (const changeSet of changeSets) {
|
|
@@ -60,14 +63,14 @@ export class ChangeSetPersister {
|
|
|
60
63
|
if (!withSchema) {
|
|
61
64
|
return this.runForEachSchema(changeSets, 'executeDeletes', options);
|
|
62
65
|
}
|
|
63
|
-
const size = this
|
|
66
|
+
const size = this.#config.get('batchSize');
|
|
64
67
|
const meta = changeSets[0].meta;
|
|
65
68
|
const pk = Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
66
69
|
for (let i = 0; i < changeSets.length; i += size) {
|
|
67
70
|
const chunk = changeSets.slice(i, i + size);
|
|
68
71
|
const pks = chunk.map(cs => cs.getPrimaryKey());
|
|
69
72
|
options = this.prepareOptions(meta, options);
|
|
70
|
-
await this
|
|
73
|
+
await this.#driver.nativeDelete(meta.root.class, { [pk]: { $in: pks } }, options);
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
async runForEachSchema(changeSets, method, options, ...args) {
|
|
@@ -107,7 +110,7 @@ export class ChangeSetPersister {
|
|
|
107
110
|
for (const prop of meta.relations) {
|
|
108
111
|
this.processProperty(changeSet, prop);
|
|
109
112
|
}
|
|
110
|
-
if (changeSet.type === ChangeSetType.CREATE && this
|
|
113
|
+
if (changeSet.type === ChangeSetType.CREATE && this.#config.get('validateRequired')) {
|
|
111
114
|
this.validateRequired(changeSet.entity);
|
|
112
115
|
}
|
|
113
116
|
}
|
|
@@ -118,31 +121,31 @@ export class ChangeSetPersister {
|
|
|
118
121
|
});
|
|
119
122
|
this.resolveTPTIdentifiers(changeSet);
|
|
120
123
|
// Use changeSet's own meta for STI entities to get correct field mappings
|
|
121
|
-
const res = await this
|
|
124
|
+
const res = await this.#driver.nativeInsertMany(changeSet.meta.class, [changeSet.payload], options);
|
|
122
125
|
if (!wrapped.hasPrimaryKey()) {
|
|
123
|
-
this.mapPrimaryKey(meta, res.insertId, changeSet);
|
|
126
|
+
this.mapPrimaryKey(meta, res.insertId ?? res.row?.[meta.primaryKeys[0]], changeSet);
|
|
124
127
|
}
|
|
125
128
|
this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
|
|
126
129
|
this.markAsPopulated(changeSet, meta);
|
|
127
130
|
wrapped.__initialized = true;
|
|
128
131
|
wrapped.__managed = true;
|
|
129
|
-
if (!this
|
|
132
|
+
if (!this.#usesReturningStatement) {
|
|
130
133
|
await this.reloadVersionValues(meta, [changeSet], options);
|
|
131
134
|
}
|
|
132
135
|
changeSet.persisted = true;
|
|
133
136
|
}
|
|
134
137
|
async persistNewEntities(meta, changeSets, options) {
|
|
135
|
-
const size = this
|
|
138
|
+
const size = this.#config.get('batchSize');
|
|
136
139
|
for (let i = 0; i < changeSets.length; i += size) {
|
|
137
140
|
const chunk = changeSets.slice(i, i + size);
|
|
138
141
|
await this.persistNewEntitiesBatch(meta, chunk, options);
|
|
139
|
-
if (!this
|
|
142
|
+
if (!this.#usesReturningStatement) {
|
|
140
143
|
await this.reloadVersionValues(meta, chunk, options);
|
|
141
144
|
}
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
147
|
prepareOptions(meta, options, additionalOptions) {
|
|
145
|
-
const loggerContext = Utils.merge({ id: this
|
|
148
|
+
const loggerContext = Utils.merge({ id: this.#em._id }, this.#em.getLoggerContext({ disableContextResolution: true }));
|
|
146
149
|
return {
|
|
147
150
|
...options,
|
|
148
151
|
...additionalOptions,
|
|
@@ -158,7 +161,7 @@ export class ChangeSetPersister {
|
|
|
158
161
|
for (const changeSet of changeSets) {
|
|
159
162
|
this.resolveTPTIdentifiers(changeSet);
|
|
160
163
|
}
|
|
161
|
-
const res = await this
|
|
164
|
+
const res = await this.#driver.nativeInsertMany(meta.class, changeSets.map(cs => cs.payload), options);
|
|
162
165
|
for (let i = 0; i < changeSets.length; i++) {
|
|
163
166
|
const changeSet = changeSets[i];
|
|
164
167
|
const wrapped = helper(changeSet.entity);
|
|
@@ -184,7 +187,7 @@ export class ChangeSetPersister {
|
|
|
184
187
|
changeSet.persisted = true;
|
|
185
188
|
}
|
|
186
189
|
async persistManagedEntities(meta, changeSets, options) {
|
|
187
|
-
const size = this
|
|
190
|
+
const size = this.#config.get('batchSize');
|
|
188
191
|
for (let i = 0; i < changeSets.length; i += size) {
|
|
189
192
|
const chunk = changeSets.slice(i, i + size);
|
|
190
193
|
await this.persistManagedEntitiesBatch(meta, chunk, options);
|
|
@@ -217,9 +220,9 @@ export class ChangeSetPersister {
|
|
|
217
220
|
cond.push(where);
|
|
218
221
|
payload.push(changeSet.payload);
|
|
219
222
|
}
|
|
220
|
-
const res = await this
|
|
223
|
+
const res = await this.#driver.nativeUpdateMany(meta.class, cond, payload, options);
|
|
221
224
|
const map = new Map();
|
|
222
|
-
res.rows?.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this
|
|
225
|
+
res.rows?.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.#platform, true), item));
|
|
223
226
|
for (const changeSet of changeSets) {
|
|
224
227
|
if (res.rows) {
|
|
225
228
|
const row = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
|
|
@@ -230,7 +233,7 @@ export class ChangeSetPersister {
|
|
|
230
233
|
}
|
|
231
234
|
mapPrimaryKey(meta, value, changeSet) {
|
|
232
235
|
const prop = meta.properties[meta.primaryKeys[0]];
|
|
233
|
-
const insertId = prop.customType ? prop.customType.convertToJSValue(value, this
|
|
236
|
+
const insertId = prop.customType ? prop.customType.convertToJSValue(value, this.#platform) : value;
|
|
234
237
|
const wrapped = helper(changeSet.entity);
|
|
235
238
|
if (!wrapped.hasPrimaryKey()) {
|
|
236
239
|
wrapped.setPrimaryKey(insertId);
|
|
@@ -238,7 +241,9 @@ export class ChangeSetPersister {
|
|
|
238
241
|
// some drivers might be returning bigint PKs as numbers when the number is small enough,
|
|
239
242
|
// but we need to have it as string so comparison works in change set tracking, so instead
|
|
240
243
|
// of using the raw value from db, we convert it back to the db value explicitly
|
|
241
|
-
value = prop.customType
|
|
244
|
+
value = prop.customType
|
|
245
|
+
? prop.customType.convertToDatabaseValue(insertId, this.#platform, { mode: 'serialization' })
|
|
246
|
+
: value;
|
|
242
247
|
changeSet.payload[wrapped.__meta.primaryKeys[0]] = value;
|
|
243
248
|
if (wrapped.__identifier && !Array.isArray(wrapped.__identifier)) {
|
|
244
249
|
wrapped.__identifier.setValue(value);
|
|
@@ -248,8 +253,8 @@ export class ChangeSetPersister {
|
|
|
248
253
|
* Sets populate flag to new entities so they are serialized like if they were loaded from the db
|
|
249
254
|
*/
|
|
250
255
|
markAsPopulated(changeSet, meta) {
|
|
251
|
-
helper(changeSet.entity).__schema = this
|
|
252
|
-
if (!this
|
|
256
|
+
helper(changeSet.entity).__schema = this.#driver.getSchemaName(meta, changeSet);
|
|
257
|
+
if (!this.#config.get('populateAfterFlush')) {
|
|
253
258
|
return;
|
|
254
259
|
}
|
|
255
260
|
helper(changeSet.entity).populated();
|
|
@@ -268,17 +273,19 @@ export class ChangeSetPersister {
|
|
|
268
273
|
options = this.prepareOptions(meta, options, {
|
|
269
274
|
convertCustomTypes: false,
|
|
270
275
|
});
|
|
271
|
-
if (meta.concurrencyCheckKeys.size === 0 &&
|
|
272
|
-
|
|
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);
|
|
273
279
|
}
|
|
274
280
|
if (meta.versionProperty) {
|
|
275
|
-
cond[meta.versionProperty] = this
|
|
281
|
+
cond[meta.versionProperty] = this.#platform.convertVersionValue(changeSet.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
|
|
276
282
|
}
|
|
277
283
|
this.checkConcurrencyKeys(meta, changeSet, cond);
|
|
278
|
-
return this
|
|
284
|
+
return this.#driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
|
|
279
285
|
}
|
|
280
286
|
async checkOptimisticLocks(meta, changeSets, options) {
|
|
281
|
-
if (meta.concurrencyCheckKeys.size === 0 &&
|
|
287
|
+
if (meta.concurrencyCheckKeys.size === 0 &&
|
|
288
|
+
(!meta.versionProperty || changeSets.every(cs => cs.entity[meta.versionProperty] == null))) {
|
|
282
289
|
return;
|
|
283
290
|
}
|
|
284
291
|
// skip entity references as they don't have version values loaded
|
|
@@ -287,7 +294,7 @@ export class ChangeSetPersister {
|
|
|
287
294
|
const cond = Utils.getPrimaryKeyCond(cs.originalEntity, meta.primaryKeys.concat(...meta.concurrencyCheckKeys));
|
|
288
295
|
if (meta.versionProperty) {
|
|
289
296
|
// @ts-ignore
|
|
290
|
-
cond[meta.versionProperty] = this
|
|
297
|
+
cond[meta.versionProperty] = this.#platform.convertVersionValue(cs.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
|
|
291
298
|
}
|
|
292
299
|
return cond;
|
|
293
300
|
});
|
|
@@ -295,7 +302,7 @@ export class ChangeSetPersister {
|
|
|
295
302
|
options = this.prepareOptions(meta, options, {
|
|
296
303
|
fields: primaryKeys,
|
|
297
304
|
});
|
|
298
|
-
const res = await this
|
|
305
|
+
const res = await this.#driver.find(meta.root.class, { $or }, options);
|
|
299
306
|
if (res.length !== changeSets.length) {
|
|
300
307
|
const compare = (a, b, keys) => keys.every(k => a[k] === b[k]);
|
|
301
308
|
const entity = changeSets.find(cs => {
|
|
@@ -314,7 +321,7 @@ export class ChangeSetPersister {
|
|
|
314
321
|
* so we use a single query in case of both versioning and default values is used.
|
|
315
322
|
*/
|
|
316
323
|
async reloadVersionValues(meta, changeSets, options) {
|
|
317
|
-
const reloadProps = meta.versionProperty && !this
|
|
324
|
+
const reloadProps = meta.versionProperty && !this.#usesReturningStatement ? [meta.properties[meta.versionProperty]] : [];
|
|
318
325
|
if (changeSets[0].type === ChangeSetType.CREATE) {
|
|
319
326
|
for (const prop of meta.props) {
|
|
320
327
|
if (prop.persist === false) {
|
|
@@ -343,10 +350,8 @@ export class ChangeSetPersister {
|
|
|
343
350
|
});
|
|
344
351
|
});
|
|
345
352
|
// reload generated columns
|
|
346
|
-
if (!this
|
|
347
|
-
meta.props
|
|
348
|
-
.filter(prop => prop.generated && !prop.primary)
|
|
349
|
-
.forEach(prop => reloadProps.push(prop));
|
|
353
|
+
if (!this.#usesReturningStatement) {
|
|
354
|
+
meta.props.filter(prop => prop.generated && !prop.primary).forEach(prop => reloadProps.push(prop));
|
|
350
355
|
reloadProps.push(...returning);
|
|
351
356
|
}
|
|
352
357
|
}
|
|
@@ -358,19 +363,19 @@ export class ChangeSetPersister {
|
|
|
358
363
|
const pks = changeSets.map(cs => {
|
|
359
364
|
const val = helper(cs.entity).getPrimaryKey(true);
|
|
360
365
|
if (Utils.isPlainObject(val)) {
|
|
361
|
-
return Utils.getCompositeKeyValue(val, meta, false, this
|
|
366
|
+
return Utils.getCompositeKeyValue(val, meta, false, this.#platform);
|
|
362
367
|
}
|
|
363
368
|
return val;
|
|
364
369
|
});
|
|
365
370
|
options = this.prepareOptions(meta, options, {
|
|
366
371
|
fields: Utils.unique(reloadProps.map(prop => prop.name)),
|
|
367
372
|
});
|
|
368
|
-
const data = await this
|
|
373
|
+
const data = await this.#driver.find(meta.class, { [pk]: { $in: pks } }, options);
|
|
369
374
|
const map = new Map();
|
|
370
|
-
data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this
|
|
375
|
+
data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.#platform, true), item));
|
|
371
376
|
for (const changeSet of changeSets) {
|
|
372
377
|
const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
|
|
373
|
-
this
|
|
378
|
+
this.#hydrator.hydrate(changeSet.entity, meta, data, this.#factory, 'full', false, true);
|
|
374
379
|
Object.assign(changeSet.payload, data); // merge to the changeset payload, so it gets saved to the entity snapshot
|
|
375
380
|
}
|
|
376
381
|
}
|
|
@@ -407,7 +412,7 @@ export class ChangeSetPersister {
|
|
|
407
412
|
return;
|
|
408
413
|
}
|
|
409
414
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && Array.isArray(value)) {
|
|
410
|
-
changeSet.payload[prop.name] = value.map(val => val instanceof EntityIdentifier ? val.getValue() : val);
|
|
415
|
+
changeSet.payload[prop.name] = value.map(val => (val instanceof EntityIdentifier ? val.getValue() : val));
|
|
411
416
|
return;
|
|
412
417
|
}
|
|
413
418
|
if (prop.name in changeSet.payload) {
|
|
@@ -426,17 +431,17 @@ export class ChangeSetPersister {
|
|
|
426
431
|
* We do need to map to the change set payload too, as it will be used in the originalEntityData for new entities.
|
|
427
432
|
*/
|
|
428
433
|
mapReturnedValues(entity, payload, row, meta, upsert = false) {
|
|
429
|
-
if ((!this
|
|
434
|
+
if ((!this.#usesReturningStatement && !upsert) || !row || !Utils.hasObjectKeys(row)) {
|
|
430
435
|
return;
|
|
431
436
|
}
|
|
432
|
-
const mapped = this
|
|
437
|
+
const mapped = this.#comparator.mapResult(meta, row);
|
|
433
438
|
if (entity) {
|
|
434
|
-
this
|
|
439
|
+
this.#hydrator.hydrate(entity, meta, mapped, this.#factory, 'full', false, true);
|
|
435
440
|
}
|
|
436
441
|
if (upsert) {
|
|
437
442
|
for (const prop of meta.props) {
|
|
438
443
|
if (prop.customType && prop.name in mapped) {
|
|
439
|
-
mapped[prop.name] = prop.customType.convertToJSValue(mapped[prop.name], this
|
|
444
|
+
mapped[prop.name] = prop.customType.convertToJSValue(mapped[prop.name], this.#platform);
|
|
440
445
|
}
|
|
441
446
|
}
|
|
442
447
|
}
|
|
@@ -26,10 +26,7 @@ export interface Edge {
|
|
|
26
26
|
* @internal
|
|
27
27
|
*/
|
|
28
28
|
export declare class CommitOrderCalculator {
|
|
29
|
-
|
|
30
|
-
private nodes;
|
|
31
|
-
/** Volatile variable holding calculated nodes during sorting process. */
|
|
32
|
-
private sortedNodeList;
|
|
29
|
+
#private;
|
|
33
30
|
/**
|
|
34
31
|
* Checks for node existence in graph.
|
|
35
32
|
*/
|
|
@@ -17,26 +17,26 @@ export var NodeState;
|
|
|
17
17
|
*/
|
|
18
18
|
export class CommitOrderCalculator {
|
|
19
19
|
/** Matrix of nodes, keys are provided hashes and values are the node definition objects. */
|
|
20
|
-
nodes = new Map();
|
|
20
|
+
#nodes = new Map();
|
|
21
21
|
/** Volatile variable holding calculated nodes during sorting process. */
|
|
22
|
-
sortedNodeList = [];
|
|
22
|
+
#sortedNodeList = [];
|
|
23
23
|
/**
|
|
24
24
|
* Checks for node existence in graph.
|
|
25
25
|
*/
|
|
26
26
|
hasNode(hash) {
|
|
27
|
-
return this
|
|
27
|
+
return this.#nodes.has(hash);
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Adds a new node to the graph, assigning its hash.
|
|
31
31
|
*/
|
|
32
32
|
addNode(hash) {
|
|
33
|
-
this
|
|
33
|
+
this.#nodes.set(hash, { hash, state: 0 /* NodeState.NOT_VISITED */, dependencies: new Map() });
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* Adds a new dependency (edge) to the graph using their hashes.
|
|
37
37
|
*/
|
|
38
38
|
addDependency(from, to, weight) {
|
|
39
|
-
this
|
|
39
|
+
this.#nodes.get(from).dependencies.set(to, { from, to, weight });
|
|
40
40
|
}
|
|
41
41
|
discoverProperty(prop, entityName) {
|
|
42
42
|
const toOneOwner = (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner) || prop.kind === ReferenceKind.MANY_TO_ONE;
|
|
@@ -57,15 +57,15 @@ export class CommitOrderCalculator {
|
|
|
57
57
|
* @internal Highly performance-sensitive method.
|
|
58
58
|
*/
|
|
59
59
|
sort() {
|
|
60
|
-
for (const vertex of this
|
|
60
|
+
for (const vertex of this.#nodes.values()) {
|
|
61
61
|
if (vertex.state !== 0 /* NodeState.NOT_VISITED */) {
|
|
62
62
|
continue;
|
|
63
63
|
}
|
|
64
64
|
this.visit(vertex);
|
|
65
65
|
}
|
|
66
|
-
const sortedList = this
|
|
67
|
-
this
|
|
68
|
-
this
|
|
66
|
+
const sortedList = this.#sortedNodeList.reverse();
|
|
67
|
+
this.#nodes = new Map();
|
|
68
|
+
this.#sortedNodeList = [];
|
|
69
69
|
return sortedList;
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
@@ -76,18 +76,20 @@ export class CommitOrderCalculator {
|
|
|
76
76
|
visit(node) {
|
|
77
77
|
node.state = 1 /* NodeState.IN_PROGRESS */;
|
|
78
78
|
for (const edge of node.dependencies.values()) {
|
|
79
|
-
const target = this
|
|
79
|
+
const target = this.#nodes.get(edge.to);
|
|
80
80
|
switch (target.state) {
|
|
81
|
-
case 2 /* NodeState.VISITED */:
|
|
81
|
+
case 2 /* NodeState.VISITED */:
|
|
82
|
+
break; // Do nothing, since node was already visited
|
|
82
83
|
case 1 /* NodeState.IN_PROGRESS */:
|
|
83
84
|
this.visitOpenNode(node, target, edge);
|
|
84
85
|
break;
|
|
85
|
-
case 0 /* NodeState.NOT_VISITED */:
|
|
86
|
+
case 0 /* NodeState.NOT_VISITED */:
|
|
87
|
+
this.visit(target);
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
if (node.state !== 2 /* NodeState.VISITED */) {
|
|
89
91
|
node.state = 2 /* NodeState.VISITED */;
|
|
90
|
-
this
|
|
92
|
+
this.#sortedNodeList.push(node.hash);
|
|
91
93
|
}
|
|
92
94
|
}
|
|
93
95
|
/**
|
|
@@ -98,12 +100,12 @@ export class CommitOrderCalculator {
|
|
|
98
100
|
return;
|
|
99
101
|
}
|
|
100
102
|
for (const edge of target.dependencies.values()) {
|
|
101
|
-
const targetNode = this
|
|
103
|
+
const targetNode = this.#nodes.get(edge.to);
|
|
102
104
|
if (targetNode.state === 0 /* NodeState.NOT_VISITED */) {
|
|
103
105
|
this.visit(targetNode);
|
|
104
106
|
}
|
|
105
107
|
}
|
|
106
108
|
target.state = 2 /* NodeState.VISITED */;
|
|
107
|
-
this
|
|
109
|
+
this.#sortedNodeList.push(target.hash);
|
|
108
110
|
}
|
|
109
111
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import type { AnyEntity, EntityMetadata } from '../typings.js';
|
|
2
2
|
export declare class IdentityMap {
|
|
3
|
-
private
|
|
4
|
-
constructor(defaultSchema?: string
|
|
5
|
-
private readonly registry;
|
|
6
|
-
/** Tracks alternate key hashes for each entity so we can clean them up on delete */
|
|
7
|
-
private readonly alternateKeys;
|
|
3
|
+
#private;
|
|
4
|
+
constructor(defaultSchema?: string);
|
|
8
5
|
store<T>(item: T): void;
|
|
9
6
|
/**
|
|
10
7
|
* Stores an entity under an alternate key (non-PK property).
|