@mikro-orm/core 6.4.17-dev.8 → 6.4.17-dev.81
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 +11 -2
- package/EntityManager.js +37 -22
- package/README.md +1 -2
- package/connections/Connection.d.ts +4 -2
- package/connections/Connection.js +2 -2
- package/decorators/Entity.d.ts +14 -0
- package/decorators/Indexed.d.ts +2 -2
- 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/EntityFactory.d.ts +6 -0
- package/entity/EntityFactory.js +12 -1
- package/entity/EntityHelper.js +4 -1
- package/entity/EntityLoader.js +26 -18
- package/entity/Reference.d.ts +5 -0
- package/entity/Reference.js +16 -0
- package/entity/WrappedEntity.js +1 -1
- package/entity/defineEntity.d.ts +528 -0
- package/entity/defineEntity.js +684 -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 +16 -3
- package/enums.d.ts +3 -1
- package/enums.js +2 -0
- package/hydration/ObjectHydrator.js +1 -1
- package/index.d.ts +1 -1
- package/index.mjs +4 -0
- package/metadata/MetadataDiscovery.d.ts +0 -1
- package/metadata/MetadataDiscovery.js +16 -13
- package/package.json +4 -4
- package/platforms/Platform.d.ts +3 -1
- package/types/BooleanType.d.ts +1 -1
- package/typings.d.ts +17 -8
- package/typings.js +10 -2
- package/unit-of-work/ChangeSetComputer.js +3 -1
- package/unit-of-work/ChangeSetPersister.d.ts +4 -2
- package/unit-of-work/ChangeSetPersister.js +13 -9
- package/unit-of-work/UnitOfWork.d.ts +1 -0
- package/unit-of-work/UnitOfWork.js +25 -11
- 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/EntityComparator.js +6 -3
- 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 -0
- package/utils/Utils.d.ts +2 -2
- package/utils/Utils.js +32 -8
- package/utils/upsert-utils.js +9 -1
package/EntityManager.d.ts
CHANGED
|
@@ -112,7 +112,9 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
112
112
|
/**
|
|
113
113
|
* Gets logger context for this entity manager.
|
|
114
114
|
*/
|
|
115
|
-
getLoggerContext<T extends Dictionary = Dictionary>(
|
|
115
|
+
getLoggerContext<T extends Dictionary = Dictionary>(options?: {
|
|
116
|
+
disableContextResolution?: boolean;
|
|
117
|
+
}): T;
|
|
116
118
|
setFlushMode(flushMode?: FlushMode): void;
|
|
117
119
|
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>>;
|
|
118
120
|
protected applyDiscriminatorCondition<Entity extends object>(entityName: string, where: FilterQuery<Entity>): FilterQuery<Entity>;
|
|
@@ -482,7 +484,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
482
484
|
* some additional lazy properties, if so, we reload and merge the data from database
|
|
483
485
|
*/
|
|
484
486
|
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;
|
|
485
|
-
protected prepareOptions(options: FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any>): void;
|
|
487
|
+
protected prepareOptions(options: FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any> | CountOptions<any, any>): void;
|
|
486
488
|
/**
|
|
487
489
|
* @internal
|
|
488
490
|
*/
|
|
@@ -542,11 +544,18 @@ export interface CreateOptions<Convert extends boolean> {
|
|
|
542
544
|
partial?: boolean;
|
|
543
545
|
/** convert raw database values based on mapped types (by default, already converted values are expected) */
|
|
544
546
|
convertCustomTypes?: Convert;
|
|
547
|
+
/**
|
|
548
|
+
* Property `onCreate` hooks are normally executed during `flush` operation.
|
|
549
|
+
* With this option, they will be processed early inside `em.create()` method.
|
|
550
|
+
*/
|
|
551
|
+
processOnCreateHooksEarly?: boolean;
|
|
545
552
|
}
|
|
546
553
|
export interface MergeOptions {
|
|
547
554
|
refresh?: boolean;
|
|
548
555
|
convertCustomTypes?: boolean;
|
|
549
556
|
schema?: string;
|
|
557
|
+
disableContextResolution?: boolean;
|
|
558
|
+
keepIdentity?: boolean;
|
|
550
559
|
}
|
|
551
560
|
export interface ForkOptions {
|
|
552
561
|
/** do we want a clear identity map? defaults to true */
|
package/EntityManager.js
CHANGED
|
@@ -12,6 +12,7 @@ const unit_of_work_1 = require("./unit-of-work");
|
|
|
12
12
|
const enums_1 = require("./enums");
|
|
13
13
|
const events_1 = require("./events");
|
|
14
14
|
const errors_1 = require("./errors");
|
|
15
|
+
const utils_2 = require("./entity/utils");
|
|
15
16
|
/**
|
|
16
17
|
* The EntityManager is the central access point to ORM functionality. It is a facade to all different ORM subsystems
|
|
17
18
|
* such as UnitOfWork, Query Language, and Repository API.
|
|
@@ -131,7 +132,6 @@ class EntityManager {
|
|
|
131
132
|
await em.entityLoader.populate(entityName, cached.data, populate, {
|
|
132
133
|
...options,
|
|
133
134
|
...em.getPopulateWhere(where, options),
|
|
134
|
-
convertCustomTypes: false,
|
|
135
135
|
ignoreLazyScalarProperties: true,
|
|
136
136
|
lookup: false,
|
|
137
137
|
});
|
|
@@ -162,7 +162,6 @@ class EntityManager {
|
|
|
162
162
|
await em.entityLoader.populate(entityName, unique, populate, {
|
|
163
163
|
...options,
|
|
164
164
|
...em.getPopulateWhere(where, options),
|
|
165
|
-
convertCustomTypes: false,
|
|
166
165
|
ignoreLazyScalarProperties: true,
|
|
167
166
|
lookup: false,
|
|
168
167
|
});
|
|
@@ -226,8 +225,8 @@ class EntityManager {
|
|
|
226
225
|
/**
|
|
227
226
|
* Gets logger context for this entity manager.
|
|
228
227
|
*/
|
|
229
|
-
getLoggerContext() {
|
|
230
|
-
const em = this.getContext();
|
|
228
|
+
getLoggerContext(options) {
|
|
229
|
+
const em = options?.disableContextResolution ? this : this.getContext();
|
|
231
230
|
em.loggerContext ??= {};
|
|
232
231
|
return em.loggerContext;
|
|
233
232
|
}
|
|
@@ -283,7 +282,8 @@ class EntityManager {
|
|
|
283
282
|
for (const hint of options.populate) {
|
|
284
283
|
const field = hint.field.split(':')[0];
|
|
285
284
|
const prop = meta.properties[field];
|
|
286
|
-
const
|
|
285
|
+
const strategy = (0, utils_2.getLoadingStrategy)(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
|
|
286
|
+
const joined = strategy === enums_1.LoadStrategy.JOINED && prop.kind !== enums_1.ReferenceKind.SCALAR;
|
|
287
287
|
if (!joined && !hint.filter) {
|
|
288
288
|
continue;
|
|
289
289
|
}
|
|
@@ -320,10 +320,19 @@ class EntityManager {
|
|
|
320
320
|
const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
|
|
321
321
|
if (!utils_1.Utils.isEmpty(cond)) {
|
|
322
322
|
const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
|
|
323
|
+
let found = false;
|
|
323
324
|
if (populated.length > 0) {
|
|
324
|
-
|
|
325
|
+
for (const hint of populated) {
|
|
326
|
+
if (!hint.all) {
|
|
327
|
+
hint.filter = true;
|
|
328
|
+
found = true;
|
|
329
|
+
}
|
|
330
|
+
else if (hint.field === `${prop.name}:ref`) {
|
|
331
|
+
found = true;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
325
334
|
}
|
|
326
|
-
|
|
335
|
+
if (!found) {
|
|
327
336
|
ret.push({ field: `${prop.name}:ref`, strategy: enums_1.LoadStrategy.JOINED, filter: true });
|
|
328
337
|
}
|
|
329
338
|
}
|
|
@@ -488,11 +497,17 @@ class EntityManager {
|
|
|
488
497
|
...options,
|
|
489
498
|
flushMode: enums_1.FlushMode.COMMIT,
|
|
490
499
|
});
|
|
500
|
+
const em = this.getContext();
|
|
491
501
|
if (reloaded) {
|
|
492
|
-
|
|
502
|
+
for (const e of fork.unitOfWork.getIdentityMap()) {
|
|
503
|
+
const ref = em.getReference(e.constructor.name, (0, entity_1.helper)(e).getPrimaryKey());
|
|
504
|
+
const data = this.comparator.prepareEntity(e);
|
|
505
|
+
em.config.getHydrator(this.metadata).hydrate(ref, (0, entity_1.helper)(ref).__meta, (0, entity_1.helper)(e).serialize({ ignoreSerializers: true }), em.entityFactory, 'full', false, true);
|
|
506
|
+
(0, entity_1.helper)(ref).__originalEntityData = data;
|
|
507
|
+
}
|
|
493
508
|
}
|
|
494
509
|
else {
|
|
495
|
-
|
|
510
|
+
em.unitOfWork.unsetIdentity(entity);
|
|
496
511
|
}
|
|
497
512
|
return reloaded ? entity : reloaded;
|
|
498
513
|
}
|
|
@@ -535,7 +550,6 @@ class EntityManager {
|
|
|
535
550
|
await em.entityLoader.populate(entityName, [cached.data], options.populate, {
|
|
536
551
|
...options,
|
|
537
552
|
...em.getPopulateWhere(where, options),
|
|
538
|
-
convertCustomTypes: false,
|
|
539
553
|
ignoreLazyScalarProperties: true,
|
|
540
554
|
lookup: false,
|
|
541
555
|
});
|
|
@@ -982,8 +996,7 @@ class EntityManager {
|
|
|
982
996
|
if (propagateToUpperContext) {
|
|
983
997
|
// ensure all entities from inner context are merged to the upper one
|
|
984
998
|
for (const entity of fork.unitOfWork.getIdentityMap()) {
|
|
985
|
-
em.
|
|
986
|
-
entity.__helper.__em = em;
|
|
999
|
+
em.merge(entity, { disableContextResolution: true, keepIdentity: true, refresh: true });
|
|
987
1000
|
}
|
|
988
1001
|
}
|
|
989
1002
|
return ret;
|
|
@@ -1168,10 +1181,10 @@ class EntityManager {
|
|
|
1168
1181
|
* via second parameter. By default, it will return already loaded entities without modifying them.
|
|
1169
1182
|
*/
|
|
1170
1183
|
merge(entityName, data, options = {}) {
|
|
1171
|
-
const em = this.getContext();
|
|
1172
1184
|
if (utils_1.Utils.isEntity(entityName)) {
|
|
1173
|
-
return
|
|
1185
|
+
return this.merge(entityName.constructor.name, entityName, data);
|
|
1174
1186
|
}
|
|
1187
|
+
const em = options.disableContextResolution ? this : this.getContext();
|
|
1175
1188
|
options.schema ??= em._schema;
|
|
1176
1189
|
entityName = utils_1.Utils.className(entityName);
|
|
1177
1190
|
em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
|
|
@@ -1181,7 +1194,12 @@ class EntityManager {
|
|
|
1181
1194
|
}
|
|
1182
1195
|
const meta = em.metadata.find(entityName);
|
|
1183
1196
|
const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
|
|
1184
|
-
|
|
1197
|
+
const dataIsEntity = utils_1.Utils.isEntity(data);
|
|
1198
|
+
if (options.keepIdentity && entity && dataIsEntity && entity !== data) {
|
|
1199
|
+
em.entityFactory.mergeData(meta, entity, (0, entity_1.helper)(data).__originalEntityData, { initialized: true, merge: true, ...options });
|
|
1200
|
+
return entity;
|
|
1201
|
+
}
|
|
1202
|
+
entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
|
|
1185
1203
|
em.validator.validate(entity, data, childMeta ?? meta);
|
|
1186
1204
|
em.unitOfWork.merge(entity);
|
|
1187
1205
|
return entity;
|
|
@@ -1246,10 +1264,8 @@ class EntityManager {
|
|
|
1246
1264
|
async count(entityName, where = {}, options = {}) {
|
|
1247
1265
|
const em = this.getContext(false);
|
|
1248
1266
|
// Shallow copy options since the object will be modified when deleting orderBy
|
|
1249
|
-
options = {
|
|
1250
|
-
|
|
1251
|
-
...options,
|
|
1252
|
-
};
|
|
1267
|
+
options = { ...options };
|
|
1268
|
+
em.prepareOptions(options);
|
|
1253
1269
|
entityName = utils_1.Utils.className(entityName);
|
|
1254
1270
|
await em.tryFlush(entityName, options);
|
|
1255
1271
|
where = await em.processWhere(entityName, where, options, 'read');
|
|
@@ -1267,7 +1283,7 @@ class EntityManager {
|
|
|
1267
1283
|
if (cached?.data !== undefined) {
|
|
1268
1284
|
return cached.data;
|
|
1269
1285
|
}
|
|
1270
|
-
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, ...options });
|
|
1286
|
+
const count = await em.driver.count(entityName, where, { ctx: em.transactionContext, em, ...options });
|
|
1271
1287
|
await em.storeCache(options.cache, cached, () => +count);
|
|
1272
1288
|
return +count;
|
|
1273
1289
|
}
|
|
@@ -1547,7 +1563,6 @@ class EntityManager {
|
|
|
1547
1563
|
...options,
|
|
1548
1564
|
...this.getPopulateWhere(where, options),
|
|
1549
1565
|
orderBy: options.populateOrderBy ?? options.orderBy,
|
|
1550
|
-
convertCustomTypes: false,
|
|
1551
1566
|
ignoreLazyScalarProperties: true,
|
|
1552
1567
|
lookup: false,
|
|
1553
1568
|
});
|
|
@@ -1670,7 +1685,7 @@ class EntityManager {
|
|
|
1670
1685
|
throw new errors_1.ValidationError(`Cannot combine 'fields' and 'exclude' option.`);
|
|
1671
1686
|
}
|
|
1672
1687
|
options.schema ??= this._schema;
|
|
1673
|
-
options.logging = utils_1.Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1688
|
+
options.logging = options.loggerContext = utils_1.Utils.merge({ id: this.id }, this.loggerContext, options.loggerContext, options.logging);
|
|
1674
1689
|
}
|
|
1675
1690
|
/**
|
|
1676
1691
|
* @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)
|
|
@@ -49,15 +49,17 @@ export declare abstract class Connection {
|
|
|
49
49
|
readOnly?: boolean;
|
|
50
50
|
ctx?: Transaction;
|
|
51
51
|
eventBroadcaster?: TransactionEventBroadcaster;
|
|
52
|
+
loggerContext?: LogContext;
|
|
52
53
|
}): Promise<T>;
|
|
53
54
|
begin(options?: {
|
|
54
55
|
isolationLevel?: IsolationLevel;
|
|
55
56
|
readOnly?: boolean;
|
|
56
57
|
ctx?: Transaction;
|
|
57
58
|
eventBroadcaster?: TransactionEventBroadcaster;
|
|
59
|
+
loggerContext?: LogContext;
|
|
58
60
|
}): Promise<Transaction>;
|
|
59
|
-
commit(ctx: Transaction, eventBroadcaster?: TransactionEventBroadcaster): Promise<void>;
|
|
60
|
-
rollback(ctx: Transaction, eventBroadcaster?: TransactionEventBroadcaster): Promise<void>;
|
|
61
|
+
commit(ctx: Transaction, eventBroadcaster?: TransactionEventBroadcaster, loggerContext?: LogContext): Promise<void>;
|
|
62
|
+
rollback(ctx: Transaction, eventBroadcaster?: TransactionEventBroadcaster, loggerContext?: LogContext): Promise<void>;
|
|
61
63
|
abstract execute<T>(query: string, params?: any[], method?: 'all' | 'get' | 'run', ctx?: Transaction): Promise<QueryResult<T> | any | any[]>;
|
|
62
64
|
getConnectionOptions(): ConnectionConfig;
|
|
63
65
|
getClientUrl(): string;
|
|
@@ -49,10 +49,10 @@ class Connection {
|
|
|
49
49
|
async begin(options) {
|
|
50
50
|
throw new Error(`Transactions are not supported by current driver`);
|
|
51
51
|
}
|
|
52
|
-
async commit(ctx, eventBroadcaster) {
|
|
52
|
+
async commit(ctx, eventBroadcaster, loggerContext) {
|
|
53
53
|
throw new Error(`Transactions are not supported by current driver`);
|
|
54
54
|
}
|
|
55
|
-
async rollback(ctx, eventBroadcaster) {
|
|
55
|
+
async rollback(ctx, eventBroadcaster, loggerContext) {
|
|
56
56
|
throw new Error(`Transactions are not supported by current driver`);
|
|
57
57
|
}
|
|
58
58
|
getConnectionOptions() {
|
package/decorators/Entity.d.ts
CHANGED
|
@@ -2,18 +2,32 @@ import type { AnyString, Constructor, Dictionary, EntityClass, ObjectQuery } fro
|
|
|
2
2
|
import type { FindOptions } from '../drivers/IDatabaseDriver';
|
|
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. */
|
|
16
28
|
hasTriggers?: boolean;
|
|
29
|
+
/** SQL query that maps to a {@doclink virtual-entities | virtual entity}. */
|
|
17
30
|
expression?: string | ((em: any, where: ObjectQuery<E>, options: FindOptions<E, any, any, any>) => object);
|
|
31
|
+
/** Set {@doclink repositories#custom-repository | custom repository class}. */
|
|
18
32
|
repository?: () => Constructor;
|
|
19
33
|
};
|
package/decorators/Indexed.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EntityClass, Dictionary, AutoPath } from '../typings';
|
|
1
|
+
import type { EntityClass, Dictionary, AutoPath, IndexCallback } from '../typings';
|
|
2
2
|
import type { DeferMode } from '../enums';
|
|
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}`;
|
|
@@ -2,6 +2,7 @@ import type { TransactionOptions } from '../enums';
|
|
|
2
2
|
import type { ContextProvider } from '../typings';
|
|
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()`.
|
|
@@ -17,10 +17,10 @@ function Transactional(options = {}) {
|
|
|
17
17
|
throw new Error('@Transactional() should be use with async functions');
|
|
18
18
|
}
|
|
19
19
|
descriptor.value = async function (...args) {
|
|
20
|
-
const { context, ...txOptions } = options;
|
|
20
|
+
const { context, contextName, ...txOptions } = options;
|
|
21
21
|
const em = await (0, resolveContextProvider_1.resolveContextProvider)(this, context)
|
|
22
|
-
|| TransactionContext_1.TransactionContext.getEntityManager()
|
|
23
|
-
|| RequestContext_1.RequestContext.getEntityManager();
|
|
22
|
+
|| TransactionContext_1.TransactionContext.getEntityManager(contextName)
|
|
23
|
+
|| RequestContext_1.RequestContext.getEntityManager(contextName);
|
|
24
24
|
if (!em) {
|
|
25
25
|
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.`);
|
|
26
26
|
}
|
|
@@ -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;
|
|
@@ -12,6 +12,7 @@ class ArrayCollection {
|
|
|
12
12
|
items = new Set();
|
|
13
13
|
initialized = true;
|
|
14
14
|
dirty = false;
|
|
15
|
+
partial = false; // mark partially loaded collections, propagation is disabled for those
|
|
15
16
|
snapshot = []; // used to create a diff of the collection at commit time, undefined marks overridden values so we need to wipe when flushing
|
|
16
17
|
_count;
|
|
17
18
|
_property;
|
|
@@ -110,11 +111,12 @@ class ArrayCollection {
|
|
|
110
111
|
/**
|
|
111
112
|
* @internal
|
|
112
113
|
*/
|
|
113
|
-
hydrate(items, forcePropagate) {
|
|
114
|
+
hydrate(items, forcePropagate, partial) {
|
|
114
115
|
for (let i = 0; i < this.items.size; i++) {
|
|
115
116
|
delete this[i];
|
|
116
117
|
}
|
|
117
118
|
this.initialized = true;
|
|
119
|
+
this.partial = !!partial;
|
|
118
120
|
this.items.clear();
|
|
119
121
|
this._count = 0;
|
|
120
122
|
this.add(items);
|
|
@@ -277,6 +279,9 @@ class ArrayCollection {
|
|
|
277
279
|
isDirty() {
|
|
278
280
|
return this.dirty;
|
|
279
281
|
}
|
|
282
|
+
isPartial() {
|
|
283
|
+
return this.partial;
|
|
284
|
+
}
|
|
280
285
|
isEmpty() {
|
|
281
286
|
return this.count() === 0;
|
|
282
287
|
}
|
|
@@ -393,7 +398,7 @@ class ArrayCollection {
|
|
|
393
398
|
/** @ignore */
|
|
394
399
|
[node_util_1.inspect.custom](depth = 2) {
|
|
395
400
|
const object = { ...this };
|
|
396
|
-
const hidden = ['items', 'owner', '_property', '_count', 'snapshot', '_populated', '_snapshot', '_lazyInitialized', '_em', 'readonly'];
|
|
401
|
+
const hidden = ['items', 'owner', '_property', '_count', 'snapshot', '_populated', '_snapshot', '_lazyInitialized', '_em', 'readonly', 'partial'];
|
|
397
402
|
hidden.forEach(k => delete object[k]);
|
|
398
403
|
const ret = (0, node_util_1.inspect)(object, { depth });
|
|
399
404
|
const name = `${this.constructor.name}<${this.property?.type ?? 'unknown'}>`;
|
|
@@ -4,6 +4,11 @@ import type { EntityComparator } from '../utils/EntityComparator';
|
|
|
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
|
@@ -74,7 +74,7 @@ class EntityFactory {
|
|
|
74
74
|
continue;
|
|
75
75
|
}
|
|
76
76
|
if ([enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils_1.Utils.isPlainObject(data[prop.name])) {
|
|
77
|
-
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta
|
|
77
|
+
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
78
78
|
}
|
|
79
79
|
data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.platform, { key: prop.name, mode: 'hydration' });
|
|
80
80
|
}
|
|
@@ -234,6 +234,13 @@ class EntityFactory {
|
|
|
234
234
|
}
|
|
235
235
|
return entity;
|
|
236
236
|
}
|
|
237
|
+
assignDefaultValues(entity, meta) {
|
|
238
|
+
for (const prop of meta.props) {
|
|
239
|
+
if (prop.onCreate) {
|
|
240
|
+
entity[prop.name] ??= prop.onCreate(entity, this.em);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
237
244
|
hydrate(entity, meta, data, options) {
|
|
238
245
|
if (options.initialized) {
|
|
239
246
|
this.hydrator.hydrate(entity, meta, data, this, 'full', options.newEntity, options.convertCustomTypes, options.schema, this.driver.getSchemaName(meta, options));
|
|
@@ -245,6 +252,10 @@ class EntityFactory {
|
|
|
245
252
|
(0, wrap_1.helper)(entity)?.__loadedProperties.add(key);
|
|
246
253
|
(0, wrap_1.helper)(entity)?.__serializationContext.fields?.add(key);
|
|
247
254
|
});
|
|
255
|
+
const processOnCreateHooksEarly = options.processOnCreateHooksEarly ?? this.config.get('processOnCreateHooksEarly');
|
|
256
|
+
if (options.newEntity && processOnCreateHooksEarly) {
|
|
257
|
+
this.assignDefaultValues(entity, meta);
|
|
258
|
+
}
|
|
248
259
|
}
|
|
249
260
|
findEntity(data, meta, options) {
|
|
250
261
|
const schema = this.driver.getSchemaName(meta, options);
|
package/entity/EntityHelper.js
CHANGED
|
@@ -155,7 +155,7 @@ class EntityHelper {
|
|
|
155
155
|
wrapped.__data[prop.name] = Reference_1.Reference.wrapReference(val, prop);
|
|
156
156
|
// when propagation from inside hydration, we set the FK to the entity data immediately
|
|
157
157
|
if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
|
|
158
|
-
wrapped.__originalEntityData[prop.name] = Utils_1.Utils.getPrimaryKeyValues(wrapped.__data[prop.name], prop.targetMeta
|
|
158
|
+
wrapped.__originalEntityData[prop.name] = Utils_1.Utils.getPrimaryKeyValues(wrapped.__data[prop.name], prop.targetMeta, true);
|
|
159
159
|
}
|
|
160
160
|
else {
|
|
161
161
|
wrapped.__touched = !hydrator.isRunning();
|
|
@@ -175,6 +175,9 @@ class EntityHelper {
|
|
|
175
175
|
continue;
|
|
176
176
|
}
|
|
177
177
|
const inverse = value?.[prop2.name];
|
|
178
|
+
if (Utils_1.Utils.isCollection(inverse) && inverse.isPartial()) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
178
181
|
if (prop.kind === enums_1.ReferenceKind.MANY_TO_ONE && Utils_1.Utils.isCollection(inverse) && inverse.isInitialized()) {
|
|
179
182
|
inverse.addWithoutPropagation(owner);
|
|
180
183
|
(0, wrap_1.helper)(owner).__em?.getUnitOfWork().cancelOrphanRemoval(owner);
|
package/entity/EntityLoader.js
CHANGED
|
@@ -41,7 +41,7 @@ class EntityLoader {
|
|
|
41
41
|
options.refresh ??= false;
|
|
42
42
|
options.convertCustomTypes ??= true;
|
|
43
43
|
if (references.length > 0) {
|
|
44
|
-
await this.populateScalar(meta, references, options);
|
|
44
|
+
await this.populateScalar(meta, references, { ...options, populateWhere: undefined });
|
|
45
45
|
}
|
|
46
46
|
populate = this.normalizePopulate(entityName, populate, options.strategy, options.lookup);
|
|
47
47
|
const invalid = populate.find(({ field }) => !this.em.canPopulate(entityName, field));
|
|
@@ -143,18 +143,22 @@ class EntityLoader {
|
|
|
143
143
|
const innerOrderBy = Utils_1.Utils.asArray(options.orderBy)
|
|
144
144
|
.filter(orderBy => (Array.isArray(orderBy[prop.name]) && orderBy[prop.name].length > 0) || Utils_1.Utils.isObject(orderBy[prop.name]))
|
|
145
145
|
.flatMap(orderBy => orderBy[prop.name]);
|
|
146
|
+
const where = await this.extractChildCondition(options, prop);
|
|
146
147
|
if (prop.kind === enums_1.ReferenceKind.MANY_TO_MANY && this.driver.getPlatform().usesPivotTable()) {
|
|
147
148
|
const res = await this.findChildrenFromPivotTable(filtered, prop, options, innerOrderBy, populate, !!ref);
|
|
148
149
|
return Utils_1.Utils.flatten(res);
|
|
149
150
|
}
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
const { items, partial } = await this.findChildren(options.filtered ?? entities, prop, populate, {
|
|
152
|
+
...options,
|
|
153
|
+
where,
|
|
154
|
+
orderBy: innerOrderBy,
|
|
155
|
+
}, !!(ref || prop.mapToPk));
|
|
156
|
+
this.initializeCollections(filtered, prop, field, items, innerOrderBy.length > 0, partial);
|
|
157
|
+
return items;
|
|
154
158
|
}
|
|
155
159
|
async populateScalar(meta, filtered, options) {
|
|
156
160
|
const pk = Utils_1.Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
157
|
-
const ids = Utils_1.Utils.unique(filtered.map(e => Utils_1.Utils.getPrimaryKeyValues(e, meta
|
|
161
|
+
const ids = Utils_1.Utils.unique(filtered.map(e => Utils_1.Utils.getPrimaryKeyValues(e, meta, true)));
|
|
158
162
|
const where = this.mergePrimaryCondition(ids, pk, options, meta, this.metadata, this.driver.getPlatform());
|
|
159
163
|
const { filters, convertCustomTypes, lockMode, strategy, populateWhere, connectionType, logging, fields } = options;
|
|
160
164
|
await this.em.find(meta.className, where, {
|
|
@@ -163,15 +167,15 @@ class EntityLoader {
|
|
|
163
167
|
populate: [],
|
|
164
168
|
});
|
|
165
169
|
}
|
|
166
|
-
initializeCollections(filtered, prop, field, children, customOrder) {
|
|
170
|
+
initializeCollections(filtered, prop, field, children, customOrder, partial) {
|
|
167
171
|
if (prop.kind === enums_1.ReferenceKind.ONE_TO_MANY) {
|
|
168
|
-
this.initializeOneToMany(filtered, children, prop, field);
|
|
172
|
+
this.initializeOneToMany(filtered, children, prop, field, partial);
|
|
169
173
|
}
|
|
170
174
|
if (prop.kind === enums_1.ReferenceKind.MANY_TO_MANY && !this.driver.getPlatform().usesPivotTable()) {
|
|
171
|
-
this.initializeManyToMany(filtered, children, prop, field, customOrder);
|
|
175
|
+
this.initializeManyToMany(filtered, children, prop, field, customOrder, partial);
|
|
172
176
|
}
|
|
173
177
|
}
|
|
174
|
-
initializeOneToMany(filtered, children, prop, field) {
|
|
178
|
+
initializeOneToMany(filtered, children, prop, field, partial) {
|
|
175
179
|
const mapToPk = prop.targetMeta.properties[prop.mappedBy].mapToPk;
|
|
176
180
|
const map = {};
|
|
177
181
|
for (const entity of filtered) {
|
|
@@ -187,14 +191,14 @@ class EntityLoader {
|
|
|
187
191
|
}
|
|
188
192
|
for (const entity of filtered) {
|
|
189
193
|
const key = (0, wrap_1.helper)(entity).getSerializedPrimaryKey();
|
|
190
|
-
entity[field].hydrate(map[key]);
|
|
194
|
+
entity[field].hydrate(map[key], undefined, partial);
|
|
191
195
|
}
|
|
192
196
|
}
|
|
193
|
-
initializeManyToMany(filtered, children, prop, field, customOrder) {
|
|
197
|
+
initializeManyToMany(filtered, children, prop, field, customOrder, partial) {
|
|
194
198
|
if (prop.mappedBy) {
|
|
195
199
|
for (const entity of filtered) {
|
|
196
200
|
const items = children.filter(child => child[prop.mappedBy].contains(entity, false));
|
|
197
|
-
entity[field].hydrate(items, true);
|
|
201
|
+
entity[field].hydrate(items, true, partial);
|
|
198
202
|
}
|
|
199
203
|
}
|
|
200
204
|
else { // owning side of M:N without pivot table needs to be reordered
|
|
@@ -204,7 +208,7 @@ class EntityLoader {
|
|
|
204
208
|
if (!customOrder) {
|
|
205
209
|
items.sort((a, b) => order.indexOf(a) - order.indexOf(b));
|
|
206
210
|
}
|
|
207
|
-
entity[field].hydrate(items, true);
|
|
211
|
+
entity[field].hydrate(items, true, partial);
|
|
208
212
|
}
|
|
209
213
|
}
|
|
210
214
|
}
|
|
@@ -213,6 +217,7 @@ class EntityLoader {
|
|
|
213
217
|
const meta = prop.targetMeta;
|
|
214
218
|
let fk = Utils_1.Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
215
219
|
let schema = options.schema;
|
|
220
|
+
const partial = !Utils_1.Utils.isEmpty(prop.where) || !Utils_1.Utils.isEmpty(options.where);
|
|
216
221
|
if (prop.kind === enums_1.ReferenceKind.ONE_TO_MANY || (prop.kind === enums_1.ReferenceKind.MANY_TO_MANY && !prop.owner)) {
|
|
217
222
|
fk = meta.properties[prop.mappedBy].name;
|
|
218
223
|
}
|
|
@@ -222,7 +227,7 @@ class EntityLoader {
|
|
|
222
227
|
children.push(...this.filterByReferences(entities, prop.name, options.refresh));
|
|
223
228
|
}
|
|
224
229
|
if (children.length === 0) {
|
|
225
|
-
return [];
|
|
230
|
+
return { items: [], partial };
|
|
226
231
|
}
|
|
227
232
|
if (!schema && [enums_1.ReferenceKind.ONE_TO_ONE, enums_1.ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
|
|
228
233
|
schema = children.find(e => e.__helper.__schema)?.__helper.__schema;
|
|
@@ -271,7 +276,7 @@ class EntityLoader {
|
|
|
271
276
|
this.em.getUnitOfWork()['loadedEntities'].delete(item);
|
|
272
277
|
}
|
|
273
278
|
}
|
|
274
|
-
return items;
|
|
279
|
+
return { items, partial };
|
|
275
280
|
}
|
|
276
281
|
mergePrimaryCondition(ids, pk, options, meta, metadata, platform) {
|
|
277
282
|
const cond1 = QueryHelper_1.QueryHelper.processWhere({ where: { [pk]: { $in: ids } }, entityName: meta.className, metadata, platform, convertCustomTypes: !options.convertCustomTypes });
|
|
@@ -320,11 +325,12 @@ class EntityLoader {
|
|
|
320
325
|
for (const entity of entities) {
|
|
321
326
|
visited.delete(entity);
|
|
322
327
|
}
|
|
323
|
-
const
|
|
328
|
+
const unique = Utils_1.Utils.unique(children);
|
|
329
|
+
const filtered = unique.filter(e => !visited.has(e));
|
|
324
330
|
for (const entity of entities) {
|
|
325
331
|
visited.add(entity);
|
|
326
332
|
}
|
|
327
|
-
await this.populate(prop.type,
|
|
333
|
+
await this.populate(prop.type, unique, populate.children ?? populate.all, {
|
|
328
334
|
where: await this.extractChildCondition(options, prop, false),
|
|
329
335
|
orderBy: innerOrderBy,
|
|
330
336
|
fields,
|
|
@@ -341,6 +347,8 @@ class EntityLoader {
|
|
|
341
347
|
refresh: refresh && !filtered.every(item => options.visited.has(item)),
|
|
342
348
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
343
349
|
visited: options.visited,
|
|
350
|
+
// @ts-ignore not a public option
|
|
351
|
+
filtered,
|
|
344
352
|
});
|
|
345
353
|
}
|
|
346
354
|
/** @internal */
|
package/entity/Reference.d.ts
CHANGED
|
@@ -56,6 +56,11 @@ export declare class ScalarReference<Value> {
|
|
|
56
56
|
* Returns either the whole entity, or the requested property.
|
|
57
57
|
*/
|
|
58
58
|
load(options?: Omit<LoadReferenceOptions<any, any>, 'populate' | 'fields' | 'exclude'>): Promise<Value | undefined>;
|
|
59
|
+
/**
|
|
60
|
+
* Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
|
|
61
|
+
* Returns the entity or throws an error just like `em.findOneOrFail()` (and respects the same config options).
|
|
62
|
+
*/
|
|
63
|
+
loadOrFail(options?: Omit<LoadReferenceOrFailOptions<any, any>, 'populate' | 'fields' | 'exclude'>): Promise<Value>;
|
|
59
64
|
set(value: Value): void;
|
|
60
65
|
bind<Entity extends object>(entity: Entity, property: EntityKey<Entity>): void;
|
|
61
66
|
unwrap(): Value | undefined;
|