@mikro-orm/core 7.0.0-dev.99 → 7.0.0-rc.1
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 +34 -17
- package/EntityManager.js +95 -103
- package/MikroORM.d.ts +5 -5
- package/MikroORM.js +25 -20
- package/cache/FileCacheAdapter.js +11 -3
- package/connections/Connection.d.ts +3 -2
- package/connections/Connection.js +4 -3
- package/drivers/DatabaseDriver.d.ts +11 -11
- package/drivers/DatabaseDriver.js +91 -25
- package/drivers/IDatabaseDriver.d.ts +50 -20
- package/entity/BaseEntity.d.ts +61 -1
- package/entity/Collection.d.ts +8 -1
- package/entity/Collection.js +12 -13
- package/entity/EntityAssigner.js +9 -9
- package/entity/EntityFactory.d.ts +6 -1
- package/entity/EntityFactory.js +40 -22
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +27 -4
- package/entity/EntityLoader.d.ts +5 -4
- package/entity/EntityLoader.js +193 -80
- package/entity/EntityRepository.d.ts +27 -7
- package/entity/EntityRepository.js +8 -2
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/WrappedEntity.d.ts +2 -2
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +89 -50
- package/entity/defineEntity.js +12 -0
- package/entity/index.d.ts +1 -0
- package/entity/index.js +1 -0
- package/entity/utils.d.ts +6 -1
- package/entity/utils.js +33 -0
- package/entity/validators.js +2 -2
- package/enums.d.ts +2 -2
- package/enums.js +1 -0
- package/errors.d.ts +16 -8
- package/errors.js +40 -13
- package/hydration/ObjectHydrator.js +63 -21
- package/index.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +7 -6
- package/logging/inspect.js +1 -6
- package/metadata/EntitySchema.d.ts +43 -13
- package/metadata/EntitySchema.js +82 -27
- package/metadata/MetadataDiscovery.d.ts +60 -3
- package/metadata/MetadataDiscovery.js +665 -154
- package/metadata/MetadataProvider.js +3 -1
- package/metadata/MetadataStorage.d.ts +13 -6
- package/metadata/MetadataStorage.js +64 -19
- package/metadata/MetadataValidator.d.ts +32 -2
- package/metadata/MetadataValidator.js +196 -31
- package/metadata/discover-entities.js +5 -5
- package/metadata/types.d.ts +111 -14
- package/naming-strategy/AbstractNamingStrategy.d.ts +11 -3
- package/naming-strategy/AbstractNamingStrategy.js +12 -0
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
- package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
- package/naming-strategy/MongoNamingStrategy.js +6 -6
- package/naming-strategy/NamingStrategy.d.ts +17 -3
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/package.json +2 -2
- package/platforms/Platform.d.ts +4 -2
- package/platforms/Platform.js +5 -2
- package/serialization/EntitySerializer.d.ts +3 -0
- package/serialization/EntitySerializer.js +15 -13
- package/serialization/EntityTransformer.js +6 -6
- package/serialization/SerializationContext.d.ts +6 -6
- package/typings.d.ts +325 -110
- package/typings.js +84 -17
- package/unit-of-work/ChangeSet.d.ts +4 -3
- package/unit-of-work/ChangeSet.js +2 -3
- package/unit-of-work/ChangeSetComputer.d.ts +3 -6
- package/unit-of-work/ChangeSetComputer.js +34 -13
- package/unit-of-work/ChangeSetPersister.d.ts +12 -10
- package/unit-of-work/ChangeSetPersister.js +55 -25
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +13 -13
- package/unit-of-work/IdentityMap.d.ts +12 -0
- package/unit-of-work/IdentityMap.js +39 -1
- package/unit-of-work/UnitOfWork.d.ts +21 -3
- package/unit-of-work/UnitOfWork.js +203 -56
- package/utils/AbstractSchemaGenerator.js +17 -8
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +52 -11
- package/utils/Configuration.js +12 -8
- package/utils/Cursor.js +21 -8
- package/utils/DataloaderUtils.js +13 -11
- package/utils/EntityComparator.d.ts +14 -7
- package/utils/EntityComparator.js +132 -46
- package/utils/QueryHelper.d.ts +16 -6
- package/utils/QueryHelper.js +53 -18
- package/utils/RawQueryFragment.d.ts +28 -23
- package/utils/RawQueryFragment.js +34 -56
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +1 -1
- package/utils/Utils.d.ts +7 -26
- package/utils/Utils.js +25 -79
- package/utils/clone.js +7 -21
- package/utils/env-vars.d.ts +4 -0
- package/utils/env-vars.js +13 -3
- package/utils/fs-utils.d.ts +21 -0
- package/utils/fs-utils.js +106 -11
- package/utils/upsert-utils.d.ts +4 -4
package/typings.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ReferenceKind, } from './enums.js';
|
|
2
2
|
import { Reference } from './entity/Reference.js';
|
|
3
3
|
import { EntityHelper } from './entity/EntityHelper.js';
|
|
4
|
+
import { helper } from './entity/wrap.js';
|
|
4
5
|
import { Utils } from './utils/Utils.js';
|
|
5
6
|
import { EntityComparator } from './utils/EntityComparator.js';
|
|
7
|
+
import { BaseEntity } from './entity/BaseEntity.js';
|
|
6
8
|
export const EntityRepositoryType = Symbol('EntityRepositoryType');
|
|
7
9
|
export const PrimaryKeyProp = Symbol('PrimaryKeyProp');
|
|
8
10
|
export const OptionalProps = Symbol('OptionalProps');
|
|
@@ -25,22 +27,24 @@ export class EntityMetadata {
|
|
|
25
27
|
this.referencingProperties = [];
|
|
26
28
|
this.concurrencyCheckKeys = new Set();
|
|
27
29
|
Object.assign(this, meta);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const name = meta.className ?? meta.name;
|
|
31
|
+
if (!this.class && name) {
|
|
32
|
+
const Class = this.extends === BaseEntity
|
|
33
|
+
? ({ [name]: class extends BaseEntity {
|
|
34
|
+
} })[name]
|
|
35
|
+
: ({ [name]: class {
|
|
36
|
+
} })[name];
|
|
37
|
+
this.class = Class;
|
|
32
38
|
}
|
|
39
|
+
}
|
|
40
|
+
addProperty(prop) {
|
|
33
41
|
this.properties[prop.name] = prop;
|
|
34
42
|
this.propertyOrder.set(prop.name, this.props.length);
|
|
35
|
-
|
|
36
|
-
if (sync) {
|
|
37
|
-
this.sync();
|
|
38
|
-
}
|
|
43
|
+
this.sync();
|
|
39
44
|
}
|
|
40
45
|
removeProperty(name, sync = true) {
|
|
41
46
|
delete this.properties[name];
|
|
42
47
|
this.propertyOrder.delete(name);
|
|
43
|
-
/* v8 ignore next */
|
|
44
48
|
if (sync) {
|
|
45
49
|
this.sync();
|
|
46
50
|
}
|
|
@@ -60,8 +64,52 @@ export class EntityMetadata {
|
|
|
60
64
|
getPrimaryProp() {
|
|
61
65
|
return this.properties[this.primaryKeys[0]];
|
|
62
66
|
}
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Creates a mapping from property names to field names.
|
|
69
|
+
* @param alias - Optional alias to prefix field names. Can be a string (same for all) or a function (per-property).
|
|
70
|
+
* When provided, also adds toString() returning the alias for backwards compatibility with formulas.
|
|
71
|
+
* @param toStringAlias - Optional alias to return from toString(). Defaults to `alias` when it's a string.
|
|
72
|
+
*/
|
|
73
|
+
createColumnMappingObject(alias, toStringAlias) {
|
|
74
|
+
const resolveAlias = typeof alias === 'function' ? alias : () => alias;
|
|
75
|
+
const defaultAlias = toStringAlias ?? (typeof alias === 'string' ? alias : undefined);
|
|
76
|
+
const result = Object.values(this.properties).reduce((o, prop) => {
|
|
77
|
+
if (prop.fieldNames) {
|
|
78
|
+
const propAlias = resolveAlias(prop);
|
|
79
|
+
o[prop.name] = propAlias ? `${propAlias}.${prop.fieldNames[0]}` : prop.fieldNames[0];
|
|
80
|
+
}
|
|
81
|
+
return o;
|
|
82
|
+
}, {});
|
|
83
|
+
// Add toString() for backwards compatibility when alias is provided
|
|
84
|
+
Object.defineProperty(result, 'toString', {
|
|
85
|
+
value: () => defaultAlias ?? '',
|
|
86
|
+
enumerable: false,
|
|
87
|
+
});
|
|
88
|
+
// Wrap in Proxy to detect old formula signature usage where the first param was FormulaTable.
|
|
89
|
+
// If user accesses `.alias` or `.qualifiedName` (FormulaTable-only properties), warn them.
|
|
90
|
+
const warnedProps = new Set(['alias', 'qualifiedName']);
|
|
91
|
+
return new Proxy(result, {
|
|
92
|
+
get(target, prop, receiver) {
|
|
93
|
+
if (typeof prop === 'string' && warnedProps.has(prop) && !(prop in target)) {
|
|
94
|
+
// eslint-disable-next-line no-console
|
|
95
|
+
console.warn(`[MikroORM] Detected old formula callback signature. The first parameter is now 'columns', not 'table'. ` +
|
|
96
|
+
`Accessing '.${prop}' on the columns object will return undefined. ` +
|
|
97
|
+
`Update your formula: formula(cols => quote\`\${cols.propName} ...\`). See the v7 upgrade guide.`);
|
|
98
|
+
}
|
|
99
|
+
return Reflect.get(target, prop, receiver);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Creates a column mapping for schema callbacks (indexes, checks, generated columns).
|
|
105
|
+
* For TPT entities, only includes properties that belong to the current table (ownProps).
|
|
106
|
+
*/
|
|
107
|
+
createSchemaColumnMappingObject() {
|
|
108
|
+
// For TPT entities, only include properties that belong to this entity's table
|
|
109
|
+
const props = this.inheritanceType === 'tpt' && this.ownProps
|
|
110
|
+
? this.ownProps
|
|
111
|
+
: Object.values(this.properties);
|
|
112
|
+
return props.reduce((o, prop) => {
|
|
65
113
|
if (prop.fieldNames) {
|
|
66
114
|
o[prop.name] = prop.fieldNames[0];
|
|
67
115
|
}
|
|
@@ -74,6 +122,9 @@ export class EntityMetadata {
|
|
|
74
122
|
set tableName(name) {
|
|
75
123
|
this.collection = name;
|
|
76
124
|
}
|
|
125
|
+
get uniqueName() {
|
|
126
|
+
return this.tableName + '_' + this._id;
|
|
127
|
+
}
|
|
77
128
|
sync(initIndexes = false, config) {
|
|
78
129
|
this.root ??= this;
|
|
79
130
|
const props = Object.values(this.properties).sort((a, b) => this.propertyOrder.get(a.name) - this.propertyOrder.get(b.name));
|
|
@@ -100,10 +151,19 @@ export class EntityMetadata {
|
|
|
100
151
|
return !prop.getter && !prop.setter && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind);
|
|
101
152
|
});
|
|
102
153
|
this.selfReferencing = this.relations.some(prop => {
|
|
103
|
-
return
|
|
154
|
+
return this.root.uniqueName === prop.targetMeta?.root.uniqueName;
|
|
104
155
|
});
|
|
105
156
|
this.hasUniqueProps = this.uniques.length + this.uniqueProps.length > 0;
|
|
106
|
-
|
|
157
|
+
// Normalize object-form `view` option: `view: { materialized: true, withData: false }`
|
|
158
|
+
// into flat metadata fields (`view: true`, `materialized: true`, `withData: false`).
|
|
159
|
+
if (typeof this.view === 'object') {
|
|
160
|
+
this.materialized = this.view.materialized;
|
|
161
|
+
this.withData = this.view.withData;
|
|
162
|
+
this.view = true;
|
|
163
|
+
}
|
|
164
|
+
// If `view` is set, this is a database view entity (not a virtual entity).
|
|
165
|
+
// Virtual entities evaluate expressions at query time, view entities create actual database views.
|
|
166
|
+
this.virtual = !!this.expression && !this.view;
|
|
107
167
|
if (config) {
|
|
108
168
|
for (const prop of this.props) {
|
|
109
169
|
if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => typeof item === 'string')) {
|
|
@@ -126,15 +186,15 @@ export class EntityMetadata {
|
|
|
126
186
|
for (const hook of Utils.keys(this.hooks)) {
|
|
127
187
|
this.hooks[hook] = Utils.removeDuplicates(this.hooks[hook]);
|
|
128
188
|
}
|
|
129
|
-
if (this.virtual) {
|
|
189
|
+
if (this.virtual || this.view) {
|
|
130
190
|
this.readonly = true;
|
|
131
191
|
}
|
|
132
192
|
if (initIndexes && this.name) {
|
|
133
193
|
this.props.forEach(prop => this.initIndexes(prop));
|
|
134
194
|
}
|
|
135
195
|
this.definedProperties = this.trackingProps.reduce((o, prop) => {
|
|
136
|
-
const
|
|
137
|
-
if (
|
|
196
|
+
const hasInverse = (prop.inversedBy || prop.mappedBy) && !prop.mapToPk;
|
|
197
|
+
if (hasInverse) {
|
|
138
198
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
139
199
|
const meta = this;
|
|
140
200
|
o[prop.name] = {
|
|
@@ -152,7 +212,10 @@ export class EntityMetadata {
|
|
|
152
212
|
wrapped.__data[prop.name] = Reference.wrapReference(val, prop);
|
|
153
213
|
// when propagation from inside hydration, we set the FK to the entity data immediately
|
|
154
214
|
if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
|
|
155
|
-
|
|
215
|
+
const targetMeta = prop.targetMeta ?? helper(entity)?.__meta;
|
|
216
|
+
if (targetMeta) {
|
|
217
|
+
wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(val, targetMeta, true);
|
|
218
|
+
}
|
|
156
219
|
}
|
|
157
220
|
EntityHelper.propagate(meta, entity, this, prop, Reference.unwrapReference(val), old);
|
|
158
221
|
},
|
|
@@ -191,4 +254,8 @@ export class EntityMetadata {
|
|
|
191
254
|
clone() {
|
|
192
255
|
return this;
|
|
193
256
|
}
|
|
257
|
+
/** @ignore */
|
|
258
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
259
|
+
return `[${this.constructor.name}<${this.className}>]`;
|
|
260
|
+
}
|
|
194
261
|
}
|
|
@@ -11,15 +11,16 @@ export declare class ChangeSet<T extends object> {
|
|
|
11
11
|
getSerializedPrimaryKey(): string | null;
|
|
12
12
|
}
|
|
13
13
|
export interface ChangeSet<T> {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
collection: string;
|
|
14
|
+
meta: EntityMetadata<T>;
|
|
15
|
+
rootMeta: EntityMetadata<T>;
|
|
17
16
|
schema?: string;
|
|
18
17
|
type: ChangeSetType;
|
|
19
18
|
entity: T;
|
|
20
19
|
payload: EntityDictionary<T>;
|
|
21
20
|
persisted: boolean;
|
|
22
21
|
originalEntity?: EntityData<T>;
|
|
22
|
+
/** For TPT: changesets for parent tables, ordered from immediate parent to root */
|
|
23
|
+
tptChangeSets?: ChangeSet<T>[];
|
|
23
24
|
}
|
|
24
25
|
export declare enum ChangeSetType {
|
|
25
26
|
CREATE = "create",
|
|
@@ -13,9 +13,8 @@ export class ChangeSet {
|
|
|
13
13
|
this.type = type;
|
|
14
14
|
this.payload = payload;
|
|
15
15
|
this.meta = meta;
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
18
|
-
this.collection = meta.root.collection;
|
|
16
|
+
this.meta = meta;
|
|
17
|
+
this.rootMeta = meta.root;
|
|
19
18
|
this.schema = helper(entity).__schema ?? meta.root.schema;
|
|
20
19
|
}
|
|
21
20
|
getPrimaryKey(object = false) {
|
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import { type Configuration } from '../utils/Configuration.js';
|
|
2
|
-
import type { MetadataStorage } from '../metadata/MetadataStorage.js';
|
|
3
1
|
import type { AnyEntity } from '../typings.js';
|
|
4
2
|
import { ChangeSet } from './ChangeSet.js';
|
|
5
3
|
import { type Collection } from '../entity/Collection.js';
|
|
6
|
-
import type { Platform } from '../platforms/Platform.js';
|
|
7
4
|
import type { EntityManager } from '../EntityManager.js';
|
|
8
5
|
export declare class ChangeSetComputer {
|
|
6
|
+
private readonly em;
|
|
9
7
|
private readonly collectionUpdates;
|
|
8
|
+
private readonly comparator;
|
|
10
9
|
private readonly metadata;
|
|
11
10
|
private readonly platform;
|
|
12
11
|
private readonly config;
|
|
13
|
-
|
|
14
|
-
private readonly comparator;
|
|
15
|
-
constructor(collectionUpdates: Set<Collection<AnyEntity>>, metadata: MetadataStorage, platform: Platform, config: Configuration, em: EntityManager);
|
|
12
|
+
constructor(em: EntityManager, collectionUpdates: Set<Collection<AnyEntity>>);
|
|
16
13
|
computeChangeSet<T extends object>(entity: T): ChangeSet<T> | null;
|
|
17
14
|
/**
|
|
18
15
|
* Traverses entity graph and executes `onCreate` and `onUpdate` methods, assigning the values to given properties.
|
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
import { Utils } from '../utils/Utils.js';
|
|
2
|
+
import { QueryHelper } from '../utils/QueryHelper.js';
|
|
2
3
|
import { ChangeSet, ChangeSetType } from './ChangeSet.js';
|
|
3
4
|
import { helper } from '../entity/wrap.js';
|
|
4
5
|
import { validateEntity } from '../entity/validators.js';
|
|
6
|
+
import { Reference } from '../entity/Reference.js';
|
|
7
|
+
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
5
8
|
import { ReferenceKind } from '../enums.js';
|
|
6
9
|
export class ChangeSetComputer {
|
|
10
|
+
em;
|
|
7
11
|
collectionUpdates;
|
|
12
|
+
comparator;
|
|
8
13
|
metadata;
|
|
9
14
|
platform;
|
|
10
15
|
config;
|
|
11
|
-
em
|
|
12
|
-
comparator;
|
|
13
|
-
constructor(collectionUpdates, metadata, platform, config, em) {
|
|
14
|
-
this.collectionUpdates = collectionUpdates;
|
|
15
|
-
this.metadata = metadata;
|
|
16
|
-
this.platform = platform;
|
|
17
|
-
this.config = config;
|
|
16
|
+
constructor(em, collectionUpdates) {
|
|
18
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();
|
|
19
22
|
this.comparator = this.config.getComparator(this.metadata);
|
|
20
23
|
}
|
|
21
24
|
computeChangeSet(entity) {
|
|
22
|
-
const meta = this.metadata.get(entity.constructor
|
|
25
|
+
const meta = this.metadata.get(entity.constructor);
|
|
23
26
|
if (meta.readonly) {
|
|
24
27
|
return null;
|
|
25
28
|
}
|
|
@@ -91,7 +94,7 @@ export class ChangeSetComputer {
|
|
|
91
94
|
computePayload(entity, ignoreUndefined = false) {
|
|
92
95
|
const data = this.comparator.prepareEntity(entity);
|
|
93
96
|
const wrapped = helper(entity);
|
|
94
|
-
const entityName = wrapped.__meta.
|
|
97
|
+
const entityName = wrapped.__meta.class;
|
|
95
98
|
const originalEntityData = wrapped.__originalEntityData;
|
|
96
99
|
if (!wrapped.__initialized) {
|
|
97
100
|
for (const prop of wrapped.__meta.primaryKeys) {
|
|
@@ -130,9 +133,25 @@ export class ChangeSetComputer {
|
|
|
130
133
|
return;
|
|
131
134
|
}
|
|
132
135
|
const targets = Utils.unwrapProperty(changeSet.entity, changeSet.meta, prop);
|
|
133
|
-
targets.forEach(([
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
targets.forEach(([rawTarget, idx]) => {
|
|
137
|
+
const target = Reference.unwrapReference(rawTarget);
|
|
138
|
+
const needsProcessing = target != null && (prop.targetKey != null || !target.__helper.hasPrimaryKey());
|
|
139
|
+
if (needsProcessing) {
|
|
140
|
+
let value = prop.targetKey ? target[prop.targetKey] : target.__helper.__identifier;
|
|
141
|
+
/* v8 ignore next */
|
|
142
|
+
if (prop.targetKey && prop.targetMeta) {
|
|
143
|
+
const targetProp = prop.targetMeta.properties[prop.targetKey];
|
|
144
|
+
if (targetProp?.customType) {
|
|
145
|
+
value = targetProp.customType.convertToDatabaseValue(value, this.platform, { mode: 'serialization' });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (prop.polymorphic) {
|
|
149
|
+
const discriminator = QueryHelper.findDiscriminatorValue(prop.discriminatorMap, target.constructor);
|
|
150
|
+
Utils.setPayloadProperty(changeSet.payload, changeSet.meta, prop, new PolymorphicRef(discriminator, value), idx);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
Utils.setPayloadProperty(changeSet.payload, changeSet.meta, prop, value, idx);
|
|
154
|
+
}
|
|
136
155
|
}
|
|
137
156
|
});
|
|
138
157
|
}
|
|
@@ -145,7 +164,9 @@ export class ChangeSetComputer {
|
|
|
145
164
|
this.collectionUpdates.add(target);
|
|
146
165
|
}
|
|
147
166
|
if (prop.owner && !this.platform.usesPivotTable()) {
|
|
148
|
-
changeSet.payload[prop.name] = target.getItems(false).map((item) =>
|
|
167
|
+
changeSet.payload[prop.name] = target.getItems(false).map((item) => {
|
|
168
|
+
return item.__helper.__identifier ?? item.__helper.getPrimaryKey();
|
|
169
|
+
});
|
|
149
170
|
}
|
|
150
171
|
}
|
|
151
172
|
}
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { Dictionary, EntityDictionary, EntityMetadata, IHydrator } from '../typings.js';
|
|
3
|
-
import { type EntityFactory } from '../entity/EntityFactory.js';
|
|
1
|
+
import type { Dictionary, EntityDictionary, EntityMetadata } from '../typings.js';
|
|
4
2
|
import { type ChangeSet } from './ChangeSet.js';
|
|
5
|
-
import {
|
|
6
|
-
import type { DriverMethodOptions, IDatabaseDriver } from '../drivers/IDatabaseDriver.js';
|
|
3
|
+
import type { DriverMethodOptions } from '../drivers/IDatabaseDriver.js';
|
|
7
4
|
import type { EntityManager } from '../EntityManager.js';
|
|
8
5
|
export declare class ChangeSetPersister {
|
|
6
|
+
private readonly em;
|
|
7
|
+
private readonly platform;
|
|
8
|
+
private readonly comparator;
|
|
9
|
+
private readonly usesReturningStatement;
|
|
9
10
|
private readonly driver;
|
|
10
11
|
private readonly metadata;
|
|
11
12
|
private readonly hydrator;
|
|
12
13
|
private readonly factory;
|
|
13
14
|
private readonly config;
|
|
14
|
-
|
|
15
|
-
private readonly platform;
|
|
16
|
-
private readonly comparator;
|
|
17
|
-
private readonly usesReturningStatement;
|
|
18
|
-
constructor(driver: IDatabaseDriver, metadata: MetadataStorage, hydrator: IHydrator, factory: EntityFactory, config: Configuration, em: EntityManager);
|
|
15
|
+
constructor(em: EntityManager);
|
|
19
16
|
executeInserts<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
|
|
20
17
|
executeUpdates<T extends object>(changeSets: ChangeSet<T>[], batched: boolean, options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
|
|
21
18
|
executeDeletes<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
|
|
@@ -43,6 +40,11 @@ export declare class ChangeSetPersister {
|
|
|
43
40
|
* so we use a single query in case of both versioning and default values is used.
|
|
44
41
|
*/
|
|
45
42
|
private reloadVersionValues;
|
|
43
|
+
/**
|
|
44
|
+
* For TPT child tables, resolve EntityIdentifier values in PK fields.
|
|
45
|
+
* The parent table insert assigns the actual PK value, which the child table references.
|
|
46
|
+
*/
|
|
47
|
+
private resolveTPTIdentifiers;
|
|
46
48
|
private processProperty;
|
|
47
49
|
/**
|
|
48
50
|
* Maps values returned via `returning` statement (postgres) or the inserted id (other sql drivers).
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EntityIdentifier } from '../entity/EntityIdentifier.js';
|
|
2
|
+
import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
2
3
|
import { helper } from '../entity/wrap.js';
|
|
3
4
|
import { ChangeSetType } from './ChangeSet.js';
|
|
4
5
|
import { isRaw } from '../utils/RawQueryFragment.js';
|
|
@@ -6,23 +7,23 @@ import { Utils } from '../utils/Utils.js';
|
|
|
6
7
|
import { OptimisticLockError, ValidationError } from '../errors.js';
|
|
7
8
|
import { ReferenceKind } from '../enums.js';
|
|
8
9
|
export class ChangeSetPersister {
|
|
10
|
+
em;
|
|
11
|
+
platform;
|
|
12
|
+
comparator;
|
|
13
|
+
usesReturningStatement;
|
|
9
14
|
driver;
|
|
10
15
|
metadata;
|
|
11
16
|
hydrator;
|
|
12
17
|
factory;
|
|
13
18
|
config;
|
|
14
|
-
em
|
|
15
|
-
platform;
|
|
16
|
-
comparator;
|
|
17
|
-
usesReturningStatement;
|
|
18
|
-
constructor(driver, metadata, hydrator, factory, config, em) {
|
|
19
|
-
this.driver = driver;
|
|
20
|
-
this.metadata = metadata;
|
|
21
|
-
this.hydrator = hydrator;
|
|
22
|
-
this.factory = factory;
|
|
23
|
-
this.config = config;
|
|
19
|
+
constructor(em) {
|
|
24
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
25
|
this.platform = this.driver.getPlatform();
|
|
26
|
+
this.hydrator = this.config.getHydrator(this.metadata);
|
|
26
27
|
this.comparator = this.config.getComparator(this.metadata);
|
|
27
28
|
this.usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
|
|
28
29
|
}
|
|
@@ -30,7 +31,7 @@ export class ChangeSetPersister {
|
|
|
30
31
|
if (!withSchema) {
|
|
31
32
|
return this.runForEachSchema(changeSets, 'executeInserts', options);
|
|
32
33
|
}
|
|
33
|
-
const meta =
|
|
34
|
+
const meta = changeSets[0].meta;
|
|
34
35
|
changeSets.forEach(changeSet => this.processProperties(changeSet));
|
|
35
36
|
if (changeSets.length > 1 && this.config.get('useBatchInserts', this.platform.usesBatchInserts())) {
|
|
36
37
|
return this.persistNewEntities(meta, changeSets, options);
|
|
@@ -43,9 +44,12 @@ export class ChangeSetPersister {
|
|
|
43
44
|
if (!withSchema) {
|
|
44
45
|
return this.runForEachSchema(changeSets, 'executeUpdates', options, batched);
|
|
45
46
|
}
|
|
46
|
-
const meta =
|
|
47
|
+
const meta = changeSets[0].meta;
|
|
47
48
|
changeSets.forEach(changeSet => this.processProperties(changeSet));
|
|
48
|
-
|
|
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 && changeSets.length > 1 && !hasMixedTypes && this.config.get('useBatchUpdates', this.platform.usesBatchUpdates())) {
|
|
49
53
|
return this.persistManagedEntities(meta, changeSets, options);
|
|
50
54
|
}
|
|
51
55
|
for (const changeSet of changeSets) {
|
|
@@ -63,7 +67,7 @@ export class ChangeSetPersister {
|
|
|
63
67
|
const chunk = changeSets.slice(i, i + size);
|
|
64
68
|
const pks = chunk.map(cs => cs.getPrimaryKey());
|
|
65
69
|
options = this.prepareOptions(meta, options);
|
|
66
|
-
await this.driver.nativeDelete(meta.root.
|
|
70
|
+
await this.driver.nativeDelete(meta.root.class, { [pk]: { $in: pks } }, options);
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
async runForEachSchema(changeSets, method, options, ...args) {
|
|
@@ -99,7 +103,7 @@ export class ChangeSetPersister {
|
|
|
99
103
|
}
|
|
100
104
|
}
|
|
101
105
|
processProperties(changeSet) {
|
|
102
|
-
const meta =
|
|
106
|
+
const meta = changeSet.meta;
|
|
103
107
|
for (const prop of meta.relations) {
|
|
104
108
|
this.processProperty(changeSet, prop);
|
|
105
109
|
}
|
|
@@ -112,7 +116,9 @@ export class ChangeSetPersister {
|
|
|
112
116
|
options = this.prepareOptions(meta, options, {
|
|
113
117
|
convertCustomTypes: false,
|
|
114
118
|
});
|
|
115
|
-
|
|
119
|
+
this.resolveTPTIdentifiers(changeSet);
|
|
120
|
+
// Use changeSet's own meta for STI entities to get correct field mappings
|
|
121
|
+
const res = await this.driver.nativeInsertMany(changeSet.meta.class, [changeSet.payload], options);
|
|
116
122
|
if (!wrapped.hasPrimaryKey()) {
|
|
117
123
|
this.mapPrimaryKey(meta, res.insertId, changeSet);
|
|
118
124
|
}
|
|
@@ -149,7 +155,10 @@ export class ChangeSetPersister {
|
|
|
149
155
|
convertCustomTypes: false,
|
|
150
156
|
processCollections: false,
|
|
151
157
|
});
|
|
152
|
-
const
|
|
158
|
+
for (const changeSet of changeSets) {
|
|
159
|
+
this.resolveTPTIdentifiers(changeSet);
|
|
160
|
+
}
|
|
161
|
+
const res = await this.driver.nativeInsertMany(meta.class, changeSets.map(cs => cs.payload), options);
|
|
153
162
|
for (let i = 0; i < changeSets.length; i++) {
|
|
154
163
|
const changeSet = changeSets[i];
|
|
155
164
|
const wrapped = helper(changeSet.entity);
|
|
@@ -167,7 +176,7 @@ export class ChangeSetPersister {
|
|
|
167
176
|
}
|
|
168
177
|
}
|
|
169
178
|
async persistManagedEntity(changeSet, options) {
|
|
170
|
-
const meta =
|
|
179
|
+
const meta = changeSet.meta;
|
|
171
180
|
const res = await this.updateEntity(meta, changeSet, options);
|
|
172
181
|
this.checkOptimisticLock(meta, changeSet, res);
|
|
173
182
|
this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
|
|
@@ -208,7 +217,7 @@ export class ChangeSetPersister {
|
|
|
208
217
|
cond.push(where);
|
|
209
218
|
payload.push(changeSet.payload);
|
|
210
219
|
}
|
|
211
|
-
const res = await this.driver.nativeUpdateMany(meta.
|
|
220
|
+
const res = await this.driver.nativeUpdateMany(meta.class, cond, payload, options);
|
|
212
221
|
const map = new Map();
|
|
213
222
|
res.rows?.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));
|
|
214
223
|
for (const changeSet of changeSets) {
|
|
@@ -260,13 +269,13 @@ export class ChangeSetPersister {
|
|
|
260
269
|
convertCustomTypes: false,
|
|
261
270
|
});
|
|
262
271
|
if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)) {
|
|
263
|
-
return this.driver.nativeUpdate(changeSet.
|
|
272
|
+
return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
|
|
264
273
|
}
|
|
265
274
|
if (meta.versionProperty) {
|
|
266
275
|
cond[meta.versionProperty] = this.platform.quoteVersionValue(changeSet.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
|
|
267
276
|
}
|
|
268
277
|
this.checkConcurrencyKeys(meta, changeSet, cond);
|
|
269
|
-
return this.driver.nativeUpdate(changeSet.
|
|
278
|
+
return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
|
|
270
279
|
}
|
|
271
280
|
async checkOptimisticLocks(meta, changeSets, options) {
|
|
272
281
|
if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSets.every(cs => cs.entity[meta.versionProperty] == null))) {
|
|
@@ -286,7 +295,7 @@ export class ChangeSetPersister {
|
|
|
286
295
|
options = this.prepareOptions(meta, options, {
|
|
287
296
|
fields: primaryKeys,
|
|
288
297
|
});
|
|
289
|
-
const res = await this.driver.find(meta.root.
|
|
298
|
+
const res = await this.driver.find(meta.root.class, { $or }, options);
|
|
290
299
|
if (res.length !== changeSets.length) {
|
|
291
300
|
const compare = (a, b, keys) => keys.every(k => a[k] === b[k]);
|
|
292
301
|
const entity = changeSets.find(cs => {
|
|
@@ -356,7 +365,7 @@ export class ChangeSetPersister {
|
|
|
356
365
|
options = this.prepareOptions(meta, options, {
|
|
357
366
|
fields: Utils.unique(reloadProps.map(prop => prop.name)),
|
|
358
367
|
});
|
|
359
|
-
const data = await this.driver.find(meta.
|
|
368
|
+
const data = await this.driver.find(meta.class, { [pk]: { $in: pks } }, options);
|
|
360
369
|
const map = new Map();
|
|
361
370
|
data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.platform, true), item));
|
|
362
371
|
for (const changeSet of changeSets) {
|
|
@@ -365,13 +374,34 @@ export class ChangeSetPersister {
|
|
|
365
374
|
Object.assign(changeSet.payload, data); // merge to the changeset payload, so it gets saved to the entity snapshot
|
|
366
375
|
}
|
|
367
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* For TPT child tables, resolve EntityIdentifier values in PK fields.
|
|
379
|
+
* The parent table insert assigns the actual PK value, which the child table references.
|
|
380
|
+
*/
|
|
381
|
+
resolveTPTIdentifiers(changeSet) {
|
|
382
|
+
if (changeSet.meta.inheritanceType !== 'tpt' || !changeSet.meta.tptParent) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
for (const pk of changeSet.meta.primaryKeys) {
|
|
386
|
+
const value = changeSet.payload[pk];
|
|
387
|
+
if (value instanceof EntityIdentifier) {
|
|
388
|
+
changeSet.payload[pk] = value.getValue();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
368
392
|
processProperty(changeSet, prop) {
|
|
369
|
-
const meta =
|
|
393
|
+
const meta = changeSet.meta;
|
|
370
394
|
const value = changeSet.payload[prop.name]; // for inline embeddables
|
|
371
395
|
if (value instanceof EntityIdentifier) {
|
|
372
396
|
changeSet.payload[prop.name] = value.getValue();
|
|
373
397
|
return;
|
|
374
398
|
}
|
|
399
|
+
if (value instanceof PolymorphicRef) {
|
|
400
|
+
if (value.id instanceof EntityIdentifier) {
|
|
401
|
+
value.id = value.id.getValue();
|
|
402
|
+
}
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
375
405
|
if (Array.isArray(value) && value.every(item => item instanceof EntityIdentifier)) {
|
|
376
406
|
changeSet.payload[prop.name] = value.map(item => item.getValue());
|
|
377
407
|
return;
|
|
@@ -399,7 +429,7 @@ export class ChangeSetPersister {
|
|
|
399
429
|
if ((!this.usesReturningStatement && !upsert) || !row || !Utils.hasObjectKeys(row)) {
|
|
400
430
|
return;
|
|
401
431
|
}
|
|
402
|
-
const mapped = this.comparator.mapResult(meta
|
|
432
|
+
const mapped = this.comparator.mapResult(meta, row);
|
|
403
433
|
if (entity) {
|
|
404
434
|
this.hydrator.hydrate(entity, meta, mapped, this.factory, 'full', false, true);
|
|
405
435
|
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EntityProperty } from '../typings.js';
|
|
2
2
|
export declare const enum NodeState {
|
|
3
3
|
NOT_VISITED = 0,
|
|
4
4
|
IN_PROGRESS = 1,
|
|
5
5
|
VISITED = 2
|
|
6
6
|
}
|
|
7
|
+
type Hash = number;
|
|
7
8
|
export interface Node {
|
|
8
|
-
hash:
|
|
9
|
+
hash: Hash;
|
|
9
10
|
state: NodeState;
|
|
10
|
-
dependencies:
|
|
11
|
+
dependencies: Map<Hash, Edge>;
|
|
11
12
|
}
|
|
12
13
|
export interface Edge {
|
|
13
|
-
from:
|
|
14
|
-
to:
|
|
14
|
+
from: Hash;
|
|
15
|
+
to: Hash;
|
|
15
16
|
weight: number;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
@@ -32,23 +33,23 @@ export declare class CommitOrderCalculator {
|
|
|
32
33
|
/**
|
|
33
34
|
* Checks for node existence in graph.
|
|
34
35
|
*/
|
|
35
|
-
hasNode(hash:
|
|
36
|
+
hasNode(hash: Hash): boolean;
|
|
36
37
|
/**
|
|
37
38
|
* Adds a new node to the graph, assigning its hash.
|
|
38
39
|
*/
|
|
39
|
-
addNode(hash:
|
|
40
|
+
addNode(hash: Hash): void;
|
|
40
41
|
/**
|
|
41
42
|
* Adds a new dependency (edge) to the graph using their hashes.
|
|
42
43
|
*/
|
|
43
|
-
addDependency(from:
|
|
44
|
-
discoverProperty(prop: EntityProperty, entityName:
|
|
44
|
+
addDependency(from: Hash, to: Hash, weight: number): void;
|
|
45
|
+
discoverProperty(prop: EntityProperty, entityName: Hash): void;
|
|
45
46
|
/**
|
|
46
47
|
* Return a valid order list of all current nodes.
|
|
47
48
|
* The desired topological sorting is the reverse post order of these searches.
|
|
48
49
|
*
|
|
49
50
|
* @internal Highly performance-sensitive method.
|
|
50
51
|
*/
|
|
51
|
-
sort():
|
|
52
|
+
sort(): Hash[];
|
|
52
53
|
/**
|
|
53
54
|
* Visit a given node definition for reordering.
|
|
54
55
|
*
|
|
@@ -60,3 +61,4 @@ export declare class CommitOrderCalculator {
|
|
|
60
61
|
*/
|
|
61
62
|
private visitOpenNode;
|
|
62
63
|
}
|
|
64
|
+
export {};
|