mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
refactoring connection class
This commit is contained in:
parent
365055c6ff
commit
edc6a31b57
@ -28,8 +28,13 @@ each for its own `findOne*` or `find*` methods
|
||||
* controller / subscriber / migrations from options tsconfig now appended with a project root directory
|
||||
* removed naming strategy decorator, naming strategy by name functionality.
|
||||
Now naming strategy should be registered by passing naming strategy instance directly
|
||||
* driver options now deprecated in connection options
|
||||
* `driver` section in connection options now deprecated. All settings should go directly to connection options root.
|
||||
* removed `fromTable` from the `QueryBuilder`. Now use regular `from` to select from tables
|
||||
|
||||
### OTHER API CHANGES
|
||||
|
||||
* moved `query`, `transaction` and `createQueryBuilder` to the `Connection`.
|
||||
`EntityManager` now simply use them from the connection.
|
||||
|
||||
### NEW FEATURES
|
||||
|
||||
|
||||
@ -35,6 +35,19 @@ import {MongoDriver} from "../driver/mongodb/MongoDriver";
|
||||
import {MongoEntityManager} from "../entity-manager/MongoEntityManager";
|
||||
import {EntitySchemaTransformer} from "../entity-schema/EntitySchemaTransformer";
|
||||
import {EntityMetadataValidator} from "../metadata-builder/EntityMetadataValidator";
|
||||
import {ConnectionOptions} from "./ConnectionOptions";
|
||||
import {MysqlDriver} from "../driver/mysql/MysqlDriver";
|
||||
import {PostgresDriver} from "../driver/postgres/PostgresDriver";
|
||||
import {SqliteDriver} from "../driver/sqlite/SqliteDriver";
|
||||
import {OracleDriver} from "../driver/oracle/OracleDriver";
|
||||
import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver";
|
||||
import {WebsqlDriver} from "../driver/websql/WebsqlDriver";
|
||||
import {MissingDriverError} from "../driver/error/MissingDriverError";
|
||||
import {OrmUtils} from "../util/OrmUtils";
|
||||
import {QueryRunnerProviderAlreadyReleasedError} from "../query-runner/error/QueryRunnerProviderAlreadyReleasedError";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {DriverFactory} from "../driver/DriverFactory";
|
||||
import {EntityManagerFactory} from "../entity-manager/EntityManagerFactory";
|
||||
|
||||
/**
|
||||
* Connection is a single database ORM connection to a specific DBMS database.
|
||||
@ -52,6 +65,11 @@ export class Connection {
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* Connection options.
|
||||
*/
|
||||
readonly options: ConnectionOptions;
|
||||
|
||||
/**
|
||||
* Indicates if connection is initialized or not.
|
||||
*/
|
||||
@ -63,34 +81,29 @@ export class Connection {
|
||||
readonly driver: Driver;
|
||||
|
||||
/**
|
||||
* Gets EntityManager of this connection.
|
||||
* EntityManager of this connection.
|
||||
*/
|
||||
readonly manager: EntityManager;
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection.
|
||||
*/
|
||||
readonly namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Logger used to log orm events.
|
||||
*/
|
||||
readonly logger: Logger;
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection.
|
||||
* Migration instances that are registered for this connection.
|
||||
*/
|
||||
readonly namingStrategy = new DefaultNamingStrategy();
|
||||
readonly migrations: MigrationInterface[] = [];
|
||||
|
||||
/**
|
||||
* All entity metadatas that are registered for this connection.
|
||||
* Entity subscriber instances that are registered for this connection.
|
||||
*/
|
||||
readonly entityMetadatas: EntityMetadata[] = [];
|
||||
|
||||
/**
|
||||
* Used to broadcast connection events.
|
||||
*/
|
||||
readonly broadcaster: Broadcaster;
|
||||
|
||||
/**
|
||||
* Used to wrap lazy relations to be able to perform lazy loadings.
|
||||
*/
|
||||
readonly lazyRelationsWrapper = new LazyRelationsWrapper(this);
|
||||
readonly subscribers: EntitySubscriberInterface<any>[] = [];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Properties
|
||||
@ -102,40 +115,21 @@ export class Connection {
|
||||
private repositoryAggregators: RepositoryAggregator[] = [];
|
||||
|
||||
/**
|
||||
* Entity subscribers that are registered for this connection.
|
||||
* All entity metadatas that are registered for this connection.
|
||||
*/
|
||||
private entitySubscribers: EntitySubscriberInterface<any>[] = [];
|
||||
|
||||
/**
|
||||
* Registered entity classes to be used for this connection.
|
||||
*/
|
||||
private entityClasses: Function[] = [];
|
||||
|
||||
/**
|
||||
* Registered entity schemas to be used for this connection.
|
||||
*/
|
||||
private entitySchemas: EntitySchema[] = [];
|
||||
|
||||
/**
|
||||
* Registered subscriber classes to be used for this connection.
|
||||
*/
|
||||
private subscriberClasses: Function[] = [];
|
||||
|
||||
/**
|
||||
* Registered migration classes to be used for this connection.
|
||||
*/
|
||||
private migrationClasses: Function[] = [];
|
||||
private entityMetadatas: EntityMetadata[] = [];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(name: string, driver: Driver, logger: Logger) {
|
||||
this.name = name;
|
||||
this.driver = driver;
|
||||
this.logger = logger;
|
||||
this.broadcaster = new Broadcaster(this, this.entitySubscribers);
|
||||
this.manager = this.createEntityManager();
|
||||
constructor(options: ConnectionOptions) {
|
||||
this.name = options.name || "default";
|
||||
this.options = options;
|
||||
this.logger = new Logger(options.logging || {});
|
||||
this.namingStrategy = options.namingStrategy || new DefaultNamingStrategy();
|
||||
this.driver = new DriverFactory().create(this);
|
||||
this.manager = new EntityManagerFactory().create(this);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -204,6 +198,7 @@ export class Connection {
|
||||
|
||||
/**
|
||||
* Creates database schema for all entities registered in this connection.
|
||||
* Can be used only after connection to the database is established.
|
||||
*
|
||||
* @param dropBeforeSync If set to true then it drops the database with all its tables and data
|
||||
*/
|
||||
@ -227,6 +222,7 @@ export class Connection {
|
||||
/**
|
||||
* Drops the database and all its data.
|
||||
* Be careful with this method on production since this method will erase all your database tables and data inside them.
|
||||
* Can be used only after connection to the database is established.
|
||||
*/
|
||||
async dropDatabase(): Promise<void> {
|
||||
const queryRunner = await this.driver.createQueryRunner();
|
||||
@ -235,6 +231,7 @@ export class Connection {
|
||||
|
||||
/**
|
||||
* Runs all pending migrations.
|
||||
* Can be used only after connection to the database is established.
|
||||
*/
|
||||
async runMigrations(): Promise<void> {
|
||||
|
||||
@ -247,6 +244,7 @@ export class Connection {
|
||||
|
||||
/**
|
||||
* Reverts last executed migration.
|
||||
* Can be used only after connection to the database is established.
|
||||
*/
|
||||
async undoLastMigration(): Promise<void> {
|
||||
|
||||
@ -258,91 +256,44 @@ export class Connection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports entities from the given paths (directories) and registers them in the current connection.
|
||||
* Creates a new entity manager with a single opened connection to the database.
|
||||
* This may be useful if you want to perform all db queries within one connection.
|
||||
* After finishing with entity manager, don't forget to release it, to release connection back to pool.
|
||||
*/
|
||||
importEntitiesFromDirectories(paths: string[]): this {
|
||||
this.importEntities(importClassesFromDirectories(paths));
|
||||
return this;
|
||||
createIsolatedManager(queryRunnerProvider?: QueryRunnerProvider): EntityManager {
|
||||
if (!queryRunnerProvider)
|
||||
queryRunnerProvider = new QueryRunnerProvider(this.driver, true);
|
||||
|
||||
return new EntityManager(this, queryRunnerProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports entity schemas from the given paths (directories) and registers them in the current connection.
|
||||
* Checks if entity metadata exist for the given entity class.
|
||||
*/
|
||||
importEntitySchemaFromDirectories(paths: string[]): this {
|
||||
this.importEntitySchemas(importJsonsFromDirectories(paths));
|
||||
return this;
|
||||
}
|
||||
hasMetadata(target: Function): boolean;
|
||||
|
||||
/**
|
||||
* Imports subscribers from the given paths (directories) and registers them in the current connection.
|
||||
* Checks if entity metadata exist for the given entity target name or table name.
|
||||
*/
|
||||
importSubscribersFromDirectories(paths: string[]): this {
|
||||
this.importSubscribers(importClassesFromDirectories(paths));
|
||||
return this;
|
||||
}
|
||||
hasMetadata(target: string): boolean;
|
||||
|
||||
/**
|
||||
* Imports migrations from the given paths (directories) and registers them in the current connection.
|
||||
* Checks if entity metadata exist for the given entity class, target name or table name.
|
||||
*/
|
||||
importMigrationsFromDirectories(paths: string[]): this {
|
||||
this.importMigrations(importClassesFromDirectories(paths));
|
||||
return this;
|
||||
}
|
||||
hasMetadata(target: Function|string): boolean;
|
||||
|
||||
/**
|
||||
* Imports entities and registers them in the current connection.
|
||||
* Checks if entity metadata exist for the given entity class, target name or table name.
|
||||
*/
|
||||
importEntities(entities: Function[]): this {
|
||||
if (this.isConnected)
|
||||
throw new CannotImportAlreadyConnectedError("entities", this.name);
|
||||
hasMetadata(target: Function|string): boolean {
|
||||
return !!this.entityMetadatas.find(metadata => {
|
||||
if (metadata.target === target)
|
||||
return true;
|
||||
if (typeof target === "string")
|
||||
return metadata.name === target || metadata.tableName === target;
|
||||
|
||||
entities.forEach(cls => this.entityClasses.push(cls));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports schemas and registers them in the current connection.
|
||||
*/
|
||||
importEntitySchemas(schemas: EntitySchema[]): this {
|
||||
if (this.isConnected)
|
||||
throw new CannotImportAlreadyConnectedError("schemas", this.name);
|
||||
|
||||
schemas.forEach(schema => this.entitySchemas.push(schema));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports subscribers and registers them in the current connection.
|
||||
*/
|
||||
importSubscribers(subscriberClasses: Function[]): this {
|
||||
if (this.isConnected)
|
||||
throw new CannotImportAlreadyConnectedError("entity subscribers", this.name);
|
||||
|
||||
subscriberClasses.forEach(cls => this.subscriberClasses.push(cls));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports migrations and registers them in the current connection.
|
||||
*/
|
||||
importMigrations(migrations: Function[]): this {
|
||||
if (this.isConnected)
|
||||
throw new CannotImportAlreadyConnectedError("migrations", this.name);
|
||||
|
||||
migrations.forEach(cls => this.migrationClasses.push(cls));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets given naming strategy to be used.
|
||||
* Naming strategy must be set to be used before connection is established.
|
||||
*/
|
||||
useNamingStrategy(namingStrategy: NamingStrategyInterface): this {
|
||||
if (this.isConnected)
|
||||
throw new CannotUseNamingStrategyNotConnectedError(this.name);
|
||||
|
||||
Object.assign(this, { namingStrategy: namingStrategy });
|
||||
return this;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -364,7 +315,14 @@ export class Connection {
|
||||
Gets entity metadata for the given entity class or schema name.
|
||||
*/
|
||||
getMetadata(target: Function|string): EntityMetadata {
|
||||
const metadata = this.entityMetadatas.find(metadata => metadata.target === target || (typeof target === "string" && metadata.targetName === target));
|
||||
const metadata = this.entityMetadatas.find(metadata => {
|
||||
if (metadata.target === target)
|
||||
return true;
|
||||
if (typeof target === "string")
|
||||
return metadata.name === target || metadata.tableName === target;
|
||||
|
||||
return false;
|
||||
});
|
||||
if (!metadata)
|
||||
throw new EntityMetadataNotFound(target);
|
||||
|
||||
@ -454,65 +412,6 @@ export class Connection {
|
||||
return this.findRepositoryAggregator(entityClassOrName).repository as MongoRepository<Entity>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity class.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityClass: ObjectType<Entity>): SpecificRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity name.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityName: string): SpecificRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity class or name.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity class or name.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificRepository<Entity> {
|
||||
return this.findRepositoryAggregator(entityClassOrName).specificRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entity manager with a single opened connection to the database.
|
||||
* This may be useful if you want to perform all db queries within one connection.
|
||||
* After finishing with entity manager, don't forget to release it, to release connection back to pool.
|
||||
*/
|
||||
createEntityManagerWithSingleDatabaseConnection(queryRunnerProvider?: QueryRunnerProvider): EntityManager {
|
||||
if (!queryRunnerProvider)
|
||||
queryRunnerProvider = new QueryRunnerProvider(this.driver, true);
|
||||
|
||||
return new EntityManager(this, queryRunnerProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets migration instances that are registered for this connection.
|
||||
*/
|
||||
getMigrations(): MigrationInterface[] {
|
||||
if (this.migrationClasses && this.migrationClasses.length) {
|
||||
return this.migrationClasses.map(migrationClass => {
|
||||
return getFromContainer<MigrationInterface>(migrationClass);
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets custom entity repository marked with @EntityRepository decorator.
|
||||
*/
|
||||
@ -520,8 +419,89 @@ export class Connection {
|
||||
return this.manager.getCustomRepository(customRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps given function execution (and all operations made there) in a transaction.
|
||||
* All database operations must be executed using provided entity manager.
|
||||
*/
|
||||
async transaction(runInTransaction: (entityManger: EntityManager) => Promise<any>,
|
||||
queryRunnerProvider?: QueryRunnerProvider): Promise<any> {
|
||||
if (this instanceof MongoEntityManager)
|
||||
throw new Error(`Transactions aren't supported by MongoDB.`);
|
||||
|
||||
if (queryRunnerProvider && queryRunnerProvider.isReleased)
|
||||
throw new QueryRunnerProviderAlreadyReleasedError();
|
||||
|
||||
const usedQueryRunnerProvider = queryRunnerProvider || new QueryRunnerProvider(this.driver, true);
|
||||
const queryRunner = await usedQueryRunnerProvider.provide();
|
||||
const transactionEntityManager = new EntityManager(this, usedQueryRunnerProvider);
|
||||
|
||||
try {
|
||||
await queryRunner.beginTransaction();
|
||||
const result = await runInTransaction(transactionEntityManager);
|
||||
await queryRunner.commitTransaction();
|
||||
return result;
|
||||
|
||||
} catch (err) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw err;
|
||||
|
||||
} finally {
|
||||
await usedQueryRunnerProvider.release(queryRunner);
|
||||
if (!queryRunnerProvider) // if we used a new query runner provider then release it
|
||||
await usedQueryRunnerProvider.releaseReused(); // todo: why we don't do same in query method?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes raw SQL query and returns raw database results.
|
||||
*/
|
||||
async query(query: string, parameters?: any[], queryRunnerProvider?: QueryRunnerProvider): Promise<any> {
|
||||
if (this instanceof MongoEntityManager)
|
||||
throw new Error(`Queries aren't supported by MongoDB.`);
|
||||
|
||||
if (queryRunnerProvider && queryRunnerProvider.isReleased)
|
||||
throw new QueryRunnerProviderAlreadyReleasedError();
|
||||
|
||||
const usedQueryRunnerProvider = queryRunnerProvider || new QueryRunnerProvider(this.driver);
|
||||
const queryRunner = await usedQueryRunnerProvider.provide();
|
||||
|
||||
try {
|
||||
return await queryRunner.query(query, parameters); // await is needed here because we are using finally
|
||||
|
||||
} finally {
|
||||
await usedQueryRunnerProvider.release(queryRunner);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new query builder that can be used to build a sql query.
|
||||
*/
|
||||
createQueryBuilder<Entity>(entityClass: ObjectType<Entity>|Function|string, alias: string, queryRunnerProvider?: QueryRunnerProvider): QueryBuilder<Entity>;
|
||||
|
||||
/**
|
||||
* Creates a new query builder that can be used to build a sql query.
|
||||
*/
|
||||
createQueryBuilder(queryRunnerProvider?: QueryRunnerProvider): QueryBuilder<any>;
|
||||
|
||||
/**
|
||||
* Creates a new query builder that can be used to build a sql query.
|
||||
*/
|
||||
createQueryBuilder<Entity>(entityClass?: ObjectType<Entity>|Function|string|QueryRunnerProvider, alias?: string, queryRunnerProvider?: QueryRunnerProvider): QueryBuilder<Entity> {
|
||||
if (this instanceof MongoEntityManager)
|
||||
throw new Error(`Query Builder is not supported by MongoDB.`);
|
||||
|
||||
if (alias) {
|
||||
const metadata = this.getMetadata(entityClass as Function|string);
|
||||
return new QueryBuilder(this, queryRunnerProvider)
|
||||
.select(alias)
|
||||
.from(metadata.target, alias);
|
||||
} else {
|
||||
return new QueryBuilder(this, entityClass as QueryRunnerProvider|undefined);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Deprecated
|
||||
// Deprecated Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@ -533,6 +513,40 @@ export class Connection {
|
||||
return this.manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity class.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @deprecated don't use it, it will be refactored or removed in the future versions
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityClass: ObjectType<Entity>): SpecificRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity name.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @deprecated don't use it, it will be refactored or removed in the future versions
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityName: string): SpecificRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity class or name.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @deprecated don't use it, it will be refactored or removed in the future versions
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity class or name.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @deprecated don't use it, it will be refactored or removed in the future versions
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificRepository<Entity> {
|
||||
return this.findRepositoryAggregator(entityClassOrName).specificRepository;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
@ -541,10 +555,7 @@ export class Connection {
|
||||
* Finds repository aggregator of the given entity class or name.
|
||||
*/
|
||||
protected findRepositoryAggregator(entityClassOrName: ObjectType<any>|string): RepositoryAggregator {
|
||||
// if (!this.isConnected)
|
||||
// throw new NoConnectionForRepositoryError(this.name);
|
||||
|
||||
if (!this.entityMetadatas.find(metadata => metadata.target === entityClassOrName || (typeof entityClassOrName === "string" && metadata.targetName === entityClassOrName)))
|
||||
if (!this.hasMetadata(entityClassOrName))
|
||||
throw new RepositoryNotFoundError(this.name, entityClassOrName);
|
||||
|
||||
const metadata = this.getMetadata(entityClassOrName);
|
||||
@ -560,56 +571,59 @@ export class Connection {
|
||||
*/
|
||||
public buildMetadatas() {
|
||||
|
||||
this.entitySubscribers.length = 0;
|
||||
// import entity schemas
|
||||
const [entitySchemaDirectories, entitySchemaClasses] = OrmUtils.splitStringsAndClasses(this.options.entitySchemas || []);
|
||||
entitySchemaClasses.push(...importJsonsFromDirectories(entitySchemaDirectories));
|
||||
|
||||
const [entityDirectories, entityClasses] = OrmUtils.splitStringsAndClasses(this.options.entities || []);
|
||||
entityClasses.push(...importClassesFromDirectories(entityDirectories));
|
||||
|
||||
const [subscriberDirectories, subscriberClasses] = OrmUtils.splitStringsAndClasses(this.options.subscribers || []);
|
||||
subscriberClasses.push(...importClassesFromDirectories(subscriberDirectories));
|
||||
|
||||
const [migrationDirectories, migrationClasses] = OrmUtils.splitStringsAndClasses(this.options.migrations || []);
|
||||
migrationClasses.push(...importClassesFromDirectories(migrationDirectories));
|
||||
|
||||
// create migration instances
|
||||
const migrations = migrationClasses.map(migrationClass => {
|
||||
return getFromContainer<MigrationInterface>(migrationClass);
|
||||
});
|
||||
Object.assign(this, { migrations });
|
||||
|
||||
this.subscribers.length = 0;
|
||||
this.repositoryAggregators.length = 0;
|
||||
this.entityMetadatas.length = 0;
|
||||
|
||||
const entityMetadataValidator = new EntityMetadataValidator();
|
||||
|
||||
// take imported event subscribers
|
||||
if (this.subscriberClasses &&
|
||||
this.subscriberClasses.length &&
|
||||
!PlatformTools.getEnvVariable("SKIP_SUBSCRIBERS_LOADING")) {
|
||||
if (!PlatformTools.getEnvVariable("SKIP_SUBSCRIBERS_LOADING")) {
|
||||
getMetadataArgsStorage()
|
||||
.filterSubscribers(this.subscriberClasses)
|
||||
.filterSubscribers(subscriberClasses)
|
||||
.map(metadata => getFromContainer(metadata.target))
|
||||
.forEach(subscriber => this.entitySubscribers.push(subscriber));
|
||||
.forEach(subscriber => this.subscribers.push(subscriber));
|
||||
}
|
||||
|
||||
// take imported entity listeners
|
||||
if (this.entityClasses && this.entityClasses.length) {
|
||||
|
||||
// build entity metadatas from metadata args storage (collected from decorators)
|
||||
new EntityMetadataBuilder(this, getMetadataArgsStorage())
|
||||
.build(this.entityClasses)
|
||||
.forEach(metadata => {
|
||||
this.entityMetadatas.push(metadata);
|
||||
this.repositoryAggregators.push(new RepositoryAggregator(this, metadata));
|
||||
});
|
||||
}
|
||||
// build entity metadatas from metadata args storage (collected from decorators)
|
||||
new EntityMetadataBuilder(this, getMetadataArgsStorage())
|
||||
.build(entityClasses)
|
||||
.forEach(metadata => {
|
||||
this.entityMetadatas.push(metadata);
|
||||
this.repositoryAggregators.push(new RepositoryAggregator(this, metadata));
|
||||
});
|
||||
|
||||
// build entity metadatas from given entity schemas
|
||||
if (this.entitySchemas && this.entitySchemas.length) {
|
||||
const metadataArgsStorage = getFromContainer(EntitySchemaTransformer).transform(this.entitySchemas);
|
||||
new EntityMetadataBuilder(this, metadataArgsStorage)
|
||||
.build()
|
||||
.forEach(metadata => {
|
||||
this.entityMetadatas.push(metadata);
|
||||
this.repositoryAggregators.push(new RepositoryAggregator(this, metadata));
|
||||
});
|
||||
}
|
||||
const metadataArgsStorage = getFromContainer(EntitySchemaTransformer)
|
||||
.transform(entitySchemaClasses);
|
||||
new EntityMetadataBuilder(this, metadataArgsStorage)
|
||||
.build()
|
||||
.forEach(metadata => {
|
||||
this.entityMetadatas.push(metadata);
|
||||
this.repositoryAggregators.push(new RepositoryAggregator(this, metadata));
|
||||
});
|
||||
|
||||
entityMetadataValidator.validateMany(this.entityMetadatas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new default entity manager without single connection setup.
|
||||
*/
|
||||
protected createEntityManager() {
|
||||
if (this.driver instanceof MongoDriver)
|
||||
return new MongoEntityManager(this);
|
||||
|
||||
return new EntityManager(this);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,21 +1,10 @@
|
||||
import {Connection} from "./Connection";
|
||||
import {ConnectionNotFoundError} from "./error/ConnectionNotFoundError";
|
||||
import {MysqlDriver} from "../driver/mysql/MysqlDriver";
|
||||
import {ConnectionOptions} from "./ConnectionOptions";
|
||||
import {DriverOptions} from "../driver/DriverOptions";
|
||||
import {Driver} from "../driver/Driver";
|
||||
import {MissingDriverError} from "./error/MissingDriverError";
|
||||
import {PostgresDriver} from "../driver/postgres/PostgresDriver";
|
||||
import {AlreadyHasActiveConnectionError} from "./error/AlreadyHasActiveConnectionError";
|
||||
import {Logger} from "../logger/Logger";
|
||||
import {SqliteDriver} from "../driver/sqlite/SqliteDriver";
|
||||
import {OracleDriver} from "../driver/oracle/OracleDriver";
|
||||
import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver";
|
||||
import {OrmUtils} from "../util/OrmUtils";
|
||||
import {CannotDetermineConnectionOptionsError} from "./error/CannotDetermineConnectionOptionsError";
|
||||
import {PlatformTools} from "../platform/PlatformTools";
|
||||
import {WebsqlDriver} from "../driver/websql/WebsqlDriver";
|
||||
import {MongoDriver} from "../driver/mongodb/MongoDriver";
|
||||
|
||||
/**
|
||||
* ConnectionManager is used to store and manage all these different connections.
|
||||
@ -64,46 +53,20 @@ export class ConnectionManager {
|
||||
*/
|
||||
create(options: ConnectionOptions): Connection {
|
||||
|
||||
const logger = new Logger(options.logging || {});
|
||||
const driver = this.createDriver(options.driver || {} as DriverOptions, logger); // || {} is temporary
|
||||
const connection = this.createConnection(options.name || "default", driver, logger);
|
||||
// (backward compatibility) if options are set in the driver section of connection options then merge them into the option
|
||||
if (options.driver)
|
||||
Object.assign(options, options.driver);
|
||||
|
||||
// import entity schemas
|
||||
if (options.entitySchemas) {
|
||||
const [directories, classes] = this.splitStringsAndClasses(options.entitySchemas);
|
||||
connection
|
||||
.importEntitySchemas(classes)
|
||||
.importEntitySchemaFromDirectories(directories);
|
||||
const existConnection = this.connections.find(connection => connection.name === (options.name || "default"));
|
||||
if (existConnection) {
|
||||
if (existConnection.isConnected)
|
||||
throw new AlreadyHasActiveConnectionError(options.name || "default");
|
||||
|
||||
this.connections.splice(this.connections.indexOf(existConnection), 1);
|
||||
}
|
||||
|
||||
// import entities
|
||||
if (options.entities) {
|
||||
const [directories, classes] = this.splitStringsAndClasses(options.entities);
|
||||
connection
|
||||
.importEntities(classes)
|
||||
.importEntitiesFromDirectories(directories);
|
||||
}
|
||||
|
||||
// import subscriber
|
||||
if (options.subscribers) {
|
||||
const [directories, classes] = this.splitStringsAndClasses(options.subscribers);
|
||||
connection
|
||||
.importSubscribers(classes)
|
||||
.importSubscribersFromDirectories(directories);
|
||||
}
|
||||
|
||||
// import migrations
|
||||
if (options.migrations) {
|
||||
const [directories, classes] = this.splitStringsAndClasses(options.migrations);
|
||||
connection
|
||||
.importMigrations(classes)
|
||||
.importMigrationsFromDirectories(directories);
|
||||
}
|
||||
|
||||
// set naming strategy to be used for this connection
|
||||
if (options.namingStrategy)
|
||||
connection.useNamingStrategy(options.namingStrategy);
|
||||
|
||||
const connection = new Connection(options);
|
||||
this.connections.push(connection);
|
||||
return connection;
|
||||
}
|
||||
|
||||
@ -380,30 +343,34 @@ export class ConnectionManager {
|
||||
throw new Error(`Connection "${connectionName}" ${PlatformTools.getEnvVariable("NODE_ENV") ? "for the environment " + PlatformTools.getEnvVariable("NODE_ENV") + " " : ""}was not found in the json configuration file.` +
|
||||
(environmentLessOptions.length ? ` However there are such configurations for other environments: ${environmentLessOptions.map(options => options.environment).join(", ")}.` : ""));
|
||||
|
||||
let connectionOptions: ConnectionOptions = Object.assign({}, options);
|
||||
// normalize directory paths
|
||||
if (options.entities) {
|
||||
options.entities = (options.entities as any[]).map(entity => {
|
||||
const entities = (options.entities as any[]).map(entity => {
|
||||
if (typeof entity === "string" || entity.substr(0, 1) !== "/")
|
||||
return PlatformTools.load("app-root-path").path + "/" + entity;
|
||||
|
||||
return entity;
|
||||
});
|
||||
Object.assign(connectionOptions, { entities: entities });
|
||||
}
|
||||
if (options.subscribers) {
|
||||
options.subscribers = (options.subscribers as any[]).map(subscriber => {
|
||||
const subscribers = (options.subscribers as any[]).map(subscriber => {
|
||||
if (typeof subscriber === "string" || subscriber.substr(0, 1) !== "/")
|
||||
return PlatformTools.load("app-root-path").path + "/" + subscriber;
|
||||
|
||||
return subscriber;
|
||||
});
|
||||
Object.assign(connectionOptions, { subscribers: subscribers });
|
||||
}
|
||||
if (options.migrations) {
|
||||
options.migrations = (options.migrations as any[]).map(migration => {
|
||||
const migrations = (options.migrations as any[]).map(migration => {
|
||||
if (typeof migration === "string" || migration.substr(0, 1) !== "/")
|
||||
return PlatformTools.load("app-root-path").path + "/" + migration;
|
||||
|
||||
return migration;
|
||||
});
|
||||
Object.assign(connectionOptions, { migrations: migrations });
|
||||
}
|
||||
|
||||
return this.createAndConnectByConnectionOptions(options);
|
||||
@ -433,57 +400,4 @@ export class ConnectionManager {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits given array of mixed strings and / or functions into two separate array of string and array of functions.
|
||||
*/
|
||||
protected splitStringsAndClasses<T>(strAndClses: string[]|T[]): [string[], T[]] {
|
||||
return [
|
||||
(strAndClses as string[]).filter(str => typeof str === "string"),
|
||||
(strAndClses as T[]).filter(cls => typeof cls !== "string"),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new driver based on the given driver type and options.
|
||||
*/
|
||||
protected createDriver(options: DriverOptions, logger: Logger): Driver {
|
||||
switch (options.type) {
|
||||
case "mysql":
|
||||
return new MysqlDriver(options, logger, undefined);
|
||||
case "postgres":
|
||||
return new PostgresDriver(options, logger);
|
||||
case "mariadb":
|
||||
return new MysqlDriver(options, logger);
|
||||
case "sqlite":
|
||||
return new SqliteDriver(options, logger);
|
||||
case "oracle":
|
||||
return new OracleDriver(options, logger);
|
||||
case "mssql":
|
||||
return new SqlServerDriver(options, logger);
|
||||
case "websql":
|
||||
return new WebsqlDriver(options, logger);
|
||||
case "mongodb":
|
||||
return new MongoDriver(options, logger);
|
||||
default:
|
||||
throw new MissingDriverError(options.type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new connection and registers it in the connection manager.
|
||||
*/
|
||||
protected createConnection(name: string, driver: Driver, logger: Logger) {
|
||||
const existConnection = this.connections.find(connection => connection.name === name);
|
||||
if (existConnection) {
|
||||
if (existConnection.isConnected)
|
||||
throw new AlreadyHasActiveConnectionError(name);
|
||||
|
||||
this.connections.splice(this.connections.indexOf(existConnection), 1);
|
||||
}
|
||||
|
||||
const connection = new Connection(name, driver, logger);
|
||||
this.connections.push(connection);
|
||||
return connection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -15,64 +15,64 @@ export interface ConnectionOptions {
|
||||
* Connection name. If connection name is not given then it will be called "default".
|
||||
* Different connections must have different names.
|
||||
*/
|
||||
name?: string;
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* Database options of this connection.
|
||||
*
|
||||
* @deprecated Define options right in the connection options section.
|
||||
*/
|
||||
driver?: DriverOptions;
|
||||
readonly driver?: DriverOptions;
|
||||
|
||||
/**
|
||||
* Database type. This value is required.
|
||||
*/
|
||||
type?: DriverType;
|
||||
readonly type?: DriverType;
|
||||
|
||||
/**
|
||||
* Connection url to where perform connection to.
|
||||
*/
|
||||
url?: string;
|
||||
readonly url?: string;
|
||||
|
||||
/**
|
||||
* Database host.
|
||||
*/
|
||||
host?: string;
|
||||
readonly host?: string;
|
||||
|
||||
/**
|
||||
* Database host port.
|
||||
*/
|
||||
port?: number;
|
||||
readonly port?: number;
|
||||
|
||||
/**
|
||||
* Database username.
|
||||
*/
|
||||
username?: string;
|
||||
readonly username?: string;
|
||||
|
||||
/**
|
||||
* Database password.
|
||||
*/
|
||||
password?: string;
|
||||
readonly password?: string;
|
||||
|
||||
/**
|
||||
* Database name to connect to.
|
||||
*/
|
||||
database?: string;
|
||||
readonly database?: string;
|
||||
|
||||
/**
|
||||
* Schema name. By default is "public" (used only in Postgres databases).
|
||||
*/
|
||||
schemaName?: string;
|
||||
readonly schemaName?: string;
|
||||
|
||||
/**
|
||||
* Connection SID (used for Oracle databases).
|
||||
*/
|
||||
sid?: string;
|
||||
readonly sid?: string;
|
||||
|
||||
/**
|
||||
* Storage type or path to the storage (used for SQLite databases).
|
||||
*/
|
||||
storage?: string;
|
||||
readonly storage?: string;
|
||||
|
||||
/**
|
||||
* Indicates if connection pooling should be used or not.
|
||||
@ -81,64 +81,64 @@ export interface ConnectionOptions {
|
||||
*
|
||||
* @todo: rename to disablePool? What about mongodb pool?
|
||||
*/
|
||||
usePool?: boolean;
|
||||
readonly usePool?: boolean;
|
||||
|
||||
/**
|
||||
* Extra connection options to be passed to the underlying driver.
|
||||
*/
|
||||
extra?: any;
|
||||
readonly extra?: any;
|
||||
|
||||
/**
|
||||
* Prefix to use on all tables (collections) of this connection in the database.
|
||||
*
|
||||
* @todo: rename to entityPrefix
|
||||
*/
|
||||
tablesPrefix?: string;
|
||||
readonly tablesPrefix?: string;
|
||||
|
||||
/**
|
||||
* Naming strategy to be used to name tables and columns in the database.
|
||||
*/
|
||||
namingStrategy?: NamingStrategyInterface;
|
||||
readonly namingStrategy?: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Entities to be loaded for this connection.
|
||||
* Accepts both entity classes and directories where from entities need to be loaded.
|
||||
* Directories support glob patterns.
|
||||
*/
|
||||
entities?: Function[]|string[];
|
||||
readonly entities?: Function[]|string[];
|
||||
|
||||
/**
|
||||
* Subscribers to be loaded for this connection.
|
||||
* Accepts both subscriber classes and directories where from subscribers need to be loaded.
|
||||
* Directories support glob patterns.
|
||||
*/
|
||||
subscribers?: Function[]|string[];
|
||||
readonly subscribers?: Function[]|string[];
|
||||
|
||||
/**
|
||||
* Entity schemas to be loaded for this connection.
|
||||
* Accepts both entity schema classes and directories where from entity schemas need to be loaded.
|
||||
* Directories support glob patterns.
|
||||
*/
|
||||
entitySchemas?: EntitySchema[]|string[];
|
||||
readonly entitySchemas?: EntitySchema[]|string[];
|
||||
|
||||
/**
|
||||
* Migrations to be loaded for this connection.
|
||||
* Accepts both migration classes and directories where from migrations need to be loaded.
|
||||
* Directories support glob patterns.
|
||||
*/
|
||||
migrations?: Function[]|string[];
|
||||
readonly migrations?: Function[]|string[];
|
||||
|
||||
/**
|
||||
* Logging options.
|
||||
*/
|
||||
logging?: LoggerOptions;
|
||||
readonly logging?: LoggerOptions;
|
||||
|
||||
/**
|
||||
* Drops the schema each time connection is being established.
|
||||
* Be careful with this option and don't use this in production - otherwise you'll loose all production data.
|
||||
* This option is useful during debug and development.
|
||||
*/
|
||||
dropSchemaOnConnection?: boolean;
|
||||
readonly dropSchemaOnConnection?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if database schema should be auto created on every application launch.
|
||||
@ -151,7 +151,7 @@ export interface ConnectionOptions {
|
||||
*
|
||||
* todo: rename it simply to synchronize: boolean ?
|
||||
*/
|
||||
autoSchemaSync?: boolean;
|
||||
readonly autoSchemaSync?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if migrations should be auto run on every application launch.
|
||||
@ -159,7 +159,7 @@ export interface ConnectionOptions {
|
||||
*
|
||||
* todo: rename it simply to runMigrations: boolean ?
|
||||
*/
|
||||
autoMigrationsRun?: boolean;
|
||||
readonly autoMigrationsRun?: boolean;
|
||||
|
||||
/**
|
||||
* Environment in which connection will run.
|
||||
@ -168,27 +168,27 @@ export interface ConnectionOptions {
|
||||
* then this connection will be created. On any other NODE_ENV value it will be skipped.
|
||||
* This option is specific to the configuration in the ormconfig.json file.
|
||||
*/
|
||||
environment?: string;
|
||||
readonly environment?: string;
|
||||
|
||||
/**
|
||||
* CLI settings.
|
||||
*/
|
||||
cli?: {
|
||||
readonly cli?: {
|
||||
|
||||
/**
|
||||
* Directory where entities should be created by default.
|
||||
*/
|
||||
entitiesDir?: string;
|
||||
readonly entitiesDir?: string;
|
||||
|
||||
/**
|
||||
* Directory where migrations should be created by default.
|
||||
*/
|
||||
migrationsDir?: string;
|
||||
readonly migrationsDir?: string;
|
||||
|
||||
/**
|
||||
* Directory where subscribers should be created by default.
|
||||
*/
|
||||
subscribersDir?: string;
|
||||
readonly subscribersDir?: string;
|
||||
|
||||
};
|
||||
|
||||
|
||||
43
src/driver/DriverFactory.ts
Normal file
43
src/driver/DriverFactory.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import {MissingDriverError} from "./error/MissingDriverError";
|
||||
import {MongoDriver} from "./mongodb/MongoDriver";
|
||||
import {WebsqlDriver} from "./websql/WebsqlDriver";
|
||||
import {SqlServerDriver} from "./sqlserver/SqlServerDriver";
|
||||
import {OracleDriver} from "./oracle/OracleDriver";
|
||||
import {SqliteDriver} from "./sqlite/SqliteDriver";
|
||||
import {MysqlDriver} from "./mysql/MysqlDriver";
|
||||
import {PostgresDriver} from "./postgres/PostgresDriver";
|
||||
import {Driver} from "./Driver";
|
||||
import {Connection} from "../connection/Connection";
|
||||
|
||||
/**
|
||||
* Helps to create drivers.
|
||||
*/
|
||||
export class DriverFactory {
|
||||
|
||||
/**
|
||||
* Creates a new driver depend on a given connection's driver type.
|
||||
*/
|
||||
create(connection: Connection): Driver {
|
||||
switch (connection.options.type) {
|
||||
case "mysql":
|
||||
return new MysqlDriver(connection);
|
||||
case "postgres":
|
||||
return new PostgresDriver(connection);
|
||||
case "mariadb":
|
||||
return new MysqlDriver(connection);
|
||||
case "sqlite":
|
||||
return new SqliteDriver(connection);
|
||||
case "oracle":
|
||||
return new OracleDriver(connection);
|
||||
case "mssql":
|
||||
return new SqlServerDriver(connection);
|
||||
case "websql":
|
||||
return new WebsqlDriver(connection);
|
||||
case "mongodb":
|
||||
return new MongoDriver(connection);
|
||||
default:
|
||||
throw new MissingDriverError(connection.options.type!);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,13 +2,15 @@ import {DriverType} from "./DriverType";
|
||||
|
||||
/**
|
||||
* Connectivity options used to connect to the database, and other database-driver-specific options.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
export interface DriverOptions {
|
||||
|
||||
/**
|
||||
* Database type. This value is required.
|
||||
*/
|
||||
type: DriverType;
|
||||
type?: DriverType;
|
||||
|
||||
/**
|
||||
* Connection url to where perform connection to.
|
||||
|
||||
@ -13,6 +13,7 @@ import {PlatformTools} from "../../platform/PlatformTools";
|
||||
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
|
||||
import {EntityMetadata} from "../../metadata/EntityMetadata";
|
||||
import {LazyRelationsWrapper} from "../../lazy-loading/LazyRelationsWrapper";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
/**
|
||||
* Organizes communication with MongoDB.
|
||||
@ -57,18 +58,15 @@ export class MongoDriver implements Driver {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DriverOptions, logger: Logger, mongodb?: any) {
|
||||
constructor(connection: Connection) {
|
||||
|
||||
// validate options to make sure everything is correct and driver will be able to establish connection
|
||||
this.validateOptions(options);
|
||||
this.validateOptions(connection.options);
|
||||
|
||||
// if mongodb package instance was not set explicitly then try to load it
|
||||
if (!mongodb)
|
||||
mongodb = this.loadDependencies();
|
||||
|
||||
this.options = options;
|
||||
this.logger = logger;
|
||||
this.mongodb = mongodb;
|
||||
// load mongodb package
|
||||
this.options = connection.options;
|
||||
this.logger = connection.logger;
|
||||
this.mongodb = this.loadDependencies();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -15,6 +15,7 @@ import {DataTransformationUtils} from "../../util/DataTransformationUtils";
|
||||
import {PlatformTools} from "../../platform/PlatformTools";
|
||||
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
|
||||
import {LazyRelationsWrapper} from "../../lazy-loading/LazyRelationsWrapper";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
/**
|
||||
* Organizes communication with MySQL DBMS.
|
||||
@ -63,11 +64,10 @@ export class MysqlDriver implements Driver {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DriverOptions, logger: Logger, mysql?: any) {
|
||||
constructor(connection: Connection) {
|
||||
|
||||
this.options = DriverUtils.buildDriverOptions(options);
|
||||
this.logger = logger;
|
||||
this.mysql = mysql;
|
||||
this.options = DriverUtils.buildDriverOptions(connection.options);
|
||||
this.logger = connection.logger;
|
||||
|
||||
// validate options to make sure everything is set
|
||||
if (!(this.options.host || (this.options.extra && this.options.extra.socketPath)))
|
||||
@ -77,9 +77,8 @@ export class MysqlDriver implements Driver {
|
||||
if (!this.options.database)
|
||||
throw new DriverOptionNotSetError("database");
|
||||
|
||||
// if mysql package instance was not set explicitly then try to load it
|
||||
if (!mysql)
|
||||
this.loadDependencies();
|
||||
// load mysql package
|
||||
this.loadDependencies();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -15,6 +15,7 @@ import {DataTransformationUtils} from "../../util/DataTransformationUtils";
|
||||
import {PlatformTools} from "../../platform/PlatformTools";
|
||||
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
|
||||
import {LazyRelationsWrapper} from "../../lazy-loading/LazyRelationsWrapper";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
/**
|
||||
* Organizes communication with Oracle DBMS.
|
||||
@ -65,11 +66,10 @@ export class OracleDriver implements Driver {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DriverOptions, logger: Logger, oracle?: any) {
|
||||
constructor(connection: Connection) {
|
||||
|
||||
this.options = DriverUtils.buildDriverOptions(options, { useSid: true });
|
||||
this.logger = logger;
|
||||
this.oracle = oracle;
|
||||
this.options = connection.options;
|
||||
this.logger = connection.logger;
|
||||
|
||||
// validate options to make sure everything is set
|
||||
if (!this.options.host)
|
||||
@ -79,10 +79,8 @@ export class OracleDriver implements Driver {
|
||||
if (!this.options.sid)
|
||||
throw new DriverOptionNotSetError("sid");
|
||||
|
||||
// if oracle package instance was not set explicitly then try to load it
|
||||
if (!oracle)
|
||||
this.loadDependencies();
|
||||
|
||||
// load oracle package
|
||||
this.loadDependencies();
|
||||
this.oracle.outFormat = this.oracle.OBJECT;
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ import {DataTransformationUtils} from "../../util/DataTransformationUtils";
|
||||
import {PlatformTools} from "../../platform/PlatformTools";
|
||||
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
|
||||
import {LazyRelationsWrapper} from "../../lazy-loading/LazyRelationsWrapper";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
// todo(tests):
|
||||
// check connection with url
|
||||
@ -75,12 +76,11 @@ export class PostgresDriver implements Driver {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(connectionOptions: DriverOptions, logger: Logger, postgres?: any) {
|
||||
constructor(connection: Connection) {
|
||||
|
||||
this.options = DriverUtils.buildDriverOptions(connectionOptions);
|
||||
this.logger = logger;
|
||||
this.postgres = postgres;
|
||||
this.schemaName = connectionOptions.schemaName || "public";
|
||||
this.options = DriverUtils.buildDriverOptions(connection.options);
|
||||
this.logger = connection.logger;
|
||||
this.schemaName = connection.options.schemaName || "public";
|
||||
|
||||
// validate options to make sure everything is set
|
||||
if (!this.options.host)
|
||||
@ -90,9 +90,8 @@ export class PostgresDriver implements Driver {
|
||||
if (!this.options.database)
|
||||
throw new DriverOptionNotSetError("database");
|
||||
|
||||
// if postgres package instance was not set explicitly then try to load it
|
||||
if (!postgres)
|
||||
this.loadDependencies();
|
||||
// load postgres package
|
||||
this.loadDependencies();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -14,6 +14,7 @@ import {DataTransformationUtils} from "../../util/DataTransformationUtils";
|
||||
import {PlatformTools} from "../../platform/PlatformTools";
|
||||
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
|
||||
import {LazyRelationsWrapper} from "../../lazy-loading/LazyRelationsWrapper";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
/**
|
||||
* Organizes communication with sqlite DBMS.
|
||||
@ -52,19 +53,17 @@ export class SqliteDriver implements Driver {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(connectionOptions: DriverOptions, logger: Logger, sqlite?: any) {
|
||||
constructor(connection: Connection) {
|
||||
|
||||
this.options = connectionOptions;
|
||||
this.logger = logger;
|
||||
this.sqlite = sqlite;
|
||||
this.options = connection.options;
|
||||
this.logger = connection.logger;
|
||||
|
||||
// validate options to make sure everything is set
|
||||
if (!this.options.storage)
|
||||
throw new DriverOptionNotSetError("storage");
|
||||
|
||||
// if sqlite package instance was not set explicitly then try to load it
|
||||
if (!sqlite)
|
||||
this.loadDependencies();
|
||||
// load sqlite package
|
||||
this.loadDependencies();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -15,6 +15,7 @@ import {DataTransformationUtils} from "../../util/DataTransformationUtils";
|
||||
import {PlatformTools} from "../../platform/PlatformTools";
|
||||
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
|
||||
import {LazyRelationsWrapper} from "../../lazy-loading/LazyRelationsWrapper";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
/**
|
||||
* Organizes communication with SQL Server DBMS.
|
||||
@ -63,11 +64,10 @@ export class SqlServerDriver implements Driver {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DriverOptions, logger: Logger, mssql?: any) {
|
||||
constructor(connection: Connection) {
|
||||
|
||||
this.options = DriverUtils.buildDriverOptions(options);
|
||||
this.logger = logger;
|
||||
this.mssql = mssql;
|
||||
this.options = DriverUtils.buildDriverOptions(connection.options);
|
||||
this.logger = connection.logger;
|
||||
|
||||
// validate options to make sure everything is set
|
||||
if (!this.options.host)
|
||||
@ -77,9 +77,8 @@ export class SqlServerDriver implements Driver {
|
||||
if (!this.options.database)
|
||||
throw new DriverOptionNotSetError("database");
|
||||
|
||||
// if mssql package instance was not set explicitly then try to load it
|
||||
if (!mssql)
|
||||
this.loadDependencies();
|
||||
// load mssql package
|
||||
this.loadDependencies();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -13,6 +13,7 @@ import {DataTransformationUtils} from "../../util/DataTransformationUtils";
|
||||
import {WebsqlQueryRunner} from "./WebsqlQueryRunner";
|
||||
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
|
||||
import {LazyRelationsWrapper} from "../../lazy-loading/LazyRelationsWrapper";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
/**
|
||||
* Declare a global function that is only available in browsers that support WebSQL.
|
||||
@ -51,10 +52,10 @@ export class WebsqlDriver implements Driver {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DriverOptions, logger: Logger) {
|
||||
constructor(connection: Connection) {
|
||||
|
||||
this.options = DriverUtils.buildDriverOptions(options);
|
||||
this.logger = logger;
|
||||
this.options = DriverUtils.buildDriverOptions(connection.options);
|
||||
this.logger = connection.logger;
|
||||
|
||||
// validate options to make sure everything is set
|
||||
// if (!this.options.host)
|
||||
|
||||
@ -63,6 +63,43 @@ export class EntityManager {
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Wraps given function execution (and all operations made there) in a transaction.
|
||||
* All database operations must be executed using provided entity manager.
|
||||
*/
|
||||
async transaction(runInTransaction: (entityManger: EntityManager) => Promise<any>): Promise<any> {
|
||||
return this.connection.transaction(runInTransaction, this.queryRunnerProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes raw SQL query and returns raw database results.
|
||||
*/
|
||||
async query(query: string, parameters?: any[]): Promise<any> {
|
||||
return this.connection.query(query, parameters, this.queryRunnerProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new query builder that can be used to build a sql query.
|
||||
*/
|
||||
createQueryBuilder<Entity>(entityClass: ObjectType<Entity>|Function|string, alias: string, queryRunnerProvider?: QueryRunnerProvider): QueryBuilder<Entity>;
|
||||
|
||||
/**
|
||||
* Creates a new query builder that can be used to build a sql query.
|
||||
*/
|
||||
createQueryBuilder(queryRunnerProvider?: QueryRunnerProvider): QueryBuilder<any>;
|
||||
|
||||
/**
|
||||
* Creates a new query builder that can be used to build a sql query.
|
||||
*/
|
||||
createQueryBuilder<Entity>(entityClass?: ObjectType<Entity>|Function|string|QueryRunnerProvider, alias?: string, queryRunnerProvider?: QueryRunnerProvider): QueryBuilder<Entity> {
|
||||
if (alias) {
|
||||
return this.connection.createQueryBuilder(entityClass as Function|string, alias, queryRunnerProvider || this.queryRunnerProvider);
|
||||
|
||||
} else {
|
||||
return this.connection.createQueryBuilder(entityClass as QueryRunnerProvider|undefined || this.queryRunnerProvider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets user data by a given key.
|
||||
* Used get stored data stored in a transactional entity manager.
|
||||
@ -120,16 +157,6 @@ export class EntityManager {
|
||||
return metadata.getEntityIdMixedMap(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new query builder that can be used to build a sql query.
|
||||
*/
|
||||
createQueryBuilder<Entity>(entityClass: ObjectType<Entity>|Function|string, alias: string, queryRunnerProvider?: QueryRunnerProvider): QueryBuilder<Entity> {
|
||||
const metadata = this.connection.getMetadata(entityClass);
|
||||
return new QueryBuilder(this.connection, queryRunnerProvider || this.queryRunnerProvider)
|
||||
.select(alias)
|
||||
.from(metadata.target, alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entity instance.
|
||||
*/
|
||||
@ -531,53 +558,6 @@ export class EntityManager {
|
||||
return qb.getOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes raw SQL query and returns raw database results.
|
||||
*/
|
||||
async query(query: string, parameters?: any[]): Promise<any> {
|
||||
if (this.queryRunnerProvider && this.queryRunnerProvider.isReleased)
|
||||
throw new QueryRunnerProviderAlreadyReleasedError();
|
||||
|
||||
const queryRunnerProvider = this.queryRunnerProvider || new QueryRunnerProvider(this.connection.driver);
|
||||
const queryRunner = await queryRunnerProvider.provide();
|
||||
|
||||
try {
|
||||
return await queryRunner.query(query, parameters); // await is needed here because we are using finally
|
||||
|
||||
} finally {
|
||||
await queryRunnerProvider.release(queryRunner);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps given function execution (and all operations made there) in a transaction.
|
||||
* All database operations must be executed using provided entity manager.
|
||||
*/
|
||||
async transaction(runInTransaction: (entityManger: EntityManager) => Promise<any>): Promise<any> {
|
||||
if (this.queryRunnerProvider && this.queryRunnerProvider.isReleased)
|
||||
throw new QueryRunnerProviderAlreadyReleasedError();
|
||||
|
||||
const queryRunnerProvider = this.queryRunnerProvider || new QueryRunnerProvider(this.connection.driver, true);
|
||||
const queryRunner = await queryRunnerProvider.provide();
|
||||
const transactionEntityManager = new EntityManager(this.connection, queryRunnerProvider);
|
||||
|
||||
try {
|
||||
await queryRunner.beginTransaction();
|
||||
const result = await runInTransaction(transactionEntityManager);
|
||||
await queryRunner.commitTransaction();
|
||||
return result;
|
||||
|
||||
} catch (err) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw err;
|
||||
|
||||
} finally {
|
||||
await queryRunnerProvider.release(queryRunner);
|
||||
if (!this.queryRunnerProvider) // if we used a new query runner provider then release it
|
||||
await queryRunnerProvider.releaseReused();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all the data from the given table (truncates/drops it).
|
||||
*/
|
||||
@ -741,7 +721,7 @@ export class EntityManager {
|
||||
const metadata = this.connection.getMetadata(target);
|
||||
const queryRunnerProvider = this.queryRunnerProvider || new QueryRunnerProvider(this.connection.driver, true);
|
||||
try {
|
||||
const transactionEntityManager = this.connection.createEntityManagerWithSingleDatabaseConnection(queryRunnerProvider);
|
||||
const transactionEntityManager = this.connection.createIsolatedManager(queryRunnerProvider);
|
||||
// transactionEntityManager.data =
|
||||
|
||||
const databaseEntityLoader = new SubjectBuilder(this.connection, queryRunnerProvider);
|
||||
@ -763,7 +743,7 @@ export class EntityManager {
|
||||
const metadata = this.connection.getMetadata(target);
|
||||
const queryRunnerProvider = this.queryRunnerProvider || new QueryRunnerProvider(this.connection.driver, true);
|
||||
try {
|
||||
const transactionEntityManager = this.connection.createEntityManagerWithSingleDatabaseConnection(queryRunnerProvider);
|
||||
const transactionEntityManager = this.connection.createIsolatedManager(queryRunnerProvider);
|
||||
|
||||
const databaseEntityLoader = new SubjectBuilder(this.connection, queryRunnerProvider);
|
||||
await databaseEntityLoader.remove(entity, metadata);
|
||||
|
||||
21
src/entity-manager/EntityManagerFactory.ts
Normal file
21
src/entity-manager/EntityManagerFactory.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {EntityManager} from "./EntityManager";
|
||||
import {MongoEntityManager} from "./MongoEntityManager";
|
||||
import {MongoDriver} from "../driver/mongodb/MongoDriver";
|
||||
|
||||
/**
|
||||
* Helps to create entity managers.
|
||||
*/
|
||||
export class EntityManagerFactory {
|
||||
|
||||
/**
|
||||
* Creates a new entity manager depend on a given connection's driver.
|
||||
*/
|
||||
create(connection: Connection): EntityManager {
|
||||
if (connection.driver instanceof MongoDriver)
|
||||
return new MongoEntityManager(connection);
|
||||
|
||||
return new EntityManager(connection);
|
||||
}
|
||||
|
||||
}
|
||||
@ -64,29 +64,6 @@ export class MongoEntityManager extends EntityManager {
|
||||
// Overridden Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Executes raw SQL query and returns raw database results.
|
||||
*/
|
||||
query(query: string, parameters?: any[]): Promise<any> {
|
||||
throw new Error(`Queries aren't supported by MongoDB.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps given function execution (and all operations made there) in a transaction.
|
||||
* All database operations must be executed using provided entity manager.
|
||||
*/
|
||||
transaction(runInTransaction: (entityManger: EntityManager) => Promise<any>): Promise<any> {
|
||||
throw new Error(`Transactions aren't supported by MongoDB.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using Query Builder with MongoDB is not supported yet.
|
||||
* Calling this method will return an error.
|
||||
*/
|
||||
createQueryBuilder<Entity>(entityClassOrName: ObjectType<Entity>|string, alias: string, queryRunnerProvider?: QueryRunnerProvider): QueryBuilder<Entity> {
|
||||
throw new Error(`Query Builder is not supported by MongoDB.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities that match given find options or conditions.
|
||||
*/
|
||||
|
||||
@ -100,7 +100,8 @@ export class LazyRelationsWrapper {
|
||||
return `${relation.entityMetadata.name}.${relation.propertyName} = ${relation.propertyName}.${joinColumn.referencedColumn!.propertyName}`;
|
||||
}).join(" AND ");
|
||||
|
||||
const qb = new QueryBuilder(this.connection)
|
||||
const qb = this.connection
|
||||
.createQueryBuilder()
|
||||
.select(relation.propertyName) // category
|
||||
.from(relation.type, relation.propertyName) // Category, category
|
||||
.innerJoin(relation.entityMetadata.target as Function, relation.entityMetadata.name, conditions);
|
||||
@ -120,7 +121,8 @@ export class LazyRelationsWrapper {
|
||||
* WHERE post.[joinColumn.name] = entity[joinColumn.referencedColumn]
|
||||
*/
|
||||
protected loadOneToManyOrOneToOneNotOwner(relation: RelationMetadata, entity: ObjectLiteral): Promise<any> {
|
||||
const qb = new QueryBuilder(this.connection)
|
||||
const qb = this.connection
|
||||
.createQueryBuilder()
|
||||
.select(relation.propertyName)
|
||||
.from(relation.inverseRelation!.entityMetadata.target, relation.propertyName);
|
||||
|
||||
@ -154,7 +156,8 @@ export class LazyRelationsWrapper {
|
||||
return parameters;
|
||||
}, {} as ObjectLiteral);
|
||||
|
||||
return new QueryBuilder(this.connection)
|
||||
return this.connection
|
||||
.createQueryBuilder()
|
||||
.select(mainAlias)
|
||||
.from(relation.type, mainAlias)
|
||||
.innerJoin(joinAlias, joinAlias, [...joinColumnConditions, ...inverseJoinColumnConditions].join(" AND "))
|
||||
@ -185,7 +188,8 @@ export class LazyRelationsWrapper {
|
||||
return parameters;
|
||||
}, {} as ObjectLiteral);
|
||||
|
||||
return new QueryBuilder(this.connection)
|
||||
return this.connection
|
||||
.createQueryBuilder()
|
||||
.select(mainAlias)
|
||||
.from(relation.type, mainAlias)
|
||||
.innerJoin(joinAlias, joinAlias, [...joinColumnConditions, ...inverseJoinColumnConditions].join(" AND "))
|
||||
|
||||
@ -3,6 +3,7 @@ import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
|
||||
import {ColumnTypes} from "../metadata/types/ColumnTypes";
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
|
||||
|
||||
/**
|
||||
* Creates EntityMetadata for junction tables of the closure entities.
|
||||
|
||||
@ -16,6 +16,7 @@ import {Connection} from "../connection/Connection";
|
||||
import {EntityListenerMetadata} from "../metadata/EntityListenerMetadata";
|
||||
import {ColumnOptions} from "../decorator/options/ColumnOptions";
|
||||
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
|
||||
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
|
||||
|
||||
/**
|
||||
* Builds EntityMetadata objects and all its sub-metadatas.
|
||||
@ -45,7 +46,9 @@ export class EntityMetadataBuilder {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(private connection: Connection, private metadataArgsStorage: MetadataArgsStorage) {
|
||||
constructor(private connection: Connection,
|
||||
private metadataArgsStorage: MetadataArgsStorage) {
|
||||
|
||||
this.junctionEntityMetadataBuilder = new JunctionEntityMetadataBuilder(connection);
|
||||
this.closureJunctionEntityMetadataBuilder = new ClosureJunctionEntityMetadataBuilder(connection);
|
||||
this.relationJoinColumnBuilder = new RelationJoinColumnBuilder(connection);
|
||||
@ -206,7 +209,8 @@ export class EntityMetadataBuilder {
|
||||
entityMetadata.relations
|
||||
.filter(relation => relation.isLazy)
|
||||
.forEach(relation => {
|
||||
this.connection.lazyRelationsWrapper.wrap((entityMetadata.target as Function).prototype, relation);
|
||||
const lazyRelationsWrapper = new LazyRelationsWrapper(this.connection);
|
||||
lazyRelationsWrapper.wrap((entityMetadata.target as Function).prototype, relation);
|
||||
});
|
||||
});
|
||||
|
||||
@ -248,7 +252,10 @@ export class EntityMetadataBuilder {
|
||||
});
|
||||
}
|
||||
|
||||
const entityMetadata = new EntityMetadata({ connection: this.connection, args: tableArgs });
|
||||
const entityMetadata = new EntityMetadata({
|
||||
connection: this.connection,
|
||||
args: tableArgs
|
||||
});
|
||||
|
||||
const inheritanceType = this.metadataArgsStorage.findInheritanceType(tableArgs.target);
|
||||
entityMetadata.inheritanceType = inheritanceType ? inheritanceType.type : undefined;
|
||||
|
||||
@ -5,6 +5,7 @@ import {IndexMetadata} from "../metadata/IndexMetadata";
|
||||
import {RelationMetadata} from "../metadata/RelationMetadata";
|
||||
import {JoinTableMetadataArgs} from "../metadata-args/JoinTableMetadataArgs";
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
|
||||
|
||||
/**
|
||||
* Creates EntityMetadata for junction tables.
|
||||
|
||||
@ -343,8 +343,8 @@ export class EntityMetadata {
|
||||
args: TableMetadataArgs
|
||||
}) {
|
||||
const namingStrategy = options.connection.namingStrategy;
|
||||
const tablesPrefix = options.connection.driver.options.tablesPrefix;
|
||||
this.lazyRelationsWrapper = options.connection.lazyRelationsWrapper;
|
||||
const tablesPrefix = options.connection.options.tablesPrefix;
|
||||
this.lazyRelationsWrapper = new LazyRelationsWrapper(options.connection);
|
||||
this.parentClosureEntityMetadata = options.parentClosureEntityMetadata!;
|
||||
this.target = options.args.target;
|
||||
this.tableType = options.args.type;
|
||||
|
||||
@ -37,7 +37,7 @@ export class MigrationExecutor {
|
||||
*/
|
||||
async executePendingMigrations(): Promise<void> {
|
||||
const queryRunner = await this.queryRunnerProvider.provide();
|
||||
const entityManager = this.connection.createEntityManagerWithSingleDatabaseConnection(this.queryRunnerProvider);
|
||||
const entityManager = this.connection.createIsolatedManager(this.queryRunnerProvider);
|
||||
|
||||
// create migrations table if its not created yet
|
||||
await this.createMigrationsTableIfNotExist();
|
||||
@ -116,7 +116,7 @@ export class MigrationExecutor {
|
||||
*/
|
||||
async undoLastMigration(): Promise<void> {
|
||||
const queryRunner = await this.queryRunnerProvider.provide();
|
||||
const entityManager = this.connection.createEntityManagerWithSingleDatabaseConnection(this.queryRunnerProvider);
|
||||
const entityManager = this.connection.createIsolatedManager(this.queryRunnerProvider);
|
||||
|
||||
// create migrations table if its not created yet
|
||||
await this.createMigrationsTableIfNotExist();
|
||||
@ -207,9 +207,10 @@ export class MigrationExecutor {
|
||||
* Loads all migrations that were executed and saved into the database.
|
||||
*/
|
||||
protected async loadExecutedMigrations(): Promise<Migration[]> {
|
||||
const migrationsRaw: ObjectLiteral[] = await new QueryBuilder(this.connection, this.queryRunnerProvider)
|
||||
const migrationsRaw: ObjectLiteral[] = await this.connection.manager
|
||||
.createQueryBuilder(this.queryRunnerProvider)
|
||||
.select()
|
||||
.fromTable("migrations", "migrations")
|
||||
.from("migrations", "migrations")
|
||||
.getRawMany();
|
||||
|
||||
return migrationsRaw.map(migrationRaw => {
|
||||
@ -221,7 +222,7 @@ export class MigrationExecutor {
|
||||
* Gets all migrations that setup for this connection.
|
||||
*/
|
||||
protected getMigrations(): Migration[] {
|
||||
const migrations = this.connection.getMigrations().map(migration => {
|
||||
const migrations = this.connection.migrations.map(migration => {
|
||||
const migrationClassName = (migration.constructor as any).name;
|
||||
const migrationTimestamp = parseInt(migrationClassName.substr(-13));
|
||||
if (!migrationTimestamp)
|
||||
|
||||
@ -10,6 +10,7 @@ import {PromiseUtils} from "../util/PromiseUtils";
|
||||
import {MongoDriver} from "../driver/mongodb/MongoDriver";
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
|
||||
import {Broadcaster} from "../subscriber/Broadcaster";
|
||||
|
||||
/**
|
||||
* Executes all database operations (inserts, updated, deletes) that must be executed
|
||||
@ -111,7 +112,8 @@ export class SubjectOperationExecutor {
|
||||
}
|
||||
|
||||
// broadcast "before" events before we start updating
|
||||
await this.connection.broadcaster.broadcastBeforeEventsForAll(this.transactionEntityManager, this.insertSubjects, this.updateSubjects, this.removeSubjects);
|
||||
const broadcaster = new Broadcaster(this.connection);
|
||||
await broadcaster.broadcastBeforeEventsForAll(this.transactionEntityManager, this.insertSubjects, this.updateSubjects, this.removeSubjects);
|
||||
|
||||
// since events can trigger some internal changes (for example update depend property) we need to perform some re-computations here
|
||||
this.updateSubjects.forEach(subject => subject.recompute());
|
||||
@ -133,7 +135,7 @@ export class SubjectOperationExecutor {
|
||||
|
||||
// finally broadcast "after" events
|
||||
// note that we are broadcasting events after commit because we want to have ids of the entities inside them to be available in subscribers
|
||||
await this.connection.broadcaster.broadcastAfterEventsForAll(this.transactionEntityManager, this.insertSubjects, this.updateSubjects, this.removeSubjects);
|
||||
await broadcaster.broadcastAfterEventsForAll(this.transactionEntityManager, this.insertSubjects, this.updateSubjects, this.removeSubjects);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
|
||||
@ -134,15 +134,17 @@ export class JoinAttribute {
|
||||
*/
|
||||
get metadata(): EntityMetadata|undefined {
|
||||
|
||||
// entityOrProperty is Entity class
|
||||
if (this.entityOrProperty instanceof Function)
|
||||
return this.connection.getMetadata(this.entityOrProperty);
|
||||
|
||||
// entityOrProperty is relation, e.g. "post.category"
|
||||
if (this.relation)
|
||||
return this.relation.inverseEntityMetadata;
|
||||
|
||||
if (typeof this.entityOrProperty === "string") { // entityOrProperty is a custom table
|
||||
// entityOrProperty is Entity class
|
||||
if (this.connection.hasMetadata(this.entityOrProperty))
|
||||
return this.connection.getMetadata(this.entityOrProperty);
|
||||
|
||||
return undefined;
|
||||
|
||||
/*if (typeof this.entityOrProperty === "string") { // entityOrProperty is a custom table
|
||||
|
||||
// first try to find entity with such name, this is needed when entity does not have a target class,
|
||||
// and its target is a string name (scenario when plain old javascript is used or entity schema is loaded from files)
|
||||
@ -152,9 +154,7 @@ export class JoinAttribute {
|
||||
|
||||
// check if we have entity with such table name, and use its metadata if found
|
||||
return this.connection.entityMetadatas.find(metadata => metadata.tableName === this.entityOrProperty);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -25,6 +25,7 @@ import {RelationIdMetadataToAttributeTransformer} from "./relation-id/RelationId
|
||||
import {RelationCountLoadResult} from "./relation-count/RelationCountLoadResult";
|
||||
import {RelationCountLoader} from "./relation-count/RelationCountLoader";
|
||||
import {RelationCountMetadataToAttributeTransformer} from "./relation-count/RelationCountMetadataToAttributeTransformer";
|
||||
import {Broadcaster} from "../subscriber/Broadcaster";
|
||||
|
||||
|
||||
// todo: fix problem with long aliases eg getMaxIdentifierLength
|
||||
@ -196,31 +197,19 @@ export class QueryBuilder<Entity> {
|
||||
* Also sets a main string alias of the selection data.
|
||||
*/
|
||||
from(entityTarget: Function|string, aliasName: string): this {
|
||||
this.expressionMap.createMainAlias({
|
||||
name: aliasName,
|
||||
target: entityTarget
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies FROM which table select/update/delete will be executed.
|
||||
* Also sets a main string alias of the selection data.
|
||||
*/
|
||||
fromTable(tableName: string, aliasName: string) {
|
||||
|
||||
// if table has a metadata then find it to properly escape its properties
|
||||
const metadata = this.connection.entityMetadatas.find(metadata => metadata.tableName === tableName);
|
||||
if (metadata) {
|
||||
// const metadata = this.connection.entityMetadatas.find(metadata => metadata.tableName === tableName);
|
||||
if (entityTarget instanceof Function || this.connection.hasMetadata(entityTarget)) {
|
||||
this.expressionMap.createMainAlias({
|
||||
name: aliasName,
|
||||
metadata: metadata,
|
||||
metadata: this.connection.getMetadata(entityTarget),
|
||||
});
|
||||
|
||||
} else {
|
||||
this.expressionMap.createMainAlias({
|
||||
name: aliasName,
|
||||
tableName: tableName,
|
||||
tableName: entityTarget,
|
||||
});
|
||||
}
|
||||
return this;
|
||||
@ -900,6 +889,7 @@ export class QueryBuilder<Entity> {
|
||||
*/
|
||||
async getEntitiesAndRawResults(): Promise<{ entities: Entity[], rawResults: any[] }> {
|
||||
const queryRunner = await this.getQueryRunner();
|
||||
const broadcaster = new Broadcaster(this.connection);
|
||||
const relationIdLoader = new RelationIdLoader(this.connection, this.queryRunnerProvider, this.expressionMap.relationIdAttributes);
|
||||
const relationCountLoader = new RelationCountLoader(this.connection, this.queryRunnerProvider, this.expressionMap.relationCountAttributes);
|
||||
const relationIdMetadataTransformer = new RelationIdMetadataToAttributeTransformer(this.expressionMap);
|
||||
@ -996,8 +986,9 @@ export class QueryBuilder<Entity> {
|
||||
const rawRelationIdResults = await relationIdLoader.load(rawResults);
|
||||
const rawRelationCountResults = await relationCountLoader.load(rawResults);
|
||||
entities = this.rawResultsToEntities(rawResults, rawRelationIdResults, rawRelationCountResults);
|
||||
if (this.expressionMap.mainAlias.hasMetadata)
|
||||
await this.connection.broadcaster.broadcastLoadEventsForAll(this.expressionMap.mainAlias.target, rawResults);
|
||||
if (this.expressionMap.mainAlias.hasMetadata) {
|
||||
await broadcaster.broadcastLoadEventsForAll(this.expressionMap.mainAlias.target, rawResults);
|
||||
}
|
||||
|
||||
}
|
||||
return {
|
||||
@ -1015,7 +1006,7 @@ export class QueryBuilder<Entity> {
|
||||
const rawRelationCountResults = await relationCountLoader.load(rawResults);
|
||||
const entities = this.rawResultsToEntities(rawResults, rawRelationIdResults, rawRelationCountResults);
|
||||
if (this.expressionMap.mainAlias.hasMetadata)
|
||||
await this.connection.broadcaster.broadcastLoadEventsForAll(this.expressionMap.mainAlias.target, rawResults);
|
||||
await broadcaster.broadcastLoadEventsForAll(this.expressionMap.mainAlias.target, rawResults);
|
||||
|
||||
return {
|
||||
entities: entities,
|
||||
@ -1152,7 +1143,7 @@ export class QueryBuilder<Entity> {
|
||||
* Clones query builder as it is.
|
||||
*/
|
||||
clone(options?: { queryRunnerProvider?: QueryRunnerProvider }): QueryBuilder<Entity> {
|
||||
const qb = new QueryBuilder(this.connection, options ? options.queryRunnerProvider : undefined);
|
||||
const qb = this.connection.createQueryBuilder(options ? options.queryRunnerProvider : undefined);
|
||||
qb.expressionMap = this.expressionMap.clone();
|
||||
return qb;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ export class RelationCountLoader {
|
||||
|
||||
// generate query:
|
||||
// SELECT category.post as parentId, COUNT(category.id) AS cnt FROM category category WHERE category.post IN (1, 2) GROUP BY category.post
|
||||
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider);
|
||||
const qb = this.connection.createQueryBuilder(this.queryRunnerProvider);
|
||||
qb.select(inverseSideTableAlias + "." + inverseSidePropertyName, "parentId")
|
||||
.addSelect("COUNT(" + qb.escapeAlias(inverseSideTableAlias) + "." + qb.escapeColumn(referenceColumnName) + ")", "cnt")
|
||||
.from(inverseSideTable, inverseSideTableAlias)
|
||||
@ -106,10 +106,10 @@ export class RelationCountLoader {
|
||||
const condition = junctionAlias + "." + firstJunctionColumn.propertyName + " IN (" + referenceColumnValues + ")" +
|
||||
" AND " + junctionAlias + "." + secondJunctionColumn.propertyName + " = " + inverseSideTableAlias + "." + inverseJoinColumnName;
|
||||
|
||||
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider);
|
||||
const qb = this.connection.createQueryBuilder(this.queryRunnerProvider);
|
||||
qb.select(junctionAlias + "." + firstJunctionColumn.propertyName, "parentId")
|
||||
.addSelect("COUNT(" + qb.escapeAlias(inverseSideTableAlias) + "." + qb.escapeColumn(inverseJoinColumnName) + ")", "cnt")
|
||||
.fromTable(inverseSideTableName, inverseSideTableAlias)
|
||||
.from(inverseSideTableName, inverseSideTableAlias)
|
||||
.innerJoin(junctionTableName, junctionAlias, condition)
|
||||
.addGroupBy(junctionAlias + "." + firstJunctionColumn.propertyName);
|
||||
|
||||
|
||||
@ -77,7 +77,7 @@ export class RelationIdLoader {
|
||||
|
||||
// generate query:
|
||||
// SELECT category.id, category.postId FROM category category ON category.postId = :postId
|
||||
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider);
|
||||
const qb = this.connection.createQueryBuilder(this.queryRunnerProvider);
|
||||
|
||||
joinColumns.forEach(joinColumn => {
|
||||
qb.addSelect(tableAlias + "." + joinColumn.propertyPath, joinColumn.databaseName);
|
||||
@ -143,7 +143,7 @@ export class RelationIdLoader {
|
||||
return "(" + condition + " AND " + inverseJoinColumnCondition + ")";
|
||||
}).join(" OR ");
|
||||
|
||||
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider);
|
||||
const qb = this.connection.createQueryBuilder(this.queryRunnerProvider);
|
||||
|
||||
inverseJoinColumns.forEach(joinColumn => {
|
||||
qb.addSelect(junctionAlias + "." + joinColumn.propertyPath, joinColumn.databaseName)
|
||||
@ -155,7 +155,7 @@ export class RelationIdLoader {
|
||||
.addOrderBy(junctionAlias + "." + joinColumn.propertyPath);
|
||||
});
|
||||
|
||||
qb.fromTable(inverseSideTableName, inverseSideTableAlias)
|
||||
qb.from(inverseSideTableName, inverseSideTableAlias)
|
||||
.innerJoin(junctionTableName, junctionAlias, condition)
|
||||
.setParameters(parameters);
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ import {Driver} from "../driver/Driver";
|
||||
/**
|
||||
* Represents functionality to provide a new query runners, and release old ones.
|
||||
* Also can provide always same query runner.
|
||||
*
|
||||
* todo: rename to QueryExecutor ?
|
||||
*/
|
||||
export class QueryRunnerProvider {
|
||||
|
||||
|
||||
@ -254,9 +254,10 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
if (!relatedEntityIds || !relatedEntityIds.length)
|
||||
return Promise.resolve();
|
||||
|
||||
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider)
|
||||
const qb = this.connection.manager
|
||||
.createQueryBuilder(this.queryRunnerProvider)
|
||||
.delete()
|
||||
.fromTable(relation.junctionEntityMetadata!.tableName, "junctionEntity");
|
||||
.from(relation.junctionEntityMetadata!.tableName, "junctionEntity");
|
||||
|
||||
const firstColumnName = this.connection.driver.escapeColumnName(relation.isOwning ? relation.junctionEntityMetadata!.columns[0].databaseName : relation.junctionEntityMetadata!.columns[1].databaseName);
|
||||
const secondColumnName = this.connection.driver.escapeColumnName(relation.isOwning ? relation.junctionEntityMetadata!.columns[1].databaseName : relation.junctionEntityMetadata!.columns[0].databaseName);
|
||||
@ -302,7 +303,8 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
if (!entityIds || !entityIds.length)
|
||||
return Promise.resolve();
|
||||
|
||||
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider)
|
||||
const qb = this.connection.manager
|
||||
.createQueryBuilder(this.queryRunnerProvider)
|
||||
.delete()
|
||||
.from(relation.junctionEntityMetadata!.tableName, "junctionEntity");
|
||||
|
||||
@ -389,7 +391,8 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
parameters["id"] = id;
|
||||
}
|
||||
|
||||
await new QueryBuilder(this.connection, this.queryRunnerProvider)
|
||||
await this.connection.manager
|
||||
.createQueryBuilder(this.queryRunnerProvider)
|
||||
.delete()
|
||||
.from(this.metadata.target, alias)
|
||||
.where(condition, parameters)
|
||||
@ -417,7 +420,8 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
parameters["ids"] = ids;
|
||||
}
|
||||
|
||||
await new QueryBuilder(this.connection, this.queryRunnerProvider)
|
||||
await this.connection.manager
|
||||
.createQueryBuilder(this.queryRunnerProvider)
|
||||
.delete()
|
||||
.from(this.metadata.target, alias)
|
||||
.where(condition, parameters)
|
||||
@ -454,11 +458,11 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
|
||||
// console.log("entityOrEntities:", entityOrEntities);
|
||||
// console.log("entityIds:", entityIds);
|
||||
const promises = (entityIds as any[]).map((entityId: any) => {
|
||||
const qb = new QueryBuilder(this.connection, this.queryRunnerProvider);
|
||||
const qb = this.connection.createQueryBuilder(this.queryRunnerProvider);
|
||||
inverseEntityColumnNames.forEach(columnName => {
|
||||
qb.select(ea("junction") + "." + ec(columnName) + " AS " + ea(columnName));
|
||||
});
|
||||
qb.fromTable(relation.junctionEntityMetadata!.tableName, "junction");
|
||||
qb.from(relation.junctionEntityMetadata!.tableName, "junction");
|
||||
Object.keys(entityId).forEach((columnName) => {
|
||||
const junctionColumnName = ownerEntityColumns.find(joinColumn => joinColumn.referencedColumn!.databaseName === columnName);
|
||||
qb.andWhere(ea("junction") + "." + ec(junctionColumnName!.databaseName) + "=:" + junctionColumnName!.databaseName + "_entityId", {[junctionColumnName!.databaseName + "_entityId"]: entityId[columnName]});
|
||||
|
||||
@ -15,8 +15,7 @@ export class Broadcaster {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(private connection: Connection,
|
||||
private subscriberMetadatas: EntitySubscriberInterface<any>[]) {
|
||||
constructor(private connection: Connection) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -57,7 +56,7 @@ export class Broadcaster {
|
||||
.filter(listener => listener.type === EventListenerTypes.BEFORE_INSERT && listener.isAllowed(subject.entity))
|
||||
.map(entityListener => subject.entity[entityListener.propertyName]()); // getValue() ?
|
||||
|
||||
const subscribers = this.subscriberMetadatas
|
||||
const subscribers = this.connection.subscribers
|
||||
.filter(subscriber => this.isAllowedSubscriber(subscriber, subject.entityTarget!) && subscriber.beforeInsert)
|
||||
.map(subscriber => subscriber.beforeInsert!({
|
||||
manager: manager,
|
||||
@ -79,7 +78,7 @@ export class Broadcaster {
|
||||
.filter(listener => listener.type === EventListenerTypes.BEFORE_UPDATE && listener.isAllowed(subject.entity))
|
||||
.map(entityListener => subject.entity[entityListener.propertyName]());
|
||||
|
||||
const subscribers = this.subscriberMetadatas
|
||||
const subscribers = this.connection.subscribers
|
||||
.filter(subscriber => this.isAllowedSubscriber(subscriber, subject.entityTarget!) && subscriber.beforeUpdate)
|
||||
.map(subscriber => subscriber.beforeUpdate!({
|
||||
manager: manager,
|
||||
@ -104,7 +103,7 @@ export class Broadcaster {
|
||||
.filter(listener => listener.type === EventListenerTypes.BEFORE_REMOVE && listener.isAllowed(subject.entity))
|
||||
.map(entityListener => subject.databaseEntity[entityListener.propertyName]());
|
||||
|
||||
const subscribers = this.subscriberMetadatas
|
||||
const subscribers = this.connection.subscribers
|
||||
.filter(subscriber => this.isAllowedSubscriber(subscriber, subject.entityTarget!) && subscriber.beforeRemove)
|
||||
.map(subscriber => subscriber.beforeRemove!({
|
||||
manager: manager,
|
||||
@ -128,7 +127,7 @@ export class Broadcaster {
|
||||
.filter(listener => listener.type === EventListenerTypes.AFTER_INSERT && listener.isAllowed(subject.entity))
|
||||
.map(entityListener => subject.entity[entityListener.propertyName]());
|
||||
|
||||
const subscribers = this.subscriberMetadatas
|
||||
const subscribers = this.connection.subscribers
|
||||
.filter(subscriber => this.isAllowedSubscriber(subscriber, subject.entityTarget!) && subscriber.afterInsert)
|
||||
.map(subscriber => subscriber.afterInsert!({
|
||||
manager: manager,
|
||||
@ -150,7 +149,7 @@ export class Broadcaster {
|
||||
.filter(listener => listener.type === EventListenerTypes.AFTER_UPDATE && listener.isAllowed(subject.entity))
|
||||
.map(entityListener => subject.entity[entityListener.propertyName]());
|
||||
|
||||
const subscribers = this.subscriberMetadatas
|
||||
const subscribers = this.connection.subscribers
|
||||
.filter(subscriber => this.isAllowedSubscriber(subscriber, subject.entityTarget!) && subscriber.afterUpdate)
|
||||
.map(subscriber => subscriber.afterUpdate!({
|
||||
manager: manager,
|
||||
@ -175,7 +174,7 @@ export class Broadcaster {
|
||||
.filter(listener => listener.type === EventListenerTypes.AFTER_REMOVE && listener.isAllowed(subject.entity))
|
||||
.map(entityListener => subject.entity[entityListener.propertyName]());
|
||||
|
||||
const subscribers = this.subscriberMetadatas
|
||||
const subscribers = this.connection.subscribers
|
||||
.filter(subscriber => this.isAllowedSubscriber(subscriber, subject.entityTarget!) && subscriber.afterRemove)
|
||||
.map(subscriber => subscriber.afterRemove!({
|
||||
manager: manager,
|
||||
@ -226,7 +225,7 @@ export class Broadcaster {
|
||||
.filter(listener => listener.type === EventListenerTypes.AFTER_LOAD && listener.isAllowed(entity))
|
||||
.map(listener => entity[listener.propertyName]());
|
||||
|
||||
const subscribers = this.subscriberMetadatas
|
||||
const subscribers = this.connection.subscribers
|
||||
.filter(subscriber => this.isAllowedSubscriber(subscriber, target) && subscriber.afterLoad)
|
||||
.map(subscriber => subscriber.afterLoad!(entity));
|
||||
|
||||
|
||||
@ -2,6 +2,13 @@ import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
|
||||
export class OrmUtils {
|
||||
|
||||
static splitStringsAndClasses<T>(strAndClses: string[]|T[]): [string[], T[]] {
|
||||
return [
|
||||
(strAndClses as string[]).filter(str => typeof str === "string"),
|
||||
(strAndClses as T[]).filter(cls => typeof cls !== "string"),
|
||||
];
|
||||
}
|
||||
|
||||
static groupBy<T, R>(array: T[], propertyCallback: (item: T) => R): { id: R, items: T[] }[] {
|
||||
return array.reduce((groupedArray, value) => {
|
||||
const key = propertyCallback(value);
|
||||
|
||||
@ -123,24 +123,10 @@ describe("Connection", () => {
|
||||
// expect(connection.reactiveEntityManager).to.be.instanceOf(ReactiveEntityManager);
|
||||
}));
|
||||
|
||||
// todo: they aren't promises anymore
|
||||
it("import entities, entity schemas, subscribers and naming strategies should not be possible once connection is done", () => connections.forEach(connection => {
|
||||
expect(() => connection.importEntities([Post])).to.throw(Error); // CannotImportAlreadyConnectedError
|
||||
expect(() => connection.importEntitySchemas([])).to.throw(Error); // CannotImportAlreadyConnectedError
|
||||
expect(() => connection.importSubscribers([])).to.throw(Error); // CannotImportAlreadyConnectedError
|
||||
expect(() => connection.importEntitiesFromDirectories([])).to.throw(Error); // CannotImportAlreadyConnectedError
|
||||
expect(() => connection.importEntitySchemaFromDirectories([])).to.throw(Error); // CannotImportAlreadyConnectedError
|
||||
expect(() => connection.importSubscribersFromDirectories([])).to.throw(Error); // CannotImportAlreadyConnectedError
|
||||
}));
|
||||
|
||||
it("should not be able to connect again", () => connections.forEach(connection => {
|
||||
return connection.connect().should.be.rejected; // CannotConnectAlreadyConnectedError
|
||||
}));
|
||||
|
||||
it("should not be able to change used naming strategy", () => connections.forEach(connection => {
|
||||
expect(() => connection.useNamingStrategy(new DefaultNamingStrategy())).to.throw(Error); // CannotUseNamingStrategyNotConnectedError
|
||||
}));
|
||||
|
||||
it("should be able to close a connection", async () => Promise.all(connections.map(connection => {
|
||||
return connection.close();
|
||||
})));
|
||||
@ -228,104 +214,6 @@ describe("Connection", () => {
|
||||
|
||||
});
|
||||
|
||||
describe("import entities and entity schemas", function() {
|
||||
|
||||
let firstConnection: Connection, secondConnection: Connection;
|
||||
beforeEach(async () => {
|
||||
firstConnection = getConnectionManager().create(setupSingleTestingConnection("mysql", {
|
||||
name: "firstConnection",
|
||||
entities: []
|
||||
}));
|
||||
secondConnection = getConnectionManager().create(setupSingleTestingConnection("mysql", {
|
||||
name: "secondConnection",
|
||||
entities: []
|
||||
}));
|
||||
});
|
||||
|
||||
it("should import first connection's entities only", async () => {
|
||||
firstConnection.importEntities([Post]);
|
||||
await firstConnection.connect();
|
||||
firstConnection.getRepository(Post).should.be.instanceOf(Repository);
|
||||
firstConnection.getRepository(Post).target.should.be.equal(Post);
|
||||
expect(() => firstConnection.getRepository(Category)).to.throw(Error); // RepositoryNotFoundError
|
||||
await firstConnection.close();
|
||||
});
|
||||
|
||||
it("should import second connection's entities only", async () => {
|
||||
secondConnection.importEntities([Category]);
|
||||
await secondConnection.connect();
|
||||
secondConnection.getRepository(Category).should.be.instanceOf(Repository);
|
||||
secondConnection.getRepository(Category).target.should.be.equal(Category);
|
||||
expect(() => secondConnection.getRepository(Post)).to.throw(Error); // RepositoryNotFoundError
|
||||
await secondConnection.close();
|
||||
});
|
||||
|
||||
it("should import first connection's entity schemas only", async () => {
|
||||
firstConnection.importEntitySchemas([ require(resourceDir + "schema/user.json") ]);
|
||||
await firstConnection.connect();
|
||||
firstConnection.getRepository("User").should.be.instanceOf(Repository);
|
||||
firstConnection.getRepository("User").target.should.be.equal("User");
|
||||
expect(() => firstConnection.getRepository("Photo")).to.throw(Error); // RepositoryNotFoundError
|
||||
await firstConnection.close();
|
||||
});
|
||||
|
||||
it("should import second connection's entity schemas only", async () => {
|
||||
secondConnection.importEntitySchemas([ require(resourceDir + "schema/photo.json") ]);
|
||||
await secondConnection.connect();
|
||||
secondConnection.getRepository("Photo").should.be.instanceOf(Repository);
|
||||
secondConnection.getRepository("Photo").target.should.be.equal("Photo");
|
||||
expect(() => secondConnection.getRepository("User")).to.throw(Error); // RepositoryNotFoundError
|
||||
await secondConnection.close();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("import entities / entity schemas / subscribers / naming strategies from directories", function() {
|
||||
|
||||
let connection: Connection;
|
||||
beforeEach(async () => {
|
||||
connection = getConnectionManager().create(setupSingleTestingConnection("mysql", {
|
||||
name: "default",
|
||||
entities: []
|
||||
}));
|
||||
});
|
||||
afterEach(() => connection.isConnected ? connection.close() : {});
|
||||
|
||||
it("should successfully load entities / entity schemas / subscribers / naming strategies from directories", async () => {
|
||||
connection.importEntitiesFromDirectories([__dirname + "/entity/*"]);
|
||||
connection.importEntitySchemaFromDirectories([resourceDir + "/schema/*"]);
|
||||
connection.importSubscribersFromDirectories([__dirname + "/subscriber/*"]);
|
||||
await connection.connect();
|
||||
connection.getRepository(Post).should.be.instanceOf(Repository);
|
||||
connection.getRepository(Post).target.should.be.equal(Post);
|
||||
connection.getRepository(Category).should.be.instanceOf(Repository);
|
||||
connection.getRepository(Category).target.should.be.equal(Category);
|
||||
connection.getRepository("User").should.be.instanceOf(Repository);
|
||||
connection.getRepository("User").target.should.be.equal("User");
|
||||
connection.getRepository("Photo").should.be.instanceOf(Repository);
|
||||
connection.getRepository("Photo").target.should.be.equal("Photo");
|
||||
});
|
||||
|
||||
it("should successfully load entities / entity schemas / subscribers / naming strategies from glob-patterned directories", async () => {
|
||||
connection.importEntitiesFromDirectories([__dirname + "/modules/**/entity/*"]);
|
||||
connection.importEntitySchemaFromDirectories([resourceDir + "/modules/**/schema/*"]);
|
||||
connection.importSubscribersFromDirectories([__dirname + "/modules/**/subscriber/*"]);
|
||||
await connection.connect();
|
||||
connection.getRepository(Blog).should.be.instanceOf(Repository);
|
||||
connection.getRepository(Blog).target.should.be.equal(Blog);
|
||||
connection.getRepository(Question).should.be.instanceOf(Repository);
|
||||
connection.getRepository(Question).target.should.be.equal(Question);
|
||||
connection.getRepository(Video).should.be.instanceOf(Repository);
|
||||
connection.getRepository(Video).target.should.be.equal(Video);
|
||||
connection.getRepository("BlogCategory").should.be.instanceOf(Repository);
|
||||
connection.getRepository("BlogCategory").target.should.be.equal("BlogCategory");
|
||||
connection.getRepository("QuestionCategory").should.be.instanceOf(Repository);
|
||||
connection.getRepository("QuestionCategory").target.should.be.equal("QuestionCategory");
|
||||
connection.getRepository("VideoCategory").should.be.instanceOf(Repository);
|
||||
connection.getRepository("VideoCategory").target.should.be.equal("VideoCategory");
|
||||
});
|
||||
});
|
||||
|
||||
describe("skip schema generation when skipSchemaSync option is used", function() {
|
||||
|
||||
let connections: Connection[];
|
||||
|
||||
@ -32,7 +32,7 @@ describe("query builder > locking", () => {
|
||||
if (connection.driver instanceof SqliteDriver || connection.driver instanceof OracleDriver)
|
||||
return;
|
||||
|
||||
const sql = connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
const sql = connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getSql();
|
||||
|
||||
@ -52,12 +52,12 @@ describe("query builder > locking", () => {
|
||||
return;
|
||||
|
||||
return Promise.all([
|
||||
connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("pessimistic_read")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne().should.be.rejectedWith(PessimisticLockTransactionRequiredError),
|
||||
|
||||
connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("pessimistic_write")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne().should.be.rejectedWith(PessimisticLockTransactionRequiredError)
|
||||
@ -87,7 +87,7 @@ describe("query builder > locking", () => {
|
||||
if (connection.driver instanceof SqliteDriver || connection.driver instanceof OracleDriver)
|
||||
return;
|
||||
|
||||
const sql = connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
const sql = connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("pessimistic_read")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getSql();
|
||||
@ -107,7 +107,7 @@ describe("query builder > locking", () => {
|
||||
if (connection.driver instanceof SqliteDriver || connection.driver instanceof OracleDriver)
|
||||
return;
|
||||
|
||||
const sql = connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
const sql = connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getSql();
|
||||
|
||||
@ -123,7 +123,7 @@ describe("query builder > locking", () => {
|
||||
if (connection.driver instanceof SqliteDriver || connection.driver instanceof OracleDriver)
|
||||
return;
|
||||
|
||||
const sql = connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
const sql = connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("pessimistic_write")
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getSql();
|
||||
@ -139,35 +139,35 @@ describe("query builder > locking", () => {
|
||||
|
||||
it("should throw error if optimistic lock used with getMany method", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
return connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
return connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("optimistic", 1)
|
||||
.getMany().should.be.rejectedWith(OptimisticLockCanNotBeUsedError);
|
||||
})));
|
||||
|
||||
it("should throw error if optimistic lock used with getCount method", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
return connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
return connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("optimistic", 1)
|
||||
.getCount().should.be.rejectedWith(OptimisticLockCanNotBeUsedError);
|
||||
})));
|
||||
|
||||
it("should throw error if optimistic lock used with getManyAndCount method", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
return connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
return connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("optimistic", 1)
|
||||
.getManyAndCount().should.be.rejectedWith(OptimisticLockCanNotBeUsedError);
|
||||
})));
|
||||
|
||||
it("should throw error if optimistic lock used with getRawMany method", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
return connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
return connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("optimistic", 1)
|
||||
.getRawMany().should.be.rejectedWith(OptimisticLockCanNotBeUsedError);
|
||||
})));
|
||||
|
||||
it("should throw error if optimistic lock used with getRawOne method", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
return connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
return connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("optimistic", 1)
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getRawOne().should.be.rejectedWith(OptimisticLockCanNotBeUsedError);
|
||||
@ -175,7 +175,7 @@ describe("query builder > locking", () => {
|
||||
|
||||
it("should not throw error if optimistic lock used with getOne method", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
return connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
return connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("optimistic", 1)
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne().should.not.be.rejected;
|
||||
@ -187,7 +187,7 @@ describe("query builder > locking", () => {
|
||||
post.title = "New post";
|
||||
await connection.manager.save(post);
|
||||
|
||||
return connection.manager.createQueryBuilder(PostWithoutVersionAndUpdateDate, "post")
|
||||
return connection.createQueryBuilder(PostWithoutVersionAndUpdateDate, "post")
|
||||
.setLock("optimistic", 1)
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne().should.be.rejectedWith(NoVersionOrUpdateDateColumnError);
|
||||
@ -199,7 +199,7 @@ describe("query builder > locking", () => {
|
||||
post.title = "New post";
|
||||
await connection.manager.save(post);
|
||||
|
||||
return connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
return connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("optimistic", 2)
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne().should.be.rejectedWith(OptimisticLockVersionMismatchError);
|
||||
@ -211,7 +211,7 @@ describe("query builder > locking", () => {
|
||||
post.title = "New post";
|
||||
await connection.manager.save(post);
|
||||
|
||||
return connection.manager.createQueryBuilder(PostWithVersion, "post")
|
||||
return connection.createQueryBuilder(PostWithVersion, "post")
|
||||
.setLock("optimistic", 1)
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne().should.not.be.rejected;
|
||||
@ -223,7 +223,7 @@ describe("query builder > locking", () => {
|
||||
post.title = "New post";
|
||||
await connection.manager.save(post);
|
||||
|
||||
return connection.manager.createQueryBuilder(PostWithUpdateDate, "post")
|
||||
return connection.createQueryBuilder(PostWithUpdateDate, "post")
|
||||
.setLock("optimistic", new Date(2017, 1, 1))
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne().should.be.rejectedWith(OptimisticLockVersionMismatchError);
|
||||
@ -235,7 +235,7 @@ describe("query builder > locking", () => {
|
||||
post.title = "New post";
|
||||
await connection.manager.save(post);
|
||||
|
||||
return connection.manager.createQueryBuilder(PostWithUpdateDate, "post")
|
||||
return connection.createQueryBuilder(PostWithUpdateDate, "post")
|
||||
.setLock("optimistic", post.updateDate)
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne().should.not.be.rejected;
|
||||
@ -248,12 +248,12 @@ describe("query builder > locking", () => {
|
||||
await connection.manager.save(post);
|
||||
|
||||
return Promise.all([
|
||||
connection.manager.createQueryBuilder(PostWithVersionAndUpdatedDate, "post")
|
||||
connection.createQueryBuilder(PostWithVersionAndUpdatedDate, "post")
|
||||
.setLock("optimistic", post.updateDate)
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne().should.not.be.rejected,
|
||||
|
||||
connection.manager.createQueryBuilder(PostWithVersionAndUpdatedDate, "post")
|
||||
connection.createQueryBuilder(PostWithVersionAndUpdatedDate, "post")
|
||||
.setLock("optimistic", 1)
|
||||
.where("post.id = :id", { id: 1 })
|
||||
.getOne().should.not.be.rejected
|
||||
|
||||
@ -30,7 +30,7 @@ describe("query builder > select", () => {
|
||||
})));
|
||||
|
||||
it("should append all entity mapped columns from both main selection and join selections to select statement", () => Promise.all(connections.map(async connection => {
|
||||
const sql = connection.manager.createQueryBuilder(Post, "post")
|
||||
const sql = connection.createQueryBuilder(Post, "post")
|
||||
.leftJoinAndSelect("category", "category")
|
||||
.disableEscaping()
|
||||
.getSql();
|
||||
@ -49,7 +49,7 @@ describe("query builder > select", () => {
|
||||
})));
|
||||
|
||||
it("should append entity mapped columns from both main alias and join aliases to select statement", () => Promise.all(connections.map(async connection => {
|
||||
const sql = connection.manager.createQueryBuilder(Post, "post")
|
||||
const sql = connection.createQueryBuilder(Post, "post")
|
||||
.select("post.id")
|
||||
.addSelect("category.name")
|
||||
.leftJoin("category", "category")
|
||||
@ -62,7 +62,7 @@ describe("query builder > select", () => {
|
||||
})));
|
||||
|
||||
it("should append entity mapped columns to select statement, if they passed as array", () => Promise.all(connections.map(async connection => {
|
||||
const sql = connection.manager.createQueryBuilder(Post, "post")
|
||||
const sql = connection.createQueryBuilder(Post, "post")
|
||||
.select(["post.id", "post.title"])
|
||||
.disableEscaping()
|
||||
.getSql();
|
||||
@ -71,7 +71,7 @@ describe("query builder > select", () => {
|
||||
})));
|
||||
|
||||
it("should append raw sql to select statement", () => Promise.all(connections.map(async connection => {
|
||||
const sql = connection.manager.createQueryBuilder(Post, "post")
|
||||
const sql = connection.createQueryBuilder(Post, "post")
|
||||
.select("COUNT(*) as cnt")
|
||||
.disableEscaping()
|
||||
.getSql();
|
||||
@ -80,7 +80,7 @@ describe("query builder > select", () => {
|
||||
})));
|
||||
|
||||
it("should append raw sql and entity mapped column to select statement", () => Promise.all(connections.map(async connection => {
|
||||
const sql = connection.manager.createQueryBuilder(Post, "post")
|
||||
const sql = connection.createQueryBuilder(Post, "post")
|
||||
.select(["COUNT(*) as cnt", "post.title"])
|
||||
.disableEscaping()
|
||||
.getSql();
|
||||
@ -89,7 +89,7 @@ describe("query builder > select", () => {
|
||||
})));
|
||||
|
||||
it("should not create alias for selection, which is not entity mapped column", () => Promise.all(connections.map(async connection => {
|
||||
const sql = connection.manager.createQueryBuilder(Post, "post")
|
||||
const sql = connection.createQueryBuilder(Post, "post")
|
||||
.select("post.name")
|
||||
.disableEscaping()
|
||||
.getSql();
|
||||
|
||||
@ -2,14 +2,15 @@ import "reflect-metadata";
|
||||
import {PostgresDriver} from "../../../src/driver/postgres/PostgresDriver";
|
||||
import {Logger} from "../../../src/logger/Logger";
|
||||
import {expect} from "chai";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
|
||||
describe("github issues > #114 Can not be parsed correctly the URL of pg.", () => {
|
||||
|
||||
let driver: PostgresDriver;
|
||||
before(() => driver = new PostgresDriver({
|
||||
before(() => driver = new PostgresDriver(new Connection({
|
||||
type: "postgres",
|
||||
url: "postgres://test:test@localhost:5432/test",
|
||||
}, new Logger({})));
|
||||
})));
|
||||
|
||||
it("should not fail in url parser", () => {
|
||||
expect(driver.options.username).to.be.eq("test");
|
||||
|
||||
@ -128,7 +128,7 @@ export function setupTestingConnections(options?: TestingOptions) {
|
||||
return false;
|
||||
|
||||
if (options && options.enabledDrivers && options.enabledDrivers.length)
|
||||
return options.enabledDrivers.indexOf(connectionOptions.driver!.type) !== -1; // ! is temporary
|
||||
return options.enabledDrivers.indexOf(connectionOptions.driver!.type!) !== -1; // ! is temporary
|
||||
|
||||
if (connectionOptions.disabledIfNotEnabledImplicitly === true)
|
||||
return false;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user