@mikro-orm/core 7.0.0-dev.22 → 7.0.0-dev.24
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 +12 -2
- package/EntityManager.js +40 -53
- package/README.md +1 -2
- package/connections/Connection.d.ts +4 -2
- package/connections/Connection.js +2 -2
- package/decorators/Entity.d.ts +15 -0
- package/decorators/Indexed.d.ts +2 -2
- package/decorators/ManyToMany.d.ts +2 -0
- package/decorators/ManyToOne.d.ts +2 -0
- package/decorators/OneToOne.d.ts +2 -0
- package/decorators/Transactional.d.ts +1 -0
- package/decorators/Transactional.js +3 -3
- package/drivers/IDatabaseDriver.d.ts +4 -0
- package/entity/ArrayCollection.d.ts +3 -1
- package/entity/ArrayCollection.js +7 -2
- package/entity/Collection.js +3 -2
- package/entity/EntityFactory.d.ts +6 -0
- package/entity/EntityFactory.js +17 -6
- package/entity/EntityHelper.js +5 -1
- package/entity/EntityLoader.js +27 -19
- package/entity/Reference.d.ts +5 -0
- package/entity/Reference.js +16 -0
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +537 -0
- package/entity/defineEntity.js +690 -0
- package/entity/index.d.ts +2 -0
- package/entity/index.js +2 -0
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +15 -3
- package/enums.d.ts +15 -2
- package/enums.js +13 -0
- package/errors.d.ts +6 -0
- package/errors.js +14 -0
- package/hydration/ObjectHydrator.js +1 -1
- package/index.d.ts +1 -1
- package/metadata/EntitySchema.js +10 -2
- package/metadata/MetadataDiscovery.d.ts +0 -1
- package/metadata/MetadataDiscovery.js +27 -18
- package/package.json +3 -3
- package/platforms/Platform.d.ts +3 -1
- package/serialization/SerializationContext.js +13 -10
- package/types/BooleanType.d.ts +1 -1
- package/types/DecimalType.js +1 -1
- package/types/DoubleType.js +1 -1
- package/typings.d.ts +32 -10
- package/typings.js +21 -4
- package/unit-of-work/ChangeSetComputer.js +3 -1
- package/unit-of-work/ChangeSetPersister.d.ts +4 -2
- package/unit-of-work/ChangeSetPersister.js +14 -10
- package/unit-of-work/UnitOfWork.d.ts +2 -1
- package/unit-of-work/UnitOfWork.js +36 -13
- package/utils/Configuration.d.ts +7 -1
- package/utils/Configuration.js +1 -0
- package/utils/ConfigurationLoader.js +2 -2
- package/utils/Cursor.js +3 -0
- package/utils/DataloaderUtils.d.ts +6 -1
- package/utils/DataloaderUtils.js +37 -20
- package/utils/EntityComparator.d.ts +6 -2
- package/utils/EntityComparator.js +29 -8
- package/utils/QueryHelper.d.ts +6 -0
- package/utils/QueryHelper.js +47 -4
- package/utils/RawQueryFragment.d.ts +34 -0
- package/utils/RawQueryFragment.js +35 -1
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +199 -0
- package/utils/Utils.d.ts +2 -2
- package/utils/Utils.js +31 -7
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/upsert-utils.js +9 -1
package/EntityManager.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
33
33
|
readonly name: string;
|
|
34
34
|
protected readonly refLoader: DataLoader<[Reference<any>, (Omit<import("./entity/Reference.js").LoadReferenceOptions<any, any, "*", never>, "dataloader"> | undefined)?], any, [Reference<any>, (Omit<import("./entity/Reference.js").LoadReferenceOptions<any, any, "*", never>, "dataloader"> | undefined)?]>;
|
|
35
35
|
protected readonly colLoader: DataLoader<[import("./index.js").Collection<any, object>, (Omit<import("./index.js").InitCollectionOptions<any, any, "*", never>, "dataloader"> | undefined)?], any, [import("./index.js").Collection<any, object>, (Omit<import("./index.js").InitCollectionOptions<any, any, "*", never>, "dataloader"> | undefined)?]>;
|
|
36
|
+
protected readonly colLoaderMtoN: DataLoader<[import("./index.js").Collection<any, object>, (Omit<import("./index.js").InitCollectionOptions<any, any, "*", never>, "dataloader"> | undefined)?], any, [import("./index.js").Collection<any, object>, (Omit<import("./index.js").InitCollectionOptions<any, any, "*", never>, "dataloader"> | undefined)?]>;
|
|
36
37
|
private readonly validator;
|
|
37
38
|
private readonly repositoryMap;
|
|
38
39
|
private readonly entityLoader;
|
|
@@ -117,7 +118,9 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
117
118
|
/**
|
|
118
119
|
* Gets logger context for this entity manager.
|
|
119
120
|
*/
|
|
120
|
-
getLoggerContext<T extends Dictionary = Dictionary>(
|
|
121
|
+
getLoggerContext<T extends Dictionary = Dictionary>(options?: {
|
|
122
|
+
disableContextResolution?: boolean;
|
|
123
|
+
}): T;
|
|
121
124
|
setFlushMode(flushMode?: FlushMode): void;
|
|
122
125
|
protected processWhere<Entity extends object, Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(entityName: string, where: FilterQuery<Entity>, options: FindOptions<Entity, Hint, Fields, Excludes> | FindOneOptions<Entity, Hint, Fields, Excludes>, type: 'read' | 'update' | 'delete'): Promise<FilterQuery<Entity>>;
|
|
123
126
|
protected applyDiscriminatorCondition<Entity extends object>(entityName: string, where: FilterQuery<Entity>): FilterQuery<Entity>;
|
|
@@ -487,7 +490,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
487
490
|
* some additional lazy properties, if so, we reload and merge the data from database
|
|
488
491
|
*/
|
|
489
492
|
protected shouldRefresh<T extends object, P extends string = never, F extends string = '*', E extends string = never>(meta: EntityMetadata<T>, entity: T, options: FindOneOptions<T, P, F, E>): boolean;
|
|
490
|
-
protected prepareOptions(options: FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any>): void;
|
|
493
|
+
protected prepareOptions(options: FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any> | CountOptions<any, any>): void;
|
|
491
494
|
/**
|
|
492
495
|
* @internal
|
|
493
496
|
*/
|
|
@@ -547,11 +550,18 @@ export interface CreateOptions<Convert extends boolean> {
|
|
|
547
550
|
partial?: boolean;
|
|
548
551
|
/** convert raw database values based on mapped types (by default, already converted values are expected) */
|
|
549
552
|
convertCustomTypes?: Convert;
|
|
553
|
+
/**
|
|
554
|
+
* Property `onCreate` hooks are normally executed during `flush` operation.
|
|
555
|
+
* With this option, they will be processed early inside `em.create()` method.
|
|
556
|
+
*/
|
|
557
|
+
processOnCreateHooksEarly?: boolean;
|
|
550
558
|
}
|
|
551
559
|
export interface MergeOptions {
|
|
552
560
|
refresh?: boolean;
|
|
553
561
|
convertCustomTypes?: boolean;
|
|
554
562
|
schema?: string;
|
|
563
|
+
disableContextResolution?: boolean;
|
|
564
|
+
keepIdentity?: boolean;
|
|
555
565
|
}
|
|
556
566
|
export interface ForkOptions {
|
|
557
567
|
/** do we want a clear identity map? defaults to true */
|
package/EntityManager.js
CHANGED
|
@@ -19,6 +19,8 @@ import { EventType, FlushMode, LoadStrategy, LockMode, PopulateHint, PopulatePat
|
|
|
19
19
|
import { EventManager } from './events/EventManager.js';
|
|
20
20
|
import { TransactionEventBroadcaster } from './events/TransactionEventBroadcaster.js';
|
|
21
21
|
import { OptimisticLockError, ValidationError } from './errors.js';
|
|
22
|
+
import { getLoadingStrategy } from './entity/utils.js';
|
|
23
|
+
import { TransactionManager } from './utils/TransactionManager.js';
|
|
22
24
|
/**
|
|
23
25
|
* The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
|
|
24
26
|
* such as UnitOfWork, Query Language, and Repository API.
|
|
@@ -36,6 +38,7 @@ export class EntityManager {
|
|
|
36
38
|
name;
|
|
37
39
|
refLoader = new DataLoader(DataloaderUtils.getRefBatchLoadFn(this));
|
|
38
40
|
colLoader = new DataLoader(DataloaderUtils.getColBatchLoadFn(this));
|
|
41
|
+
colLoaderMtoN = new DataLoader(DataloaderUtils.getManyToManyColBatchLoadFn(this));
|
|
39
42
|
validator;
|
|
40
43
|
repositoryMap = {};
|
|
41
44
|
entityLoader;
|
|
@@ -137,7 +140,6 @@ export class EntityManager {
|
|
|
137
140
|
await em.entityLoader.populate(entityName, cached.data, populate, {
|
|
138
141
|
...options,
|
|
139
142
|
...em.getPopulateWhere(where, options),
|
|
140
|
-
convertCustomTypes: false,
|
|
141
143
|
ignoreLazyScalarProperties: true,
|
|
142
144
|
lookup: false,
|
|
143
145
|
});
|
|
@@ -168,7 +170,6 @@ export class EntityManager {
|
|
|
168
170
|
await em.entityLoader.populate(entityName, unique, populate, {
|
|
169
171
|
...options,
|
|
170
172
|
...em.getPopulateWhere(where, options),
|
|
171
|
-
convertCustomTypes: false,
|
|
172
173
|
ignoreLazyScalarProperties: true,
|
|
173
174
|
lookup: false,
|
|
174
175
|
});
|
|
@@ -232,8 +233,8 @@ export class EntityManager {
|
|
|
232
233
|
/**
|
|
233
234
|
* Gets logger context for this entity manager.
|
|
234
235
|
*/
|
|
235
|
-
getLoggerContext() {
|
|
236
|
-
const em = this.getContext();
|
|
236
|
+
getLoggerContext(options) {
|
|
237
|
+
const em = options?.disableContextResolution ? this : this.getContext();
|
|
237
238
|
em.loggerContext ??= {};
|
|
238
239
|
return em.loggerContext;
|
|
239
240
|
}
|
|
@@ -289,7 +290,8 @@ export class EntityManager {
|
|
|
289
290
|
for (const hint of options.populate) {
|
|
290
291
|
const field = hint.field.split(':')[0];
|
|
291
292
|
const prop = meta.properties[field];
|
|
292
|
-
const
|
|
293
|
+
const strategy = getLoadingStrategy(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
294
|
+
const joined = strategy === LoadStrategy.JOINED && prop.kind !== ReferenceKind.SCALAR;
|
|
293
295
|
if (!joined && !hint.filter) {
|
|
294
296
|
continue;
|
|
295
297
|
}
|
|
@@ -326,10 +328,19 @@ export class EntityManager {
|
|
|
326
328
|
const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
|
|
327
329
|
if (!Utils.isEmpty(cond)) {
|
|
328
330
|
const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
|
|
331
|
+
let found = false;
|
|
329
332
|
if (populated.length > 0) {
|
|
330
|
-
|
|
333
|
+
for (const hint of populated) {
|
|
334
|
+
if (!hint.all) {
|
|
335
|
+
hint.filter = true;
|
|
336
|
+
found = true;
|
|
337
|
+
}
|
|
338
|
+
else if (hint.field === `${prop.name}:ref`) {
|
|
339
|
+
found = true;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
331
342
|
}
|
|
332
|
-
|
|
343
|
+
if (!found) {
|
|
333
344
|
ret.push({ field: `${prop.name}:ref`, strategy: LoadStrategy.JOINED, filter: true });
|
|
334
345
|
}
|
|
335
346
|
}
|
|
@@ -494,11 +505,17 @@ export class EntityManager {
|
|
|
494
505
|
...options,
|
|
495
506
|
flushMode: FlushMode.COMMIT,
|
|
496
507
|
});
|
|
508
|
+
const em = this.getContext();
|
|
497
509
|
if (reloaded) {
|
|
498
|
-
|
|
510
|
+
for (const e of fork.unitOfWork.getIdentityMap()) {
|
|
511
|
+
const ref = em.getReference(e.constructor.name, helper(e).getPrimaryKey());
|
|
512
|
+
const data = this.comparator.prepareEntity(e);
|
|
513
|
+
em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, helper(e).serialize({ ignoreSerializers: true }), em.entityFactory, 'full', false, true);
|
|
514
|
+
helper(ref).__originalEntityData = data;
|
|
515
|
+
}
|
|
499
516
|
}
|
|
500
517
|
else {
|
|
501
|
-
|
|
518
|
+
em.unitOfWork.unsetIdentity(entity);
|
|
502
519
|
}
|
|
503
520
|
return reloaded ? entity : reloaded;
|
|
504
521
|
}
|
|
@@ -541,7 +558,6 @@ export class EntityManager {
|
|
|
541
558
|
await em.entityLoader.populate(entityName, [cached.data], options.populate, {
|
|
542
559
|
...options,
|
|
543
560
|
...em.getPopulateWhere(where, options),
|
|
544
|
-
convertCustomTypes: false,
|
|
545
561
|
ignoreLazyScalarProperties: true,
|
|
546
562
|
lookup: false,
|
|
547
563
|
});
|
|
@@ -969,39 +985,8 @@ export class EntityManager {
|
|
|
969
985
|
if (this.disableTransactions || em.disableTransactions) {
|
|
970
986
|
return cb(em);
|
|
971
987
|
}
|
|
972
|
-
const
|
|
973
|
-
|
|
974
|
-
flushMode: options.flushMode,
|
|
975
|
-
cloneEventManager: true,
|
|
976
|
-
disableTransactions: options.ignoreNestedTransactions,
|
|
977
|
-
loggerContext: options.loggerContext,
|
|
978
|
-
});
|
|
979
|
-
options.ctx ??= em.transactionContext;
|
|
980
|
-
const propagateToUpperContext = !em.global || this.config.get('allowGlobalContext');
|
|
981
|
-
return TransactionContext.create(fork, async () => {
|
|
982
|
-
return fork.getConnection().transactional(async (trx) => {
|
|
983
|
-
fork.transactionContext = trx;
|
|
984
|
-
if (propagateToUpperContext) {
|
|
985
|
-
fork.eventManager.registerSubscriber({
|
|
986
|
-
afterFlush(args) {
|
|
987
|
-
args.uow.getChangeSets()
|
|
988
|
-
.filter(cs => [ChangeSetType.DELETE, ChangeSetType.DELETE_EARLY].includes(cs.type))
|
|
989
|
-
.forEach(cs => em.unitOfWork.unsetIdentity(cs.entity));
|
|
990
|
-
},
|
|
991
|
-
});
|
|
992
|
-
}
|
|
993
|
-
const ret = await cb(fork);
|
|
994
|
-
await fork.flush();
|
|
995
|
-
if (propagateToUpperContext) {
|
|
996
|
-
// ensure all entities from inner context are merged to the upper one
|
|
997
|
-
for (const entity of fork.unitOfWork.getIdentityMap()) {
|
|
998
|
-
em.unitOfWork.register(entity);
|
|
999
|
-
entity.__helper.__em = em;
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
return ret;
|
|
1003
|
-
}, { ...options, eventBroadcaster: new TransactionEventBroadcaster(fork, { topLevelTransaction: !options.ctx }) });
|
|
1004
|
-
});
|
|
988
|
+
const manager = new TransactionManager(this);
|
|
989
|
+
return manager.handle(cb, options);
|
|
1005
990
|
}
|
|
1006
991
|
/**
|
|
1007
992
|
* Starts new transaction bound to this EntityManager. Use `ctx` parameter to provide the parent when nesting transactions.
|
|
@@ -1181,10 +1166,10 @@ export class EntityManager {
|
|
|
1181
1166
|
* via second parameter. By default, it will return already loaded entities without modifying them.
|
|
1182
1167
|
*/
|
|
1183
1168
|
merge(entityName, data, options = {}) {
|
|
1184
|
-
const em = this.getContext();
|
|
1185
1169
|
if (Utils.isEntity(entityName)) {
|
|
1186
|
-
return
|
|
1170
|
+
return this.merge(entityName.constructor.name, entityName, data);
|
|
1187
1171
|
}
|
|
1172
|
+
const em = options.disableContextResolution ? this : this.getContext();
|
|
1188
1173
|
options.schema ??= em._schema;
|
|
1189
1174
|
entityName = Utils.className(entityName);
|
|
1190
1175
|
em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
|
|
@@ -1194,7 +1179,12 @@ export class EntityManager {
|
|
|
1194
1179
|
}
|
|
1195
1180
|
const meta = em.metadata.find(entityName);
|
|
1196
1181
|
const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
|
|
1197
|
-
|
|
1182
|
+
const dataIsEntity = Utils.isEntity(data);
|
|
1183
|
+
if (options.keepIdentity && entity && dataIsEntity && entity !== data) {
|
|
1184
|
+
em.entityFactory.mergeData(meta, entity, helper(data).__originalEntityData, { initialized: true, merge: true, ...options });
|
|
1185
|
+
return entity;
|
|
1186
|
+
}
|
|
1187
|
+
entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1198
1188
|
em.validator.validate(entity, data, childMeta ?? meta);
|
|
1199
1189
|
em.unitOfWork.merge(entity);
|
|
1200
1190
|
return entity;
|
|
@@ -1259,10 +1249,8 @@ export class EntityManager {
|
|
|
1259
1249
|
async count(entityName, where = {}, options = {}) {
|
|
1260
1250
|
const em = this.getContext(false);
|
|
1261
1251
|
// Shallow copy options since the object will be modified when deleting orderBy
|
|
1262
|
-
options = {
|
|
1263
|
-
|
|
1264
|
-
...options,
|
|
1265
|
-
};
|
|
1252
|
+
options = { ...options };
|
|
1253
|
+
em.prepareOptions(options);
|
|
1266
1254
|
entityName = Utils.className(entityName);
|
|
1267
1255
|
await em.tryFlush(entityName, options);
|
|
1268
1256
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
@@ -1280,7 +1268,7 @@ export class EntityManager {
|
|
|
1280
1268
|
if (cached?.data !== undefined) {
|
|
1281
1269
|
return cached.data;
|
|
1282
1270
|
}
|
|
1283
|
-
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, ...options });
|
|
1271
|
+
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
1284
1272
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1285
1273
|
return +count;
|
|
1286
1274
|
}
|
|
@@ -1568,7 +1556,6 @@ export class EntityManager {
|
|
|
1568
1556
|
...options,
|
|
1569
1557
|
...this.getPopulateWhere(where, options),
|
|
1570
1558
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
1571
|
-
convertCustomTypes: false,
|
|
1572
1559
|
ignoreLazyScalarProperties: true,
|
|
1573
1560
|
lookup: false,
|
|
1574
1561
|
});
|
|
@@ -1691,7 +1678,7 @@ export class EntityManager {
|
|
|
1691
1678
|
throw new ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
|
|
1692
1679
|
}
|
|
1693
1680
|
options.schema ??= this._schema;
|
|
1694
|
-
options.logging = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1681
|
+
options.logging = options.loggerContext = Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1695
1682
|
}
|
|
1696
1683
|
/**
|
|
1697
1684
|
* @internal
|
package/README.md
CHANGED
|
@@ -11,7 +11,6 @@ TypeScript ORM for Node.js based on Data Mapper, [Unit of Work](https://mikro-or
|
|
|
11
11
|
[](https://discord.gg/w8bjxFHS7X)
|
|
12
12
|
[](https://www.npmjs.com/package/@mikro-orm/core)
|
|
13
13
|
[](https://coveralls.io/r/mikro-orm/mikro-orm?branch=master)
|
|
14
|
-
[](https://codeclimate.com/github/mikro-orm/mikro-orm/maintainability)
|
|
15
14
|
[](https://github.com/mikro-orm/mikro-orm/actions?workflow=tests)
|
|
16
15
|
|
|
17
16
|
## 🤔 Unit of What?
|
|
@@ -141,7 +140,7 @@ There is also auto-generated [CHANGELOG.md](CHANGELOG.md) file based on commit m
|
|
|
141
140
|
- [Composite and Foreign Keys as Primary Key](https://mikro-orm.io/docs/composite-keys)
|
|
142
141
|
- [Filters](https://mikro-orm.io/docs/filters)
|
|
143
142
|
- [Using `QueryBuilder`](https://mikro-orm.io/docs/query-builder)
|
|
144
|
-
- [
|
|
143
|
+
- [Populating relations](https://mikro-orm.io/docs/populating-relations)
|
|
145
144
|
- [Property Validation](https://mikro-orm.io/docs/property-validation)
|
|
146
145
|
- [Lifecycle Hooks](https://mikro-orm.io/docs/events#hooks)
|
|
147
146
|
- [Vanilla JS Support](https://mikro-orm.io/docs/usage-with-js)
|
|
@@ -45,15 +45,17 @@ export declare abstract class Connection {
|
|
|
45
45
|
readOnly?: boolean;
|
|
46
46
|
ctx?: Transaction;
|
|
47
47
|
eventBroadcaster?: TransactionEventBroadcaster;
|
|
48
|
+
loggerContext?: LogContext;
|
|
48
49
|
}): Promise<T>;
|
|
49
50
|
begin(options?: {
|
|
50
51
|
isolationLevel?: IsolationLevel;
|
|
51
52
|
readOnly?: boolean;
|
|
52
53
|
ctx?: Transaction;
|
|
53
54
|
eventBroadcaster?: TransactionEventBroadcaster;
|
|
55
|
+
loggerContext?: LogContext;
|
|
54
56
|
}): Promise<Transaction>;
|
|
55
|
-
commit(ctx: Transaction, eventBroadcaster?: TransactionEventBroadcaster): Promise<void>;
|
|
56
|
-
rollback(ctx: Transaction, eventBroadcaster?: TransactionEventBroadcaster): Promise<void>;
|
|
57
|
+
commit(ctx: Transaction, eventBroadcaster?: TransactionEventBroadcaster, loggerContext?: LogContext): Promise<void>;
|
|
58
|
+
rollback(ctx: Transaction, eventBroadcaster?: TransactionEventBroadcaster, loggerContext?: LogContext): Promise<void>;
|
|
57
59
|
abstract execute<T>(query: string, params?: any[], method?: 'all' | 'get' | 'run', ctx?: Transaction): Promise<QueryResult<T> | any | any[]>;
|
|
58
60
|
getConnectionOptions(): ConnectionConfig;
|
|
59
61
|
getClientUrl(): string;
|
|
@@ -46,10 +46,10 @@ export class Connection {
|
|
|
46
46
|
async begin(options) {
|
|
47
47
|
throw new Error(`Transactions are not supported by current driver`);
|
|
48
48
|
}
|
|
49
|
-
async commit(ctx, eventBroadcaster) {
|
|
49
|
+
async commit(ctx, eventBroadcaster, loggerContext) {
|
|
50
50
|
throw new Error(`Transactions are not supported by current driver`);
|
|
51
51
|
}
|
|
52
|
-
async rollback(ctx, eventBroadcaster) {
|
|
52
|
+
async rollback(ctx, eventBroadcaster, loggerContext) {
|
|
53
53
|
throw new Error(`Transactions are not supported by current driver`);
|
|
54
54
|
}
|
|
55
55
|
getConnectionOptions() {
|
package/decorators/Entity.d.ts
CHANGED
|
@@ -2,17 +2,32 @@ import type { AnyString, Constructor, Dictionary, EntityClass, ObjectQuery } fro
|
|
|
2
2
|
import type { FindOptions } from '../drivers/IDatabaseDriver.js';
|
|
3
3
|
export declare function Entity<T extends EntityClass<unknown>>(options?: EntityOptions<T>): (target: T) => void;
|
|
4
4
|
export type EntityOptions<T, E = T extends EntityClass<infer P> ? P : T> = {
|
|
5
|
+
/** Override default collection/table name. Alias for `collection`. */
|
|
5
6
|
tableName?: string;
|
|
7
|
+
/** Sets the schema name. */
|
|
6
8
|
schema?: string;
|
|
9
|
+
/** Override default collection/table name. Alias for `tableName`. */
|
|
7
10
|
collection?: string;
|
|
11
|
+
/** For {@doclink inheritance-mapping#single-table-inheritance | Single Table Inheritance}. */
|
|
8
12
|
discriminatorColumn?: (T extends EntityClass<infer P> ? keyof P : string) | AnyString;
|
|
13
|
+
/** For {@doclink inheritance-mapping#single-table-inheritance | Single Table Inheritance}. */
|
|
9
14
|
discriminatorMap?: Dictionary<string>;
|
|
15
|
+
/** For {@doclink inheritance-mapping#single-table-inheritance | Single Table Inheritance}. */
|
|
10
16
|
discriminatorValue?: number | string;
|
|
17
|
+
/** Enforce use of constructor when creating managed entity instances. */
|
|
11
18
|
forceConstructor?: boolean;
|
|
19
|
+
/** Specify comment to table. (SQL only) */
|
|
12
20
|
comment?: string;
|
|
21
|
+
/** Marks entity as abstract, such entities are inlined during discovery. */
|
|
13
22
|
abstract?: boolean;
|
|
23
|
+
/** Disables change tracking - such entities are ignored during flush. */
|
|
14
24
|
readonly?: boolean;
|
|
25
|
+
/** Marks entity as {@doclink virtual-entities | virtual}. This is set automatically when you use `expression` option. */
|
|
15
26
|
virtual?: boolean;
|
|
27
|
+
/** Used to make ORM aware of externally defined triggers. This is needed for MS SQL Server multi inserts, ignored in other dialects. */
|
|
28
|
+
hasTriggers?: boolean;
|
|
29
|
+
/** SQL query that maps to a {@doclink virtual-entities | virtual entity}. */
|
|
16
30
|
expression?: string | ((em: any, where: ObjectQuery<E>, options: FindOptions<E, any, any, any>) => object);
|
|
31
|
+
/** Set {@doclink repositories#custom-repository | custom repository class}. */
|
|
17
32
|
repository?: () => Constructor;
|
|
18
33
|
};
|
package/decorators/Indexed.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EntityClass, Dictionary, AutoPath } from '../typings.js';
|
|
1
|
+
import type { EntityClass, Dictionary, AutoPath, IndexCallback } from '../typings.js';
|
|
2
2
|
import type { DeferMode } from '../enums.js';
|
|
3
3
|
export declare function Index<T extends object, H extends string>(options?: IndexOptions<T, H>): (target: T, propertyName?: (T extends EntityClass<unknown> ? undefined : keyof T) | undefined) => any;
|
|
4
4
|
export declare function Unique<T extends object, H extends string>(options?: UniqueOptions<T, H>): (target: T, propertyName?: (T extends EntityClass<unknown> ? undefined : keyof T) | undefined) => any;
|
|
@@ -8,7 +8,7 @@ interface BaseOptions<T, H extends string> {
|
|
|
8
8
|
name?: string;
|
|
9
9
|
properties?: (T extends EntityClass<infer P> ? Properties<P, H> : Properties<T, H>);
|
|
10
10
|
options?: Dictionary;
|
|
11
|
-
expression?: string;
|
|
11
|
+
expression?: string | (T extends EntityClass<infer P> ? IndexCallback<P> : IndexCallback<T>);
|
|
12
12
|
}
|
|
13
13
|
export interface UniqueOptions<T, H extends string = string> extends BaseOptions<T, H> {
|
|
14
14
|
deferMode?: DeferMode | `${DeferMode}`;
|
|
@@ -37,4 +37,6 @@ export interface ManyToManyOptions<Owner, Target> extends ReferenceOptions<Owner
|
|
|
37
37
|
deleteRule?: 'cascade' | 'no action' | 'set null' | 'set default' | AnyString;
|
|
38
38
|
/** What to do when the reference to the target entity gets updated. */
|
|
39
39
|
updateRule?: 'cascade' | 'no action' | 'set null' | 'set default' | AnyString;
|
|
40
|
+
/** Enable/disable foreign key constraint creation on this relation */
|
|
41
|
+
createForeignKeyConstraint?: boolean;
|
|
40
42
|
}
|
|
@@ -27,6 +27,8 @@ export interface ManyToOneOptions<Owner, Target> extends ReferenceOptions<Owner,
|
|
|
27
27
|
updateRule?: 'cascade' | 'no action' | 'set null' | 'set default' | AnyString;
|
|
28
28
|
/** Set the constraint type. Immediate constraints are checked for each statement, while deferred ones are only checked at the end of the transaction. Only for postgres unique constraints. */
|
|
29
29
|
deferMode?: DeferMode | `${DeferMode}`;
|
|
30
|
+
/** Enable/disable foreign key constraint creation on this relation */
|
|
31
|
+
createForeignKeyConstraint?: boolean;
|
|
30
32
|
/** Set a custom foreign key constraint name, overriding NamingStrategy.indexName(). */
|
|
31
33
|
foreignKeyName?: string;
|
|
32
34
|
}
|
package/decorators/OneToOne.d.ts
CHANGED
|
@@ -23,4 +23,6 @@ export interface OneToOneOptions<Owner, Target> extends Partial<Omit<OneToManyOp
|
|
|
23
23
|
deferMode?: DeferMode | `${DeferMode}`;
|
|
24
24
|
/** Set a custom foreign key constraint name, overriding NamingStrategy.indexName(). */
|
|
25
25
|
foreignKeyName?: string;
|
|
26
|
+
/** Enable/disable foreign key constraint creation on this relation */
|
|
27
|
+
createForeignKeyConstraint?: boolean;
|
|
26
28
|
}
|
|
@@ -2,6 +2,7 @@ import type { TransactionOptions } from '../enums.js';
|
|
|
2
2
|
import type { ContextProvider } from '../typings.js';
|
|
3
3
|
type TransactionalOptions<T> = TransactionOptions & {
|
|
4
4
|
context?: ContextProvider<T>;
|
|
5
|
+
contextName?: string;
|
|
5
6
|
};
|
|
6
7
|
/**
|
|
7
8
|
* This decorator wraps the method with `em.transactional()`, so you can provide `TransactionOptions` just like with `em.transactional()`.
|
|
@@ -14,10 +14,10 @@ export function Transactional(options = {}) {
|
|
|
14
14
|
throw new Error('@Transactional() should be use with async functions');
|
|
15
15
|
}
|
|
16
16
|
descriptor.value = async function (...args) {
|
|
17
|
-
const { context, ...txOptions } = options;
|
|
17
|
+
const { context, contextName, ...txOptions } = options;
|
|
18
18
|
const em = (await resolveContextProvider(this, context))
|
|
19
|
-
|| TransactionContext.getEntityManager()
|
|
20
|
-
|| RequestContext.getEntityManager();
|
|
19
|
+
|| TransactionContext.getEntityManager(contextName)
|
|
20
|
+
|| RequestContext.getEntityManager(contextName);
|
|
21
21
|
if (!em) {
|
|
22
22
|
throw new Error(`@Transactional() decorator can only be applied to methods of classes with \`orm: MikroORM\` property, \`em: EntityManager\` property, or with a callback parameter like \`@Transactional(() => orm)\` that returns one of those types. The parameter will contain a reference to current \`this\`. Returning an EntityRepository from it is also supported.`);
|
|
23
23
|
}
|
|
@@ -166,6 +166,7 @@ export interface NativeInsertUpdateOptions<T> {
|
|
|
166
166
|
schema?: string;
|
|
167
167
|
/** `nativeUpdate()` only option */
|
|
168
168
|
upsert?: boolean;
|
|
169
|
+
loggerContext?: LogContext;
|
|
169
170
|
}
|
|
170
171
|
export interface NativeInsertUpdateManyOptions<T> extends NativeInsertUpdateOptions<T> {
|
|
171
172
|
processCollections?: boolean;
|
|
@@ -200,6 +201,8 @@ export interface CountOptions<T extends object, P extends string = never> {
|
|
|
200
201
|
hintComments?: string | string[];
|
|
201
202
|
loggerContext?: LogContext;
|
|
202
203
|
logging?: LoggingOptions;
|
|
204
|
+
/** @internal used to apply filters to the auto-joined relations */
|
|
205
|
+
em?: EntityManager;
|
|
203
206
|
}
|
|
204
207
|
export interface UpdateOptions<T> {
|
|
205
208
|
filters?: FilterOptions;
|
|
@@ -221,6 +224,7 @@ export interface LockOptions extends DriverMethodOptions {
|
|
|
221
224
|
export interface DriverMethodOptions {
|
|
222
225
|
ctx?: Transaction;
|
|
223
226
|
schema?: string;
|
|
227
|
+
loggerContext?: LogContext;
|
|
224
228
|
}
|
|
225
229
|
export interface GetReferenceOptions {
|
|
226
230
|
wrapped?: boolean;
|
|
@@ -6,6 +6,7 @@ export declare class ArrayCollection<T extends object, O extends object> {
|
|
|
6
6
|
protected readonly items: Set<T>;
|
|
7
7
|
protected initialized: boolean;
|
|
8
8
|
protected dirty: boolean;
|
|
9
|
+
protected partial: boolean;
|
|
9
10
|
protected snapshot: T[] | undefined;
|
|
10
11
|
protected _count?: number;
|
|
11
12
|
private _property?;
|
|
@@ -25,7 +26,7 @@ export declare class ArrayCollection<T extends object, O extends object> {
|
|
|
25
26
|
/**
|
|
26
27
|
* @internal
|
|
27
28
|
*/
|
|
28
|
-
hydrate(items: T[], forcePropagate?: boolean): void;
|
|
29
|
+
hydrate(items: T[], forcePropagate?: boolean, partial?: boolean): void;
|
|
29
30
|
/**
|
|
30
31
|
* Remove specified item(s) from the collection. Note that removing item from collection does not necessarily imply deleting the target entity,
|
|
31
32
|
* it means we are disconnecting the relation - removing items from collection, not removing entities from database - `Collection.remove()`
|
|
@@ -83,6 +84,7 @@ export declare class ArrayCollection<T extends object, O extends object> {
|
|
|
83
84
|
count(): number;
|
|
84
85
|
isInitialized(fully?: boolean): boolean;
|
|
85
86
|
isDirty(): boolean;
|
|
87
|
+
isPartial(): boolean;
|
|
86
88
|
isEmpty(): boolean;
|
|
87
89
|
setDirty(dirty?: boolean): void;
|
|
88
90
|
get length(): number;
|
|
@@ -9,6 +9,7 @@ export class ArrayCollection {
|
|
|
9
9
|
items = new Set();
|
|
10
10
|
initialized = true;
|
|
11
11
|
dirty = false;
|
|
12
|
+
partial = false; // mark partially loaded collections, propagation is disabled for those
|
|
12
13
|
snapshot = []; // used to create a diff of the collection at commit time, undefined marks overridden values so we need to wipe when flushing
|
|
13
14
|
_count;
|
|
14
15
|
_property;
|
|
@@ -107,11 +108,12 @@ export class ArrayCollection {
|
|
|
107
108
|
/**
|
|
108
109
|
* @internal
|
|
109
110
|
*/
|
|
110
|
-
hydrate(items, forcePropagate) {
|
|
111
|
+
hydrate(items, forcePropagate, partial) {
|
|
111
112
|
for (let i = 0; i < this.items.size; i++) {
|
|
112
113
|
delete this[i];
|
|
113
114
|
}
|
|
114
115
|
this.initialized = true;
|
|
116
|
+
this.partial = !!partial;
|
|
115
117
|
this.items.clear();
|
|
116
118
|
this._count = 0;
|
|
117
119
|
this.add(items);
|
|
@@ -274,6 +276,9 @@ export class ArrayCollection {
|
|
|
274
276
|
isDirty() {
|
|
275
277
|
return this.dirty;
|
|
276
278
|
}
|
|
279
|
+
isPartial() {
|
|
280
|
+
return this.partial;
|
|
281
|
+
}
|
|
277
282
|
isEmpty() {
|
|
278
283
|
return this.count() === 0;
|
|
279
284
|
}
|
|
@@ -390,7 +395,7 @@ export class ArrayCollection {
|
|
|
390
395
|
/** @ignore */
|
|
391
396
|
[inspect.custom](depth = 2) {
|
|
392
397
|
const object = { ...this };
|
|
393
|
-
const hidden = ['items', 'owner', '_property', '_count', 'snapshot', '_populated', '_snapshot', '_lazyInitialized', '_em', 'readonly'];
|
|
398
|
+
const hidden = ['items', 'owner', '_property', '_count', 'snapshot', '_populated', '_snapshot', '_lazyInitialized', '_em', 'readonly', 'partial'];
|
|
394
399
|
hidden.forEach(k => delete object[k]);
|
|
395
400
|
const ret = inspect(object, { depth });
|
|
396
401
|
const name = `${this.constructor.name}<${this.property?.type ?? 'unknown'}>`;
|
package/entity/Collection.js
CHANGED
|
@@ -221,8 +221,9 @@ export class Collection extends ArrayCollection {
|
|
|
221
221
|
const order = [...this.items]; // copy order of references
|
|
222
222
|
const orderBy = this.createOrderBy(options.orderBy);
|
|
223
223
|
const customOrder = orderBy.length > 0;
|
|
224
|
-
|
|
225
|
-
const
|
|
224
|
+
const pivotTable = this.property.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable();
|
|
225
|
+
const loader = pivotTable ? 'colLoaderMtoN' : 'colLoader';
|
|
226
|
+
const items = await em[loader].load([
|
|
226
227
|
this,
|
|
227
228
|
{ ...options, orderBy },
|
|
228
229
|
]);
|
|
@@ -4,6 +4,11 @@ import type { EntityComparator } from '../utils/EntityComparator.js';
|
|
|
4
4
|
export interface FactoryOptions {
|
|
5
5
|
initialized?: boolean;
|
|
6
6
|
newEntity?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Property `onCreate` hooks are normally executed during `flush` operation.
|
|
9
|
+
* With this option, they will be processed early inside `em.create()` method.
|
|
10
|
+
*/
|
|
11
|
+
processOnCreateHooksEarly?: boolean;
|
|
7
12
|
merge?: boolean;
|
|
8
13
|
refresh?: boolean;
|
|
9
14
|
convertCustomTypes?: boolean;
|
|
@@ -27,6 +32,7 @@ export declare class EntityFactory {
|
|
|
27
32
|
createEmbeddable<T extends object>(entityName: EntityName<T>, data: EntityData<T>, options?: Pick<FactoryOptions, 'newEntity' | 'convertCustomTypes'>): T;
|
|
28
33
|
getComparator(): EntityComparator;
|
|
29
34
|
private createEntity;
|
|
35
|
+
private assignDefaultValues;
|
|
30
36
|
private hydrate;
|
|
31
37
|
private findEntity;
|
|
32
38
|
private processDiscriminatorColumn;
|
package/entity/EntityFactory.js
CHANGED
|
@@ -71,7 +71,7 @@ export class EntityFactory {
|
|
|
71
71
|
continue;
|
|
72
72
|
}
|
|
73
73
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils.isPlainObject(data[prop.name])) {
|
|
74
|
-
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta
|
|
74
|
+
data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
75
75
|
}
|
|
76
76
|
data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.platform, { key: prop.name, mode: 'hydration' });
|
|
77
77
|
}
|
|
@@ -113,7 +113,7 @@ export class EntityFactory {
|
|
|
113
113
|
if (meta.versionProperty && data[meta.versionProperty] && data[meta.versionProperty] !== originalEntityData[meta.versionProperty]) {
|
|
114
114
|
diff[meta.versionProperty] = data[meta.versionProperty];
|
|
115
115
|
}
|
|
116
|
-
const diff2 = this.comparator.diffEntities(meta.className, existsData, data);
|
|
116
|
+
const diff2 = this.comparator.diffEntities(meta.className, existsData, data, { includeInverseSides: true });
|
|
117
117
|
// do not override values changed by user
|
|
118
118
|
Utils.keys(diff).forEach(key => delete diff2[key]);
|
|
119
119
|
Utils.keys(diff2).filter(key => {
|
|
@@ -171,8 +171,8 @@ export class EntityFactory {
|
|
|
171
171
|
if (Array.isArray(id)) {
|
|
172
172
|
id = Utils.getPrimaryKeyCondFromArray(id, meta);
|
|
173
173
|
}
|
|
174
|
-
const pks = Utils.getOrderedPrimaryKeys(id, meta, this.platform
|
|
175
|
-
const exists = this.unitOfWork.getById(entityName, pks, schema);
|
|
174
|
+
const pks = Utils.getOrderedPrimaryKeys(id, meta, this.platform);
|
|
175
|
+
const exists = this.unitOfWork.getById(entityName, pks, schema, options.convertCustomTypes);
|
|
176
176
|
if (exists) {
|
|
177
177
|
return exists;
|
|
178
178
|
}
|
|
@@ -231,6 +231,13 @@ export class EntityFactory {
|
|
|
231
231
|
}
|
|
232
232
|
return entity;
|
|
233
233
|
}
|
|
234
|
+
assignDefaultValues(entity, meta) {
|
|
235
|
+
for (const prop of meta.props) {
|
|
236
|
+
if (prop.onCreate) {
|
|
237
|
+
entity[prop.name] ??= prop.onCreate(entity, this.em);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
234
241
|
hydrate(entity, meta, data, options) {
|
|
235
242
|
if (options.initialized) {
|
|
236
243
|
this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
|
|
@@ -242,6 +249,10 @@ export class EntityFactory {
|
|
|
242
249
|
helper(entity)?.__loadedProperties.add(key);
|
|
243
250
|
helper(entity)?.__serializationContext.fields?.add(key);
|
|
244
251
|
});
|
|
252
|
+
const processOnCreateHooksEarly = options.processOnCreateHooksEarly ?? this.config.get('processOnCreateHooksEarly');
|
|
253
|
+
if (options.newEntity && processOnCreateHooksEarly) {
|
|
254
|
+
this.assignDefaultValues(entity, meta);
|
|
255
|
+
}
|
|
245
256
|
}
|
|
246
257
|
findEntity(data, meta, options) {
|
|
247
258
|
const schema = this.driver.getSchemaName(meta, options);
|
|
@@ -251,7 +262,7 @@ export class EntityFactory {
|
|
|
251
262
|
if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
|
|
252
263
|
return undefined;
|
|
253
264
|
}
|
|
254
|
-
const pks = Utils.getOrderedPrimaryKeys(data, meta, this.platform);
|
|
265
|
+
const pks = Utils.getOrderedPrimaryKeys(data, meta, this.platform, options.convertCustomTypes);
|
|
255
266
|
return this.unitOfWork.getById(meta.className, pks, schema);
|
|
256
267
|
}
|
|
257
268
|
processDiscriminatorColumn(meta, data) {
|
|
@@ -285,7 +296,7 @@ export class EntityFactory {
|
|
|
285
296
|
return meta.constructorParams.map(k => {
|
|
286
297
|
if (meta.properties[k] && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(meta.properties[k].kind) && data[k]) {
|
|
287
298
|
const pk = Reference.unwrapReference(data[k]);
|
|
288
|
-
const entity = this.unitOfWork.getById(meta.properties[k].type, pk, options.schema);
|
|
299
|
+
const entity = this.unitOfWork.getById(meta.properties[k].type, pk, options.schema, true);
|
|
289
300
|
if (entity) {
|
|
290
301
|
return entity;
|
|
291
302
|
}
|
package/entity/EntityHelper.js
CHANGED
|
@@ -152,7 +152,7 @@ export class EntityHelper {
|
|
|
152
152
|
wrapped.__data[prop.name] = Reference.wrapReference(val, prop);
|
|
153
153
|
// when propagation from inside hydration, we set the FK to the entity data immediately
|
|
154
154
|
if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
|
|
155
|
-
wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(wrapped.__data[prop.name], prop.targetMeta
|
|
155
|
+
wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(wrapped.__data[prop.name], prop.targetMeta, true);
|
|
156
156
|
}
|
|
157
157
|
else {
|
|
158
158
|
wrapped.__touched = !hydrator.isRunning();
|
|
@@ -172,6 +172,9 @@ export class EntityHelper {
|
|
|
172
172
|
continue;
|
|
173
173
|
}
|
|
174
174
|
const inverse = value?.[prop2.name];
|
|
175
|
+
if (Utils.isCollection(inverse) && inverse.isPartial()) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
175
178
|
if (prop.kind === ReferenceKind.MANY_TO_ONE && Utils.isCollection(inverse) && inverse.isInitialized()) {
|
|
176
179
|
inverse.addWithoutPropagation(owner);
|
|
177
180
|
helper(owner).__em?.getUnitOfWork().cancelOrphanRemoval(owner);
|
|
@@ -210,6 +213,7 @@ export class EntityHelper {
|
|
|
210
213
|
}
|
|
211
214
|
if (old?.[prop2.name] != null) {
|
|
212
215
|
delete helper(old).__data[prop2.name];
|
|
216
|
+
old[prop2.name] = null;
|
|
213
217
|
}
|
|
214
218
|
}
|
|
215
219
|
static ensurePropagation(entity) {
|