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

typeorm / typeorm / 15219332477

23 May 2025 09:13PM UTC coverage: 17.216% (-59.1%) from 76.346%
15219332477

Pull #11332

github

naorpeled
cr comments - move if block
Pull Request #11332: feat: add new undefined and null behavior flags

1603 of 12759 branches covered (12.56%)

Branch coverage included in aggregate %.

0 of 31 new or added lines in 3 files covered. (0.0%)

14132 existing lines in 166 files now uncovered.

4731 of 24033 relevant lines covered (19.69%)

60.22 hits per line

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

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

46
registerQueryBuilders()
1✔
47

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

59
    // -------------------------------------------------------------------------
60
    // Public Readonly Properties
61
    // -------------------------------------------------------------------------
62

63
    /**
64
     * Connection name.
65
     *
66
     * @deprecated we don't need names anymore since we are going to drop all related methods relying on this property.
67
     */
68
    readonly name: string
69

70
    /**
71
     * Connection options.
72
     */
73
    readonly options: DataSourceOptions
74

75
    /**
76
     * Indicates if DataSource is initialized or not.
77
     */
78
    readonly isInitialized: boolean
79

80
    /**
81
     * Database driver used by this connection.
82
     */
83
    driver: Driver
84

85
    /**
86
     * EntityManager of this connection.
87
     */
88
    readonly manager: EntityManager
89

90
    /**
91
     * Naming strategy used in the connection.
92
     */
93
    namingStrategy: NamingStrategyInterface
94

95
    /**
96
     * Name for the metadata table
97
     */
98
    readonly metadataTableName: string
99

100
    /**
101
     * Logger used to log orm events.
102
     */
103
    logger: Logger
104

105
    /**
106
     * Migration instances that are registered for this connection.
107
     */
108
    readonly migrations: MigrationInterface[] = []
42✔
109

110
    /**
111
     * Entity subscriber instances that are registered for this connection.
112
     */
113
    readonly subscribers: EntitySubscriberInterface<any>[] = []
42✔
114

115
    /**
116
     * All entity metadatas that are registered for this connection.
117
     */
118
    readonly entityMetadatas: EntityMetadata[] = []
42✔
119

120
    /**
121
     * All entity metadatas that are registered for this connection.
122
     * This is a copy of #.entityMetadatas property -> used for more performant searches.
123
     */
124
    readonly entityMetadatasMap = new Map<EntityTarget<any>, EntityMetadata>()
42✔
125

126
    /**
127
     * Used to work with query result cache.
128
     */
129
    queryResultCache?: QueryResultCache
130

131
    /**
132
     * Used to load relations and work with lazy relations.
133
     */
134
    readonly relationLoader: RelationLoader
135

136
    readonly relationIdLoader: RelationIdLoader
137

138
    // -------------------------------------------------------------------------
139
    // Constructor
140
    // -------------------------------------------------------------------------
141

142
    constructor(options: DataSourceOptions) {
143
        registerQueryBuilders()
42✔
144
        this.name = options.name || "default"
42✔
145
        this.options = options
42✔
146
        this.logger = new LoggerFactory().create(
42✔
147
            this.options.logger,
148
            this.options.logging,
149
        )
150
        this.driver = new DriverFactory().create(this)
42✔
151
        this.manager = this.createEntityManager()
42✔
152
        this.namingStrategy =
42✔
153
            options.namingStrategy || new DefaultNamingStrategy()
84✔
154
        this.metadataTableName = options.metadataTableName || "typeorm_metadata"
42✔
155
        this.queryResultCache = options.cache
42!
156
            ? new QueryResultCacheFactory(this).create()
157
            : undefined
158
        this.relationLoader = new RelationLoader(this)
42✔
159
        this.relationIdLoader = new RelationIdLoader(this)
42✔
160
        this.isInitialized = false
42✔
161
    }
162

163
    // -------------------------------------------------------------------------
164
    // Public Accessors
165
    // -------------------------------------------------------------------------
166

167
    /**
168
     Indicates if DataSource is initialized or not.
169
     *
170
     * @deprecated use .isInitialized instead
171
     */
172
    get isConnected() {
173
        return this.isInitialized
×
174
    }
175

176
    /**
177
     * Gets the mongodb entity manager that allows to perform mongodb-specific repository operations
178
     * with any entity in this connection.
179
     *
180
     * Available only in mongodb connections.
181
     */
182
    get mongoManager(): MongoEntityManager {
183
        if (!InstanceChecker.isMongoEntityManager(this.manager))
5!
184
            throw new TypeORMError(
×
185
                `MongoEntityManager is only available for MongoDB databases.`,
186
            )
187

188
        return this.manager as MongoEntityManager
5✔
189
    }
190

191
    /**
192
     * Gets a sql.js specific Entity Manager that allows to perform special load and save operations
193
     *
194
     * Available only in connection with the sqljs driver.
195
     */
196
    get sqljsManager(): SqljsEntityManager {
UNCOV
197
        if (!InstanceChecker.isSqljsEntityManager(this.manager))
×
198
            throw new TypeORMError(
×
199
                `SqljsEntityManager is only available for Sqljs databases.`,
200
            )
201

UNCOV
202
        return this.manager
×
203
    }
204

205
    // -------------------------------------------------------------------------
206
    // Public Methods
207
    // -------------------------------------------------------------------------
208
    /**
209
     * Updates current connection options with provided options.
210
     */
211
    setOptions(options: Partial<DataSourceOptions>): this {
UNCOV
212
        Object.assign(this.options, options)
×
213

UNCOV
214
        if (options.logger || options.logging) {
×
215
            this.logger = new LoggerFactory().create(
×
216
                options.logger || this.options.logger,
×
217
                options.logging || this.options.logging,
×
218
            )
219
        }
220

UNCOV
221
        if (options.namingStrategy) {
×
222
            this.namingStrategy = options.namingStrategy
×
223
        }
224

UNCOV
225
        if (options.cache) {
×
226
            this.queryResultCache = new QueryResultCacheFactory(this).create()
×
227
        }
228

229
        // todo: we must update the database in the driver as well, if it was set by setOptions method
230
        //  in the future we need to refactor the code and remove "database" from the driver, and instead
231
        //  use database (and options) from a single place - data source.
UNCOV
232
        if (options.database) {
×
UNCOV
233
            this.driver.database = DriverUtils.buildDriverOptions(
×
234
                this.options,
235
            ).database
236
        }
237

238
        // todo: need to take a look if we need to update schema and other "poor" properties
239

UNCOV
240
        return this
×
241
    }
242

243
    /**
244
     * Performs connection to the database.
245
     * This method should be called once on application bootstrap.
246
     * This method not necessarily creates database connection (depend on database type),
247
     * but it also can setup a connection pool with database to use.
248
     */
249
    async initialize(): Promise<this> {
250
        if (this.isInitialized)
29!
UNCOV
251
            throw new CannotConnectAlreadyConnectedError(this.name)
×
252

253
        // connect to the database via its driver
254
        await this.driver.connect()
29✔
255

256
        // connect to the cache-specific database if cache is enabled
257
        if (this.queryResultCache) await this.queryResultCache.connect()
29!
258

259
        // set connected status for the current connection
260
        ObjectUtils.assign(this, { isInitialized: true })
29✔
261

262
        try {
29✔
263
            // build all metadatas registered in the current connection
264
            await this.buildMetadatas()
29✔
265

266
            await this.driver.afterConnect()
29✔
267

268
            // if option is set - drop schema once connection is done
269
            if (this.options.dropSchema) await this.dropDatabase()
29✔
270

271
            // if option is set - automatically synchronize a schema
272
            if (this.options.migrationsRun)
29!
273
                await this.runMigrations({
×
274
                    transaction: this.options.migrationsTransactionMode,
275
                })
276

277
            // if option is set - automatically synchronize a schema
278
            if (this.options.synchronize) await this.synchronize()
29✔
279
        } catch (error) {
280
            // if for some reason build metadata fail (for example validation error during entity metadata check)
281
            // connection needs to be closed
UNCOV
282
            await this.destroy()
×
UNCOV
283
            throw error
×
284
        }
285

286
        return this
29✔
287
    }
288

289
    /**
290
     * Performs connection to the database.
291
     * This method should be called once on application bootstrap.
292
     * This method not necessarily creates database connection (depend on database type),
293
     * but it also can setup a connection pool with database to use.
294
     *
295
     * @deprecated use .initialize method instead
296
     */
297
    async connect(): Promise<this> {
UNCOV
298
        return this.initialize()
×
299
    }
300

301
    /**
302
     * Closes connection with the database.
303
     * Once connection is closed, you cannot use repositories or perform any operations except opening connection again.
304
     */
305
    async destroy(): Promise<void> {
306
        if (!this.isInitialized)
29!
UNCOV
307
            throw new CannotExecuteNotConnectedError(this.name)
×
308

309
        await this.driver.disconnect()
29✔
310

311
        // disconnect from the cache-specific database if cache was enabled
312
        if (this.queryResultCache) await this.queryResultCache.disconnect()
29!
313

314
        ObjectUtils.assign(this, { isInitialized: false })
29✔
315
    }
316

317
    /**
318
     * Closes connection with the database.
319
     * Once connection is closed, you cannot use repositories or perform any operations except opening connection again.
320
     *
321
     * @deprecated use .destroy method instead
322
     */
323
    async close(): Promise<void> {
UNCOV
324
        return this.destroy()
×
325
    }
326

327
    /**
328
     * Creates database schema for all entities registered in this connection.
329
     * Can be used only after connection to the database is established.
330
     *
331
     * @param dropBeforeSync If set to true then it drops the database with all its tables and data
332
     */
333
    async synchronize(dropBeforeSync: boolean = false): Promise<void> {
2✔
334
        if (!this.isInitialized)
71!
UNCOV
335
            throw new CannotExecuteNotConnectedError(this.name)
×
336

337
        if (dropBeforeSync) await this.dropDatabase()
71✔
338

339
        const schemaBuilder = this.driver.createSchemaBuilder()
71✔
340
        await schemaBuilder.build()
71✔
341
    }
342

343
    /**
344
     * Drops the database and all its data.
345
     * Be careful with this method on production since this method will erase all your database tables and their data.
346
     * Can be used only after connection to the database is established.
347
     */
348
    // TODO rename
349
    async dropDatabase(): Promise<void> {
350
        const queryRunner = this.createQueryRunner()
75✔
351
        try {
75✔
352
            if (
75!
353
                this.driver.options.type === "mssql" ||
300✔
354
                DriverUtils.isMySQLFamily(this.driver) ||
355
                this.driver.options.type === "aurora-mysql" ||
356
                DriverUtils.isSQLiteFamily(this.driver)
357
            ) {
UNCOV
358
                const databases: string[] = []
×
UNCOV
359
                this.entityMetadatas.forEach((metadata) => {
×
UNCOV
360
                    if (
×
361
                        metadata.database &&
×
362
                        databases.indexOf(metadata.database) === -1
363
                    )
UNCOV
364
                        databases.push(metadata.database)
×
365
                })
UNCOV
366
                if (databases.length === 0 && this.driver.database) {
×
UNCOV
367
                    databases.push(this.driver.database)
×
368
                }
369

UNCOV
370
                if (databases.length === 0) {
×
UNCOV
371
                    await queryRunner.clearDatabase()
×
372
                } else {
UNCOV
373
                    for (const database of databases) {
×
UNCOV
374
                        await queryRunner.clearDatabase(database)
×
375
                    }
376
                }
377
            } else {
378
                await queryRunner.clearDatabase()
75✔
379
            }
380
        } finally {
381
            await queryRunner.release()
75✔
382
        }
383
    }
384

385
    /**
386
     * Runs all pending migrations.
387
     * Can be used only after connection to the database is established.
388
     */
389
    async runMigrations(options?: {
390
        transaction?: "all" | "none" | "each"
391
        fake?: boolean
392
    }): Promise<Migration[]> {
393
        if (!this.isInitialized)
2!
394
            throw new CannotExecuteNotConnectedError(this.name)
×
395

396
        const migrationExecutor = new MigrationExecutor(this)
2✔
397
        migrationExecutor.transaction =
2✔
398
            options?.transaction ||
6✔
399
            this.options?.migrationsTransactionMode ||
400
            "all"
401
        migrationExecutor.fake = (options && options.fake) || false
2!
402

403
        const successMigrations =
404
            await migrationExecutor.executePendingMigrations()
2✔
405
        return successMigrations
2✔
406
    }
407

408
    /**
409
     * Reverts last executed migration.
410
     * Can be used only after connection to the database is established.
411
     */
412
    async undoLastMigration(options?: {
413
        transaction?: "all" | "none" | "each"
414
        fake?: boolean
415
    }): Promise<void> {
416
        if (!this.isInitialized)
1!
417
            throw new CannotExecuteNotConnectedError(this.name)
×
418

419
        const migrationExecutor = new MigrationExecutor(this)
1✔
420
        migrationExecutor.transaction =
1✔
421
            (options && options.transaction) || "all"
2!
422
        migrationExecutor.fake = (options && options.fake) || false
1!
423

424
        await migrationExecutor.undoLastMigration()
1✔
425
    }
426

427
    /**
428
     * Lists all migrations and whether they have been run.
429
     * Returns true if there are pending migrations
430
     */
431
    async showMigrations(): Promise<boolean> {
UNCOV
432
        if (!this.isInitialized) {
×
433
            throw new CannotExecuteNotConnectedError(this.name)
×
434
        }
UNCOV
435
        const migrationExecutor = new MigrationExecutor(this)
×
UNCOV
436
        return await migrationExecutor.showMigrations()
×
437
    }
438

439
    /**
440
     * Checks if entity metadata exist for the given entity class, target name or table name.
441
     */
442
    hasMetadata(target: EntityTarget<any>): boolean {
UNCOV
443
        return !!this.findMetadata(target)
×
444
    }
445

446
    /**
447
     * Gets entity metadata for the given entity class or schema name.
448
     */
449
    getMetadata(target: EntityTarget<any>): EntityMetadata {
450
        const metadata = this.findMetadata(target)
1,717✔
451
        if (!metadata) throw new EntityMetadataNotFoundError(target)
1,717!
452

453
        return metadata
1,717✔
454
    }
455

456
    /**
457
     * Gets repository for the given entity.
458
     */
459
    getRepository<Entity extends ObjectLiteral>(
460
        target: EntityTarget<Entity>,
461
    ): Repository<Entity> {
462
        return this.manager.getRepository(target)
25✔
463
    }
464

465
    /**
466
     * Gets tree repository for the given entity class or name.
467
     * Only tree-type entities can have a TreeRepository, like ones decorated with @Tree decorator.
468
     */
469
    getTreeRepository<Entity extends ObjectLiteral>(
470
        target: EntityTarget<Entity>,
471
    ): TreeRepository<Entity> {
UNCOV
472
        return this.manager.getTreeRepository(target)
×
473
    }
474

475
    /**
476
     * Gets mongodb-specific repository for the given entity class or name.
477
     * Works only if connection is mongodb-specific.
478
     */
479
    getMongoRepository<Entity extends ObjectLiteral>(
480
        target: EntityTarget<Entity>,
481
    ): MongoRepository<Entity> {
482
        if (!(this.driver.options.type === "mongodb"))
44!
483
            throw new TypeORMError(
×
484
                `You can use getMongoRepository only for MongoDB connections.`,
485
            )
486

487
        return this.manager.getRepository(target) as any
44✔
488
    }
489

490
    /**
491
     * Gets custom entity repository marked with @EntityRepository decorator.
492
     *
493
     * @deprecated use Repository.extend function to create a custom repository
494
     */
495
    getCustomRepository<T>(customRepository: ObjectType<T>): T {
496
        return this.manager.getCustomRepository(customRepository)
×
497
    }
498

499
    /**
500
     * Wraps given function execution (and all operations made there) into a transaction.
501
     * All database operations must be executed using provided entity manager.
502
     */
503
    async transaction<T>(
504
        runInTransaction: (entityManager: EntityManager) => Promise<T>,
505
    ): Promise<T>
506
    async transaction<T>(
507
        isolationLevel: IsolationLevel,
508
        runInTransaction: (entityManager: EntityManager) => Promise<T>,
509
    ): Promise<T>
510
    async transaction<T>(
511
        isolationOrRunInTransaction:
512
            | IsolationLevel
513
            | ((entityManager: EntityManager) => Promise<T>),
514
        runInTransactionParam?: (entityManager: EntityManager) => Promise<T>,
515
    ): Promise<any> {
UNCOV
516
        return this.manager.transaction(
×
517
            isolationOrRunInTransaction as any,
518
            runInTransactionParam as any,
519
        )
520
    }
521

522
    /**
523
     * Executes raw SQL query and returns raw database results.
524
     *
525
     * @see [Official docs](https://typeorm.io/data-source-api) for examples.
526
     */
527
    async query<T = any>(
528
        query: string,
529
        parameters?: any[],
530
        queryRunner?: QueryRunner,
531
    ): Promise<T> {
UNCOV
532
        if (InstanceChecker.isMongoEntityManager(this.manager))
×
533
            throw new TypeORMError(`Queries aren't supported by MongoDB.`)
×
534

UNCOV
535
        if (queryRunner && queryRunner.isReleased)
×
536
            throw new QueryRunnerProviderAlreadyReleasedError()
×
537

UNCOV
538
        const usedQueryRunner = queryRunner || this.createQueryRunner()
×
539

UNCOV
540
        try {
×
UNCOV
541
            return await usedQueryRunner.query(query, parameters) // await is needed here because we are using finally
×
542
        } finally {
UNCOV
543
            if (!queryRunner) await usedQueryRunner.release()
×
544
        }
545
    }
546

547
    /**
548
     * Tagged template function that executes raw SQL query and returns raw database results.
549
     * Template expressions are automatically transformed into database parameters.
550
     * Raw query execution is supported only by relational databases (MongoDB is not supported).
551
     * Note: Don't call this as a regular function, it is meant to be used with backticks to tag a template literal.
552
     * Example: dataSource.sql`SELECT * FROM table_name WHERE id = ${id}`
553
     */
554
    async sql<T = any>(
555
        strings: TemplateStringsArray,
556
        ...values: unknown[]
557
    ): Promise<T> {
UNCOV
558
        const { query, parameters } = buildSqlTag({
×
559
            driver: this.driver,
560
            strings: strings,
561
            expressions: values,
562
        })
563

UNCOV
564
        return await this.query(query, parameters)
×
565
    }
566

567
    /**
568
     * Creates a new query builder that can be used to build a SQL query.
569
     */
570
    createQueryBuilder<Entity extends ObjectLiteral>(
571
        entityClass: EntityTarget<Entity>,
572
        alias: string,
573
        queryRunner?: QueryRunner,
574
    ): SelectQueryBuilder<Entity>
575

576
    /**
577
     * Creates a new query builder that can be used to build a SQL query.
578
     */
579
    createQueryBuilder(queryRunner?: QueryRunner): SelectQueryBuilder<any>
580

581
    /**
582
     * Creates a new query builder that can be used to build a SQL query.
583
     */
584
    createQueryBuilder<Entity extends ObjectLiteral>(
585
        entityOrRunner?: EntityTarget<Entity> | QueryRunner,
586
        alias?: string,
587
        queryRunner?: QueryRunner,
588
    ): SelectQueryBuilder<Entity> {
UNCOV
589
        if (InstanceChecker.isMongoEntityManager(this.manager))
×
590
            throw new TypeORMError(`Query Builder is not supported by MongoDB.`)
×
591

UNCOV
592
        if (alias) {
×
UNCOV
593
            alias = DriverUtils.buildAlias(this.driver, undefined, alias)
×
UNCOV
594
            const metadata = this.getMetadata(
×
595
                entityOrRunner as EntityTarget<Entity>,
596
            )
UNCOV
597
            return new SelectQueryBuilder(this, queryRunner)
×
598
                .select(alias)
599
                .from(metadata.target, alias)
600
        } else {
UNCOV
601
            return new SelectQueryBuilder(
×
602
                this,
603
                entityOrRunner as QueryRunner | undefined,
604
            )
605
        }
606
    }
607

608
    /**
609
     * Creates a query runner used for perform queries on a single database connection.
610
     * Using query runners you can control your queries to execute using single database connection and
611
     * manually control your database transaction.
612
     *
613
     * Mode is used in replication mode and indicates whatever you want to connect
614
     * to master database or any of slave databases.
615
     * If you perform writes you must use master database,
616
     * if you perform reads you can use slave databases.
617
     */
618
    createQueryRunner(mode: ReplicationMode = "master"): QueryRunner {
289✔
619
        const queryRunner = this.driver.createQueryRunner(mode)
289✔
620
        const manager = this.createEntityManager(queryRunner)
289✔
621
        Object.assign(queryRunner, { manager: manager })
289✔
622
        return queryRunner
289✔
623
    }
624

625
    /**
626
     * Gets entity metadata of the junction table (many-to-many table).
627
     */
628
    getManyToManyMetadata(
629
        entityTarget: EntityTarget<any>,
630
        relationPropertyPath: string,
631
    ) {
632
        const relationMetadata =
UNCOV
633
            this.getMetadata(entityTarget).findRelationWithPropertyPath(
×
634
                relationPropertyPath,
635
            )
UNCOV
636
        if (!relationMetadata)
×
637
            throw new TypeORMError(
×
638
                `Relation "${relationPropertyPath}" was not found in ${entityTarget} entity.`,
639
            )
UNCOV
640
        if (!relationMetadata.isManyToMany)
×
641
            throw new TypeORMError(
×
642
                `Relation "${entityTarget}#${relationPropertyPath}" does not have a many-to-many relationship.` +
643
                    `You can use this method only on many-to-many relations.`,
644
            )
645

UNCOV
646
        return relationMetadata.junctionEntityMetadata
×
647
    }
648

649
    /**
650
     * Creates an Entity Manager for the current connection with the help of the EntityManagerFactory.
651
     */
652
    createEntityManager(queryRunner?: QueryRunner): EntityManager {
653
        return new EntityManagerFactory().create(this, queryRunner)
331✔
654
    }
655

656
    // -------------------------------------------------------------------------
657
    // Protected Methods
658
    // -------------------------------------------------------------------------
659

660
    /**
661
     * Finds exist entity metadata by the given entity class, target name or table name.
662
     */
663
    protected findMetadata(
664
        target: EntityTarget<any>,
665
    ): EntityMetadata | undefined {
666
        const metadataFromMap = this.entityMetadatasMap.get(target)
1,717✔
667
        if (metadataFromMap) return metadataFromMap
1,717✔
668

669
        for (const [_, metadata] of this.entityMetadatasMap) {
2✔
670
            if (
3✔
671
                InstanceChecker.isEntitySchema(target) &&
6✔
672
                metadata.name === target.options.name
673
            ) {
674
                return metadata
2✔
675
            }
676
            if (typeof target === "string") {
1!
UNCOV
677
                if (target.indexOf(".") !== -1) {
×
UNCOV
678
                    if (metadata.tablePath === target) {
×
UNCOV
679
                        return metadata
×
680
                    }
681
                } else {
UNCOV
682
                    if (
×
683
                        metadata.name === target ||
×
684
                        metadata.tableName === target
685
                    ) {
UNCOV
686
                        return metadata
×
687
                    }
688
                }
689
            }
690
            if (
1!
691
                ObjectUtils.isObjectWithName(target) &&
1!
692
                typeof target.name === "string"
693
            ) {
694
                if (target.name.indexOf(".") !== -1) {
×
695
                    if (metadata.tablePath === target.name) {
×
696
                        return metadata
×
697
                    }
698
                } else {
699
                    if (
×
700
                        metadata.name === target.name ||
×
701
                        metadata.tableName === target.name
702
                    ) {
703
                        return metadata
×
704
                    }
705
                }
706
            }
707
        }
708

UNCOV
709
        return undefined
×
710
    }
711

712
    /**
713
     * Builds metadatas for all registered classes inside this connection.
714
     */
715
    protected async buildMetadatas(): Promise<void> {
716
        const connectionMetadataBuilder = new ConnectionMetadataBuilder(this)
29✔
717
        const entityMetadataValidator = new EntityMetadataValidator()
29✔
718

719
        // create subscribers instances if they are not disallowed from high-level (for example they can disallowed from migrations run process)
720
        const flattenedSubscribers = ObjectUtils.mixedListToArray(
29✔
721
            this.options.subscribers || [],
29!
722
        )
723
        const subscribers = await connectionMetadataBuilder.buildSubscribers(
29✔
724
            flattenedSubscribers,
725
        )
726
        ObjectUtils.assign(this, { subscribers: subscribers })
29✔
727

728
        // build entity metadatas
729
        const flattenedEntities = ObjectUtils.mixedListToArray(
29✔
730
            this.options.entities || [],
29!
731
        )
732
        const entityMetadatas =
733
            await connectionMetadataBuilder.buildEntityMetadatas(
29✔
734
                flattenedEntities,
735
            )
736
        ObjectUtils.assign(this, {
29✔
737
            entityMetadatas: entityMetadatas,
738
            entityMetadatasMap: new Map(
739
                entityMetadatas.map((metadata) => [metadata.target, metadata]),
33✔
740
            ),
741
        })
742

743
        // create migration instances
744
        const flattenedMigrations = ObjectUtils.mixedListToArray(
29✔
745
            this.options.migrations || [],
29!
746
        )
747
        const migrations = await connectionMetadataBuilder.buildMigrations(
29✔
748
            flattenedMigrations,
749
        )
750
        ObjectUtils.assign(this, { migrations: migrations })
29✔
751

752
        // validate all created entity metadatas to make sure user created entities are valid and correct
753
        entityMetadataValidator.validateMany(
29✔
754
            this.entityMetadatas.filter(
755
                (metadata) => metadata.tableType !== "view",
33✔
756
            ),
757
            this.driver,
758
        )
759

760
        // set current data source to the entities
761
        for (const entityMetadata of entityMetadatas) {
29✔
762
            if (
33!
763
                InstanceChecker.isBaseEntityConstructor(entityMetadata.target)
764
            ) {
UNCOV
765
                entityMetadata.target.useDataSource(this)
×
766
            }
767
        }
768
    }
769

770
    /**
771
     * Get the replication mode SELECT queries should use for this datasource by default
772
     */
773
    defaultReplicationModeForReads(): ReplicationMode {
UNCOV
774
        if (
×
775
            "replication" in this.driver.options &&
×
776
            this.driver.options.replication
777
        ) {
778
            const value = (
UNCOV
779
                this.driver.options.replication as {
×
780
                    defaultMode?: ReplicationMode
781
                }
782
            ).defaultMode
UNCOV
783
            if (value) {
×
UNCOV
784
                return value
×
785
            }
786
        }
UNCOV
787
        return "slave"
×
788
    }
789
}
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

© 2025 Coveralls, Inc