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

typeorm / typeorm / 23390157208

21 Mar 2026 10:26PM UTC coverage: 56.678% (-16.6%) from 73.277%
23390157208

Pull #12252

github

web-flow
Merge 5b60ba41c into 7038fa166
Pull Request #12252: fix: unskip cascade soft remove test

17767 of 26580 branches covered (66.84%)

Branch coverage included in aggregate %.

64033 of 117744 relevant lines covered (54.38%)

1514.83 hits per line

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

77.28
/src/data-source/DataSource.ts
1
import type { Driver } from "../driver/Driver"
28✔
2
import { registerQueryBuilders } from "../query-builder"
28✔
3
import type { Repository } from "../repository/Repository"
28✔
4
import type { EntitySubscriberInterface } from "../subscriber/EntitySubscriberInterface"
28✔
5
import type { EntityTarget } from "../common/EntityTarget"
28✔
6
import type { EntityManager } from "../entity-manager/EntityManager"
28✔
7
import { DefaultNamingStrategy } from "../naming-strategy/DefaultNamingStrategy"
28✔
8
import {
28✔
9
    CannotConnectAlreadyConnectedError,
28✔
10
    CannotExecuteNotConnectedError,
28✔
11
    EntityMetadataNotFoundError,
28✔
12
    QueryRunnerProviderAlreadyReleasedError,
28✔
13
    TypeORMError,
28✔
14
} from "../error"
28✔
15
import type { TreeRepository } from "../repository/TreeRepository"
28✔
16
import type { NamingStrategyInterface } from "../naming-strategy/NamingStrategyInterface"
28✔
17
import type { EntityMetadata } from "../metadata/EntityMetadata"
28✔
18
import type { Logger } from "../logger/Logger"
28✔
19
import type { MigrationInterface } from "../migration/MigrationInterface"
28✔
20
import { MigrationExecutor } from "../migration/MigrationExecutor"
28✔
21
import type { Migration } from "../migration/Migration"
28✔
22
import type { MongoRepository } from "../repository/MongoRepository"
28✔
23
import type { MongoEntityManager } from "../entity-manager/MongoEntityManager"
28✔
24
import { EntityMetadataValidator } from "../metadata-builder/EntityMetadataValidator"
28✔
25
import type { DataSourceOptions } from "./DataSourceOptions"
28✔
26
import { EntityManagerFactory } from "../entity-manager/EntityManagerFactory"
28✔
27
import { DriverFactory } from "../driver/DriverFactory"
28✔
28
import { ConnectionMetadataBuilder } from "../connection/ConnectionMetadataBuilder"
28✔
29
import type { QueryRunner } from "../query-runner/QueryRunner"
28✔
30
import { SelectQueryBuilder } from "../query-builder/SelectQueryBuilder"
28✔
31
import { LoggerFactory } from "../logger/LoggerFactory"
28✔
32
import { QueryResultCacheFactory } from "../cache/QueryResultCacheFactory"
28✔
33
import type { QueryResultCache } from "../cache/QueryResultCache"
28✔
34
import type { SqljsEntityManager } from "../entity-manager/SqljsEntityManager"
28✔
35
import { RelationLoader } from "../query-builder/RelationLoader"
28✔
36
import { ObjectUtils } from "../util/ObjectUtils"
28✔
37
import type { IsolationLevel } from "../driver/types/IsolationLevel"
28✔
38
import type { ReplicationMode } from "../driver/types/ReplicationMode"
28✔
39
import { RelationIdLoader } from "../query-builder/RelationIdLoader"
28✔
40
import { DriverUtils } from "../driver/DriverUtils"
28✔
41
import { InstanceChecker } from "../util/InstanceChecker"
28✔
42
import type { ObjectLiteral } from "../common/ObjectLiteral"
28✔
43
import { buildSqlTag } from "../util/SqlTagUtils"
28✔
44

28✔
45
registerQueryBuilders()
28✔
46

28✔
47
/**
28✔
48
 * DataSource is a pre-defined connection configuration to a specific database.
28✔
49
 * You can have multiple data sources connected (with multiple connections in it),
28✔
50
 * connected to multiple databases in your application.
28✔
51
 *
28✔
52
 * Before, it was called `Connection`, but now `Connection` is deprecated
28✔
53
 * because `Connection` isn't the best name for what it's actually is.
28✔
54
 */
28✔
55
export class DataSource {
28✔
56
    readonly "@instanceof" = Symbol.for("DataSource")
28✔
57

28✔
58
    // -------------------------------------------------------------------------
28✔
59
    // Public Readonly Properties
28✔
60
    // -------------------------------------------------------------------------
28✔
61

28✔
62
    /**
28✔
63
     * Connection options.
28✔
64
     */
28✔
65
    readonly options: DataSourceOptions
28✔
66

28✔
67
    /**
28✔
68
     * Indicates if DataSource is initialized or not.
28✔
69
     */
28✔
70
    readonly isInitialized: boolean
28✔
71

28✔
72
    /**
28✔
73
     * Database driver used by this connection.
28✔
74
     */
28✔
75
    driver: Driver
28✔
76

28✔
77
    /**
28✔
78
     * EntityManager of this connection.
28✔
79
     */
28✔
80
    readonly manager: EntityManager
28✔
81

28✔
82
    /**
28✔
83
     * Naming strategy used in the connection.
28✔
84
     */
28✔
85
    namingStrategy: NamingStrategyInterface
28✔
86

28✔
87
    /**
28✔
88
     * Name for the metadata table
28✔
89
     */
28✔
90
    readonly metadataTableName: string
28✔
91

28✔
92
    /**
28✔
93
     * Logger used to log orm events.
28✔
94
     */
28✔
95
    logger: Logger
28✔
96

28✔
97
    /**
28✔
98
     * Migration instances that are registered for this connection.
28✔
99
     */
28✔
100
    readonly migrations: MigrationInterface[] = []
28✔
101

28✔
102
    /**
28✔
103
     * Entity subscriber instances that are registered for this connection.
28✔
104
     */
28✔
105
    readonly subscribers: EntitySubscriberInterface<any>[] = []
28✔
106

28✔
107
    /**
28✔
108
     * All entity metadatas that are registered for this connection.
28✔
109
     */
28✔
110
    readonly entityMetadatas: EntityMetadata[] = []
28✔
111

28✔
112
    /**
28✔
113
     * All entity metadatas that are registered for this connection.
28✔
114
     * This is a copy of #.entityMetadatas property -> used for more performant searches.
28✔
115
     */
28✔
116
    readonly entityMetadatasMap = new Map<EntityTarget<any>, EntityMetadata>()
28✔
117

28✔
118
    /**
28✔
119
     * Used to work with query result cache.
28✔
120
     */
28✔
121
    queryResultCache?: QueryResultCache
28✔
122

28✔
123
    /**
28✔
124
     * Used to load relations and work with lazy relations.
28✔
125
     */
28✔
126
    readonly relationLoader: RelationLoader
28✔
127

28✔
128
    readonly relationIdLoader: RelationIdLoader
28✔
129

28✔
130
    // -------------------------------------------------------------------------
28✔
131
    // Constructor
28✔
132
    // -------------------------------------------------------------------------
28✔
133

28✔
134
    constructor(options: DataSourceOptions) {
28✔
135
        registerQueryBuilders()
438✔
136
        this.options = options
438✔
137
        this.logger = new LoggerFactory().create(
438✔
138
            this.options.logger,
438✔
139
            this.options.logging,
438✔
140
        )
438✔
141
        this.driver = new DriverFactory().create(this)
438✔
142
        this.manager = this.createEntityManager()
438✔
143
        this.namingStrategy =
438✔
144
            options.namingStrategy || new DefaultNamingStrategy()
438✔
145
        this.metadataTableName = options.metadataTableName || "typeorm_metadata"
438✔
146
        this.queryResultCache = options.cache
438✔
147
            ? new QueryResultCacheFactory(this).create()
438✔
148
            : undefined
438✔
149
        this.relationLoader = new RelationLoader(this)
438✔
150
        this.relationIdLoader = new RelationIdLoader(this)
438✔
151
        this.isInitialized = false
438✔
152
    }
438✔
153

28✔
154
    // -------------------------------------------------------------------------
28✔
155
    // Public Accessors
28✔
156
    // -------------------------------------------------------------------------
28✔
157

28✔
158
    /**
28✔
159
     * Gets the mongodb entity manager that allows to perform mongodb-specific repository operations
28✔
160
     * with any entity in this connection.
28✔
161
     *
28✔
162
     * Available only in mongodb connections.
28✔
163
     * @returns the mongodb entity manager
28✔
164
     */
28✔
165
    get mongoManager(): MongoEntityManager {
28✔
166
        if (!InstanceChecker.isMongoEntityManager(this.manager))
×
167
            throw new TypeORMError(
×
168
                `MongoEntityManager is only available for MongoDB databases.`,
×
169
            )
×
170

×
171
        return this.manager as MongoEntityManager
×
172
    }
×
173

28✔
174
    /**
28✔
175
     * Gets a sql.js specific Entity Manager that allows to perform special load and save operations
28✔
176
     *
28✔
177
     * Available only in connection with the sqljs driver.
28✔
178
     * @returns an sqljs specific Entity Manager
28✔
179
     */
28✔
180
    get sqljsManager(): SqljsEntityManager {
28✔
181
        if (!InstanceChecker.isSqljsEntityManager(this.manager))
×
182
            throw new TypeORMError(
×
183
                `SqljsEntityManager is only available for Sqljs databases.`,
×
184
            )
×
185

×
186
        return this.manager
×
187
    }
×
188

28✔
189
    // -------------------------------------------------------------------------
28✔
190
    // Public Methods
28✔
191
    // -------------------------------------------------------------------------
28✔
192
    /**
28✔
193
     * Updates current connection options with provided options.
28✔
194
     * @param options
28✔
195
     */
28✔
196
    setOptions(options: Partial<DataSourceOptions>): this {
28✔
197
        Object.assign(this.options, options)
×
198

×
199
        if (options.logger || options.logging) {
×
200
            this.logger = new LoggerFactory().create(
×
201
                options.logger || this.options.logger,
×
202
                options.logging || this.options.logging,
×
203
            )
×
204
        }
×
205

×
206
        if (options.namingStrategy) {
×
207
            this.namingStrategy = options.namingStrategy
×
208
        }
×
209

×
210
        if (options.cache) {
×
211
            this.queryResultCache = new QueryResultCacheFactory(this).create()
×
212
        }
×
213

×
214
        // todo: we must update the database in the driver as well, if it was set by setOptions method
×
215
        //  in the future we need to refactor the code and remove "database" from the driver, and instead
×
216
        //  use database (and options) from a single place - data source.
×
217
        if (options.database) {
×
218
            this.driver.database = DriverUtils.buildDriverOptions(
×
219
                this.options,
×
220
            ).database
×
221
        }
×
222

×
223
        // todo: need to take a look if we need to update schema and other "poor" properties
×
224

×
225
        return this
×
226
    }
×
227

28✔
228
    /**
28✔
229
     * Performs connection to the database.
28✔
230
     * This method should be called once on application bootstrap.
28✔
231
     * This method not necessarily creates database connection (depend on database type),
28✔
232
     * but it also can setup a connection pool with database to use.
28✔
233
     */
28✔
234
    async initialize(): Promise<this> {
28✔
235
        if (this.isInitialized) throw new CannotConnectAlreadyConnectedError()
433!
236

432✔
237
        // connect to the database via its driver
432✔
238
        await this.driver.connect()
432✔
239

432✔
240
        // connect to the cache-specific database if cache is enabled
432✔
241
        if (this.queryResultCache) await this.queryResultCache.connect()
433!
242

432✔
243
        // set connected status for the current connection
432✔
244
        ObjectUtils.assign(this, { isInitialized: true })
432✔
245

432✔
246
        try {
432✔
247
            // build all metadatas registered in the current connection
432✔
248
            await this.buildMetadatas()
432✔
249

430✔
250
            await this.driver.afterConnect()
430✔
251

430✔
252
            // if option is set - drop schema once connection is done
430✔
253
            if (this.options.dropSchema) await this.dropDatabase()
433✔
254

430✔
255
            // if option is set - automatically synchronize a schema
430✔
256
            if (this.options.migrationsRun)
430✔
257
                await this.runMigrations({
433!
258
                    transaction: this.options.migrationsTransactionMode,
×
259
                })
×
260

430✔
261
            // if option is set - automatically synchronize a schema
430✔
262
            if (this.options.synchronize) await this.synchronize()
433✔
263
        } catch (error) {
433!
264
            // if for some reason build metadata fail (for example validation error during entity metadata check)
2✔
265
            // connection needs to be closed
2✔
266
            await this.destroy()
2✔
267
            throw error
2✔
268
        }
2✔
269

430✔
270
        return this
430✔
271
    }
430✔
272

28✔
273
    /**
28✔
274
     * Closes connection with the database.
28✔
275
     * Once connection is closed, you cannot use repositories or perform any operations except opening connection again.
28✔
276
     */
28✔
277
    async destroy(): Promise<void> {
28✔
278
        if (!this.isInitialized) throw new CannotExecuteNotConnectedError()
432!
279

431✔
280
        await this.driver.disconnect()
431✔
281

431✔
282
        // disconnect from the cache-specific database if cache was enabled
431✔
283
        if (this.queryResultCache) await this.queryResultCache.disconnect()
432!
284

431✔
285
        ObjectUtils.assign(this, { isInitialized: false })
431✔
286
    }
431✔
287

28✔
288
    /**
28✔
289
     * Creates database schema for all entities registered in this connection.
28✔
290
     * Can be used only after connection to the database is established.
28✔
291
     * @param dropBeforeSync If set to true then it drops the database with all its tables and data
28✔
292
     */
28✔
293
    async synchronize(dropBeforeSync: boolean = false): Promise<void> {
28✔
294
        if (!this.isInitialized) throw new CannotExecuteNotConnectedError()
1,254!
295

1,254✔
296
        if (dropBeforeSync) await this.dropDatabase()
1,254✔
297

1,254✔
298
        const schemaBuilder = this.driver.createSchemaBuilder()
1,254✔
299
        await schemaBuilder.build()
1,254✔
300
    }
1,254✔
301

28✔
302
    /**
28✔
303
     * Drops the database and all its data.
28✔
304
     * Be careful with this method on production since this method will erase all your database tables and their data.
28✔
305
     * Can be used only after connection to the database is established.
28✔
306
     */
28✔
307
    // TODO rename
28✔
308
    async dropDatabase(): Promise<void> {
28✔
309
        const queryRunner = this.createQueryRunner()
1,247✔
310
        try {
1,247✔
311
            if (
1,247✔
312
                this.driver.options.type === "mssql" ||
1,247!
313
                DriverUtils.isMySQLFamily(this.driver) ||
1,247!
314
                this.driver.options.type === "aurora-mysql" ||
1,247!
315
                DriverUtils.isSQLiteFamily(this.driver)
1,227✔
316
            ) {
1,247!
317
                const databases: string[] = []
32✔
318
                this.entityMetadatas.forEach((metadata) => {
32✔
319
                    if (
96✔
320
                        metadata.database &&
96!
321
                        databases.indexOf(metadata.database) === -1
×
322
                    )
96✔
323
                        databases.push(metadata.database)
96!
324
                })
32✔
325
                if (databases.length === 0 && this.driver.database) {
32!
326
                    databases.push(this.driver.database)
26✔
327
                }
26✔
328

32✔
329
                if (databases.length === 0) {
32!
330
                    await queryRunner.clearDatabase()
6✔
331
                } else {
32!
332
                    for (const database of databases) {
26✔
333
                        await queryRunner.clearDatabase(database)
26✔
334
                    }
26✔
335
                }
26✔
336
            } else {
1,247!
337
                await queryRunner.clearDatabase()
1,215✔
338
            }
1,215✔
339
        } finally {
1,247✔
340
            await queryRunner.release()
1,247✔
341
        }
1,247✔
342
    }
1,247✔
343

28✔
344
    /**
28✔
345
     * Runs all pending migrations.
28✔
346
     * Can be used only after connection to the database is established.
28✔
347
     * @param options
28✔
348
     * @param options.transaction
28✔
349
     * @param options.fake
28✔
350
     */
28✔
351
    async runMigrations(options?: {
28✔
352
        transaction?: "all" | "none" | "each"
4✔
353
        fake?: boolean
4✔
354
    }): Promise<Migration[]> {
4✔
355
        if (!this.isInitialized) throw new CannotExecuteNotConnectedError()
4!
356

4✔
357
        const migrationExecutor = new MigrationExecutor(this)
4✔
358
        migrationExecutor.transaction =
4✔
359
            options?.transaction ||
4!
360
            this.options?.migrationsTransactionMode ||
4!
361
            "all"
1✔
362
        migrationExecutor.fake = (options && options.fake) || false
4!
363

4✔
364
        const successMigrations =
4✔
365
            await migrationExecutor.executePendingMigrations()
4✔
366
        return successMigrations
2!
367
    }
2✔
368

28✔
369
    /**
28✔
370
     * Reverts last executed migration.
28✔
371
     * Can be used only after connection to the database is established.
28✔
372
     * @param options
28✔
373
     * @param options.transaction
28✔
374
     * @param options.fake
28✔
375
     */
28✔
376
    async undoLastMigration(options?: {
28✔
377
        transaction?: "all" | "none" | "each"
2✔
378
        fake?: boolean
2✔
379
    }): Promise<void> {
2✔
380
        if (!this.isInitialized) throw new CannotExecuteNotConnectedError()
2!
381

2✔
382
        const migrationExecutor = new MigrationExecutor(this)
2✔
383
        migrationExecutor.transaction =
2✔
384
            (options && options.transaction) || "all"
2!
385
        migrationExecutor.fake = (options && options.fake) || false
2✔
386

2✔
387
        await migrationExecutor.undoLastMigration()
2✔
388
    }
1✔
389

28✔
390
    /**
28✔
391
     * Lists all migrations and whether they have been run.
28✔
392
     * Returns true if there are pending migrations
28✔
393
     */
28✔
394
    async showMigrations(): Promise<boolean> {
28✔
395
        if (!this.isInitialized) {
1!
396
            throw new CannotExecuteNotConnectedError()
×
397
        }
×
398
        const migrationExecutor = new MigrationExecutor(this)
1✔
399
        return await migrationExecutor.showMigrations()
1✔
400
    }
1✔
401

28✔
402
    /**
28✔
403
     * Checks if entity metadata exist for the given entity class, target name or table name.
28✔
404
     * @param target
28✔
405
     */
28✔
406
    hasMetadata(target: EntityTarget<any>): boolean {
28✔
407
        return !!this.findMetadata(target)
19,867✔
408
    }
19,867✔
409

28✔
410
    /**
28✔
411
     * Gets entity metadata for the given entity class or schema name.
28✔
412
     * @param target
28✔
413
     */
28✔
414
    getMetadata(target: EntityTarget<any>): EntityMetadata {
28✔
415
        const metadata = this.findMetadata(target)
78,173✔
416
        if (!metadata) throw new EntityMetadataNotFoundError(target)
78,173!
417

78,173✔
418
        return metadata
78,173✔
419
    }
78,173✔
420

28✔
421
    /**
28✔
422
     * Gets repository for the given entity.
28✔
423
     * @param target
28✔
424
     */
28✔
425
    getRepository<Entity extends ObjectLiteral>(
28✔
426
        target: EntityTarget<Entity>,
1,033✔
427
    ): Repository<Entity> {
1,033✔
428
        return this.manager.getRepository(target)
1,033✔
429
    }
1,033✔
430

28✔
431
    /**
28✔
432
     * Gets tree repository for the given entity class or name.
28✔
433
     * Only tree-type entities can have a TreeRepository, like ones decorated with `@Tree` decorator.
28✔
434
     * @param target
28✔
435
     */
28✔
436
    getTreeRepository<Entity extends ObjectLiteral>(
28✔
437
        target: EntityTarget<Entity>,
75✔
438
    ): TreeRepository<Entity> {
75✔
439
        return this.manager.getTreeRepository(target)
75✔
440
    }
75✔
441

28✔
442
    /**
28✔
443
     * Gets mongodb-specific repository for the given entity class or name.
28✔
444
     * Works only if connection is mongodb-specific.
28✔
445
     * @param target
28✔
446
     */
28✔
447
    getMongoRepository<Entity extends ObjectLiteral>(
28✔
448
        target: EntityTarget<Entity>,
×
449
    ): MongoRepository<Entity> {
×
450
        if (!(this.driver.options.type === "mongodb"))
×
451
            throw new TypeORMError(
×
452
                `You can use getMongoRepository only for MongoDB connections.`,
×
453
            )
×
454

×
455
        return this.manager.getRepository(target) as any
×
456
    }
×
457

28✔
458
    /**
28✔
459
     * Wraps given function execution (and all operations made there) into a transaction.
28✔
460
     * All database operations must be executed using provided entity manager.
28✔
461
     */
28✔
462
    async transaction<T>(
28✔
463
        runInTransaction: (entityManager: EntityManager) => Promise<T>,
28✔
464
    ): Promise<T>
28✔
465
    async transaction<T>(
28✔
466
        isolationLevel: IsolationLevel,
28✔
467
        runInTransaction: (entityManager: EntityManager) => Promise<T>,
28✔
468
    ): Promise<T>
28✔
469
    async transaction<T>(
28✔
470
        isolationOrRunInTransaction:
2✔
471
            | IsolationLevel
2✔
472
            | ((entityManager: EntityManager) => Promise<T>),
2✔
473
        runInTransactionParam?: (entityManager: EntityManager) => Promise<T>,
2✔
474
    ): Promise<any> {
2✔
475
        return this.manager.transaction(
2✔
476
            isolationOrRunInTransaction as any,
2✔
477
            runInTransactionParam as any,
2✔
478
        )
2✔
479
    }
2✔
480

28✔
481
    /**
28✔
482
     * Executes raw SQL query and returns raw database results.
28✔
483
     * @param query
28✔
484
     * @param parameters
28✔
485
     * @param queryRunner
28✔
486
     * @returns a raw response from the database client
28✔
487
     * @see {@link https://typeorm.io/data-source-api | Official docs} for examples.
28✔
488
     */
28✔
489
    async query<T = any>(
28✔
490
        query: string,
416✔
491
        parameters?: any[],
416✔
492
        queryRunner?: QueryRunner,
416✔
493
    ): Promise<T> {
416✔
494
        if (InstanceChecker.isMongoEntityManager(this.manager))
416✔
495
            throw new TypeORMError(`Queries aren't supported by MongoDB.`)
416!
496

416✔
497
        if (queryRunner && queryRunner.isReleased)
416!
498
            throw new QueryRunnerProviderAlreadyReleasedError()
416!
499

416✔
500
        const usedQueryRunner = queryRunner || this.createQueryRunner()
416✔
501

416✔
502
        try {
416✔
503
            return await usedQueryRunner.query(query, parameters) // await is needed here because we are using finally
416✔
504
        } finally {
416✔
505
            if (!queryRunner) await usedQueryRunner.release()
416✔
506
        }
416✔
507
    }
416✔
508

28✔
509
    /**
28✔
510
     * Tagged template function that executes raw SQL query and returns raw database results.
28✔
511
     * Template expressions are automatically transformed into database parameters.
28✔
512
     * Raw query execution is supported only by relational databases (MongoDB is not supported).
28✔
513
     * Note: Don't call this as a regular function, it is meant to be used with backticks to tag a template literal.
28✔
514
     * Example: dataSource.sql`SELECT * FROM table_name WHERE id = ${id}`
28✔
515
     * @param strings
28✔
516
     * @param values
28✔
517
     * @returns a raw response from the database client
28✔
518
     */
28✔
519
    async sql<T = any>(
28✔
520
        strings: TemplateStringsArray,
×
521
        ...values: unknown[]
×
522
    ): Promise<T> {
×
523
        const { query, parameters } = buildSqlTag({
×
524
            driver: this.driver,
×
525
            strings: strings,
×
526
            expressions: values,
×
527
        })
×
528

×
529
        return await this.query(query, parameters)
×
530
    }
×
531

28✔
532
    /**
28✔
533
     * Creates a new query builder that can be used to build a SQL query.
28✔
534
     */
28✔
535
    createQueryBuilder<Entity extends ObjectLiteral>(
28✔
536
        entityClass: EntityTarget<Entity>,
28✔
537
        alias: string,
28✔
538
        queryRunner?: QueryRunner,
28✔
539
    ): SelectQueryBuilder<Entity>
28✔
540

28✔
541
    /**
28✔
542
     * Creates a new query builder that can be used to build a SQL query.
28✔
543
     */
28✔
544
    createQueryBuilder(queryRunner?: QueryRunner): SelectQueryBuilder<any>
28✔
545

28✔
546
    /**
28✔
547
     * Creates a new query builder that can be used to build a SQL query.
28✔
548
     * @param entityOrRunner
28✔
549
     * @param alias
28✔
550
     * @param queryRunner
28✔
551
     */
28✔
552
    createQueryBuilder<Entity extends ObjectLiteral>(
28✔
553
        entityOrRunner?: EntityTarget<Entity> | QueryRunner,
15,121✔
554
        alias?: string,
15,121✔
555
        queryRunner?: QueryRunner,
15,121✔
556
    ): SelectQueryBuilder<Entity> {
15,121✔
557
        if (InstanceChecker.isMongoEntityManager(this.manager))
15,121✔
558
            throw new TypeORMError(`Query Builder is not supported by MongoDB.`)
15,121!
559

15,121✔
560
        if (alias) {
15,121✔
561
            alias = DriverUtils.buildAlias(this.driver, undefined, alias)
5,784✔
562
            const metadata = this.getMetadata(
5,784✔
563
                entityOrRunner as EntityTarget<Entity>,
5,784✔
564
            )
5,784✔
565
            return new SelectQueryBuilder(this, queryRunner)
5,784✔
566
                .select(alias)
5,784✔
567
                .from(metadata.target, alias)
5,784✔
568
        } else {
15,121✔
569
            return new SelectQueryBuilder(
9,337✔
570
                this,
9,337✔
571
                entityOrRunner as QueryRunner | undefined,
9,337✔
572
            )
9,337✔
573
        }
9,337✔
574
    }
15,121✔
575

28✔
576
    /**
28✔
577
     * Creates a query runner used for perform queries on a single database connection.
28✔
578
     * Using query runners you can control your queries to execute using single database connection and
28✔
579
     * manually control your database transaction.
28✔
580
     *
28✔
581
     * Mode is used in replication mode and indicates whatever you want to connect
28✔
582
     * to master database or any of slave databases.
28✔
583
     * If you perform writes you must use master database,
28✔
584
     * if you perform reads you can use slave databases.
28✔
585
     * @param mode
28✔
586
     */
28✔
587
    createQueryRunner(mode: ReplicationMode = "master"): QueryRunner {
28✔
588
        const queryRunner = this.driver.createQueryRunner(mode)
11,672✔
589
        const manager = this.createEntityManager(queryRunner)
11,672✔
590
        Object.assign(queryRunner, { manager: manager })
11,672✔
591
        return queryRunner
11,672✔
592
    }
11,672✔
593

28✔
594
    /**
28✔
595
     * Gets entity metadata of the junction table (many-to-many table).
28✔
596
     * @param entityTarget
28✔
597
     * @param relationPropertyPath
28✔
598
     */
28✔
599
    getManyToManyMetadata(
28✔
600
        entityTarget: EntityTarget<any>,
1✔
601
        relationPropertyPath: string,
1✔
602
    ) {
1✔
603
        const relationMetadata =
1✔
604
            this.getMetadata(entityTarget).findRelationWithPropertyPath(
1✔
605
                relationPropertyPath,
1✔
606
            )
1✔
607
        if (!relationMetadata)
1✔
608
            throw new TypeORMError(
1!
609
                `Relation "${relationPropertyPath}" was not found in ${entityTarget} entity.`,
×
610
            )
×
611
        if (!relationMetadata.isManyToMany)
1✔
612
            throw new TypeORMError(
1!
613
                `Relation "${entityTarget}#${relationPropertyPath}" does not have a many-to-many relationship.` +
×
614
                    `You can use this method only on many-to-many relations.`,
×
615
            )
×
616

1✔
617
        return relationMetadata.junctionEntityMetadata
1✔
618
    }
1✔
619

28✔
620
    /**
28✔
621
     * Creates an Entity Manager for the current connection with the help of the EntityManagerFactory.
28✔
622
     * @param queryRunner
28✔
623
     */
28✔
624
    createEntityManager(queryRunner?: QueryRunner): EntityManager {
28✔
625
        return new EntityManagerFactory().create(this, queryRunner)
12,116✔
626
    }
12,116✔
627

28✔
628
    // -------------------------------------------------------------------------
28✔
629
    // Protected Methods
28✔
630
    // -------------------------------------------------------------------------
28✔
631

28✔
632
    /**
28✔
633
     * Finds exist entity metadata by the given entity class, target name or table name.
28✔
634
     * @param target
28✔
635
     */
28✔
636
    protected findMetadata(
28✔
637
        target: EntityTarget<any>,
98,040✔
638
    ): EntityMetadata | undefined {
98,040✔
639
        const metadataFromMap = this.entityMetadatasMap.get(target)
98,040✔
640
        if (metadataFromMap) return metadataFromMap
98,040✔
641

3,524✔
642
        for (const [_, metadata] of this.entityMetadatasMap) {
98,040✔
643
            if (
9,376✔
644
                InstanceChecker.isEntitySchema(target) &&
9,376!
645
                metadata.name === target.options.name
37✔
646
            ) {
9,376!
647
                return metadata
26✔
648
            }
26✔
649
            if (typeof target === "string") {
9,374✔
650
                if (target.indexOf(".") !== -1) {
8,775✔
651
                    if (metadata.tablePath === target) {
4,270!
652
                        return metadata
×
653
                    }
×
654
                } else {
8,637✔
655
                    if (
4,505✔
656
                        metadata.name === target ||
4,505✔
657
                        metadata.tableName === target
3,374✔
658
                    ) {
4,505✔
659
                        return metadata
2,224✔
660
                    }
2,224✔
661
                }
4,505✔
662
            }
8,775✔
663
            if (
7,126✔
664
                ObjectUtils.isObjectWithName(target) &&
7,126!
665
                typeof target.name === "string"
×
666
            ) {
9,376!
667
                if (target.name.indexOf(".") !== -1) {
×
668
                    if (metadata.tablePath === target.name) {
×
669
                        return metadata
×
670
                    }
×
671
                } else {
×
672
                    if (
×
673
                        metadata.name === target.name ||
×
674
                        metadata.tableName === target.name
×
675
                    ) {
×
676
                        return metadata
×
677
                    }
×
678
                }
×
679
            }
×
680
        }
9,376✔
681

1,274✔
682
        return undefined
1,274✔
683
    }
1,274✔
684

28✔
685
    /**
28✔
686
     * Builds metadatas for all registered classes inside this connection.
28✔
687
     */
28✔
688
    protected async buildMetadatas(): Promise<void> {
28✔
689
        const connectionMetadataBuilder = new ConnectionMetadataBuilder(this)
432✔
690
        const entityMetadataValidator = new EntityMetadataValidator()
432✔
691

432✔
692
        // create subscribers instances if they are not disallowed from high-level (for example they can disallowed from migrations run process)
432✔
693
        const flattenedSubscribers = ObjectUtils.mixedListToArray(
432✔
694
            this.options.subscribers || [],
432!
695
        )
432✔
696
        const subscribers =
432✔
697
            await connectionMetadataBuilder.buildSubscribers(
432✔
698
                flattenedSubscribers,
432✔
699
            )
432✔
700
        ObjectUtils.assign(this, { subscribers: subscribers })
432✔
701

432✔
702
        // build entity metadatas
432✔
703
        const flattenedEntities = ObjectUtils.mixedListToArray(
432✔
704
            this.options.entities || [],
432!
705
        )
432✔
706
        const entityMetadatas =
432✔
707
            await connectionMetadataBuilder.buildEntityMetadatas(
432✔
708
                flattenedEntities,
432✔
709
            )
432✔
710
        ObjectUtils.assign(this, {
432✔
711
            entityMetadatas: entityMetadatas,
432✔
712
            entityMetadatasMap: new Map(
432✔
713
                entityMetadatas.map((metadata) => [metadata.target, metadata]),
432✔
714
            ),
432✔
715
        })
432✔
716

432✔
717
        // create migration instances
432✔
718
        const flattenedMigrations = ObjectUtils.mixedListToArray(
432✔
719
            this.options.migrations || [],
432!
720
        )
432✔
721
        const migrations =
432✔
722
            await connectionMetadataBuilder.buildMigrations(flattenedMigrations)
432✔
723
        ObjectUtils.assign(this, { migrations: migrations })
432✔
724

432✔
725
        // validate all created entity metadatas to make sure user created entities are valid and correct
432✔
726
        entityMetadataValidator.validateMany(
432✔
727
            this.entityMetadatas.filter(
432✔
728
                (metadata) => metadata.tableType !== "view",
432✔
729
            ),
432✔
730
            this.driver,
432✔
731
        )
432✔
732

432✔
733
        // set current data source to the entities
432✔
734
        for (const entityMetadata of entityMetadatas) {
432✔
735
            if (
1,210✔
736
                InstanceChecker.isBaseEntityConstructor(entityMetadata.target)
1,210✔
737
            ) {
1,210!
738
                entityMetadata.target.useDataSource(this)
38✔
739
            }
38✔
740
        }
1,210✔
741
    }
430✔
742

28✔
743
    /**
28✔
744
     * Get the replication mode SELECT queries should use for this datasource by default
28✔
745
     */
28✔
746
    defaultReplicationModeForReads(): ReplicationMode {
28✔
747
        if (
1,750✔
748
            "replication" in this.driver.options &&
1,750!
749
            this.driver.options.replication
×
750
        ) {
1,750!
751
            const value = (
×
752
                this.driver.options.replication as {
×
753
                    defaultMode?: ReplicationMode
×
754
                }
×
755
            ).defaultMode
×
756
            if (value) {
×
757
                return value
×
758
            }
×
759
        }
×
760
        return "slave"
1,750✔
761
    }
1,750✔
762
}
28✔
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