• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

typeorm / typeorm / 16405830134

21 Jul 2025 12:22AM UTC coverage: 76.442% (+0.002%) from 76.44%
16405830134

Pull #11576

github

web-flow
Merge ccddce2d3 into d57fe3bd8
Pull Request #11576: fix(sqlserver): queries returning multiple record sets

9324 of 12885 branches covered (72.36%)

Branch coverage included in aggregate %.

55 of 78 new or added lines in 25 files covered. (70.51%)

2073 existing lines in 24 files now uncovered.

19016 of 24189 relevant lines covered (78.61%)

119538.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.54
/src/entity-manager/EntityManager.ts
1
import { DataSource } from "../data-source/DataSource"
2
import { FindManyOptions } from "../find-options/FindManyOptions"
3
import { EntityTarget } from "../common/EntityTarget"
4
import { ObjectType } from "../common/ObjectType"
5
import { EntityNotFoundError } from "../error/EntityNotFoundError"
24✔
6
import { QueryRunnerProviderAlreadyReleasedError } from "../error/QueryRunnerProviderAlreadyReleasedError"
24✔
7
import { FindOneOptions } from "../find-options/FindOneOptions"
8
import { DeepPartial } from "../common/DeepPartial"
9
import { RemoveOptions } from "../repository/RemoveOptions"
10
import { SaveOptions } from "../repository/SaveOptions"
11
import { NoNeedToReleaseEntityManagerError } from "../error/NoNeedToReleaseEntityManagerError"
24✔
12
import { MongoRepository } from "../repository/MongoRepository"
24✔
13
import { TreeRepository } from "../repository/TreeRepository"
24✔
14
import { Repository } from "../repository/Repository"
24✔
15
import { FindOptionsUtils } from "../find-options/FindOptionsUtils"
24✔
16
import { PlainObjectToNewEntityTransformer } from "../query-builder/transformer/PlainObjectToNewEntityTransformer"
24✔
17
import { PlainObjectToDatabaseEntityTransformer } from "../query-builder/transformer/PlainObjectToDatabaseEntityTransformer"
24✔
18
import {
24✔
19
    CustomRepositoryCannotInheritRepositoryError,
20
    CustomRepositoryNotFoundError,
21
    TreeRepositoryNotSupportedError,
22
    TypeORMError,
23
} from "../error"
24
import { AbstractRepository } from "../repository/AbstractRepository"
24✔
25
import { QueryRunner } from "../query-runner/QueryRunner"
26
import { SelectQueryBuilder } from "../query-builder/SelectQueryBuilder"
27
import { QueryDeepPartialEntity } from "../query-builder/QueryPartialEntity"
28
import { EntityPersistExecutor } from "../persistence/EntityPersistExecutor"
24✔
29
import { ObjectId } from "../driver/mongodb/typings"
30
import { InsertResult } from "../query-builder/result/InsertResult"
31
import { UpdateResult } from "../query-builder/result/UpdateResult"
32
import { DeleteResult } from "../query-builder/result/DeleteResult"
33
import { FindOptionsWhere } from "../find-options/FindOptionsWhere"
34
import { IsolationLevel } from "../driver/types/IsolationLevel"
35
import { ObjectUtils } from "../util/ObjectUtils"
24✔
36
import { getMetadataArgsStorage } from "../globals"
24✔
37
import { UpsertOptions } from "../repository/UpsertOptions"
38
import { InstanceChecker } from "../util/InstanceChecker"
24✔
39
import { ObjectLiteral } from "../common/ObjectLiteral"
40
import { PickKeysByType } from "../common/PickKeysByType"
41
import { buildSqlTag } from "../util/SqlTagUtils"
24✔
42
import { OrmUtils } from "../util/OrmUtils"
24✔
43

44
/**
45
 * Entity manager supposed to work with any entity, automatically find its repository and call its methods,
46
 * whatever entity type are you passing.
47
 */
48
export class EntityManager {
24✔
49
    readonly "@instanceof" = Symbol.for("EntityManager")
297,501✔
50

51
    // -------------------------------------------------------------------------
52
    // Public Properties
53
    // -------------------------------------------------------------------------
54

55
    /**
56
     * Connection used by this entity manager.
57
     */
58
    readonly connection: DataSource
59

60
    /**
61
     * Custom query runner to be used for operations in this entity manager.
62
     * Used only in non-global entity manager.
63
     */
64
    readonly queryRunner?: QueryRunner
65

66
    // -------------------------------------------------------------------------
67
    // Protected Properties
68
    // -------------------------------------------------------------------------
69

70
    /**
71
     * Once created and then reused by repositories.
72
     * Created as a future replacement for the #repositories to provide a bit more perf optimization.
73
     */
74
    protected repositories = new Map<EntityTarget<any>, Repository<any>>()
297,501✔
75

76
    /**
77
     * Once created and then reused by repositories.
78
     */
79
    protected treeRepositories: TreeRepository<any>[] = []
297,501✔
80

81
    /**
82
     * Plain to object transformer used in create and merge operations.
83
     */
84
    protected plainObjectToEntityTransformer =
297,501✔
85
        new PlainObjectToNewEntityTransformer()
86

87
    // -------------------------------------------------------------------------
88
    // Constructor
89
    // -------------------------------------------------------------------------
90

91
    constructor(connection: DataSource, queryRunner?: QueryRunner) {
92
        this.connection = connection
297,501✔
93
        if (queryRunner) {
297,501✔
94
            this.queryRunner = queryRunner
284,543✔
95
            // dynamic: this.queryRunner = manager;
96
            ObjectUtils.assign(this.queryRunner, { manager: this })
284,543✔
97
        }
98
    }
99

100
    // -------------------------------------------------------------------------
101
    // Public Methods
102
    // -------------------------------------------------------------------------
103

104
    /**
105
     * Wraps given function execution (and all operations made there) in a transaction.
106
     * All database operations must be executed using provided entity manager.
107
     */
108
    async transaction<T>(
109
        runInTransaction: (entityManager: EntityManager) => Promise<T>,
110
    ): Promise<T>
111

112
    /**
113
     * Wraps given function execution (and all operations made there) in a transaction.
114
     * All database operations must be executed using provided entity manager.
115
     */
116
    async transaction<T>(
117
        isolationLevel: IsolationLevel,
118
        runInTransaction: (entityManager: EntityManager) => Promise<T>,
119
    ): Promise<T>
120

121
    /**
122
     * Wraps given function execution (and all operations made there) in a transaction.
123
     * All database operations must be executed using provided entity manager.
124
     */
125
    async transaction<T>(
126
        isolationOrRunInTransaction:
127
            | IsolationLevel
128
            | ((entityManager: EntityManager) => Promise<T>),
129
        runInTransactionParam?: (entityManager: EntityManager) => Promise<T>,
130
    ): Promise<T> {
131
        const isolation =
132
            typeof isolationOrRunInTransaction === "string"
881✔
133
                ? isolationOrRunInTransaction
134
                : undefined
135
        const runInTransaction =
136
            typeof isolationOrRunInTransaction === "function"
881✔
137
                ? isolationOrRunInTransaction
138
                : runInTransactionParam
139

140
        if (!runInTransaction) {
881!
UNCOV
141
            throw new TypeORMError(
×
142
                `Transaction method requires callback in second parameter if isolation level is supplied.`,
143
            )
144
        }
145

146
        if (this.queryRunner && this.queryRunner.isReleased)
881!
UNCOV
147
            throw new QueryRunnerProviderAlreadyReleasedError()
×
148

149
        // if query runner is already defined in this class, it means this entity manager was already created for a single connection
150
        // if its not defined we create a new query runner - single connection where we'll execute all our operations
151
        const queryRunner =
152
            this.queryRunner || this.connection.createQueryRunner()
881✔
153

154
        try {
881✔
155
            await queryRunner.startTransaction(isolation)
881✔
156
            const result = await runInTransaction(queryRunner.manager)
875✔
157
            await queryRunner.commitTransaction()
612✔
158
            return result
610✔
159
        } catch (err) {
160
            try {
271✔
161
                // we throw original error even if rollback thrown an error
162
                await queryRunner.rollbackTransaction()
271✔
163
            } catch (rollbackError) {}
164
            throw err
271✔
165
        } finally {
166
            if (!this.queryRunner)
881✔
167
                // if we used a new query runner provider then release it
168
                await queryRunner.release()
541✔
169
        }
170
    }
171

172
    /**
173
     * Executes raw SQL query and returns raw database results.
174
     *
175
     * @see [Official docs](https://typeorm.io/docs/Working%20with%20Entity%20Manager/entity-manager-api/) for examples.
176
     */
177
    async query<T = any>(
178
        query: string,
179
        parameters?: any[],
180
        useStructuredResult?: boolean,
181
    ): Promise<T> {
182
        return this.connection.query(
184✔
183
            query,
184
            parameters,
185
            this.queryRunner,
186
            useStructuredResult,
187
        )
188
    }
189

190
    /**
191
     * Tagged template function that executes raw SQL query and returns raw database results.
192
     * Template expressions are automatically transformed into database parameters.
193
     * Raw query execution is supported only by relational databases (MongoDB is not supported).
194
     * Note: Don't call this as a regular function, it is meant to be used with backticks to tag a template literal.
195
     * Example: entityManager.sql`SELECT * FROM table_name WHERE id = ${id}`
196
     */
197
    async sql<T = any>(
198
        strings: TemplateStringsArray,
199
        ...values: unknown[]
200
    ): Promise<T> {
201
        const { query, parameters } = buildSqlTag({
6✔
202
            driver: this.connection.driver,
203
            strings: strings,
204
            expressions: values,
205
        })
206

207
        return await this.query(query, parameters)
6✔
208
    }
209

210
    /**
211
     * Creates a new query builder that can be used to build a SQL query.
212
     */
213
    createQueryBuilder<Entity extends ObjectLiteral>(
214
        entityClass: EntityTarget<Entity>,
215
        alias: string,
216
        queryRunner?: QueryRunner,
217
    ): SelectQueryBuilder<Entity>
218

219
    /**
220
     * Creates a new query builder that can be used to build a SQL query.
221
     */
222
    createQueryBuilder(queryRunner?: QueryRunner): SelectQueryBuilder<any>
223

224
    /**
225
     * Creates a new query builder that can be used to build a SQL query.
226
     */
227
    createQueryBuilder<Entity extends ObjectLiteral>(
228
        entityClass?: EntityTarget<Entity> | QueryRunner,
229
        alias?: string,
230
        queryRunner?: QueryRunner,
231
    ): SelectQueryBuilder<Entity> {
232
        if (alias) {
652,516✔
233
            return this.connection.createQueryBuilder(
419,441✔
234
                entityClass as EntityTarget<Entity>,
235
                alias,
236
                queryRunner || this.queryRunner,
738,186✔
237
            )
238
        } else {
239
            return this.connection.createQueryBuilder(
233,075✔
240
                (entityClass as QueryRunner | undefined) ||
698,663✔
241
                    queryRunner ||
242
                    this.queryRunner,
243
            )
244
        }
245
    }
246

247
    /**
248
     * Checks if entity has an id.
249
     */
250
    hasId(entity: any): boolean
251

252
    /**
253
     * Checks if entity of given schema name has an id.
254
     */
255
    hasId(target: Function | string, entity: any): boolean
256

257
    /**
258
     * Checks if entity has an id by its Function type or schema name.
259
     */
260
    hasId(targetOrEntity: any | Function | string, maybeEntity?: any): boolean {
261
        const target =
262
            arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor
314!
263
        const entity = arguments.length === 2 ? maybeEntity : targetOrEntity
314!
264
        const metadata = this.connection.getMetadata(target)
314✔
265
        return metadata.hasId(entity)
314✔
266
    }
267

268
    /**
269
     * Gets entity mixed id.
270
     */
271
    getId(entity: any): any
272

273
    /**
274
     * Gets entity mixed id.
275
     */
276
    getId(target: EntityTarget<any>, entity: any): any
277

278
    /**
279
     * Gets entity mixed id.
280
     */
281
    getId(targetOrEntity: any | EntityTarget<any>, maybeEntity?: any): any {
282
        const target =
283
            arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor
72!
284
        const entity = arguments.length === 2 ? maybeEntity : targetOrEntity
72!
285
        const metadata = this.connection.getMetadata(target)
72✔
286
        return metadata.getEntityIdMixedMap(entity)
72✔
287
    }
288

289
    /**
290
     * Creates a new entity instance and copies all entity properties from this object into a new entity.
291
     * Note that it copies only properties that present in entity schema.
292
     */
293
    create<Entity, EntityLike extends DeepPartial<Entity>>(
294
        entityClass: EntityTarget<Entity>,
295
        plainObject?: EntityLike,
296
    ): Entity
297

298
    /**
299
     * Creates a new entities and copies all entity properties from given objects into their new entities.
300
     * Note that it copies only properties that present in entity schema.
301
     */
302
    create<Entity, EntityLike extends DeepPartial<Entity>>(
303
        entityClass: EntityTarget<Entity>,
304
        plainObjects?: EntityLike[],
305
    ): Entity[]
306

307
    /**
308
     * Creates a new entity instance or instances.
309
     * Can copy properties from the given object into new entities.
310
     */
311
    create<Entity, EntityLike extends DeepPartial<Entity>>(
312
        entityClass: EntityTarget<Entity>,
313
        plainObjectOrObjects?: EntityLike | EntityLike[],
314
    ): Entity | Entity[] {
315
        const metadata = this.connection.getMetadata(entityClass)
4,137✔
316

317
        if (!plainObjectOrObjects) return metadata.create(this.queryRunner)
4,137✔
318

319
        if (Array.isArray(plainObjectOrObjects))
2,042✔
320
            return (plainObjectOrObjects as EntityLike[]).map(
96✔
321
                (plainEntityLike) => this.create(entityClass, plainEntityLike),
240✔
322
            )
323

324
        const mergeIntoEntity = metadata.create(this.queryRunner)
1,946✔
325
        this.plainObjectToEntityTransformer.transform(
1,946✔
326
            mergeIntoEntity,
327
            plainObjectOrObjects,
328
            metadata,
329
            true,
330
        )
331
        return mergeIntoEntity
1,946✔
332
    }
333

334
    /**
335
     * Merges two entities into one new entity.
336
     */
337
    merge<Entity extends ObjectLiteral>(
338
        entityClass: EntityTarget<Entity>,
339
        mergeIntoEntity: Entity,
340
        ...entityLikes: DeepPartial<Entity>[]
341
    ): Entity {
342
        // todo: throw exception if entity manager is released
343
        const metadata = this.connection.getMetadata(entityClass)
1,063,609✔
344
        entityLikes.forEach((object) =>
1,063,609✔
345
            this.plainObjectToEntityTransformer.transform(
1,063,711✔
346
                mergeIntoEntity,
347
                object,
348
                metadata,
349
            ),
350
        )
351
        return mergeIntoEntity
1,063,609✔
352
    }
353

354
    /**
355
     * Creates a new entity from the given plain javascript object. If entity already exist in the database, then
356
     * it loads it (and everything related to it), replaces all values with the new ones from the given object
357
     * and returns this new entity. This new entity is actually a loaded from the db entity with all properties
358
     * replaced from the new object.
359
     */
360
    async preload<Entity extends ObjectLiteral>(
361
        entityClass: EntityTarget<Entity>,
362
        entityLike: DeepPartial<Entity>,
363
    ): Promise<Entity | undefined> {
364
        const metadata = this.connection.getMetadata(entityClass)
76✔
365
        const plainObjectToDatabaseEntityTransformer =
366
            new PlainObjectToDatabaseEntityTransformer(this.connection.manager)
76✔
367
        const transformedEntity =
368
            await plainObjectToDatabaseEntityTransformer.transform(
76✔
369
                entityLike,
370
                metadata,
371
            )
372
        if (transformedEntity)
76✔
373
            return this.merge(
76✔
374
                entityClass as any,
375
                transformedEntity as Entity,
376
                entityLike,
377
            )
378

UNCOV
379
        return undefined
×
380
    }
381

382
    /**
383
     * Saves all given entities in the database.
384
     * If entities do not exist in the database then inserts, otherwise updates.
385
     */
386
    save<Entity>(entities: Entity[], options?: SaveOptions): Promise<Entity[]>
387

388
    /**
389
     * Saves all given entities in the database.
390
     * If entities do not exist in the database then inserts, otherwise updates.
391
     */
392
    save<Entity>(entity: Entity, options?: SaveOptions): Promise<Entity>
393

394
    /**
395
     * Saves all given entities in the database.
396
     * If entities do not exist in the database then inserts, otherwise updates.
397
     */
398
    save<Entity, T extends DeepPartial<Entity>>(
399
        targetOrEntity: EntityTarget<Entity>,
400
        entities: T[],
401
        options: SaveOptions & { reload: false },
402
    ): Promise<T[]>
403

404
    /**
405
     * Saves all given entities in the database.
406
     * If entities do not exist in the database then inserts, otherwise updates.
407
     */
408
    save<Entity, T extends DeepPartial<Entity>>(
409
        targetOrEntity: EntityTarget<Entity>,
410
        entities: T[],
411
        options?: SaveOptions,
412
    ): Promise<(T & Entity)[]>
413

414
    /**
415
     * Saves a given entity in the database.
416
     * If entity does not exist in the database then inserts, otherwise updates.
417
     */
418
    save<Entity, T extends DeepPartial<Entity>>(
419
        targetOrEntity: EntityTarget<Entity>,
420
        entity: T,
421
        options: SaveOptions & { reload: false },
422
    ): Promise<T>
423

424
    /**
425
     * Saves a given entity in the database.
426
     * If entity does not exist in the database then inserts, otherwise updates.
427
     */
428
    save<Entity, T extends DeepPartial<Entity>>(
429
        targetOrEntity: EntityTarget<Entity>,
430
        entity: T,
431
        options?: SaveOptions,
432
    ): Promise<T & Entity>
433

434
    /**
435
     * Saves a given entity in the database.
436
     */
437
    save<Entity extends ObjectLiteral, T extends DeepPartial<Entity>>(
438
        targetOrEntity: (T | T[]) | EntityTarget<Entity>,
439
        maybeEntityOrOptions?: T | T[],
440
        maybeOptions?: SaveOptions,
441
    ): Promise<T | T[]> {
442
        // normalize mixed parameters
443
        let target =
444
            arguments.length > 1 &&
153,543✔
445
            (typeof targetOrEntity === "function" ||
446
                InstanceChecker.isEntitySchema(targetOrEntity) ||
447
                typeof targetOrEntity === "string")
448
                ? (targetOrEntity as Function | string)
449
                : undefined
450
        const entity: T | T[] = target
153,543✔
451
            ? (maybeEntityOrOptions as T | T[])
452
            : (targetOrEntity as T | T[])
453
        const options = target
153,543✔
454
            ? maybeOptions
455
            : (maybeEntityOrOptions as SaveOptions)
456

457
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
153,543✔
458

459
        // if user passed empty array of entities then we don't need to do anything
460
        if (Array.isArray(entity) && entity.length === 0)
153,543✔
461
            return Promise.resolve(entity)
26✔
462

463
        // execute save operation
464
        return new EntityPersistExecutor(
153,517✔
465
            this.connection,
466
            this.queryRunner,
467
            "save",
468
            target,
469
            entity,
470
            options,
471
        )
472
            .execute()
473
            .then(() => entity)
153,225✔
474
    }
475

476
    /**
477
     * Removes a given entity from the database.
478
     */
479
    remove<Entity>(entity: Entity, options?: RemoveOptions): Promise<Entity>
480

481
    /**
482
     * Removes a given entity from the database.
483
     */
484
    remove<Entity>(
485
        targetOrEntity: EntityTarget<Entity>,
486
        entity: Entity,
487
        options?: RemoveOptions,
488
    ): Promise<Entity>
489

490
    /**
491
     * Removes a given entity from the database.
492
     */
493
    remove<Entity>(entity: Entity[], options?: RemoveOptions): Promise<Entity>
494

495
    /**
496
     * Removes a given entity from the database.
497
     */
498
    remove<Entity>(
499
        targetOrEntity: EntityTarget<Entity>,
500
        entity: Entity[],
501
        options?: RemoveOptions,
502
    ): Promise<Entity[]>
503

504
    /**
505
     * Removes a given entity from the database.
506
     */
507
    remove<Entity extends ObjectLiteral>(
508
        targetOrEntity: (Entity | Entity[]) | EntityTarget<Entity>,
509
        maybeEntityOrOptions?: Entity | Entity[],
510
        maybeOptions?: RemoveOptions,
511
    ): Promise<Entity | Entity[]> {
512
        // normalize mixed parameters
513
        const target =
514
            arguments.length > 1 &&
1,214✔
515
            (typeof targetOrEntity === "function" ||
516
                InstanceChecker.isEntitySchema(targetOrEntity) ||
517
                typeof targetOrEntity === "string")
518
                ? (targetOrEntity as Function | string)
519
                : undefined
520
        const entity: Entity | Entity[] = target
1,214✔
521
            ? (maybeEntityOrOptions as Entity | Entity[])
522
            : (targetOrEntity as Entity | Entity[])
523
        const options = target
1,214✔
524
            ? maybeOptions
525
            : (maybeEntityOrOptions as SaveOptions)
526

527
        // if user passed empty array of entities then we don't need to do anything
528
        if (Array.isArray(entity) && entity.length === 0)
1,214!
UNCOV
529
            return Promise.resolve(entity)
×
530

531
        // execute save operation
532
        return new EntityPersistExecutor(
1,214✔
533
            this.connection,
534
            this.queryRunner,
535
            "remove",
536
            target,
537
            entity,
538
            options,
539
        )
540
            .execute()
541
            .then(() => entity)
1,070✔
542
    }
543

544
    /**
545
     * Records the delete date of all given entities.
546
     */
547
    softRemove<Entity>(
548
        entities: Entity[],
549
        options?: SaveOptions,
550
    ): Promise<Entity[]>
551

552
    /**
553
     * Records the delete date of a given entity.
554
     */
555
    softRemove<Entity>(entity: Entity, options?: SaveOptions): Promise<Entity>
556

557
    /**
558
     * Records the delete date of all given entities.
559
     */
560
    softRemove<Entity, T extends DeepPartial<Entity>>(
561
        targetOrEntity: EntityTarget<Entity>,
562
        entities: T[],
563
        options?: SaveOptions,
564
    ): Promise<T[]>
565

566
    /**
567
     * Records the delete date of a given entity.
568
     */
569
    softRemove<Entity, T extends DeepPartial<Entity>>(
570
        targetOrEntity: EntityTarget<Entity>,
571
        entity: T,
572
        options?: SaveOptions,
573
    ): Promise<T>
574

575
    /**
576
     * Records the delete date of one or many given entities.
577
     */
578
    softRemove<Entity extends ObjectLiteral, T extends DeepPartial<Entity>>(
579
        targetOrEntity: (T | T[]) | EntityTarget<Entity>,
580
        maybeEntityOrOptions?: T | T[],
581
        maybeOptions?: SaveOptions,
582
    ): Promise<T | T[]> {
583
        // normalize mixed parameters
584
        let target =
585
            arguments.length > 1 &&
390✔
586
            (typeof targetOrEntity === "function" ||
587
                InstanceChecker.isEntitySchema(targetOrEntity) ||
588
                typeof targetOrEntity === "string")
589
                ? (targetOrEntity as Function | string)
590
                : undefined
591
        const entity: T | T[] = target
390✔
592
            ? (maybeEntityOrOptions as T | T[])
593
            : (targetOrEntity as T | T[])
594
        const options = target
390✔
595
            ? maybeOptions
596
            : (maybeEntityOrOptions as SaveOptions)
597

598
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
390!
599

600
        // if user passed empty array of entities then we don't need to do anything
601
        if (Array.isArray(entity) && entity.length === 0)
390!
UNCOV
602
            return Promise.resolve(entity)
×
603

604
        // execute soft-remove operation
605
        return new EntityPersistExecutor(
390✔
606
            this.connection,
607
            this.queryRunner,
608
            "soft-remove",
609
            target,
610
            entity,
611
            options,
612
        )
613
            .execute()
614
            .then(() => entity)
342✔
615
    }
616

617
    /**
618
     * Recovers all given entities.
619
     */
620
    recover<Entity>(
621
        entities: Entity[],
622
        options?: SaveOptions,
623
    ): Promise<Entity[]>
624

625
    /**
626
     * Recovers a given entity.
627
     */
628
    recover<Entity>(entity: Entity, options?: SaveOptions): Promise<Entity>
629

630
    /**
631
     * Recovers all given entities.
632
     */
633
    recover<Entity, T extends DeepPartial<Entity>>(
634
        targetOrEntity: EntityTarget<Entity>,
635
        entities: T[],
636
        options?: SaveOptions,
637
    ): Promise<T[]>
638

639
    /**
640
     * Recovers a given entity.
641
     */
642
    recover<Entity, T extends DeepPartial<Entity>>(
643
        targetOrEntity: EntityTarget<Entity>,
644
        entity: T,
645
        options?: SaveOptions,
646
    ): Promise<T>
647

648
    /**
649
     * Recovers one or many given entities.
650
     */
651
    recover<Entity extends ObjectLiteral, T extends DeepPartial<Entity>>(
652
        targetOrEntity: (T | T[]) | EntityTarget<Entity>,
653
        maybeEntityOrOptions?: T | T[],
654
        maybeOptions?: SaveOptions,
655
    ): Promise<T | T[]> {
656
        // normalize mixed parameters
657
        let target =
658
            arguments.length > 1 &&
144✔
659
            (typeof targetOrEntity === "function" ||
660
                InstanceChecker.isEntitySchema(targetOrEntity) ||
661
                typeof targetOrEntity === "string")
662
                ? (targetOrEntity as Function | string)
663
                : undefined
664
        const entity: T | T[] = target
144✔
665
            ? (maybeEntityOrOptions as T | T[])
666
            : (targetOrEntity as T | T[])
667
        const options = target
144✔
668
            ? maybeOptions
669
            : (maybeEntityOrOptions as SaveOptions)
670

671
        if (InstanceChecker.isEntitySchema(target)) target = target.options.name
144!
672

673
        // if user passed empty array of entities then we don't need to do anything
674
        if (Array.isArray(entity) && entity.length === 0)
144!
UNCOV
675
            return Promise.resolve(entity)
×
676

677
        // execute recover operation
678
        return new EntityPersistExecutor(
144✔
679
            this.connection,
680
            this.queryRunner,
681
            "recover",
682
            target,
683
            entity,
684
            options,
685
        )
686
            .execute()
687
            .then(() => entity)
96✔
688
    }
689

690
    /**
691
     * Inserts a given entity into the database.
692
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
693
     * Executes fast and efficient INSERT query.
694
     * Does not check if entity exist in the database, so query will fail if duplicate entity is being inserted.
695
     * You can execute bulk inserts using this method.
696
     */
697
    async insert<Entity extends ObjectLiteral>(
698
        target: EntityTarget<Entity>,
699
        entity:
700
            | QueryDeepPartialEntity<Entity>
701
            | QueryDeepPartialEntity<Entity>[],
702
    ): Promise<InsertResult> {
703
        return this.createQueryBuilder()
321✔
704
            .insert()
705
            .into(target)
706
            .values(entity)
707
            .execute()
708
    }
709

710
    async upsert<Entity extends ObjectLiteral>(
711
        target: EntityTarget<Entity>,
712
        entityOrEntities:
713
            | QueryDeepPartialEntity<Entity>
714
            | QueryDeepPartialEntity<Entity>[],
715
        conflictPathsOrOptions: string[] | UpsertOptions<Entity>,
716
    ): Promise<InsertResult> {
717
        const metadata = this.connection.getMetadata(target)
502✔
718

719
        let options: UpsertOptions<Entity>
720

721
        if (Array.isArray(conflictPathsOrOptions)) {
502✔
722
            options = {
456✔
723
                conflictPaths: conflictPathsOrOptions,
724
            }
725
        } else {
726
            options = conflictPathsOrOptions
46✔
727
        }
728

729
        let entities: QueryDeepPartialEntity<Entity>[]
730

731
        if (!Array.isArray(entityOrEntities)) {
502✔
732
            entities = [entityOrEntities]
389✔
733
        } else {
734
            entities = entityOrEntities
113✔
735
        }
736

737
        const conflictColumns = metadata.mapPropertyPathsToColumns(
502✔
738
            Array.isArray(options.conflictPaths)
502✔
739
                ? options.conflictPaths
740
                : Object.keys(options.conflictPaths),
741
        )
742

743
        const overwriteColumns = metadata.columns.filter(
502✔
744
            (col) =>
745
                !conflictColumns.includes(col) &&
2,258✔
746
                entities.some(
747
                    (entity) =>
748
                        typeof col.getEntityValue(entity) !== "undefined",
2,256✔
749
                ),
750
        )
751

752
        return this.createQueryBuilder()
502✔
753
            .insert()
754
            .into(target)
755
            .values(entities)
756
            .orUpdate(
757
                [...conflictColumns, ...overwriteColumns].map(
758
                    (col) => col.databaseName,
1,132✔
759
                ),
760
                conflictColumns.map((col) => col.databaseName),
506✔
761
                {
762
                    skipUpdateIfNoValuesChanged:
763
                        options.skipUpdateIfNoValuesChanged,
764
                    indexPredicate: options.indexPredicate,
765
                    upsertType:
766
                        options.upsertType ||
999✔
767
                        this.connection.driver.supportedUpsertTypes[0],
768
                },
769
            )
770
            .execute()
771
    }
772

773
    /**
774
     * Updates entity partially. Entity can be found by a given condition(s).
775
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
776
     * Executes fast and efficient UPDATE query.
777
     * Does not check if entity exist in the database.
778
     * Condition(s) cannot be empty.
779
     */
780
    update<Entity extends ObjectLiteral>(
781
        target: EntityTarget<Entity>,
782
        criteria:
783
            | string
784
            | string[]
785
            | number
786
            | number[]
787
            | Date
788
            | Date[]
789
            | ObjectId
790
            | ObjectId[]
791
            | any,
792
        partialEntity: QueryDeepPartialEntity<Entity>,
793
    ): Promise<UpdateResult> {
794
        // if user passed empty criteria or empty list of criterias, then throw an error
795
        if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
421✔
796
            return Promise.reject(
96✔
797
                new TypeORMError(
798
                    `Empty criteria(s) are not allowed for the update method.`,
799
                ),
800
            )
801
        }
802

803
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
325✔
804
            return this.createQueryBuilder()
148✔
805
                .update(target)
806
                .set(partialEntity)
807
                .whereInIds(criteria)
808
                .execute()
809
        } else {
810
            return this.createQueryBuilder()
177✔
811
                .update(target)
812
                .set(partialEntity)
813
                .where(criteria)
814
                .execute()
815
        }
816
    }
817

818
    /**
819
     * Updates all entities of target type, setting fields from supplied partial entity.
820
     * This is a primitive operation without cascades, relations or other operations included.
821
     * Executes fast and efficient UPDATE query without WHERE clause.
822
     *
823
     * WARNING! This method updates ALL rows in the target table.
824
     */
825
    updateAll<Entity extends ObjectLiteral>(
826
        target: EntityTarget<Entity>,
827
        partialEntity: QueryDeepPartialEntity<Entity>,
828
    ): Promise<UpdateResult> {
829
        return this.createQueryBuilder()
24✔
830
            .update(target)
831
            .set(partialEntity)
832
            .execute()
833
    }
834

835
    /**
836
     * Deletes entities by a given condition(s).
837
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
838
     * Executes fast and efficient DELETE query.
839
     * Does not check if entity exist in the database.
840
     * Condition(s) cannot be empty.
841
     */
842
    delete<Entity extends ObjectLiteral>(
843
        targetOrEntity: EntityTarget<Entity>,
844
        criteria:
845
            | string
846
            | string[]
847
            | number
848
            | number[]
849
            | Date
850
            | Date[]
851
            | ObjectId
852
            | ObjectId[]
853
            | any,
854
    ): Promise<DeleteResult> {
855
        // if user passed empty criteria or empty list of criterias, then throw an error
856
        if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
326✔
857
            return Promise.reject(
96✔
858
                new TypeORMError(
859
                    `Empty criteria(s) are not allowed for the delete method.`,
860
                ),
861
            )
862
        }
863

864
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
230✔
865
            return this.createQueryBuilder()
134✔
866
                .delete()
867
                .from(targetOrEntity)
868
                .whereInIds(criteria)
869
                .execute()
870
        } else {
871
            return this.createQueryBuilder()
96✔
872
                .delete()
873
                .from(targetOrEntity)
874
                .where(criteria)
875
                .execute()
876
        }
877
    }
878

879
    /**
880
     * Deletes all entities of target type.
881
     * This is a primitive operation without cascades, relations or other operations included.
882
     * Executes fast and efficient DELETE query without WHERE clause.
883
     *
884
     * WARNING! This method deletes ALL rows in the target table.
885
     */
886
    deleteAll<Entity extends ObjectLiteral>(
887
        targetOrEntity: EntityTarget<Entity>,
888
    ): Promise<DeleteResult> {
889
        return this.createQueryBuilder().delete().from(targetOrEntity).execute()
24✔
890
    }
891

892
    /**
893
     * Records the delete date of entities by a given condition(s).
894
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
895
     * Executes fast and efficient UPDATE query.
896
     * Does not check if entity exist in the database.
897
     * Condition(s) cannot be empty.
898
     */
899
    softDelete<Entity extends ObjectLiteral>(
900
        targetOrEntity: EntityTarget<Entity>,
901
        criteria:
902
            | string
903
            | string[]
904
            | number
905
            | number[]
906
            | Date
907
            | Date[]
908
            | ObjectId
909
            | ObjectId[]
910
            | any,
911
    ): Promise<UpdateResult> {
912
        // if user passed empty criteria or empty list of criterias, then throw an error
913
        if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
96!
UNCOV
914
            return Promise.reject(
×
915
                new TypeORMError(
916
                    `Empty criteria(s) are not allowed for the softDelete method.`,
917
                ),
918
            )
919
        }
920

921
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
96!
UNCOV
922
            return this.createQueryBuilder()
×
923
                .softDelete()
924
                .from(targetOrEntity)
925
                .whereInIds(criteria)
926
                .execute()
927
        } else {
928
            return this.createQueryBuilder()
96✔
929
                .softDelete()
930
                .from(targetOrEntity)
931
                .where(criteria)
932
                .execute()
933
        }
934
    }
935

936
    /**
937
     * Restores entities by a given condition(s).
938
     * Unlike save method executes a primitive operation without cascades, relations and other operations included.
939
     * Executes fast and efficient UPDATE query.
940
     * Does not check if entity exist in the database.
941
     * Condition(s) cannot be empty.
942
     */
943
    restore<Entity extends ObjectLiteral>(
944
        targetOrEntity: EntityTarget<Entity>,
945
        criteria:
946
            | string
947
            | string[]
948
            | number
949
            | number[]
950
            | Date
951
            | Date[]
952
            | ObjectId
953
            | ObjectId[]
954
            | any,
955
    ): Promise<UpdateResult> {
956
        // if user passed empty criteria or empty list of criterias, then throw an error
957
        if (OrmUtils.isCriteriaNullOrEmpty(criteria)) {
48!
UNCOV
958
            return Promise.reject(
×
959
                new TypeORMError(
960
                    `Empty criteria(s) are not allowed for the restore method.`,
961
                ),
962
            )
963
        }
964

965
        if (OrmUtils.isPrimitiveCriteria(criteria)) {
48!
UNCOV
966
            return this.createQueryBuilder()
×
967
                .restore()
968
                .from(targetOrEntity)
969
                .whereInIds(criteria)
970
                .execute()
971
        } else {
972
            return this.createQueryBuilder()
48✔
973
                .restore()
974
                .from(targetOrEntity)
975
                .where(criteria)
976
                .execute()
977
        }
978
    }
979

980
    /**
981
     * Checks whether any entity exists with the given options.
982
     */
983
    exists<Entity extends ObjectLiteral>(
984
        entityClass: EntityTarget<Entity>,
985
        options?: FindManyOptions<Entity>,
986
    ): Promise<boolean> {
987
        const metadata = this.connection.getMetadata(entityClass)
168✔
988
        return this.createQueryBuilder(
168✔
989
            entityClass,
990
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
336✔
991
                metadata.name,
992
        )
993
            .setFindOptions(options || {})
240✔
994
            .getExists()
995
    }
996

997
    /**
998
     * Checks whether any entity exists with the given conditions.
999
     */
1000
    async existsBy<Entity extends ObjectLiteral>(
1001
        entityClass: EntityTarget<Entity>,
1002
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1003
    ): Promise<boolean> {
UNCOV
1004
        const metadata = this.connection.getMetadata(entityClass)
×
1005
        return this.createQueryBuilder(entityClass, metadata.name)
×
1006
            .setFindOptions({ where })
1007
            .getExists()
1008
    }
1009

1010
    /**
1011
     * Counts entities that match given options.
1012
     * Useful for pagination.
1013
     */
1014
    count<Entity extends ObjectLiteral>(
1015
        entityClass: EntityTarget<Entity>,
1016
        options?: FindManyOptions<Entity>,
1017
    ): Promise<number> {
1018
        const metadata = this.connection.getMetadata(entityClass)
366✔
1019
        return this.createQueryBuilder(
366✔
1020
            entityClass,
1021
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
732✔
1022
                metadata.name,
1023
        )
1024
            .setFindOptions(options || {})
564✔
1025
            .getCount()
1026
    }
1027

1028
    /**
1029
     * Counts entities that match given conditions.
1030
     * Useful for pagination.
1031
     */
1032
    countBy<Entity extends ObjectLiteral>(
1033
        entityClass: EntityTarget<Entity>,
1034
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1035
    ): Promise<number> {
1036
        const metadata = this.connection.getMetadata(entityClass)
48✔
1037
        return this.createQueryBuilder(entityClass, metadata.name)
48✔
1038
            .setFindOptions({ where })
1039
            .getCount()
1040
    }
1041

1042
    /**
1043
     * Return the SUM of a column
1044
     */
1045
    sum<Entity extends ObjectLiteral>(
1046
        entityClass: EntityTarget<Entity>,
1047
        columnName: PickKeysByType<Entity, number>,
1048
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1049
    ): Promise<number | null> {
1050
        return this.callAggregateFun(entityClass, "SUM", columnName, where)
50✔
1051
    }
1052

1053
    /**
1054
     * Return the AVG of a column
1055
     */
1056
    average<Entity extends ObjectLiteral>(
1057
        entityClass: EntityTarget<Entity>,
1058
        columnName: PickKeysByType<Entity, number>,
1059
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1060
    ): Promise<number | null> {
1061
        return this.callAggregateFun(entityClass, "AVG", columnName, where)
50✔
1062
    }
1063

1064
    /**
1065
     * Return the MIN of a column
1066
     */
1067
    minimum<Entity extends ObjectLiteral>(
1068
        entityClass: EntityTarget<Entity>,
1069
        columnName: PickKeysByType<Entity, number>,
1070
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1071
    ): Promise<number | null> {
1072
        return this.callAggregateFun(entityClass, "MIN", columnName, where)
50✔
1073
    }
1074

1075
    /**
1076
     * Return the MAX of a column
1077
     */
1078
    maximum<Entity extends ObjectLiteral>(
1079
        entityClass: EntityTarget<Entity>,
1080
        columnName: PickKeysByType<Entity, number>,
1081
        where?: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1082
    ): Promise<number | null> {
1083
        return this.callAggregateFun(entityClass, "MAX", columnName, where)
50✔
1084
    }
1085

1086
    private async callAggregateFun<Entity extends ObjectLiteral>(
1087
        entityClass: EntityTarget<Entity>,
1088
        fnName: "SUM" | "AVG" | "MIN" | "MAX",
1089
        columnName: PickKeysByType<Entity, number>,
1090
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[] = {},
96✔
1091
    ): Promise<number | null> {
1092
        const metadata = this.connection.getMetadata(entityClass)
200✔
1093
        const column = metadata.columns.find(
200✔
1094
            (item) => item.propertyPath === columnName,
400✔
1095
        )
1096
        if (!column) {
200!
UNCOV
1097
            throw new TypeORMError(
×
1098
                `Column "${columnName}" was not found in table "${metadata.name}"`,
1099
            )
1100
        }
1101

1102
        const result = await this.createQueryBuilder(entityClass, metadata.name)
200✔
1103
            .setFindOptions({ where })
1104
            .select(
1105
                `${fnName}(${this.connection.driver.escape(
1106
                    column.databaseName,
1107
                )})`,
1108
                fnName,
1109
            )
1110
            .getRawOne()
1111
        return result[fnName] === null ? null : parseFloat(result[fnName])
200✔
1112
    }
1113

1114
    /**
1115
     * Finds entities that match given find options.
1116
     */
1117
    async find<Entity extends ObjectLiteral>(
1118
        entityClass: EntityTarget<Entity>,
1119
        options?: FindManyOptions<Entity>,
1120
    ): Promise<Entity[]> {
1121
        const metadata = this.connection.getMetadata(entityClass)
3,903✔
1122
        return this.createQueryBuilder<Entity>(
3,903✔
1123
            entityClass as any,
1124
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
7,686✔
1125
                metadata.name,
1126
        )
1127
            .setFindOptions(options || {})
5,466✔
1128
            .getMany()
1129
    }
1130

1131
    /**
1132
     * Finds entities that match given find options.
1133
     */
1134
    async findBy<Entity extends ObjectLiteral>(
1135
        entityClass: EntityTarget<Entity>,
1136
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1137
    ): Promise<Entity[]> {
1138
        const metadata = this.connection.getMetadata(entityClass)
1,064✔
1139
        return this.createQueryBuilder<Entity>(
1,064✔
1140
            entityClass as any,
1141
            metadata.name,
1142
        )
1143
            .setFindOptions({ where: where })
1144
            .getMany()
1145
    }
1146

1147
    /**
1148
     * Finds entities that match given find options.
1149
     * Also counts all entities that match given conditions,
1150
     * but ignores pagination settings (from and take options).
1151
     */
1152
    findAndCount<Entity extends ObjectLiteral>(
1153
        entityClass: EntityTarget<Entity>,
1154
        options?: FindManyOptions<Entity>,
1155
    ): Promise<[Entity[], number]> {
1156
        const metadata = this.connection.getMetadata(entityClass)
292✔
1157
        return this.createQueryBuilder<Entity>(
292✔
1158
            entityClass as any,
1159
            FindOptionsUtils.extractFindManyOptionsAlias(options) ||
584✔
1160
                metadata.name,
1161
        )
1162
            .setFindOptions(options || {})
340✔
1163
            .getManyAndCount()
1164
    }
1165

1166
    /**
1167
     * Finds entities that match given WHERE conditions.
1168
     * Also counts all entities that match given conditions,
1169
     * but ignores pagination settings (from and take options).
1170
     */
1171
    findAndCountBy<Entity extends ObjectLiteral>(
1172
        entityClass: EntityTarget<Entity>,
1173
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1174
    ): Promise<[Entity[], number]> {
UNCOV
1175
        const metadata = this.connection.getMetadata(entityClass)
×
1176
        return this.createQueryBuilder<Entity>(
×
1177
            entityClass as any,
1178
            metadata.name,
1179
        )
1180
            .setFindOptions({ where })
1181
            .getManyAndCount()
1182
    }
1183

1184
    /**
1185
     * Finds entities with ids.
1186
     * Optionally find options or conditions can be applied.
1187
     *
1188
     * @deprecated use `findBy` method instead in conjunction with `In` operator, for example:
1189
     *
1190
     * .findBy({
1191
     *     id: In([1, 2, 3])
1192
     * })
1193
     */
1194
    async findByIds<Entity extends ObjectLiteral>(
1195
        entityClass: EntityTarget<Entity>,
1196
        ids: any[],
1197
    ): Promise<Entity[]> {
1198
        // if no ids passed, no need to execute a query - just return an empty array of values
1199
        if (!ids.length) return Promise.resolve([])
196✔
1200

1201
        const metadata = this.connection.getMetadata(entityClass)
172✔
1202
        return this.createQueryBuilder<Entity>(
172✔
1203
            entityClass as any,
1204
            metadata.name,
1205
        )
1206
            .andWhereInIds(ids)
1207
            .getMany()
1208
    }
1209

1210
    /**
1211
     * Finds first entity by a given find options.
1212
     * If entity was not found in the database - returns null.
1213
     */
1214
    async findOne<Entity extends ObjectLiteral>(
1215
        entityClass: EntityTarget<Entity>,
1216
        options: FindOneOptions<Entity>,
1217
    ): Promise<Entity | null> {
1218
        const metadata = this.connection.getMetadata(entityClass)
8,503✔
1219

1220
        // prepare alias for built query
1221
        let alias: string = metadata.name
8,503✔
1222
        if (options && options.join) {
8,503✔
1223
            alias = options.join.alias
437✔
1224
        }
1225

1226
        if (!options.where) {
8,503✔
1227
            throw new Error(
24✔
1228
                `You must provide selection conditions in order to find a single row.`,
1229
            )
1230
        }
1231

1232
        // create query builder and apply find options
1233
        return this.createQueryBuilder<Entity>(entityClass, alias)
8,479✔
1234
            .setFindOptions({
1235
                ...options,
1236
                take: 1,
1237
            })
1238
            .getOne()
1239
    }
1240

1241
    /**
1242
     * Finds first entity that matches given where condition.
1243
     * If entity was not found in the database - returns null.
1244
     */
1245
    async findOneBy<Entity extends ObjectLiteral>(
1246
        entityClass: EntityTarget<Entity>,
1247
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1248
    ): Promise<Entity | null> {
1249
        const metadata = this.connection.getMetadata(entityClass)
5,201✔
1250

1251
        // create query builder and apply find options
1252
        return this.createQueryBuilder<Entity>(entityClass, metadata.name)
5,201✔
1253
            .setFindOptions({
1254
                where,
1255
                take: 1,
1256
            })
1257
            .getOne()
1258
    }
1259

1260
    /**
1261
     * Finds first entity that matches given id.
1262
     * If entity was not found in the database - returns null.
1263
     *
1264
     * @deprecated use `findOneBy` method instead in conjunction with `In` operator, for example:
1265
     *
1266
     * .findOneBy({
1267
     *     id: 1 // where "id" is your primary column name
1268
     * })
1269
     */
1270
    async findOneById<Entity extends ObjectLiteral>(
1271
        entityClass: EntityTarget<Entity>,
1272
        id: number | string | Date | ObjectId,
1273
    ): Promise<Entity | null> {
1274
        const metadata = this.connection.getMetadata(entityClass)
98✔
1275

1276
        // create query builder and apply find options
1277
        return this.createQueryBuilder<Entity>(entityClass, metadata.name)
98✔
1278
            .setFindOptions({
1279
                take: 1,
1280
            })
1281
            .whereInIds(metadata.ensureEntityIdMap(id))
1282
            .getOne()
1283
    }
1284

1285
    /**
1286
     * Finds first entity by a given find options.
1287
     * If entity was not found in the database - rejects with error.
1288
     */
1289
    async findOneOrFail<Entity extends ObjectLiteral>(
1290
        entityClass: EntityTarget<Entity>,
1291
        options: FindOneOptions<Entity>,
1292
    ): Promise<Entity> {
1293
        return this.findOne<Entity>(entityClass as any, options).then(
422✔
1294
            (value) => {
1295
                if (value === null) {
422✔
1296
                    return Promise.reject(
48✔
1297
                        new EntityNotFoundError(entityClass, options),
1298
                    )
1299
                }
1300
                return Promise.resolve(value)
374✔
1301
            },
1302
        )
1303
    }
1304

1305
    /**
1306
     * Finds first entity that matches given where condition.
1307
     * If entity was not found in the database - rejects with error.
1308
     */
1309
    async findOneByOrFail<Entity extends ObjectLiteral>(
1310
        entityClass: EntityTarget<Entity>,
1311
        where: FindOptionsWhere<Entity> | FindOptionsWhere<Entity>[],
1312
    ): Promise<Entity> {
1313
        return this.findOneBy<Entity>(entityClass as any, where).then(
985✔
1314
            (value) => {
1315
                if (value === null) {
985✔
1316
                    return Promise.reject(
24✔
1317
                        new EntityNotFoundError(entityClass, where),
1318
                    )
1319
                }
1320
                return Promise.resolve(value)
961✔
1321
            },
1322
        )
1323
    }
1324

1325
    /**
1326
     * Clears all the data from the given table (truncates/drops it).
1327
     *
1328
     * Note: this method uses TRUNCATE and may not work as you expect in transactions on some platforms.
1329
     * @see https://stackoverflow.com/a/5972738/925151
1330
     */
1331
    async clear<Entity>(entityClass: EntityTarget<Entity>): Promise<void> {
1332
        const metadata = this.connection.getMetadata(entityClass)
52✔
1333
        const queryRunner =
1334
            this.queryRunner || this.connection.createQueryRunner()
52✔
1335
        try {
52✔
1336
            return await queryRunner.clearTable(metadata.tablePath) // await is needed here because we are using finally
52✔
1337
        } finally {
1338
            if (!this.queryRunner) await queryRunner.release()
52✔
1339
        }
1340
    }
1341

1342
    /**
1343
     * Increments some column by provided value of the entities matched given conditions.
1344
     */
1345
    async increment<Entity extends ObjectLiteral>(
1346
        entityClass: EntityTarget<Entity>,
1347
        conditions: any,
1348
        propertyPath: string,
1349
        value: number | string,
1350
    ): Promise<UpdateResult> {
1351
        const metadata = this.connection.getMetadata(entityClass)
212✔
1352
        const column = metadata.findColumnWithPropertyPath(propertyPath)
212✔
1353
        if (!column)
212✔
1354
            throw new TypeORMError(
24✔
1355
                `Column ${propertyPath} was not found in ${metadata.targetName} entity.`,
1356
            )
1357

1358
        if (isNaN(Number(value)))
188✔
1359
            throw new TypeORMError(`Value "${value}" is not a number.`)
24✔
1360

1361
        // convert possible embedded path "social.likes" into object { social: { like: () => value } }
1362
        const values: QueryDeepPartialEntity<Entity> = propertyPath
164✔
1363
            .split(".")
1364
            .reduceRight(
1365
                (value, key) => ({ [key]: value } as any),
188✔
1366
                () =>
1367
                    this.connection.driver.escape(column.databaseName) +
164✔
1368
                    " + " +
1369
                    value,
1370
            )
1371

1372
        return this.createQueryBuilder<Entity>(entityClass as any, "entity")
164✔
1373
            .update(entityClass)
1374
            .set(values)
1375
            .where(conditions)
1376
            .execute()
1377
    }
1378

1379
    /**
1380
     * Decrements some column by provided value of the entities matched given conditions.
1381
     */
1382
    async decrement<Entity extends ObjectLiteral>(
1383
        entityClass: EntityTarget<Entity>,
1384
        conditions: any,
1385
        propertyPath: string,
1386
        value: number | string,
1387
    ): Promise<UpdateResult> {
1388
        const metadata = this.connection.getMetadata(entityClass)
208✔
1389
        const column = metadata.findColumnWithPropertyPath(propertyPath)
208✔
1390
        if (!column)
208✔
1391
            throw new TypeORMError(
24✔
1392
                `Column ${propertyPath} was not found in ${metadata.targetName} entity.`,
1393
            )
1394

1395
        if (isNaN(Number(value)))
184✔
1396
            throw new TypeORMError(`Value "${value}" is not a number.`)
24✔
1397

1398
        // convert possible embedded path "social.likes" into object { social: { like: () => value } }
1399
        const values: QueryDeepPartialEntity<Entity> = propertyPath
160✔
1400
            .split(".")
1401
            .reduceRight(
1402
                (value, key) => ({ [key]: value } as any),
184✔
1403
                () =>
1404
                    this.connection.driver.escape(column.databaseName) +
160✔
1405
                    " - " +
1406
                    value,
1407
            )
1408

1409
        return this.createQueryBuilder<Entity>(entityClass as any, "entity")
160✔
1410
            .update(entityClass)
1411
            .set(values)
1412
            .where(conditions)
1413
            .execute()
1414
    }
1415

1416
    /**
1417
     * Gets repository for the given entity class or name.
1418
     * If single database connection mode is used, then repository is obtained from the
1419
     * repository aggregator, where each repository is individually created for this entity manager.
1420
     * When single database connection is not used, repository is being obtained from the connection.
1421
     */
1422
    getRepository<Entity extends ObjectLiteral>(
1423
        target: EntityTarget<Entity>,
1424
    ): Repository<Entity> {
1425
        // find already created repository instance and return it if found
1426
        const repoFromMap = this.repositories.get(target)
364,016✔
1427
        if (repoFromMap) return repoFromMap
364,016✔
1428

1429
        // if repository was not found then create it, store its instance and return it
1430
        if (this.connection.driver.options.type === "mongodb") {
108,341✔
1431
            const newRepository = new MongoRepository(
94✔
1432
                target,
1433
                this,
1434
                this.queryRunner,
1435
            )
1436
            this.repositories.set(target, newRepository)
94✔
1437
            return newRepository
94✔
1438
        } else {
1439
            const newRepository = new Repository<any>(
108,247✔
1440
                target,
1441
                this,
1442
                this.queryRunner,
1443
            )
1444
            this.repositories.set(target, newRepository)
108,247✔
1445
            return newRepository
108,247✔
1446
        }
1447
    }
1448

1449
    /**
1450
     * Gets tree repository for the given entity class or name.
1451
     * If single database connection mode is used, then repository is obtained from the
1452
     * repository aggregator, where each repository is individually created for this entity manager.
1453
     * When single database connection is not used, repository is being obtained from the connection.
1454
     */
1455
    getTreeRepository<Entity extends ObjectLiteral>(
1456
        target: EntityTarget<Entity>,
1457
    ): TreeRepository<Entity> {
1458
        // tree tables aren't supported by some drivers (mongodb)
1459
        if (this.connection.driver.treeSupport === false)
2,470!
UNCOV
1460
            throw new TreeRepositoryNotSupportedError(this.connection.driver)
×
1461

1462
        // find already created repository instance and return it if found
1463
        const repository = this.treeRepositories.find(
2,470✔
1464
            (repository) => repository.target === target,
2,712✔
1465
        )
1466
        if (repository) return repository
2,470✔
1467

1468
        // check if repository is real tree repository
1469
        const newRepository = new TreeRepository(target, this, this.queryRunner)
566✔
1470
        this.treeRepositories.push(newRepository)
566✔
1471
        return newRepository
566✔
1472
    }
1473

1474
    /**
1475
     * Gets mongodb repository for the given entity class.
1476
     */
1477
    getMongoRepository<Entity extends ObjectLiteral>(
1478
        target: EntityTarget<Entity>,
1479
    ): MongoRepository<Entity> {
1480
        return this.connection.getMongoRepository<Entity>(target)
6✔
1481
    }
1482

1483
    /**
1484
     * Creates a new repository instance out of a given Repository and
1485
     * sets current EntityManager instance to it. Used to work with custom repositories
1486
     * in transactions.
1487
     */
1488
    withRepository<Entity extends ObjectLiteral, R extends Repository<any>>(
1489
        repository: R & Repository<Entity>,
1490
    ): R {
1491
        const repositoryConstructor =
1492
            repository.constructor as typeof Repository
32✔
1493
        const { target, manager, queryRunner, ...otherRepositoryProperties } =
1494
            repository
32✔
1495
        return Object.assign(
32✔
1496
            new repositoryConstructor(repository.target, this) as R,
1497
            {
1498
                ...otherRepositoryProperties,
1499
            },
1500
        )
1501
    }
1502

1503
    /**
1504
     * Gets custom entity repository marked with @EntityRepository decorator.
1505
     *
1506
     * @deprecated use Repository.extend to create custom repositories
1507
     */
1508
    getCustomRepository<T>(customRepository: ObjectType<T>): T {
1509
        const entityRepositoryMetadataArgs =
1510
            getMetadataArgsStorage().entityRepositories.find((repository) => {
32✔
1511
                return (
44✔
1512
                    repository.target ===
1513
                    (typeof customRepository === "function"
44!
1514
                        ? customRepository
1515
                        : (customRepository as any).constructor)
1516
                )
1517
            })
1518
        if (!entityRepositoryMetadataArgs)
32!
UNCOV
1519
            throw new CustomRepositoryNotFoundError(customRepository)
×
1520

1521
        const entityMetadata = entityRepositoryMetadataArgs.entity
32✔
1522
            ? this.connection.getMetadata(entityRepositoryMetadataArgs.entity)
1523
            : undefined
1524
        const entityRepositoryInstance =
1525
            new (entityRepositoryMetadataArgs.target as any)(
32✔
1526
                this,
1527
                entityMetadata,
1528
            )
1529

1530
        // NOTE: dynamic access to protected properties. We need this to prevent unwanted properties in those classes to be exposed,
1531
        // however we need these properties for internal work of the class
1532
        if (entityRepositoryInstance instanceof AbstractRepository) {
32!
1533
            if (!(entityRepositoryInstance as any)["manager"])
32✔
1534
                (entityRepositoryInstance as any)["manager"] = this
32✔
1535
        } else {
UNCOV
1536
            if (!entityMetadata)
×
1537
                throw new CustomRepositoryCannotInheritRepositoryError(
×
1538
                    customRepository,
1539
                )
UNCOV
1540
            ;(entityRepositoryInstance as any)["manager"] = this
×
1541
            ;(entityRepositoryInstance as any)["metadata"] = entityMetadata
×
1542
        }
1543

1544
        return entityRepositoryInstance
32✔
1545
    }
1546

1547
    /**
1548
     * Releases all resources used by entity manager.
1549
     * This is used when entity manager is created with a single query runner,
1550
     * and this single query runner needs to be released after job with entity manager is done.
1551
     */
1552
    async release(): Promise<void> {
UNCOV
1553
        if (!this.queryRunner) throw new NoNeedToReleaseEntityManagerError()
×
1554

UNCOV
1555
        return this.queryRunner.release()
×
1556
    }
1557
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc