@mikro-orm/core 7.1.9-dev.2 → 7.1.9-dev.21
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/hydration/ObjectHydrator.js +3 -0
- package/naming-strategy/AbstractNamingStrategy.js +2 -1
- package/naming-strategy/NamingStrategy.d.ts +2 -1
- package/package.json +1 -1
- package/unit-of-work/ChangeSet.js +10 -7
- package/unit-of-work/ChangeSetPersister.js +15 -7
- package/utils/Configuration.js +2 -1
- package/utils/EntityComparator.js +1 -0
- package/utils/Utils.js +4 -2
|
@@ -364,6 +364,9 @@ export class ObjectHydrator extends Hydrator {
|
|
|
364
364
|
ret.push(` data${dataKey}.forEach((_, idx_${idx}) => {`);
|
|
365
365
|
ret.push(...hydrateEmbedded(prop, [...path, `[idx_${idx}]`], `${dataKey}[idx_${idx}]`).map(l => ' ' + l));
|
|
366
366
|
ret.push(` });`);
|
|
367
|
+
ret.push(` } else if (data${dataKey} === null) {`);
|
|
368
|
+
/* v8 ignore next */
|
|
369
|
+
ret.push(` entity${entityKey} = ${this.config.get('forceUndefined') ? 'undefined' : 'null'};`);
|
|
367
370
|
ret.push(` }`);
|
|
368
371
|
return ret;
|
|
369
372
|
};
|
|
@@ -10,7 +10,8 @@ export class AbstractNamingStrategy {
|
|
|
10
10
|
classToMigrationName(timestamp, customMigrationName) {
|
|
11
11
|
let migrationName = `Migration${timestamp}`;
|
|
12
12
|
if (customMigrationName) {
|
|
13
|
-
|
|
13
|
+
// the name becomes part of a class identifier
|
|
14
|
+
migrationName += `_${customMigrationName.replace(/[^$\p{ID_Continue}]+/gu, '_')}`;
|
|
14
15
|
}
|
|
15
16
|
return migrationName;
|
|
16
17
|
}
|
|
@@ -9,7 +9,8 @@ export interface NamingStrategy {
|
|
|
9
9
|
*/
|
|
10
10
|
classToTableName(entityName: string, tableName?: string): string;
|
|
11
11
|
/**
|
|
12
|
-
* Return a migration name. This name should allow ordering
|
|
12
|
+
* Return a migration name. This name should allow ordering, and has to be a valid class identifier,
|
|
13
|
+
* as it is used as the class name in the generated migration file.
|
|
13
14
|
*/
|
|
14
15
|
classToMigrationName(timestamp: string, customMigrationName?: string): string;
|
|
15
16
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "7.1.9-dev.
|
|
3
|
+
"version": "7.1.9-dev.21",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
|
@@ -29,15 +29,18 @@ export class ChangeSet {
|
|
|
29
29
|
else {
|
|
30
30
|
this.primaryKey = this.originalEntity[this.meta.primaryKeys[0]];
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
});
|
|
32
|
+
const primaryProp = this.meta.getPrimaryProp();
|
|
33
|
+
const relationPK = !this.meta.compositePK && !!primaryProp.targetMeta?.compositePK;
|
|
34
|
+
// arrays are already the ordered tuple of the target's primary keys
|
|
35
|
+
if (relationPK && Utils.isPlainObject(this.primaryKey)) {
|
|
36
|
+
const pk = this.primaryKey;
|
|
37
|
+
this.primaryKey = primaryProp.targetMeta.primaryKeys.map(childPK => pk[childPK]);
|
|
39
38
|
}
|
|
40
39
|
if (object && this.primaryKey != null) {
|
|
40
|
+
// the whole tuple belongs to the single relation PK, it must not be spread over the (single) PK prop
|
|
41
|
+
if (relationPK) {
|
|
42
|
+
return { [primaryProp.name]: this.primaryKey };
|
|
43
|
+
}
|
|
41
44
|
return Utils.primaryKeyToObject(this.meta, this.primaryKey);
|
|
42
45
|
}
|
|
43
46
|
return this.primaryKey ?? null;
|
|
@@ -3,7 +3,7 @@ import { PolymorphicRef } from '../entity/PolymorphicRef.js';
|
|
|
3
3
|
import { helper } from '../entity/wrap.js';
|
|
4
4
|
import { ChangeSetType } from './ChangeSet.js';
|
|
5
5
|
import { isRaw } from '../utils/RawQueryFragment.js';
|
|
6
|
-
import { Utils } from '../utils/Utils.js';
|
|
6
|
+
import { equals, Utils } from '../utils/Utils.js';
|
|
7
7
|
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. */
|
|
@@ -230,11 +230,15 @@ export class ChangeSetPersister {
|
|
|
230
230
|
}
|
|
231
231
|
const res = await this.#driver.nativeUpdateMany(meta.class, cond, payload, options);
|
|
232
232
|
const map = new Map();
|
|
233
|
-
|
|
233
|
+
// returning rows are not mapped yet, so they are keyed by field names - we need to build the hash
|
|
234
|
+
// from those to be able to match them with `getSerializedPrimaryKey()` of the entity
|
|
235
|
+
const pkFields = meta.getPrimaryProps().flatMap(prop => prop.fieldNames);
|
|
236
|
+
res.rows?.forEach(item => map.set(Utils.getPrimaryKeyHash(pkFields.map(field => item[field])), item));
|
|
234
237
|
for (const changeSet of changeSets) {
|
|
235
238
|
if (res.rows) {
|
|
236
239
|
const row = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
|
|
237
|
-
|
|
240
|
+
// STI batches can mix child types, so map through the change set's own metadata
|
|
241
|
+
this.mapReturnedValues(changeSet.entity, changeSet.payload, row, changeSet.meta);
|
|
238
242
|
}
|
|
239
243
|
changeSet.persisted = true;
|
|
240
244
|
}
|
|
@@ -334,7 +338,8 @@ export class ChangeSetPersister {
|
|
|
334
338
|
});
|
|
335
339
|
const res = await this.#driver.find(meta.root.class, { $or }, options);
|
|
336
340
|
if (res.length !== changeSets.length) {
|
|
337
|
-
|
|
341
|
+
// a FK pointing to a composite PK is an array, so the values need to be compared deeply
|
|
342
|
+
const compare = (a, b, keys) => keys.every(k => equals(a[k], b[k]));
|
|
338
343
|
const entity = changeSets.find(cs => {
|
|
339
344
|
return !res.some(row => compare(Utils.getPrimaryKeyCond(cs.entity, primaryKeys), row, primaryKeys));
|
|
340
345
|
}).entity;
|
|
@@ -375,7 +380,8 @@ export class ChangeSetPersister {
|
|
|
375
380
|
changeSets.forEach(cs => {
|
|
376
381
|
Utils.keys(cs.payload).forEach(k => {
|
|
377
382
|
if (isRaw(cs.payload[k]) && isRaw(cs.entity[k])) {
|
|
378
|
-
|
|
383
|
+
// STI batches can mix child types, so the property might not exist on `meta`
|
|
384
|
+
returning.add(cs.meta.properties[k]);
|
|
379
385
|
}
|
|
380
386
|
});
|
|
381
387
|
});
|
|
@@ -400,12 +406,14 @@ export class ChangeSetPersister {
|
|
|
400
406
|
options = this.prepareOptions(meta, options, {
|
|
401
407
|
fields: Utils.unique(reloadProps.map(prop => prop.name)),
|
|
402
408
|
});
|
|
403
|
-
|
|
409
|
+
// a mixed STI batch shares one table but the child discriminator would filter out the siblings
|
|
410
|
+
const target = changeSets.some(cs => cs.meta !== meta) && meta.root.discriminatorColumn ? meta.root : meta;
|
|
411
|
+
const data = await this.#driver.find(target.class, { [pk]: { $in: pks } }, options);
|
|
404
412
|
const map = new Map();
|
|
405
413
|
data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.#platform, true), item));
|
|
406
414
|
for (const changeSet of changeSets) {
|
|
407
415
|
const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
|
|
408
|
-
this.#hydrator.hydrate(changeSet.entity, meta, data, this.#factory, 'full', false, true);
|
|
416
|
+
this.#hydrator.hydrate(changeSet.entity, changeSet.meta, data, this.#factory, 'full', false, true);
|
|
409
417
|
Object.assign(changeSet.payload, data); // merge to the changeset payload, so it gets saved to the entity snapshot
|
|
410
418
|
}
|
|
411
419
|
}
|
package/utils/Configuration.js
CHANGED
|
@@ -85,7 +85,8 @@ const DEFAULTS = {
|
|
|
85
85
|
safe: false,
|
|
86
86
|
snapshot: true,
|
|
87
87
|
emit: 'ts',
|
|
88
|
-
|
|
88
|
+
// mirrors `NamingStrategy.classToMigrationName`, so the file name matches the class it declares
|
|
89
|
+
fileName: (timestamp, name) => `Migration${timestamp}${name ? '_' + name.replace(/[^$\p{ID_Continue}]+/gu, '_') : ''}`,
|
|
89
90
|
},
|
|
90
91
|
schemaGenerator: {
|
|
91
92
|
createForeignKeyConstraints: true,
|
|
@@ -447,6 +447,7 @@ export class EntityComparator {
|
|
|
447
447
|
const ret = [];
|
|
448
448
|
const padding = ' '.repeat(level * 2);
|
|
449
449
|
const idx = this.#tmpIndex++;
|
|
450
|
+
ret.push(`${padding}if (entity${entityKey} === null) ret${dataKey} = null;`);
|
|
450
451
|
ret.push(`${padding}if (Array.isArray(entity${entityKey})) {`);
|
|
451
452
|
ret.push(`${padding} ret${dataKey} = [];`);
|
|
452
453
|
ret.push(`${padding} entity${entityKey}.forEach((_, idx_${idx}) => {`);
|
package/utils/Utils.js
CHANGED
|
@@ -153,7 +153,7 @@ export function parseJsonSafe(value) {
|
|
|
153
153
|
/** Collection of general-purpose utility methods used throughout the ORM. */
|
|
154
154
|
export class Utils {
|
|
155
155
|
static PK_SEPARATOR = '~~~';
|
|
156
|
-
static #ORM_VERSION = '7.1.9-dev.
|
|
156
|
+
static #ORM_VERSION = '7.1.9-dev.21';
|
|
157
157
|
/**
|
|
158
158
|
* Checks if the argument is instance of `Object`. Returns false for arrays.
|
|
159
159
|
*/
|
|
@@ -462,7 +462,9 @@ export class Utils {
|
|
|
462
462
|
}
|
|
463
463
|
static getPrimaryKeyCond(entity, primaryKeys) {
|
|
464
464
|
const cond = primaryKeys.reduce((o, pk) => {
|
|
465
|
-
|
|
465
|
+
const value = entity[pk];
|
|
466
|
+
// FKs pointing to a composite PK are arrays, which `extractPK` rejects
|
|
467
|
+
o[pk] = Utils.isPrimaryKey(value, true) ? value : Utils.extractPK(value);
|
|
466
468
|
return o;
|
|
467
469
|
}, {});
|
|
468
470
|
if (Object.values(cond).some(v => v === null)) {
|