mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
Merge branch 'master' into fix-oracle-limit-offset
This commit is contained in:
commit
11c281583d
@ -25,7 +25,16 @@ each for its own `findOne*` or `find*` methods
|
||||
* `transaction` method has been removed from `Repository`. Use `EntityManager#transaction` method instead
|
||||
* custom repositories do not support container anymore
|
||||
* added ActiveRecord support (by extending EntityModel) class
|
||||
* 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` 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
|
||||
|
||||
|
||||
@ -89,7 +89,7 @@ export class Photo {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
fileName: string;
|
||||
filename: string;
|
||||
views: number;
|
||||
}
|
||||
```
|
||||
@ -106,7 +106,7 @@ export class Photo {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
fileName: string;
|
||||
filename: string;
|
||||
views: number;
|
||||
isPublished: boolean;
|
||||
}
|
||||
@ -134,7 +134,7 @@ export class Photo {
|
||||
description: string;
|
||||
|
||||
@Column()
|
||||
fileName: string;
|
||||
filename: string;
|
||||
|
||||
@Column()
|
||||
views: number;
|
||||
@ -165,7 +165,7 @@ export class Photo {
|
||||
description: string;
|
||||
|
||||
@Column()
|
||||
fileName: string;
|
||||
filename: string;
|
||||
|
||||
@Column()
|
||||
views: number;
|
||||
@ -195,7 +195,7 @@ export class Photo {
|
||||
description: string;
|
||||
|
||||
@Column()
|
||||
fileName: string;
|
||||
filename: string;
|
||||
|
||||
@Column()
|
||||
views: number;
|
||||
@ -225,7 +225,7 @@ export class Photo {
|
||||
description: string;
|
||||
|
||||
@Column()
|
||||
fileName: string;
|
||||
filename: string;
|
||||
|
||||
@Column()
|
||||
views: number;
|
||||
@ -258,7 +258,7 @@ export class Photo {
|
||||
description: string;
|
||||
|
||||
@Column()
|
||||
fileName: string;
|
||||
filename: string;
|
||||
|
||||
@Column("int")
|
||||
views: number;
|
||||
|
||||
21
README.md
21
README.md
@ -30,7 +30,8 @@ Share this library with friends on Twitter and everywhere else you can.
|
||||
If you notice bug or have something not working please report an issue, we'll try to fix it as soon as possible.
|
||||
More documentation and features expected to be soon. Feel free to contribute.
|
||||
|
||||
> For the latest release changes see [changelog](./CHANGELOG.md).
|
||||
> Important note: if you want latest stable version install `npm i typeorm@0.0.11`. You can find 0.0.11 version [README here](https://github.com/typeorm/typeorm/tree/0.0.x-version).
|
||||
> If you want the latest development version simply install `npm i typeorm`. For the latest development release changes see [changelog](./CHANGELOG.md).
|
||||
|
||||
TypeORM is an [Object Relational Mapper](1) (ORM) for Node.js written in
|
||||
TypeScript that can be used with TypeScript or JavaScript (ES5, ES6, ES7).
|
||||
@ -62,6 +63,10 @@ TypeORM is highly influenced by other ORMs, such as [Hibernate](http://hibernate
|
||||
1. Install module:
|
||||
|
||||
`npm install typeorm --save`
|
||||
|
||||
|
||||
Important note: if you want latest stable version install `npm i typeorm@0.0.11`
|
||||
If you want the latest development version simply install `npm i typeorm`. For the latest development release changes see [changelog](./CHANGELOG.md).
|
||||
|
||||
2. You need to install `reflect-metadata` shim:
|
||||
|
||||
@ -137,7 +142,7 @@ export class Photo {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
fileName: string;
|
||||
filename: string;
|
||||
views: number;
|
||||
}
|
||||
````
|
||||
@ -154,7 +159,7 @@ export class Photo {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
fileName: string;
|
||||
filename: string;
|
||||
views: number;
|
||||
isPublished: boolean;
|
||||
}
|
||||
@ -182,7 +187,7 @@ export class Photo {
|
||||
description: string;
|
||||
|
||||
@Column()
|
||||
fileName: string;
|
||||
filename: string;
|
||||
|
||||
@Column()
|
||||
views: number;
|
||||
@ -217,7 +222,7 @@ export class Photo {
|
||||
description: string;
|
||||
|
||||
@Column()
|
||||
fileName: string;
|
||||
filename: string;
|
||||
|
||||
@Column()
|
||||
views: number;
|
||||
@ -248,7 +253,7 @@ export class Photo {
|
||||
description: string;
|
||||
|
||||
@Column()
|
||||
fileName: string;
|
||||
filename: string;
|
||||
|
||||
@Column()
|
||||
views: number;
|
||||
@ -281,7 +286,7 @@ export class Photo {
|
||||
description: string;
|
||||
|
||||
@Column()
|
||||
fileName: string;
|
||||
filename: string;
|
||||
|
||||
@Column()
|
||||
views: number;
|
||||
@ -316,7 +321,7 @@ export class Photo {
|
||||
description: string;
|
||||
|
||||
@Column()
|
||||
fileName: string;
|
||||
filename: string;
|
||||
|
||||
@Column("int")
|
||||
views: number;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "typeorm",
|
||||
"private": true,
|
||||
"version": "0.1.0-alpha.3",
|
||||
"version": "0.1.0-alpha.4",
|
||||
"description": "Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL, MongoDB databases.",
|
||||
"license": "MIT",
|
||||
"readmeFilename": "README.md",
|
||||
@ -75,10 +75,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"app-root-path": "^2.0.1",
|
||||
"dotenv": "^4.0.0",
|
||||
"glob": "^7.1.1",
|
||||
"js-yaml": "^3.8.4",
|
||||
"reflect-metadata": "^0.1.10",
|
||||
"yargonaut": "^1.1.2",
|
||||
"yargs": "^8.0.1"
|
||||
"yargs": "^8.0.1",
|
||||
"xml2js": "^0.4.17"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node_modules/.bin/gulp tests"
|
||||
|
||||
@ -13,9 +13,8 @@ const options: ConnectionOptions = {
|
||||
database: "test"
|
||||
},
|
||||
autoSchemaSync: true,
|
||||
usedNamingStrategy: "custom_strategy",
|
||||
entities: [Post],
|
||||
namingStrategies: [CustomNamingStrategy]
|
||||
namingStrategy: new CustomNamingStrategy(),
|
||||
entities: [Post]
|
||||
};
|
||||
|
||||
createConnection(options).then(connection => {
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import {NamingStrategyInterface} from "../../../src/naming-strategy/NamingStrategyInterface";
|
||||
import {NamingStrategy} from "../../../src/decorator/NamingStrategy";
|
||||
import {DefaultNamingStrategy} from "../../../src/naming-strategy/DefaultNamingStrategy";
|
||||
import {snakeCase} from "../../../src/util/StringUtils";
|
||||
|
||||
@NamingStrategy("custom_strategy")
|
||||
export class CustomNamingStrategy extends DefaultNamingStrategy implements NamingStrategyInterface {
|
||||
|
||||
tableName(targetName: string, userSpecifiedName: string): string {
|
||||
|
||||
@ -4,24 +4,14 @@ import {EntitySubscriberInterface} from "../subscriber/EntitySubscriberInterface
|
||||
import {RepositoryNotFoundError} from "./error/RepositoryNotFoundError";
|
||||
import {ObjectType} from "../common/ObjectType";
|
||||
import {EntityManager} from "../entity-manager/EntityManager";
|
||||
import {importClassesFromDirectories, importJsonsFromDirectories} from "../util/DirectoryExportedClassesLoader";
|
||||
import {getFromContainer, getMetadataArgsStorage} from "../index";
|
||||
import {EntityMetadataBuilder} from "../metadata-builder/EntityMetadataBuilder";
|
||||
import {DefaultNamingStrategy} from "../naming-strategy/DefaultNamingStrategy";
|
||||
import {CannotImportAlreadyConnectedError} from "./error/CannotImportAlreadyConnectedError";
|
||||
import {CannotCloseNotConnectedError} from "./error/CannotCloseNotConnectedError";
|
||||
import {CannotConnectAlreadyConnectedError} from "./error/CannotConnectAlreadyConnectedError";
|
||||
import {TreeRepository} from "../repository/TreeRepository";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {NamingStrategyNotFoundError} from "./error/NamingStrategyNotFoundError";
|
||||
import {RepositoryNotTreeError} from "./error/RepositoryNotTreeError";
|
||||
import {EntitySchema} from "../entity-schema/EntitySchema";
|
||||
import {CannotSyncNotConnectedError} from "./error/CannotSyncNotConnectedError";
|
||||
import {CannotUseNamingStrategyNotConnectedError} from "./error/CannotUseNamingStrategyNotConnectedError";
|
||||
import {Broadcaster} from "../subscriber/Broadcaster";
|
||||
import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
|
||||
import {SpecificRepository} from "../repository/SpecificRepository";
|
||||
import {RepositoryAggregator} from "../repository/RepositoryAggregator";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {SchemaBuilder} from "../schema-builder/SchemaBuilder";
|
||||
import {Logger} from "../logger/Logger";
|
||||
@ -31,18 +21,22 @@ import {MigrationInterface} from "../migration/MigrationInterface";
|
||||
import {MigrationExecutor} from "../migration/MigrationExecutor";
|
||||
import {CannotRunMigrationNotConnectedError} from "./error/CannotRunMigrationNotConnectedError";
|
||||
import {PlatformTools} from "../platform/PlatformTools";
|
||||
import {AbstractRepository} from "../repository/AbstractRepository";
|
||||
import {CustomRepositoryNotFoundError} from "../repository/error/CustomRepositoryNotFoundError";
|
||||
import {CustomRepositoryReusedError} from "../repository/error/CustomRepositoryReusedError";
|
||||
import {CustomRepositoryCannotInheritRepositoryError} from "../repository/error/CustomRepositoryCannotInheritRepositoryError";
|
||||
import {MongoRepository} from "../repository/MongoRepository";
|
||||
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 {QueryRunnerProviderAlreadyReleasedError} from "../query-runner/error/QueryRunnerProviderAlreadyReleasedError";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {EntityManagerFactory} from "../entity-manager/EntityManagerFactory";
|
||||
import {LoggerFactory} from "../logger/LoggerFactory";
|
||||
import {RepositoryFactory} from "../repository/RepositoryFactory";
|
||||
import {DriverFactory} from "../driver/DriverFactory";
|
||||
import {ConnectionMetadataBuilder} from "./ConnectionMetadataBuilder";
|
||||
|
||||
/**
|
||||
* Connection is a single database connection to a specific database of a database management system.
|
||||
* Connection is a single database ORM connection to a specific DBMS database.
|
||||
* Its not required to be a database connection, depend on database type it can create connection pool.
|
||||
* You can have multiple connections to multiple databases in your application.
|
||||
*/
|
||||
export class Connection {
|
||||
@ -54,122 +48,75 @@ export class Connection {
|
||||
/**
|
||||
* Connection name.
|
||||
*/
|
||||
public readonly name: string;
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* Connection options.
|
||||
*/
|
||||
readonly options: ConnectionOptions;
|
||||
|
||||
/**
|
||||
* Indicates if connection is initialized or not.
|
||||
*/
|
||||
readonly isConnected = false;
|
||||
|
||||
/**
|
||||
* Database driver used by this connection.
|
||||
*/
|
||||
public readonly driver: Driver;
|
||||
readonly driver: Driver;
|
||||
|
||||
/**
|
||||
* Logger used to log orm events.
|
||||
*/
|
||||
public readonly logger: Logger;
|
||||
|
||||
/**
|
||||
* All entity metadatas that are registered for this connection.
|
||||
*/
|
||||
public readonly entityMetadatas: EntityMetadata[] = [];
|
||||
|
||||
/**
|
||||
* Used to broadcast connection events.
|
||||
*/
|
||||
public readonly broadcaster: Broadcaster;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets EntityManager of this connection.
|
||||
* EntityManager of this connection.
|
||||
*/
|
||||
readonly manager: EntityManager;
|
||||
|
||||
/**
|
||||
* Stores all registered repositories.
|
||||
* Naming strategy used in the connection.
|
||||
*/
|
||||
private readonly repositoryAggregators: RepositoryAggregator[] = [];
|
||||
readonly namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Stores all entity repository instances.
|
||||
* Logger used to log orm events.
|
||||
*/
|
||||
private readonly entityRepositories: Object[] = [];
|
||||
readonly logger: Logger;
|
||||
|
||||
/**
|
||||
* Entity subscribers that are registered for this connection.
|
||||
* Migration instances that are registered for this connection.
|
||||
*/
|
||||
private readonly entitySubscribers: EntitySubscriberInterface<any>[] = [];
|
||||
readonly migrations: MigrationInterface[] = [];
|
||||
|
||||
/**
|
||||
* Registered entity classes to be used for this connection.
|
||||
* Entity subscriber instances that are registered for this connection.
|
||||
*/
|
||||
private readonly entityClasses: Function[] = [];
|
||||
readonly subscribers: EntitySubscriberInterface<any>[] = [];
|
||||
|
||||
/**
|
||||
* Registered entity schemas to be used for this connection.
|
||||
* All entity metadatas that are registered for this connection.
|
||||
*/
|
||||
private readonly entitySchemas: EntitySchema[] = [];
|
||||
|
||||
/**
|
||||
* Registered subscriber classes to be used for this connection.
|
||||
*/
|
||||
private readonly subscriberClasses: Function[] = [];
|
||||
|
||||
/**
|
||||
* Registered naming strategy classes to be used for this connection.
|
||||
*/
|
||||
private readonly namingStrategyClasses: Function[] = [];
|
||||
|
||||
/**
|
||||
* Registered migration classes to be used for this connection.
|
||||
*/
|
||||
private readonly migrationClasses: Function[] = [];
|
||||
|
||||
/**
|
||||
* Naming strategy to be used in this connection.
|
||||
*/
|
||||
private usedNamingStrategy: Function|string;
|
||||
|
||||
/**
|
||||
* Indicates if connection has been done or not.
|
||||
*/
|
||||
private _isConnected = false;
|
||||
readonly entityMetadatas: EntityMetadata[] = [];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(name: string, driver: Driver, logger: Logger) {
|
||||
this.name = name;
|
||||
this.driver = driver;
|
||||
this.logger = logger;
|
||||
this.manager = this.createEntityManager();
|
||||
this.broadcaster = this.createBroadcaster();
|
||||
constructor(options: ConnectionOptions) {
|
||||
this.name = options.name || "default";
|
||||
this.options = options;
|
||||
this.logger = new LoggerFactory().create(this.options.logging || {});
|
||||
this.driver = new DriverFactory().create(this);
|
||||
this.manager = new EntityManagerFactory().create(this);
|
||||
this.namingStrategy = options.namingStrategy || new DefaultNamingStrategy();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Accessors
|
||||
// Public Accessors
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Indicates if connection to the database already established for this connection.
|
||||
*/
|
||||
get isConnected(): boolean {
|
||||
return this._isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entity manager that allows to perform repository operations with any entity in this connection.
|
||||
*
|
||||
* @deprecated use manager instead.
|
||||
*/
|
||||
get entityManager(): EntityManager {
|
||||
return this.manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mongodb entity manager that allows to perform mongodb-specific repository operations
|
||||
* with any entity in this connection.
|
||||
*
|
||||
* Available only in mongodb connections.
|
||||
*/
|
||||
get mongoEntityManager(): MongoEntityManager {
|
||||
if (!(this.manager instanceof MongoEntityManager))
|
||||
@ -184,6 +131,9 @@ export class Connection {
|
||||
|
||||
/**
|
||||
* Performs connection to the database.
|
||||
* This method should be called once on application bootstrap.
|
||||
* This method not necessarily creates database connection (depend on database type),
|
||||
* but it also can setup a connection pool with database to use.
|
||||
*/
|
||||
async connect(): Promise<this> {
|
||||
if (this.isConnected)
|
||||
@ -193,7 +143,7 @@ export class Connection {
|
||||
await this.driver.connect();
|
||||
|
||||
// set connected status for the current connection
|
||||
this._isConnected = true;
|
||||
Object.assign(this, { isConnected: true });
|
||||
|
||||
// build all metadatas registered in the current connection
|
||||
try {
|
||||
@ -212,34 +162,26 @@ export class Connection {
|
||||
|
||||
/**
|
||||
* Closes connection with the database.
|
||||
* Once connection is closed, you cannot use repositories and perform any operations except
|
||||
* opening connection again.
|
||||
* Once connection is closed, you cannot use repositories or perform any operations except opening connection again.
|
||||
*/
|
||||
async close(): Promise<void> {
|
||||
if (!this.isConnected)
|
||||
throw new CannotCloseNotConnectedError(this.name);
|
||||
|
||||
await this.driver.disconnect();
|
||||
this._isConnected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the database and all its data.
|
||||
*/
|
||||
async dropDatabase(): Promise<void> {
|
||||
const queryRunner = await this.driver.createQueryRunner();
|
||||
await queryRunner.clearDatabase();
|
||||
Object.assign(this, { isConnected: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
async syncSchema(dropBeforeSync: boolean = false): Promise<void> {
|
||||
|
||||
if (!this.isConnected)
|
||||
return Promise.reject(new CannotSyncNotConnectedError(this.name));
|
||||
throw new CannotCloseNotConnectedError(this.name);
|
||||
|
||||
if (dropBeforeSync)
|
||||
await this.dropDatabase();
|
||||
@ -248,17 +190,29 @@ export class Connection {
|
||||
await this.driver.syncSchema(this.entityMetadatas);
|
||||
|
||||
} else {
|
||||
await this.createSchemaBuilder().build();
|
||||
const schemaBuilder = new SchemaBuilder(this.driver, this.logger, this.entityMetadatas);
|
||||
await schemaBuilder.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
await queryRunner.clearDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all pending migrations.
|
||||
* Can be used only after connection to the database is established.
|
||||
*/
|
||||
async runMigrations(): Promise<void> {
|
||||
|
||||
if (!this.isConnected)
|
||||
return Promise.reject(new CannotRunMigrationNotConnectedError(this.name));
|
||||
throw new CannotCloseNotConnectedError(this.name);
|
||||
|
||||
const migrationExecutor = new MigrationExecutor(this);
|
||||
await migrationExecutor.executePendingMigrations();
|
||||
@ -266,155 +220,29 @@ export class Connection {
|
||||
|
||||
/**
|
||||
* Reverts last executed migration.
|
||||
* Can be used only after connection to the database is established.
|
||||
*/
|
||||
async undoLastMigration(): Promise<void> {
|
||||
|
||||
if (!this.isConnected)
|
||||
return Promise.reject(new CannotRunMigrationNotConnectedError(this.name));
|
||||
throw new CannotCloseNotConnectedError(this.name);
|
||||
|
||||
const migrationExecutor = new MigrationExecutor(this);
|
||||
await migrationExecutor.undoLastMigration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports entities 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.
|
||||
*/
|
||||
importEntitiesFromDirectories(paths: string[]): this {
|
||||
this.importEntities(importClassesFromDirectories(paths));
|
||||
return this;
|
||||
hasMetadata(target: Function|string): boolean {
|
||||
return !!this.findMetadata(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports entity schemas from the given paths (directories) and registers them in the current connection.
|
||||
*/
|
||||
importEntitySchemaFromDirectories(paths: string[]): this {
|
||||
this.importEntitySchemas(importJsonsFromDirectories(paths));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports subscribers from the given paths (directories) and registers them in the current connection.
|
||||
*/
|
||||
importSubscribersFromDirectories(paths: string[]): this {
|
||||
this.importSubscribers(importClassesFromDirectories(paths));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports naming strategies from the given paths (directories) and registers them in the current connection.
|
||||
*/
|
||||
importNamingStrategiesFromDirectories(paths: string[]): this {
|
||||
this.importNamingStrategies(importClassesFromDirectories(paths));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports migrations from the given paths (directories) and registers them in the current connection.
|
||||
*/
|
||||
importMigrationsFromDirectories(paths: string[]): this {
|
||||
this.importMigrations(importClassesFromDirectories(paths));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports entities and registers them in the current connection.
|
||||
*/
|
||||
importEntities(entities: Function[]): this {
|
||||
if (this.isConnected)
|
||||
throw new CannotImportAlreadyConnectedError("entities", this.name);
|
||||
|
||||
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 naming strategies and registers them in the current connection.
|
||||
*/
|
||||
importNamingStrategies(strategies: Function[]): this {
|
||||
if (this.isConnected)
|
||||
throw new CannotImportAlreadyConnectedError("naming strategies", this.name);
|
||||
|
||||
strategies.forEach(cls => this.namingStrategyClasses.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(name: string): this;
|
||||
|
||||
/**
|
||||
* Sets given naming strategy to be used.
|
||||
* Naming strategy must be set to be used before connection is established.
|
||||
*/
|
||||
useNamingStrategy(strategy: Function): this;
|
||||
|
||||
/**
|
||||
* Sets given naming strategy to be used.
|
||||
* Naming strategy must be set to be used before connection is established.
|
||||
*/
|
||||
useNamingStrategy(strategyClassOrName: string|Function): this {
|
||||
if (this.isConnected)
|
||||
throw new CannotUseNamingStrategyNotConnectedError(this.name);
|
||||
|
||||
this.usedNamingStrategy = strategyClassOrName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity metadata of the given entity class.
|
||||
*/
|
||||
getMetadata(target: Function): EntityMetadata;
|
||||
|
||||
/**
|
||||
* Gets the entity metadata of the given entity name.
|
||||
*/
|
||||
getMetadata(target: string): EntityMetadata;
|
||||
|
||||
/**
|
||||
* Gets the entity metadata of the given entity class or schema name.
|
||||
*/
|
||||
getMetadata(target: Function|string): EntityMetadata;
|
||||
|
||||
/**
|
||||
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.findMetadata(target);
|
||||
if (!metadata)
|
||||
throw new EntityMetadataNotFound(target);
|
||||
|
||||
@ -422,145 +250,41 @@ export class Connection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets repository for the given entity class.
|
||||
* Gets repository for the given entity.
|
||||
*/
|
||||
getRepository<Entity>(entityClass: ObjectType<Entity>): Repository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets repository for the given entity name.
|
||||
*/
|
||||
getRepository<Entity>(entityName: string): Repository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets repository for the given entity name.
|
||||
*/
|
||||
getRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): Repository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets repository for the given entity class or name.
|
||||
*/
|
||||
getRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): Repository<Entity> {
|
||||
return this.findRepositoryAggregator(entityClassOrName).repository;
|
||||
getRepository<Entity>(target: ObjectType<Entity>|string): Repository<Entity> {
|
||||
return this.getMetadata(target).repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets tree repository for the given entity class.
|
||||
* Only tree-type entities can have a TreeRepository,
|
||||
* like ones decorated with @ClosureEntity decorator.
|
||||
*/
|
||||
getTreeRepository<Entity>(entityClass: ObjectType<Entity>): TreeRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets tree repository for the given entity class.
|
||||
* Only tree-type entities can have a TreeRepository,
|
||||
* like ones decorated with @ClosureEntity decorator.
|
||||
*/
|
||||
getTreeRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): TreeRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets tree repository for the given entity class.
|
||||
* Only tree-type entities can have a TreeRepository,
|
||||
* like ones decorated with @ClosureEntity decorator.
|
||||
*/
|
||||
getTreeRepository<Entity>(entityName: string): TreeRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets tree repository for the given entity class or name.
|
||||
* Only tree-type entities can have a TreeRepository,
|
||||
* like ones decorated with @ClosureEntity decorator.
|
||||
* Only tree-type entities can have a TreeRepository, like ones decorated with @ClosureEntity decorator.
|
||||
*/
|
||||
getTreeRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): TreeRepository<Entity> {
|
||||
// todo: add checks if tree repository is supported by driver (not supported by mongodb at least)
|
||||
getTreeRepository<Entity>(target: ObjectType<Entity>|string): TreeRepository<Entity> {
|
||||
if (this.driver instanceof MongoDriver)
|
||||
throw new Error(`You cannot use getTreeRepository for MongoDB connections.`);
|
||||
|
||||
const repository = this.findRepositoryAggregator(entityClassOrName).treeRepository;
|
||||
if (!repository)
|
||||
throw new RepositoryNotTreeError(entityClassOrName);
|
||||
if (!this.hasMetadata(target))
|
||||
throw new RepositoryNotFoundError(this.name, target);
|
||||
|
||||
const repository = this.getMetadata(target).repository;
|
||||
if (!(repository instanceof TreeRepository))
|
||||
throw new RepositoryNotTreeError(target);
|
||||
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets mongodb-specific repository for the given entity class.
|
||||
*/
|
||||
getMongoRepository<Entity>(entityClass: ObjectType<Entity>): MongoRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets mongodb-specific repository for the given entity name.
|
||||
*/
|
||||
getMongoRepository<Entity>(entityName: string): MongoRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets mongodb-specific repository for the given entity name.
|
||||
*/
|
||||
getMongoRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): MongoRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets mongodb-specific repository for the given entity class or name.
|
||||
*/
|
||||
getMongoRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): MongoRepository<Entity> {
|
||||
getMongoRepository<Entity>(target: ObjectType<Entity>|string): MongoRepository<Entity> {
|
||||
if (!(this.driver instanceof MongoDriver))
|
||||
throw new Error(`You can use getMongoRepository only for MongoDB connections.`);
|
||||
|
||||
return this.findRepositoryAggregator(entityClassOrName).repository as MongoRepository<Entity>;
|
||||
}
|
||||
if (!this.hasMetadata(target))
|
||||
throw new RepositoryNotFoundError(this.name, target);
|
||||
|
||||
/**
|
||||
* 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 [];
|
||||
return this.getMetadata(target).repository as MongoRepository<Entity>;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -570,132 +294,199 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
createIsolatedManager(queryRunnerProvider?: QueryRunnerProvider): EntityManager {
|
||||
if (!queryRunnerProvider)
|
||||
queryRunnerProvider = new QueryRunnerProvider(this.driver, true);
|
||||
|
||||
return new EntityManagerFactory().create(this, queryRunnerProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new repository 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.
|
||||
*/
|
||||
createIsolatedRepository<Entity>(entityClassOrName: ObjectType<Entity>|string, queryRunnerProvider?: QueryRunnerProvider): Repository<Entity> {
|
||||
if (!queryRunnerProvider)
|
||||
queryRunnerProvider = new QueryRunnerProvider(this.driver, true);
|
||||
|
||||
return new RepositoryFactory().createRepository(this, this.getMetadata(entityClassOrName), queryRunnerProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new specific repository 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.
|
||||
*/
|
||||
createIsolatedSpecificRepository<Entity>(entityClassOrName: ObjectType<Entity>|string, queryRunnerProvider?: QueryRunnerProvider): SpecificRepository<Entity> {
|
||||
if (!queryRunnerProvider)
|
||||
queryRunnerProvider = new QueryRunnerProvider(this.driver, true);
|
||||
|
||||
return new RepositoryFactory().createSpecificRepository(this, this.getMetadata(entityClassOrName), queryRunnerProvider);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Deprecated Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets entity manager that allows to perform repository operations with any entity in this connection.
|
||||
*
|
||||
* @deprecated use manager instead.
|
||||
*/
|
||||
get entityManager(): EntityManager {
|
||||
return this.manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
if (!this.hasMetadata(entityClassOrName))
|
||||
throw new RepositoryNotFoundError(this.name, entityClassOrName);
|
||||
|
||||
return this.getMetadata(entityClassOrName).specificRepository;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Finds repository aggregator of the given entity class or name.
|
||||
* Finds entity metadata exist for the given entity class, target name or table name.
|
||||
*/
|
||||
protected findRepositoryAggregator(entityClassOrName: ObjectType<any>|string): RepositoryAggregator {
|
||||
// if (!this.isConnected)
|
||||
// throw new NoConnectionForRepositoryError(this.name);
|
||||
protected findMetadata(target: Function|string): EntityMetadata|undefined {
|
||||
return this.entityMetadatas.find(metadata => {
|
||||
if (metadata.target === target)
|
||||
return true;
|
||||
if (typeof target === "string")
|
||||
return metadata.name === target || metadata.tableName === target;
|
||||
|
||||
if (!this.entityMetadatas.find(metadata => metadata.target === entityClassOrName || (typeof entityClassOrName === "string" && metadata.targetName === entityClassOrName)))
|
||||
throw new RepositoryNotFoundError(this.name, entityClassOrName);
|
||||
|
||||
const metadata = this.getMetadata(entityClassOrName);
|
||||
const repositoryAggregator = this.repositoryAggregators.find(repositoryAggregate => repositoryAggregate.metadata === metadata);
|
||||
if (!repositoryAggregator)
|
||||
throw new RepositoryNotFoundError(this.name, entityClassOrName);
|
||||
|
||||
return repositoryAggregator;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds all registered metadatas.
|
||||
*/
|
||||
public buildMetadatas() {
|
||||
protected buildMetadatas(): void {
|
||||
|
||||
this.entitySubscribers.length = 0;
|
||||
this.repositoryAggregators.length = 0;
|
||||
this.entityMetadatas.length = 0;
|
||||
|
||||
this.driver.namingStrategy = this.createNamingStrategy(); // todo: why they are in the driver
|
||||
this.driver.lazyRelationsWrapper = this.createLazyRelationsWrapper(); // todo: why they are in the driver
|
||||
const connectionMetadataBuilder = new ConnectionMetadataBuilder(this);
|
||||
const repositoryFactory = new RepositoryFactory();
|
||||
const entityMetadataValidator = new EntityMetadataValidator();
|
||||
|
||||
// take imported event subscribers
|
||||
if (this.subscriberClasses && this.subscriberClasses.length && !PlatformTools.getEnvVariable("SKIP_SUBSCRIBERS_LOADING")) {
|
||||
getMetadataArgsStorage()
|
||||
.filterSubscribers(this.subscriberClasses)
|
||||
.map(metadata => getFromContainer(metadata.target))
|
||||
.forEach(subscriber => this.entitySubscribers.push(subscriber));
|
||||
// build subscribers if they are not disallowed from high-level (for example they can disallowed from migrations run process)
|
||||
if (!PlatformTools.getEnvVariable("SKIP_SUBSCRIBERS_LOADING")) {
|
||||
const subscribers = connectionMetadataBuilder.buildSubscribers(this.options.subscribers || []);
|
||||
Object.assign(this, { subscribers: subscribers });
|
||||
}
|
||||
|
||||
// take imported entity listeners
|
||||
if (this.entityClasses && this.entityClasses.length) {
|
||||
// build entity metadatas
|
||||
const entityMetadatas = connectionMetadataBuilder.buildEntityMetadatas(this.options.entities || [], this.options.entitySchemas || []);
|
||||
Object.assign(this, { entityMetadatas: entityMetadatas });
|
||||
|
||||
// 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));
|
||||
});
|
||||
}
|
||||
// create migration instances
|
||||
const migrations = connectionMetadataBuilder.buildMigrations(this.options.migrations || []);
|
||||
Object.assign(this, { migrations: migrations });
|
||||
|
||||
// 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));
|
||||
});
|
||||
}
|
||||
// initialize repositories for all entity metadatas
|
||||
this.entityMetadatas.forEach(metadata => {
|
||||
metadata.repository = repositoryFactory.createRepository(this, metadata);
|
||||
metadata.specificRepository = repositoryFactory.createSpecificRepository(this, metadata);
|
||||
});
|
||||
|
||||
// validate all created entity metadatas to make sure user created entities are valid and correct
|
||||
entityMetadataValidator.validateMany(this.entityMetadatas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a naming strategy to be used for this connection.
|
||||
*/
|
||||
protected createNamingStrategy(): NamingStrategyInterface {
|
||||
|
||||
// if naming strategies are not loaded, or used naming strategy is not set then use default naming strategy
|
||||
if (!this.namingStrategyClasses || !this.namingStrategyClasses.length || !this.usedNamingStrategy)
|
||||
return getFromContainer(DefaultNamingStrategy);
|
||||
|
||||
// try to find used naming strategy in the list of loaded naming strategies
|
||||
const namingMetadata = getMetadataArgsStorage()
|
||||
.filterNamingStrategies(this.namingStrategyClasses)
|
||||
.find(strategy => {
|
||||
if (typeof this.usedNamingStrategy === "string") {
|
||||
return strategy.name === this.usedNamingStrategy;
|
||||
} else {
|
||||
return strategy.target === this.usedNamingStrategy;
|
||||
}
|
||||
});
|
||||
|
||||
// throw an error if not found
|
||||
if (!namingMetadata)
|
||||
throw new NamingStrategyNotFoundError(this.usedNamingStrategy, this.name);
|
||||
|
||||
// initialize a naming strategy instance
|
||||
return getFromContainer<NamingStrategyInterface>(namingMetadata.target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entity broadcaster using in this connection.
|
||||
*/
|
||||
protected createBroadcaster() {
|
||||
return new Broadcaster(this, this.entitySubscribers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a schema builder used to build a database schema for the entities of the current connection.
|
||||
*/
|
||||
protected createSchemaBuilder() {
|
||||
return new SchemaBuilder(this.driver, this.logger, this.entityMetadatas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a lazy relations wrapper.
|
||||
*/
|
||||
protected createLazyRelationsWrapper() {
|
||||
return new LazyRelationsWrapper(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,54 +53,20 @@ export class ConnectionManager {
|
||||
*/
|
||||
create(options: ConnectionOptions): Connection {
|
||||
|
||||
const logger = new Logger(options.logging || {});
|
||||
const driver = this.createDriver(options.driver, logger);
|
||||
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 naming strategies
|
||||
if (options.namingStrategies) {
|
||||
const [directories, classes] = this.splitStringsAndClasses(options.namingStrategies);
|
||||
connection
|
||||
.importNamingStrategies(classes)
|
||||
.importNamingStrategiesFromDirectories(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.usedNamingStrategy)
|
||||
connection.useNamingStrategy(options.usedNamingStrategy as any);
|
||||
|
||||
const connection = new Connection(options);
|
||||
this.connections.push(connection);
|
||||
return connection;
|
||||
}
|
||||
|
||||
@ -341,8 +296,6 @@ export class ConnectionManager {
|
||||
entities: PlatformTools.getEnvVariable("TYPEORM_ENTITIES") ? PlatformTools.getEnvVariable("TYPEORM_ENTITIES").split(",") : [],
|
||||
subscribers: PlatformTools.getEnvVariable("TYPEORM_SUBSCRIBERS") ? PlatformTools.getEnvVariable("TYPEORM_SUBSCRIBERS").split(",") : [],
|
||||
entitySchemas: PlatformTools.getEnvVariable("TYPEORM_ENTITY_SCHEMAS") ? PlatformTools.getEnvVariable("TYPEORM_ENTITY_SCHEMAS").split(",") : [],
|
||||
namingStrategies: PlatformTools.getEnvVariable("TYPEORM_NAMING_STRATEGIES") ? PlatformTools.getEnvVariable("TYPEORM_NAMING_STRATEGIES").split(",") : [],
|
||||
usedNamingStrategy: PlatformTools.getEnvVariable("TYPEORM_USED_NAMING_STRATEGY"),
|
||||
logging: {
|
||||
logQueries: OrmUtils.toBoolean(PlatformTools.getEnvVariable("TYPEORM_LOGGING_QUERIES")),
|
||||
logFailedQueryError: OrmUtils.toBoolean(PlatformTools.getEnvVariable("TYPEORM_LOGGING_FAILED_QUERIES")),
|
||||
@ -384,13 +337,43 @@ export class ConnectionManager {
|
||||
throw new Error(`Configuration ${path || "ormconfig.json"} was not found. Add connection configuration inside ormconfig.json file.`);
|
||||
|
||||
const environmentLessOptions = optionsArray.filter(options => (options.name || "default") === connectionName);
|
||||
const options = environmentLessOptions.filter(options => !options.environment || options.environment === PlatformTools.getEnvVariable("NODE_ENV")); // skip connection creation if environment is set in the options, and its not equal to the value in the NODE_ENV variable
|
||||
const options = environmentLessOptions.find(options => !options.environment || options.environment === PlatformTools.getEnvVariable("NODE_ENV")); // skip connection creation if environment is set in the options, and its not equal to the value in the NODE_ENV variable
|
||||
|
||||
if (!options.length)
|
||||
if (!options)
|
||||
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(", ")}.` : ""));
|
||||
|
||||
return this.createAndConnectByConnectionOptions(options[0]);
|
||||
let connectionOptions: ConnectionOptions = Object.assign({}, options);
|
||||
// normalize directory paths
|
||||
if (options.entities) {
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -417,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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
53
src/connection/ConnectionMetadataBuilder.ts
Normal file
53
src/connection/ConnectionMetadataBuilder.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import {importClassesFromDirectories, importJsonsFromDirectories} from "../util/DirectoryExportedClassesLoader";
|
||||
import {OrmUtils} from "../util/OrmUtils";
|
||||
import {getFromContainer} from "../container";
|
||||
import {MigrationInterface} from "../migration/MigrationInterface";
|
||||
import {getMetadataArgsStorage} from "../index";
|
||||
import {EntityMetadataBuilder} from "../metadata-builder/EntityMetadataBuilder";
|
||||
import {EntitySchemaTransformer} from "../entity-schema/EntitySchemaTransformer";
|
||||
import {Connection} from "./Connection";
|
||||
import {EntitySchema} from "../entity-schema/EntitySchema";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {EntitySubscriberInterface} from "../subscriber/EntitySubscriberInterface";
|
||||
|
||||
export class ConnectionMetadataBuilder {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected connection: Connection) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
buildMigrations(migrations: Function[]|string[]): MigrationInterface[] {
|
||||
const [migrationClasses, migrationDirectories] = OrmUtils.splitClassesAndStrings(migrations);
|
||||
const allMigrationClasses = [...migrationClasses, ...importClassesFromDirectories(migrationDirectories)];
|
||||
return allMigrationClasses.map(migrationClass => getFromContainer<MigrationInterface>(migrationClass));
|
||||
}
|
||||
|
||||
buildSubscribers(subscribers: Function[]|string[]): EntitySubscriberInterface<any>[] {
|
||||
const [subscriberClasses, subscriberDirectories] = OrmUtils.splitClassesAndStrings(subscribers || []);
|
||||
const allSubscriberClasses = [...subscriberClasses, ...importClassesFromDirectories(subscriberDirectories)];
|
||||
return getMetadataArgsStorage()
|
||||
.filterSubscribers(allSubscriberClasses)
|
||||
.map(metadata => getFromContainer<EntitySubscriberInterface<any>>(metadata.target));
|
||||
}
|
||||
|
||||
buildEntityMetadatas(entities: Function[]|string[], schemas: EntitySchema[]|string[]): EntityMetadata[] {
|
||||
const [entityClasses, entityDirectories] = OrmUtils.splitClassesAndStrings(entities || []);
|
||||
const allEntityClasses = [...entityClasses, ...importClassesFromDirectories(entityDirectories)];
|
||||
const decoratorEntityMetadatas = new EntityMetadataBuilder(this.connection, getMetadataArgsStorage()).build(allEntityClasses);
|
||||
|
||||
const [entitySchemaClasses, entitySchemaDirectories] = OrmUtils.splitClassesAndStrings(schemas || []);
|
||||
const allEntitySchemaClasses = [...entitySchemaClasses, ...importJsonsFromDirectories(entitySchemaDirectories)];
|
||||
const metadataArgsStorageFromSchema = new EntitySchemaTransformer().transform(allEntitySchemaClasses);
|
||||
const schemaEntityMetadatas = new EntityMetadataBuilder(this.connection, metadataArgsStorageFromSchema).build();
|
||||
|
||||
return [...decoratorEntityMetadatas, ...schemaEntityMetadatas];
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,6 +1,11 @@
|
||||
import {DriverOptions} from "../driver/DriverOptions";
|
||||
import {EntitySchema} from "../entity-schema/EntitySchema";
|
||||
import {LoggerOptions} from "../logger/LoggerOptions";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {DriverType} from "../driver/DriverType";
|
||||
import {LoggerFactory} from "../logger/LoggerFactory";
|
||||
import {DriverFactory} from "../driver/DriverFactory";
|
||||
import {EntityManagerFactory} from "../entity-manager/EntityManagerFactory";
|
||||
|
||||
/**
|
||||
* ConnectionOptions is an interface with settings and options for specific connection.
|
||||
@ -9,11 +14,6 @@ import {LoggerOptions} from "../logger/LoggerOptions";
|
||||
*/
|
||||
export interface ConnectionOptions {
|
||||
|
||||
/**
|
||||
* Database options of this connection.
|
||||
*/
|
||||
readonly driver: DriverOptions;
|
||||
|
||||
/**
|
||||
* Connection name. If connection name is not given then it will be called "default".
|
||||
* Different connections must have different names.
|
||||
@ -21,9 +21,87 @@ export interface ConnectionOptions {
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* Name of the naming strategy or target class of the naming strategy to be used for this connection.
|
||||
* Database options of this connection.
|
||||
*
|
||||
* @deprecated Define options right in the connection options section.
|
||||
*/
|
||||
readonly usedNamingStrategy?: string|Function;
|
||||
readonly driver?: DriverOptions;
|
||||
|
||||
/**
|
||||
* Database type. This value is required.
|
||||
*/
|
||||
readonly type?: DriverType;
|
||||
|
||||
/**
|
||||
* Connection url to where perform connection to.
|
||||
*/
|
||||
readonly url?: string;
|
||||
|
||||
/**
|
||||
* Database host.
|
||||
*/
|
||||
readonly host?: string;
|
||||
|
||||
/**
|
||||
* Database host port.
|
||||
*/
|
||||
readonly port?: number;
|
||||
|
||||
/**
|
||||
* Database username.
|
||||
*/
|
||||
readonly username?: string;
|
||||
|
||||
/**
|
||||
* Database password.
|
||||
*/
|
||||
readonly password?: string;
|
||||
|
||||
/**
|
||||
* Database name to connect to.
|
||||
*/
|
||||
readonly database?: string;
|
||||
|
||||
/**
|
||||
* Schema name. By default is "public" (used only in Postgres databases).
|
||||
*/
|
||||
readonly schemaName?: string;
|
||||
|
||||
/**
|
||||
* Connection SID (used for Oracle databases).
|
||||
*/
|
||||
readonly sid?: string;
|
||||
|
||||
/**
|
||||
* Storage type or path to the storage (used for SQLite databases).
|
||||
*/
|
||||
readonly storage?: string;
|
||||
|
||||
/**
|
||||
* Indicates if connection pooling should be used or not.
|
||||
* Be default it is enabled if its supported by a platform.
|
||||
* Set to false to disable it.
|
||||
*
|
||||
* @todo: rename to disablePool? What about mongodb pool?
|
||||
*/
|
||||
readonly usePool?: boolean;
|
||||
|
||||
/**
|
||||
* Extra connection options to be passed to the underlying driver.
|
||||
*/
|
||||
readonly extra?: any;
|
||||
|
||||
/**
|
||||
* Prefix to use on all tables (collections) of this connection in the database.
|
||||
*
|
||||
* @todo: rename to entityPrefix
|
||||
*/
|
||||
readonly tablesPrefix?: string;
|
||||
|
||||
/**
|
||||
* Naming strategy to be used to name tables and columns in the database.
|
||||
*/
|
||||
readonly namingStrategy?: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Entities to be loaded for this connection.
|
||||
@ -39,13 +117,6 @@ export interface ConnectionOptions {
|
||||
*/
|
||||
readonly subscribers?: Function[]|string[];
|
||||
|
||||
/**
|
||||
* Naming strategies to be loaded for this connection.
|
||||
* Accepts both naming strategy classes and directories where from naming strategies need to be loaded.
|
||||
* Directories support glob patterns.
|
||||
*/
|
||||
readonly namingStrategies?: 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.
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
import {getMetadataArgsStorage} from "../index";
|
||||
import {NamingStrategyMetadataArgs} from "../metadata-args/NamingStrategyMetadataArgs";
|
||||
|
||||
/**
|
||||
* Decorator registers a new naming strategy to be used in naming things.
|
||||
*
|
||||
* todo: deprecate using naming strategies this way. use it without decorators
|
||||
* todo: but add multiple default naming strategies for use
|
||||
*/
|
||||
export function NamingStrategy(name?: string): Function {
|
||||
return function (target: Function) {
|
||||
const strategyName = name ? name : (<any> target).name;
|
||||
const args: NamingStrategyMetadataArgs = {
|
||||
target: target,
|
||||
name: strategyName
|
||||
};
|
||||
getMetadataArgsStorage().namingStrategies.push(args);
|
||||
};
|
||||
}
|
||||
@ -10,36 +10,6 @@ import {LazyRelationsWrapper} from "../lazy-loading/LazyRelationsWrapper";
|
||||
*/
|
||||
export interface Driver {
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection where this driver is used.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Used to wrap lazy relations to be able to perform lazy loadings.
|
||||
*/
|
||||
lazyRelationsWrapper: LazyRelationsWrapper;
|
||||
|
||||
/**
|
||||
* Driver options contains connectivity options used to connection to the database.
|
||||
*/
|
||||
readonly options: DriverOptions;
|
||||
|
||||
/**
|
||||
* Creates repository instance of this driver.
|
||||
*/
|
||||
// createRepository(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider): Repository<any>;
|
||||
|
||||
/**
|
||||
* Creates tree repository instance of this driver.
|
||||
*/
|
||||
// createTreeRepository(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider): TreeRepository<any>;
|
||||
|
||||
/**
|
||||
* Creates specific repository instance of this driver.
|
||||
*/
|
||||
// createSpecificRepository(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider): SpecificRepository<any>;
|
||||
|
||||
/**
|
||||
* Performs connection to the database.
|
||||
* Based on pooling options, it can either create connection immediately,
|
||||
|
||||
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!);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,62 +1,61 @@
|
||||
/**
|
||||
* Driver type.
|
||||
*/
|
||||
export type DriverType = "mysql"|"postgres"|"mariadb"|"sqlite"|"oracle"|"mssql"|"websql"|"mongodb";
|
||||
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.
|
||||
*/
|
||||
readonly type: DriverType;
|
||||
type?: DriverType;
|
||||
|
||||
/**
|
||||
* Connection url to where perform connection to.
|
||||
*/
|
||||
readonly url?: string;
|
||||
url?: string;
|
||||
|
||||
/**
|
||||
* Database host.
|
||||
*/
|
||||
readonly host?: string;
|
||||
host?: string;
|
||||
|
||||
/**
|
||||
* Database host port.
|
||||
*/
|
||||
readonly port?: number;
|
||||
port?: number;
|
||||
|
||||
/**
|
||||
* Database username.
|
||||
*/
|
||||
readonly username?: string;
|
||||
username?: string;
|
||||
|
||||
/**
|
||||
* Database password.
|
||||
*/
|
||||
readonly password?: string;
|
||||
password?: string;
|
||||
|
||||
/**
|
||||
* Database name to connect to.
|
||||
*/
|
||||
readonly database?: string;
|
||||
database?: string;
|
||||
|
||||
/**
|
||||
* Schema name. By default is "public" (used only in Postgres databases).
|
||||
*/
|
||||
readonly schemaName?: string;
|
||||
schemaName?: string;
|
||||
|
||||
/**
|
||||
* Connection SID (used for Oracle databases).
|
||||
*/
|
||||
readonly sid?: string;
|
||||
sid?: string;
|
||||
|
||||
/**
|
||||
* Storage type or path to the storage (used for SQLite databases).
|
||||
*/
|
||||
readonly storage?: string;
|
||||
storage?: string;
|
||||
|
||||
/**
|
||||
* Indicates if connection pooling should be used or not.
|
||||
@ -65,18 +64,18 @@ export interface DriverOptions {
|
||||
*
|
||||
* @todo: rename to disablePool? What about mongodb pool?
|
||||
*/
|
||||
readonly usePool?: boolean;
|
||||
usePool?: boolean;
|
||||
|
||||
/**
|
||||
* Extra connection options to be passed to the underlying driver.
|
||||
*/
|
||||
readonly extra?: any;
|
||||
extra?: any;
|
||||
|
||||
/**
|
||||
* Prefix to use on all tables (collections) of this connection in the database.
|
||||
*
|
||||
* @todo: rename to entityPrefix
|
||||
*/
|
||||
readonly tablesPrefix?: string;
|
||||
tablesPrefix?: string;
|
||||
|
||||
}
|
||||
|
||||
4
src/driver/DriverType.ts
Normal file
4
src/driver/DriverType.ts
Normal file
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Driver type.
|
||||
*/
|
||||
export type DriverType = "mysql"|"postgres"|"mariadb"|"sqlite"|"oracle"|"mssql"|"websql"|"mongodb";
|
||||
@ -3,16 +3,14 @@ import {ConnectionIsNotSetError} from "../error/ConnectionIsNotSetError";
|
||||
import {DriverOptions} from "../DriverOptions";
|
||||
import {DatabaseConnection} from "../DatabaseConnection";
|
||||
import {DriverPackageNotInstalledError} from "../error/DriverPackageNotInstalledError";
|
||||
import {Logger} from "../../logger/Logger";
|
||||
import {QueryRunner} from "../../query-runner/QueryRunner";
|
||||
import {MongoQueryRunner} from "./MongoQueryRunner";
|
||||
import {ObjectLiteral} from "../../common/ObjectLiteral";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {DriverOptionNotSetError} from "../error/DriverOptionNotSetError";
|
||||
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.
|
||||
@ -23,27 +21,12 @@ export class MongoDriver implements Driver {
|
||||
// Public Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection where this driver is used.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Used to wrap lazy relations to be able to perform lazy loadings.
|
||||
*/
|
||||
lazyRelationsWrapper: LazyRelationsWrapper;
|
||||
|
||||
/**
|
||||
* Mongodb does not require to dynamically create query runner each time,
|
||||
* because it does not have a regular pool.
|
||||
*/
|
||||
queryRunner: MongoQueryRunner;
|
||||
|
||||
/**
|
||||
* Driver connection options.
|
||||
*/
|
||||
readonly options: DriverOptions;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Properties
|
||||
// -------------------------------------------------------------------------
|
||||
@ -58,27 +41,17 @@ export class MongoDriver implements Driver {
|
||||
*/
|
||||
protected pool: any;
|
||||
|
||||
/**
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DriverOptions, logger: Logger, mongodb?: any) {
|
||||
constructor(protected 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.mongodb = this.loadDependencies();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -90,7 +63,7 @@ export class MongoDriver implements Driver {
|
||||
*/
|
||||
connect(): Promise<void> {
|
||||
return new Promise<void>((ok, fail) => {
|
||||
this.mongodb.MongoClient.connect(this.buildConnectionUrl(), this.options.extra, (err: any, database: any) => {
|
||||
this.mongodb.MongoClient.connect(this.buildConnectionUrl(), this.connection.options!.extra, (err: any, database: any) => {
|
||||
if (err) return fail(err);
|
||||
|
||||
this.pool = database;
|
||||
@ -99,7 +72,7 @@ export class MongoDriver implements Driver {
|
||||
connection: this.pool,
|
||||
isTransactionActive: false
|
||||
};
|
||||
this.queryRunner = new MongoQueryRunner(databaseConnection, this, this.logger);
|
||||
this.queryRunner = new MongoQueryRunner(this.connection, databaseConnection);
|
||||
ok();
|
||||
});
|
||||
});
|
||||
@ -266,10 +239,10 @@ export class MongoDriver implements Driver {
|
||||
* Builds connection url that is passed to underlying driver to perform connection to the mongodb database.
|
||||
*/
|
||||
protected buildConnectionUrl(): string {
|
||||
if (this.options.url)
|
||||
return this.options.url;
|
||||
if (this.connection.options.url)
|
||||
return this.connection.options.url;
|
||||
|
||||
return `mongodb://${this.options.host || "127.0.0.1"}:${this.options.port || "27017"}/${this.options.database}`;
|
||||
return `mongodb://${this.connection.options.host || "127.0.0.1"}:${this.connection.options.port || "27017"}/${this.connection.options.database}`;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,8 +1,6 @@
|
||||
import {QueryRunner} from "../../query-runner/QueryRunner";
|
||||
import {DatabaseConnection} from "../DatabaseConnection";
|
||||
import {ObjectLiteral} from "../../common/ObjectLiteral";
|
||||
import {Logger} from "../../logger/Logger";
|
||||
import {MongoDriver} from "./MongoDriver";
|
||||
import {ColumnSchema} from "../../schema-builder/schema/ColumnSchema";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {TableSchema} from "../../schema-builder/schema/TableSchema";
|
||||
@ -40,6 +38,7 @@ import {
|
||||
UpdateWriteOpResult,
|
||||
CollStats
|
||||
} from "./typings";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
/**
|
||||
* Runs queries on a single MongoDB connection.
|
||||
@ -50,9 +49,8 @@ export class MongoQueryRunner implements QueryRunner {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected databaseConnection: DatabaseConnection,
|
||||
protected driver: MongoDriver,
|
||||
protected logger: Logger) {
|
||||
constructor(protected connection: Connection,
|
||||
protected databaseConnection: DatabaseConnection) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -678,7 +676,7 @@ export class MongoQueryRunner implements QueryRunner {
|
||||
* Database name shortcut.
|
||||
*/
|
||||
protected get dbName(): string {
|
||||
return this.driver.options.database as string;
|
||||
return this.connection.options.database as string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { EventEmitter } from "events";
|
||||
import { Readable, Writable } from "stream";
|
||||
import {EventEmitter} from "events";
|
||||
import {Readable, Writable} from "stream";
|
||||
|
||||
/**
|
||||
* Creates a new MongoClient instance.
|
||||
|
||||
@ -15,31 +15,13 @@ 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.
|
||||
*/
|
||||
export class MysqlDriver implements Driver {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection where this driver is used.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Used to wrap lazy relations to be able to perform lazy loadings.
|
||||
*/
|
||||
lazyRelationsWrapper: LazyRelationsWrapper;
|
||||
|
||||
/**
|
||||
* Driver connection options.
|
||||
*/
|
||||
readonly options: DriverOptions;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Properties
|
||||
// -------------------------------------------------------------------------
|
||||
@ -64,32 +46,24 @@ export class MysqlDriver implements Driver {
|
||||
*/
|
||||
protected databaseConnectionPool: DatabaseConnection[] = [];
|
||||
|
||||
/**
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DriverOptions, logger: Logger, mysql?: any) {
|
||||
constructor(protected connection: Connection) {
|
||||
|
||||
this.options = DriverUtils.buildDriverOptions(options);
|
||||
this.logger = logger;
|
||||
this.mysql = mysql;
|
||||
Object.assign(connection.options, DriverUtils.buildDriverOptions(connection.options)); // todo: do it better way
|
||||
|
||||
// validate options to make sure everything is set
|
||||
if (!(this.options.host || (this.options.extra && this.options.extra.socketPath)))
|
||||
if (!(connection.options.host || (connection.options.extra && connection.options.extra.socketPath)))
|
||||
throw new DriverOptionNotSetError("socketPath and host");
|
||||
if (!this.options.username)
|
||||
if (!connection.options.username)
|
||||
throw new DriverOptionNotSetError("username");
|
||||
if (!this.options.database)
|
||||
if (!connection.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();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -105,16 +79,16 @@ export class MysqlDriver implements Driver {
|
||||
|
||||
// build connection options for the driver
|
||||
const options = Object.assign({}, {
|
||||
host: this.options.host,
|
||||
user: this.options.username,
|
||||
password: this.options.password,
|
||||
database: this.options.database,
|
||||
port: this.options.port
|
||||
}, this.options.extra || {});
|
||||
host: this.connection.options.host,
|
||||
user: this.connection.options.username,
|
||||
password: this.connection.options.password,
|
||||
database: this.connection.options.database,
|
||||
port: this.connection.options.port
|
||||
}, this.connection.options.extra || {});
|
||||
|
||||
// pooling is enabled either when its set explicitly to true,
|
||||
// either when its not defined at all (e.g. enabled by default)
|
||||
if (this.options.usePool === undefined || this.options.usePool === true) {
|
||||
if (this.connection.options.usePool === undefined || this.connection.options.usePool === true) {
|
||||
this.pool = this.mysql.createPool(options);
|
||||
return Promise.resolve();
|
||||
|
||||
@ -164,7 +138,7 @@ export class MysqlDriver implements Driver {
|
||||
return Promise.reject(new ConnectionIsNotSetError("mysql"));
|
||||
|
||||
const databaseConnection = await this.retrieveDatabaseConnection();
|
||||
return new MysqlQueryRunner(databaseConnection, this, this.logger);
|
||||
return new MysqlQueryRunner(this.connection, databaseConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -14,6 +14,7 @@ import {PrimaryKeySchema} from "../../schema-builder/schema/PrimaryKeySchema";
|
||||
import {IndexSchema} from "../../schema-builder/schema/IndexSchema";
|
||||
import {QueryRunnerAlreadyReleasedError} from "../../query-runner/error/QueryRunnerAlreadyReleasedError";
|
||||
import {ColumnType} from "../../metadata/types/ColumnTypes";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
/**
|
||||
* Runs queries on a single mysql database connection.
|
||||
@ -34,9 +35,8 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected databaseConnection: DatabaseConnection,
|
||||
protected driver: MysqlDriver,
|
||||
protected logger: Logger) {
|
||||
constructor(protected connection: Connection,
|
||||
protected databaseConnection: DatabaseConnection) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -145,11 +145,11 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
return new Promise((ok, fail) => {
|
||||
this.logger.logQuery(query, parameters);
|
||||
this.connection.logger.logQuery(query, parameters);
|
||||
this.databaseConnection.connection.query(query, parameters, (err: any, result: any) => {
|
||||
if (err) {
|
||||
this.logger.logFailedQuery(query, parameters);
|
||||
this.logger.logQueryError(err);
|
||||
this.connection.logger.logFailedQuery(query, parameters);
|
||||
this.connection.logger.logQueryError(err);
|
||||
return fail(err);
|
||||
}
|
||||
|
||||
@ -166,10 +166,10 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
const keys = Object.keys(keyValues);
|
||||
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
|
||||
const columns = keys.map(key => this.connection.driver.escapeColumnName(key)).join(", ");
|
||||
const values = keys.map(key => "?").join(",");
|
||||
const parameters = keys.map(key => keyValues[key]);
|
||||
const sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`;
|
||||
const sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`;
|
||||
const result = await this.query(sql, parameters);
|
||||
return generatedColumn ? result.insertId : undefined;
|
||||
}
|
||||
@ -183,7 +183,7 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
|
||||
const updateValues = this.parametrize(valuesMap).join(", ");
|
||||
const conditionString = this.parametrize(conditions).join(" AND ");
|
||||
const sql = `UPDATE ${this.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
const sql = `UPDATE ${this.connection.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
const conditionParams = Object.keys(conditions).map(key => conditions[key]);
|
||||
const updateParams = Object.keys(valuesMap).map(key => valuesMap[key]);
|
||||
const allParameters = updateParams.concat(conditionParams);
|
||||
@ -210,7 +210,7 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
|
||||
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
|
||||
|
||||
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
const sql = `DELETE FROM ${this.connection.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
await this.query(sql, parameters);
|
||||
}
|
||||
|
||||
@ -223,16 +223,16 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
|
||||
let sql = "";
|
||||
if (hasLevel) {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
|
||||
} else {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
|
||||
}
|
||||
await this.query(sql);
|
||||
const results: ObjectLiteral[] = await this.query(`SELECT MAX(level) as level FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId}`);
|
||||
const results: ObjectLiteral[] = await this.query(`SELECT MAX(level) as level FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId}`);
|
||||
return results && results[0] && results[0]["level"] ? parseInt(results[0]["level"]) + 1 : 1;
|
||||
}
|
||||
|
||||
@ -765,7 +765,7 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
* Truncates table.
|
||||
*/
|
||||
async truncate(tableName: string): Promise<void> {
|
||||
await this.query(`TRUNCATE TABLE ${this.driver.escapeTableName(tableName)}`);
|
||||
await this.query(`TRUNCATE TABLE ${this.connection.driver.escapeTableName(tableName)}`);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -776,14 +776,14 @@ export class MysqlQueryRunner implements QueryRunner {
|
||||
* Database name shortcut.
|
||||
*/
|
||||
protected get dbName(): string {
|
||||
return this.driver.options.database as string;
|
||||
return this.connection.options.database as string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parametrizes given object of values. Used to create column=value queries.
|
||||
*/
|
||||
protected parametrize(objectLiteral: ObjectLiteral): string[] {
|
||||
return Object.keys(objectLiteral).map(key => this.driver.escapeColumnName(key) + "=?");
|
||||
return Object.keys(objectLiteral).map(key => this.connection.driver.escapeColumnName(key) + "=?");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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.
|
||||
@ -23,25 +24,6 @@ import {LazyRelationsWrapper} from "../../lazy-loading/LazyRelationsWrapper";
|
||||
*/
|
||||
export class OracleDriver implements Driver {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection where this driver is used.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Used to wrap lazy relations to be able to perform lazy loadings.
|
||||
*/
|
||||
lazyRelationsWrapper: LazyRelationsWrapper;
|
||||
|
||||
/**
|
||||
* Driver connection options.
|
||||
*/
|
||||
readonly options: DriverOptions;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Properties
|
||||
// -------------------------------------------------------------------------
|
||||
@ -66,33 +48,24 @@ export class OracleDriver implements Driver {
|
||||
*/
|
||||
protected databaseConnectionPool: DatabaseConnection[] = [];
|
||||
|
||||
/**
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DriverOptions, logger: Logger, oracle?: any) {
|
||||
constructor(protected connection: Connection) {
|
||||
|
||||
this.options = DriverUtils.buildDriverOptions(options, { useSid: true });
|
||||
this.logger = logger;
|
||||
this.oracle = oracle;
|
||||
// Object.assign(connection.options, DriverUtils.buildDriverOptions(connection.options)); // todo: do it better way
|
||||
|
||||
// validate options to make sure everything is set
|
||||
if (!this.options.host)
|
||||
if (!connection.options.host)
|
||||
throw new DriverOptionNotSetError("host");
|
||||
if (!this.options.username)
|
||||
if (!connection.options.username)
|
||||
throw new DriverOptionNotSetError("username");
|
||||
if (!this.options.sid)
|
||||
if (!connection.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;
|
||||
}
|
||||
|
||||
@ -109,14 +82,14 @@ export class OracleDriver implements Driver {
|
||||
|
||||
// build connection options for the driver
|
||||
const options = Object.assign({}, {
|
||||
user: this.options.username,
|
||||
password: this.options.password,
|
||||
connectString: this.options.host + ":" + this.options.port + "/" + this.options.sid,
|
||||
}, this.options.extra || {});
|
||||
user: this.connection.options.username,
|
||||
password: this.connection.options.password,
|
||||
connectString: this.connection.options.host + ":" + this.connection.options.port + "/" + this.connection.options.sid,
|
||||
}, this.connection.options.extra || {});
|
||||
|
||||
// pooling is enabled either when its set explicitly to true,
|
||||
// either when its not defined at all (e.g. enabled by default)
|
||||
if (this.options.usePool === undefined || this.options.usePool === true) {
|
||||
if (this.connection.options.usePool === undefined || this.connection.options.usePool === true) {
|
||||
return new Promise<void>((ok, fail) => {
|
||||
this.oracle.createPool(options, (err: any, pool: any) => {
|
||||
if (err)
|
||||
@ -177,7 +150,7 @@ export class OracleDriver implements Driver {
|
||||
return Promise.reject(new ConnectionIsNotSetError("oracle"));
|
||||
|
||||
const databaseConnection = await this.retrieveDatabaseConnection();
|
||||
return new OracleQueryRunner(databaseConnection, this, this.logger);
|
||||
return new OracleQueryRunner(this.connection, databaseConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -3,8 +3,6 @@ import {DatabaseConnection} from "../DatabaseConnection";
|
||||
import {ObjectLiteral} from "../../common/ObjectLiteral";
|
||||
import {TransactionAlreadyStartedError} from "../error/TransactionAlreadyStartedError";
|
||||
import {TransactionNotStartedError} from "../error/TransactionNotStartedError";
|
||||
import {Logger} from "../../logger/Logger";
|
||||
import {OracleDriver} from "./OracleDriver";
|
||||
import {DataTypeNotSupportedByDriverError} from "../error/DataTypeNotSupportedByDriverError";
|
||||
import {ColumnSchema} from "../../schema-builder/schema/ColumnSchema";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
@ -14,6 +12,8 @@ import {PrimaryKeySchema} from "../../schema-builder/schema/PrimaryKeySchema";
|
||||
import {IndexSchema} from "../../schema-builder/schema/IndexSchema";
|
||||
import {QueryRunnerAlreadyReleasedError} from "../../query-runner/error/QueryRunnerAlreadyReleasedError";
|
||||
import {ColumnType} from "../../metadata/types/ColumnTypes";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
import {OracleDriver} from "./OracleDriver";
|
||||
|
||||
/**
|
||||
* Runs queries on a single mysql database connection.
|
||||
@ -36,9 +36,8 @@ export class OracleQueryRunner implements QueryRunner {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected databaseConnection: DatabaseConnection,
|
||||
protected driver: OracleDriver,
|
||||
protected logger: Logger) {
|
||||
constructor(protected connection: Connection,
|
||||
protected databaseConnection: DatabaseConnection) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -146,11 +145,11 @@ export class OracleQueryRunner implements QueryRunner {
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
return new Promise((ok, fail) => {
|
||||
this.logger.logQuery(query, parameters);
|
||||
this.connection.logger.logQuery(query, parameters);
|
||||
const handler = (err: any, result: any) => {
|
||||
if (err) {
|
||||
this.logger.logFailedQuery(query, parameters);
|
||||
this.logger.logQueryError(err);
|
||||
this.connection.logger.logFailedQuery(query, parameters);
|
||||
this.connection.logger.logQueryError(err);
|
||||
return fail(err);
|
||||
}
|
||||
|
||||
@ -171,18 +170,19 @@ export class OracleQueryRunner implements QueryRunner {
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
const keys = Object.keys(keyValues);
|
||||
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
|
||||
const columns = keys.map(key => this.connection.driver.escapeColumnName(key)).join(", ");
|
||||
const values = keys.map(key => ":" + key).join(", ");
|
||||
const parameters = keys.map(key => keyValues[key]);
|
||||
|
||||
const insertSql = columns.length > 0
|
||||
? `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`
|
||||
: `INSERT INTO ${this.driver.escapeTableName(tableName)} DEFAULT VALUES`;
|
||||
? `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`
|
||||
: `INSERT INTO ${this.connection.driver.escapeTableName(tableName)} DEFAULT VALUES`;
|
||||
if (generatedColumn) {
|
||||
const sql2 = `declare lastId number; begin ${insertSql} returning "id" into lastId; dbms_output.enable; dbms_output.put_line(lastId); dbms_output.get_line(:ln, :st); end;`;
|
||||
const oracle = (this.connection.driver as OracleDriver).oracle;
|
||||
const saveResult = await this.query(sql2, parameters.concat([
|
||||
{ dir: this.driver.oracle.BIND_OUT, type: this.driver.oracle.STRING, maxSize: 32767 },
|
||||
{ dir: this.driver.oracle.BIND_OUT, type: this.driver.oracle.NUMBER }
|
||||
{ dir: oracle.BIND_OUT, type: oracle.STRING, maxSize: 32767 },
|
||||
{ dir: oracle.BIND_OUT, type: oracle.NUMBER }
|
||||
]));
|
||||
return parseInt(saveResult[0]);
|
||||
} else {
|
||||
@ -199,7 +199,7 @@ export class OracleQueryRunner implements QueryRunner {
|
||||
|
||||
const updateValues = this.parametrize(valuesMap).join(", ");
|
||||
const conditionString = this.parametrize(conditions).join(" AND ");
|
||||
const sql = `UPDATE ${this.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
const sql = `UPDATE ${this.connection.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
const conditionParams = Object.keys(conditions).map(key => conditions[key]);
|
||||
const updateParams = Object.keys(valuesMap).map(key => valuesMap[key]);
|
||||
const allParameters = updateParams.concat(conditionParams);
|
||||
@ -226,7 +226,7 @@ export class OracleQueryRunner implements QueryRunner {
|
||||
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
|
||||
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
|
||||
|
||||
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
const sql = `DELETE FROM ${this.connection.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
await this.query(sql, parameters);
|
||||
}
|
||||
|
||||
@ -239,16 +239,16 @@ export class OracleQueryRunner implements QueryRunner {
|
||||
|
||||
let sql = "";
|
||||
if (hasLevel) {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
|
||||
} else {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
|
||||
}
|
||||
await this.query(sql);
|
||||
const results: ObjectLiteral[] = await this.query(`SELECT MAX(level) as level FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId}`);
|
||||
const results: ObjectLiteral[] = await this.query(`SELECT MAX(level) as level FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId}`);
|
||||
return results && results[0] && results[0]["level"] ? parseInt(results[0]["level"]) + 1 : 1;
|
||||
}
|
||||
|
||||
@ -836,7 +836,7 @@ AND cons.constraint_name = cols.constraint_name AND cons.owner = cols.owner ORDE
|
||||
* Truncates table.
|
||||
*/
|
||||
async truncate(tableName: string): Promise<void> {
|
||||
await this.query(`TRUNCATE TABLE ${this.driver.escapeTableName(tableName)}`);
|
||||
await this.query(`TRUNCATE TABLE ${this.connection.driver.escapeTableName(tableName)}`);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -847,14 +847,14 @@ AND cons.constraint_name = cols.constraint_name AND cons.owner = cols.owner ORDE
|
||||
* Database name shortcut.
|
||||
*/
|
||||
protected get dbName(): string {
|
||||
return this.driver.options.schemaName as string;
|
||||
return this.connection.options.schemaName as string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parametrizes given object of values. Used to create column=value queries.
|
||||
*/
|
||||
protected parametrize(objectLiteral: ObjectLiteral): string[] {
|
||||
return Object.keys(objectLiteral).map(key => this.driver.escapeColumnName(key) + "=:" + key);
|
||||
return Object.keys(objectLiteral).map(key => this.connection.driver.escapeColumnName(key) + "=:" + key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
@ -26,25 +27,6 @@ import {LazyRelationsWrapper} from "../../lazy-loading/LazyRelationsWrapper";
|
||||
*/
|
||||
export class PostgresDriver implements Driver {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection where this driver is used.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Used to wrap lazy relations to be able to perform lazy loadings.
|
||||
*/
|
||||
lazyRelationsWrapper: LazyRelationsWrapper;
|
||||
|
||||
/**
|
||||
* Driver connection options.
|
||||
*/
|
||||
readonly options: DriverOptions;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Properties
|
||||
// -------------------------------------------------------------------------
|
||||
@ -79,30 +61,26 @@ export class PostgresDriver implements Driver {
|
||||
* default: "public"
|
||||
*/
|
||||
public schemaName?: string;
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(connectionOptions: DriverOptions, logger: Logger, postgres?: any) {
|
||||
constructor(protected connection: Connection) {
|
||||
|
||||
this.options = DriverUtils.buildDriverOptions(connectionOptions);
|
||||
this.logger = logger;
|
||||
this.postgres = postgres;
|
||||
this.schemaName = connectionOptions.schemaName || "public";
|
||||
Object.assign(connection.options, DriverUtils.buildDriverOptions(connection.options)); // todo: do it better way
|
||||
this.schemaName = connection.options.schemaName || "public";
|
||||
|
||||
// validate options to make sure everything is set
|
||||
if (!this.options.host)
|
||||
if (!connection.options.host)
|
||||
throw new DriverOptionNotSetError("host");
|
||||
if (!this.options.username)
|
||||
if (!connection.options.username)
|
||||
throw new DriverOptionNotSetError("username");
|
||||
if (!this.options.database)
|
||||
if (!connection.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();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -118,16 +96,16 @@ export class PostgresDriver implements Driver {
|
||||
|
||||
// build connection options for the driver
|
||||
const options = Object.assign({}, {
|
||||
host: this.options.host,
|
||||
user: this.options.username,
|
||||
password: this.options.password,
|
||||
database: this.options.database,
|
||||
port: this.options.port
|
||||
}, this.options.extra || {});
|
||||
host: this.connection.options.host,
|
||||
user: this.connection.options.username,
|
||||
password: this.connection.options.password,
|
||||
database: this.connection.options.database,
|
||||
port: this.connection.options.port
|
||||
}, this.connection.options.extra || {});
|
||||
|
||||
// pooling is enabled either when its set explicitly to true,
|
||||
// either when its not defined at all (e.g. enabled by default)
|
||||
if (this.options.usePool === undefined || this.options.usePool === true) {
|
||||
if (this.connection.options.usePool === undefined || this.connection.options.usePool === true) {
|
||||
this.pool = new this.postgres.Pool(options);
|
||||
return Promise.resolve();
|
||||
|
||||
@ -144,8 +122,8 @@ export class PostgresDriver implements Driver {
|
||||
} else {
|
||||
this.databaseConnection!.connection.query(`SET search_path TO '${this.schemaName}', 'public';`, (err: any, result: any) => {
|
||||
if (err) {
|
||||
this.logger.logFailedQuery(`SET search_path TO '${this.schemaName}', 'public';`);
|
||||
this.logger.logQueryError(err);
|
||||
this.connection.logger.logFailedQuery(`SET search_path TO '${this.schemaName}', 'public';`);
|
||||
this.connection.logger.logQueryError(err);
|
||||
fail(err);
|
||||
} else {
|
||||
ok();
|
||||
@ -195,7 +173,7 @@ export class PostgresDriver implements Driver {
|
||||
return Promise.reject(new ConnectionIsNotSetError("postgres"));
|
||||
|
||||
const databaseConnection = await this.retrieveDatabaseConnection();
|
||||
return new PostgresQueryRunner(databaseConnection, this, this.logger);
|
||||
return new PostgresQueryRunner(this.connection, databaseConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -356,8 +334,8 @@ export class PostgresDriver implements Driver {
|
||||
};
|
||||
dbConnection.connection.query(`SET search_path TO '${this.schemaName}', 'public';`, (err: any) => {
|
||||
if (err) {
|
||||
this.logger.logFailedQuery(`SET search_path TO '${this.schemaName}', 'public';`);
|
||||
this.logger.logQueryError(err);
|
||||
this.connection.logger.logFailedQuery(`SET search_path TO '${this.schemaName}', 'public';`);
|
||||
this.connection.logger.logQueryError(err);
|
||||
fail(err);
|
||||
} else {
|
||||
ok(dbConnection);
|
||||
|
||||
@ -14,6 +14,7 @@ import {ForeignKeySchema} from "../../schema-builder/schema/ForeignKeySchema";
|
||||
import {PrimaryKeySchema} from "../../schema-builder/schema/PrimaryKeySchema";
|
||||
import {QueryRunnerAlreadyReleasedError} from "../../query-runner/error/QueryRunnerAlreadyReleasedError";
|
||||
import {ColumnType} from "../../metadata/types/ColumnTypes";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
/**
|
||||
* Runs queries on a single postgres database connection.
|
||||
@ -36,10 +37,9 @@ export class PostgresQueryRunner implements QueryRunner {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected databaseConnection: DatabaseConnection,
|
||||
protected driver: PostgresDriver,
|
||||
protected logger: Logger) {
|
||||
this.schemaName = driver.schemaName || "public";
|
||||
constructor(protected connection: Connection,
|
||||
protected databaseConnection: DatabaseConnection) {
|
||||
this.schemaName = connection.options.schemaName || "public";
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -139,11 +139,11 @@ export class PostgresQueryRunner implements QueryRunner {
|
||||
// console.log("query: ", query);
|
||||
// console.log("parameters: ", parameters);
|
||||
return new Promise<any[]>((ok, fail) => {
|
||||
this.logger.logQuery(query, parameters);
|
||||
this.connection.logger.logQuery(query, parameters);
|
||||
this.databaseConnection.connection.query(query, parameters, (err: any, result: any) => {
|
||||
if (err) {
|
||||
this.logger.logFailedQuery(query, parameters);
|
||||
this.logger.logQueryError(err);
|
||||
this.connection.logger.logFailedQuery(query, parameters);
|
||||
this.connection.logger.logQueryError(err);
|
||||
fail(err);
|
||||
} else {
|
||||
ok(result.rows);
|
||||
@ -160,11 +160,11 @@ export class PostgresQueryRunner implements QueryRunner {
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
const keys = Object.keys(keyValues);
|
||||
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
|
||||
const columns = keys.map(key => this.connection.driver.escapeColumnName(key)).join(", ");
|
||||
const values = keys.map((key, index) => "$" + (index + 1)).join(",");
|
||||
const sql = columns.length > 0
|
||||
? `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values}) ${ generatedColumn ? " RETURNING " + this.driver.escapeColumnName(generatedColumn.databaseName) : "" }`
|
||||
: `INSERT INTO ${this.driver.escapeTableName(tableName)} DEFAULT VALUES ${ generatedColumn ? " RETURNING " + this.driver.escapeColumnName(generatedColumn.databaseName) : "" }`;
|
||||
? `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(${columns}) VALUES (${values}) ${ generatedColumn ? " RETURNING " + this.connection.driver.escapeColumnName(generatedColumn.databaseName) : "" }`
|
||||
: `INSERT INTO ${this.connection.driver.escapeTableName(tableName)} DEFAULT VALUES ${ generatedColumn ? " RETURNING " + this.connection.driver.escapeColumnName(generatedColumn.databaseName) : "" }`;
|
||||
const parameters = keys.map(key => keyValues[key]);
|
||||
const result: ObjectLiteral[] = await this.query(sql, parameters);
|
||||
if (generatedColumn)
|
||||
@ -182,7 +182,7 @@ export class PostgresQueryRunner implements QueryRunner {
|
||||
|
||||
const updateValues = this.parametrize(valuesMap).join(", ");
|
||||
const conditionString = this.parametrize(conditions, Object.keys(valuesMap).length).join(" AND ");
|
||||
const query = `UPDATE ${this.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
const query = `UPDATE ${this.connection.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
const updateParams = Object.keys(valuesMap).map(key => valuesMap[key]);
|
||||
const conditionParams = Object.keys(conditions).map(key => conditions[key]);
|
||||
const allParameters = updateParams.concat(conditionParams);
|
||||
@ -209,7 +209,7 @@ export class PostgresQueryRunner implements QueryRunner {
|
||||
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
|
||||
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
|
||||
|
||||
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
const sql = `DELETE FROM ${this.connection.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
await this.query(sql, parameters);
|
||||
}
|
||||
|
||||
@ -222,12 +222,12 @@ export class PostgresQueryRunner implements QueryRunner {
|
||||
|
||||
let sql = "";
|
||||
if (hasLevel) {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
|
||||
} else {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
|
||||
}
|
||||
await this.query(sql);
|
||||
@ -837,7 +837,7 @@ where constraint_type = 'PRIMARY KEY' AND c.table_schema = '${this.schemaName}'
|
||||
* Truncates table.
|
||||
*/
|
||||
async truncate(tableName: string): Promise<void> {
|
||||
await this.query(`TRUNCATE TABLE ${this.driver.escapeTableName(tableName)}`);
|
||||
await this.query(`TRUNCATE TABLE ${this.connection.driver.escapeTableName(tableName)}`);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -848,14 +848,14 @@ where constraint_type = 'PRIMARY KEY' AND c.table_schema = '${this.schemaName}'
|
||||
* Database name shortcut.
|
||||
*/
|
||||
protected get dbName(): string {
|
||||
return this.driver.options.database as string;
|
||||
return this.connection.options.database as string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parametrizes given object of values. Used to create column=value queries.
|
||||
*/
|
||||
protected parametrize(objectLiteral: ObjectLiteral, startIndex: number = 0): string[] {
|
||||
return Object.keys(objectLiteral).map((key, index) => this.driver.escapeColumnName(key) + "=$" + (startIndex + index + 1));
|
||||
return Object.keys(objectLiteral).map((key, index) => this.connection.driver.escapeColumnName(key) + "=$" + (startIndex + index + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -14,31 +14,13 @@ 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.
|
||||
*/
|
||||
export class SqliteDriver implements Driver {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection where this driver is used.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Used to wrap lazy relations to be able to perform lazy loadings.
|
||||
*/
|
||||
lazyRelationsWrapper: LazyRelationsWrapper;
|
||||
|
||||
/**
|
||||
* Driver connection options.
|
||||
*/
|
||||
readonly options: DriverOptions;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Properties
|
||||
// -------------------------------------------------------------------------
|
||||
@ -53,28 +35,18 @@ export class SqliteDriver implements Driver {
|
||||
*/
|
||||
protected databaseConnection: DatabaseConnection|undefined;
|
||||
|
||||
/**
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(connectionOptions: DriverOptions, logger: Logger, sqlite?: any) {
|
||||
|
||||
this.options = connectionOptions;
|
||||
this.logger = logger;
|
||||
this.sqlite = sqlite;
|
||||
constructor(protected connection: Connection) {
|
||||
|
||||
// validate options to make sure everything is set
|
||||
if (!this.options.storage)
|
||||
if (!connection.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();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -86,7 +58,7 @@ export class SqliteDriver implements Driver {
|
||||
*/
|
||||
connect(): Promise<void> {
|
||||
return new Promise<void>((ok, fail) => {
|
||||
const connection = new this.sqlite.Database(this.options.storage, (err: any) => {
|
||||
const connection = new this.sqlite.Database(this.connection.options.storage, (err: any) => {
|
||||
if (err)
|
||||
return fail(err);
|
||||
|
||||
@ -126,7 +98,7 @@ export class SqliteDriver implements Driver {
|
||||
return Promise.reject(new ConnectionIsNotSetError("sqlite"));
|
||||
|
||||
const databaseConnection = await this.retrieveDatabaseConnection();
|
||||
return new SqliteQueryRunner(databaseConnection, this, this.logger);
|
||||
return new SqliteQueryRunner(this.connection, databaseConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -14,6 +14,8 @@ import {ForeignKeySchema} from "../../schema-builder/schema/ForeignKeySchema";
|
||||
import {PrimaryKeySchema} from "../../schema-builder/schema/PrimaryKeySchema";
|
||||
import {QueryRunnerAlreadyReleasedError} from "../../query-runner/error/QueryRunnerAlreadyReleasedError";
|
||||
import {ColumnType} from "../../metadata/types/ColumnTypes";
|
||||
import {RandomGenerator} from "../../util/RandomGenerator";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
/**
|
||||
* Runs queries on a single sqlite database connection.
|
||||
@ -37,9 +39,8 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected databaseConnection: DatabaseConnection,
|
||||
protected driver: SqliteDriver,
|
||||
protected logger: Logger) {
|
||||
constructor(protected connection: Connection,
|
||||
protected databaseConnection: DatabaseConnection) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -138,11 +139,11 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
return new Promise<any[]>((ok, fail) => {
|
||||
this.logger.logQuery(query, parameters);
|
||||
this.connection.logger.logQuery(query, parameters);
|
||||
this.databaseConnection.connection.all(query, parameters, (err: any, result: any) => {
|
||||
if (err) {
|
||||
this.logger.logFailedQuery(query, parameters);
|
||||
this.logger.logQueryError(err);
|
||||
this.connection.logger.logFailedQuery(query, parameters);
|
||||
this.connection.logger.logQueryError(err);
|
||||
fail(err);
|
||||
} else {
|
||||
ok(result);
|
||||
@ -159,18 +160,18 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
const keys = Object.keys(keyValues);
|
||||
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
|
||||
const columns = keys.map(key => this.connection.driver.escapeColumnName(key)).join(", ");
|
||||
const values = keys.map((key, index) => "$" + (index + 1)).join(",");
|
||||
const sql = columns.length > 0 ? (`INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`) : `INSERT INTO ${this.driver.escapeTableName(tableName)} DEFAULT VALUES`;
|
||||
const sql = columns.length > 0 ? (`INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`) : `INSERT INTO ${this.connection.driver.escapeTableName(tableName)} DEFAULT VALUES`;
|
||||
const parameters = keys.map(key => keyValues[key]);
|
||||
|
||||
return new Promise<any[]>((ok, fail) => {
|
||||
this.logger.logQuery(sql, parameters);
|
||||
this.connection.logger.logQuery(sql, parameters);
|
||||
const __this = this;
|
||||
this.databaseConnection.connection.run(sql, parameters, function (err: any): void {
|
||||
if (err) {
|
||||
__this.logger.logFailedQuery(sql, parameters);
|
||||
__this.logger.logQueryError(err);
|
||||
__this.connection.logger.logFailedQuery(sql, parameters);
|
||||
__this.connection.logger.logQueryError(err);
|
||||
fail(err);
|
||||
} else {
|
||||
if (generatedColumn)
|
||||
@ -191,7 +192,7 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
|
||||
const updateValues = this.parametrize(valuesMap).join(", ");
|
||||
const conditionString = this.parametrize(conditions, Object.keys(valuesMap).length).join(" AND ");
|
||||
const query = `UPDATE ${this.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
const query = `UPDATE ${this.connection.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
const updateParams = Object.keys(valuesMap).map(key => valuesMap[key]);
|
||||
const conditionParams = Object.keys(conditions).map(key => conditions[key]);
|
||||
const allParameters = updateParams.concat(conditionParams);
|
||||
@ -218,7 +219,7 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
|
||||
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
|
||||
|
||||
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
const sql = `DELETE FROM ${this.connection.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
await this.query(sql, parameters);
|
||||
}
|
||||
|
||||
@ -231,12 +232,12 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
|
||||
let sql = "";
|
||||
if (hasLevel) {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
|
||||
} else {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
|
||||
}
|
||||
await this.query(sql);
|
||||
@ -316,7 +317,10 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
const columnForeignKeys = dbForeignKeys
|
||||
.filter(foreignKey => foreignKey["from"] === dbColumn["name"])
|
||||
.map(foreignKey => {
|
||||
const keyName = this.driver.namingStrategy.foreignKeyName(dbTable["name"], [foreignKey["from"]], foreignKey["table"], [foreignKey["to"]]);
|
||||
// const keyName = this.connection.driver.namingStrategy.foreignKeyName(dbTable["name"], [foreignKey["from"]], foreignKey["table"], [foreignKey["to"]]);
|
||||
// todo: figure out solution here, name should be same as naming strategy generates!
|
||||
const key = `${dbTable["name"]}_${[foreignKey["from"]].join("_")}_${foreignKey["table"]}_${[foreignKey["to"]].join("_")}`;
|
||||
const keyName = "fk_" + RandomGenerator.sha1(key).substr(0, 27);
|
||||
return new ForeignKeySchema(keyName, [foreignKey["from"]], [foreignKey["to"]], foreignKey["table"], foreignKey["on_delete"]); // todo: how sqlite return from and to when they are arrays? (multiple column foreign keys)
|
||||
});
|
||||
tableSchema.addForeignKeys(columnForeignKeys);
|
||||
@ -811,7 +815,7 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
* Truncates table.
|
||||
*/
|
||||
async truncate(tableName: string): Promise<void> {
|
||||
await this.query(`DELETE FROM ${this.driver.escapeTableName(tableName)}`);
|
||||
await this.query(`DELETE FROM ${this.connection.driver.escapeTableName(tableName)}`);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -822,7 +826,7 @@ export class SqliteQueryRunner implements QueryRunner {
|
||||
* Parametrizes given object of values. Used to create column=value queries.
|
||||
*/
|
||||
protected parametrize(objectLiteral: ObjectLiteral, startIndex: number = 0): string[] {
|
||||
return Object.keys(objectLiteral).map((key, index) => this.driver.escapeColumnName(key) + "=$" + (startIndex + index + 1));
|
||||
return Object.keys(objectLiteral).map((key, index) => this.connection.driver.escapeColumnName(key) + "=$" + (startIndex + index + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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.
|
||||
@ -25,21 +26,6 @@ export class SqlServerDriver implements Driver {
|
||||
// Public Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection where this driver is used.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Used to wrap lazy relations to be able to perform lazy loadings.
|
||||
*/
|
||||
lazyRelationsWrapper: LazyRelationsWrapper;
|
||||
|
||||
/**
|
||||
* Driver connection options.
|
||||
*/
|
||||
readonly options: DriverOptions;
|
||||
|
||||
/**
|
||||
* SQL Server library.
|
||||
*/
|
||||
@ -57,7 +43,7 @@ export class SqlServerDriver implements Driver {
|
||||
/**
|
||||
* SQL Server pool.
|
||||
*/
|
||||
protected connection: any;
|
||||
protected connectionPool: any;
|
||||
|
||||
/**
|
||||
* Pool of database connections.
|
||||
@ -73,23 +59,20 @@ export class SqlServerDriver implements Driver {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DriverOptions, logger: Logger, mssql?: any) {
|
||||
constructor(protected connection: Connection) {
|
||||
|
||||
this.options = DriverUtils.buildDriverOptions(options);
|
||||
this.logger = logger;
|
||||
this.mssql = mssql;
|
||||
Object.assign(connection.options, DriverUtils.buildDriverOptions(connection.options)); // todo: do it better way
|
||||
|
||||
// validate options to make sure everything is set
|
||||
if (!this.options.host)
|
||||
if (!connection.options.host)
|
||||
throw new DriverOptionNotSetError("host");
|
||||
if (!this.options.username)
|
||||
if (!connection.options.username)
|
||||
throw new DriverOptionNotSetError("username");
|
||||
if (!this.options.database)
|
||||
if (!connection.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();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -105,12 +88,12 @@ export class SqlServerDriver implements Driver {
|
||||
|
||||
// build connection options for the driver
|
||||
const options = Object.assign({}, {
|
||||
server: this.options.host,
|
||||
user: this.options.username,
|
||||
password: this.options.password,
|
||||
database: this.options.database,
|
||||
port: this.options.port
|
||||
}, this.options.extra || {});
|
||||
server: this.connection.options.host,
|
||||
user: this.connection.options.username,
|
||||
password: this.connection.options.password,
|
||||
database: this.connection.options.database,
|
||||
port: this.connection.options.port
|
||||
}, this.connection.options.extra || {});
|
||||
|
||||
// set default useUTC option if it hasn't been set
|
||||
if (!options.options) options.options = { useUTC: false };
|
||||
@ -121,8 +104,8 @@ export class SqlServerDriver implements Driver {
|
||||
return new Promise<void>((ok, fail) => {
|
||||
const connection = new this.mssql.Connection(options).connect((err: any) => {
|
||||
if (err) return fail(err);
|
||||
this.connection = connection;
|
||||
if (this.options.usePool === false) {
|
||||
this.connectionPool = connection;
|
||||
if (this.connection.options.usePool === false) {
|
||||
this.databaseConnection = {
|
||||
id: 1,
|
||||
connection: new this.mssql.Request(connection),
|
||||
@ -138,11 +121,11 @@ export class SqlServerDriver implements Driver {
|
||||
* Closes connection with the database.
|
||||
*/
|
||||
async disconnect(): Promise<void> {
|
||||
if (!this.connection)
|
||||
if (!this.connectionPool)
|
||||
throw new ConnectionIsNotSetError("mssql");
|
||||
|
||||
this.connection.close();
|
||||
this.connection = undefined;
|
||||
this.connectionPool.close();
|
||||
this.connectionPool = undefined;
|
||||
this.databaseConnection = undefined;
|
||||
this.databaseConnectionPool = [];
|
||||
}
|
||||
@ -151,11 +134,11 @@ export class SqlServerDriver implements Driver {
|
||||
* Creates a query runner used for common queries.
|
||||
*/
|
||||
async createQueryRunner(): Promise<QueryRunner> {
|
||||
if (!this.connection)
|
||||
if (!this.connectionPool)
|
||||
return Promise.reject(new ConnectionIsNotSetError("mssql"));
|
||||
|
||||
const databaseConnection = await this.retrieveDatabaseConnection();
|
||||
return new SqlServerQueryRunner(databaseConnection, this, this.logger);
|
||||
return new SqlServerQueryRunner(this.connection, databaseConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -165,7 +148,7 @@ export class SqlServerDriver implements Driver {
|
||||
return {
|
||||
driver: this.mssql,
|
||||
connection: this.databaseConnection ? this.databaseConnection.connection : undefined,
|
||||
pool: this.connection
|
||||
pool: this.connectionPool
|
||||
};
|
||||
}
|
||||
|
||||
@ -286,7 +269,7 @@ export class SqlServerDriver implements Driver {
|
||||
*/
|
||||
protected retrieveDatabaseConnection(): Promise<DatabaseConnection> {
|
||||
|
||||
if (!this.connection)
|
||||
if (!this.connectionPool)
|
||||
throw new ConnectionIsNotSetError("mssql");
|
||||
|
||||
return new Promise((ok, fail) => {
|
||||
@ -309,7 +292,7 @@ export class SqlServerDriver implements Driver {
|
||||
// if (!dbConnection) {
|
||||
let dbConnection: DatabaseConnection = {
|
||||
id: this.databaseConnectionPool.length,
|
||||
connection: this.connection,
|
||||
connection: this.connectionPool,
|
||||
isTransactionActive: false
|
||||
};
|
||||
dbConnection.releaseCallback = () => {
|
||||
|
||||
@ -14,6 +14,8 @@ import {PrimaryKeySchema} from "../../schema-builder/schema/PrimaryKeySchema";
|
||||
import {IndexSchema} from "../../schema-builder/schema/IndexSchema";
|
||||
import {QueryRunnerAlreadyReleasedError} from "../../query-runner/error/QueryRunnerAlreadyReleasedError";
|
||||
import {ColumnType} from "../../metadata/types/ColumnTypes";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
import {MysqlDriver} from "../mysql/MysqlDriver";
|
||||
|
||||
/**
|
||||
* Runs queries on a single mysql database connection.
|
||||
@ -34,11 +36,9 @@ export class SqlServerQueryRunner implements QueryRunner {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected databaseConnection: DatabaseConnection,
|
||||
protected driver: SqlServerDriver,
|
||||
protected logger: Logger) {
|
||||
constructor(protected connection: Connection,
|
||||
protected databaseConnection: DatabaseConnection) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
@ -98,12 +98,12 @@ export class SqlServerQueryRunner implements QueryRunner {
|
||||
//
|
||||
// return new Promise<void>((ok, fail) => {
|
||||
//
|
||||
// const request = new this.driver.mssql.Request(this.isTransactionActive() ? this.databaseConnection.transaction : this.databaseConnection.connection);
|
||||
// const request = new this.connection.driver.mssql.Request(this.isTransactionActive() ? this.databaseConnection.transaction : this.databaseConnection.connection);
|
||||
// request.multiple = true;
|
||||
// request.query(allQueries, (err: any, result: any) => {
|
||||
// if (err) {
|
||||
// this.logger.logFailedQuery(allQueries);
|
||||
// this.logger.logQueryError(err);
|
||||
// this.connection.logger.logFailedQuery(allQueries);
|
||||
// this.connection.logger.logQueryError(err);
|
||||
// return fail(err);
|
||||
// }
|
||||
//
|
||||
@ -195,8 +195,8 @@ export class SqlServerQueryRunner implements QueryRunner {
|
||||
|
||||
return new Promise((ok, fail) => {
|
||||
|
||||
this.logger.logQuery(query, parameters);
|
||||
const request = new this.driver.mssql.Request(this.isTransactionActive() ? this.databaseConnection.transaction : this.databaseConnection.connection);
|
||||
this.connection.logger.logQuery(query, parameters);
|
||||
const request = new (this.connection.driver as SqlServerDriver).mssql.Request(this.isTransactionActive() ? this.databaseConnection.transaction : this.databaseConnection.connection);
|
||||
if (parameters && parameters.length) {
|
||||
parameters.forEach((parameter, index) => {
|
||||
request.input(index, parameters![index]);
|
||||
@ -204,8 +204,8 @@ export class SqlServerQueryRunner implements QueryRunner {
|
||||
}
|
||||
request.query(query, (err: any, result: any) => {
|
||||
if (err) {
|
||||
this.logger.logFailedQuery(query, parameters);
|
||||
this.logger.logQueryError(err);
|
||||
this.connection.logger.logFailedQuery(query, parameters);
|
||||
this.connection.logger.logQueryError(err);
|
||||
return fail(err);
|
||||
}
|
||||
|
||||
@ -222,13 +222,13 @@ export class SqlServerQueryRunner implements QueryRunner {
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
const keys = Object.keys(keyValues);
|
||||
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
|
||||
const columns = keys.map(key => this.connection.driver.escapeColumnName(key)).join(", ");
|
||||
const values = keys.map((key, index) => "@" + index).join(",");
|
||||
const parameters = keys.map(key => keyValues[key]);
|
||||
|
||||
const sql = columns.length > 0
|
||||
? `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) ${ generatedColumn ? "OUTPUT INSERTED." + generatedColumn.databaseName + " " : "" }VALUES (${values})`
|
||||
: `INSERT INTO ${this.driver.escapeTableName(tableName)} ${ generatedColumn ? "OUTPUT INSERTED." + generatedColumn.databaseName + " " : "" }DEFAULT VALUES `;
|
||||
? `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(${columns}) ${ generatedColumn ? "OUTPUT INSERTED." + generatedColumn.databaseName + " " : "" }VALUES (${values})`
|
||||
: `INSERT INTO ${this.connection.driver.escapeTableName(tableName)} ${ generatedColumn ? "OUTPUT INSERTED." + generatedColumn.databaseName + " " : "" }DEFAULT VALUES `;
|
||||
|
||||
const result = await this.query(sql, parameters);
|
||||
return generatedColumn ? result instanceof Array ? result[0][generatedColumn.databaseName] : result[generatedColumn.databaseName] : undefined;
|
||||
@ -247,7 +247,7 @@ export class SqlServerQueryRunner implements QueryRunner {
|
||||
|
||||
const updateValues = this.parametrize(valuesMap).join(", ");
|
||||
const conditionString = this.parametrize(conditions, updateParams.length).join(" AND ");
|
||||
const sql = `UPDATE ${this.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
const sql = `UPDATE ${this.connection.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
|
||||
await this.query(sql, allParameters);
|
||||
}
|
||||
@ -272,7 +272,7 @@ export class SqlServerQueryRunner implements QueryRunner {
|
||||
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
|
||||
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
|
||||
|
||||
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
const sql = `DELETE FROM ${this.connection.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
await this.query(sql, parameters);
|
||||
}
|
||||
|
||||
@ -285,16 +285,16 @@ export class SqlServerQueryRunner implements QueryRunner {
|
||||
|
||||
let sql = "";
|
||||
if (hasLevel) {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
|
||||
} else {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
|
||||
}
|
||||
await this.query(sql);
|
||||
const results: ObjectLiteral[] = await this.query(`SELECT MAX(level) as level FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId}`);
|
||||
const results: ObjectLiteral[] = await this.query(`SELECT MAX(level) as level FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId}`);
|
||||
return results && results[0] && results[0]["level"] ? parseInt(results[0]["level"]) + 1 : 1;
|
||||
}
|
||||
|
||||
@ -870,7 +870,7 @@ WHERE columnUsages.TABLE_CATALOG = '${this.dbName}' AND tableConstraints.TABLE_C
|
||||
* Truncates table.
|
||||
*/
|
||||
async truncate(tableName: string): Promise<void> {
|
||||
await this.query(`TRUNCATE TABLE ${this.driver.escapeTableName(tableName)}`);
|
||||
await this.query(`TRUNCATE TABLE ${this.connection.driver.escapeTableName(tableName)}`);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -881,7 +881,7 @@ WHERE columnUsages.TABLE_CATALOG = '${this.dbName}' AND tableConstraints.TABLE_C
|
||||
* Database name shortcut.
|
||||
*/
|
||||
protected get dbName(): string {
|
||||
return this.driver.options.database as string;
|
||||
return this.connection.options.database as string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -889,7 +889,7 @@ WHERE columnUsages.TABLE_CATALOG = '${this.dbName}' AND tableConstraints.TABLE_C
|
||||
*/
|
||||
protected parametrize(objectLiteral: ObjectLiteral, startFrom: number = 0): string[] {
|
||||
return Object.keys(objectLiteral).map((key, index) => {
|
||||
return this.driver.escapeColumnName(key) + "=@" + (startFrom + index);
|
||||
return this.connection.driver.escapeColumnName(key) + "=@" + (startFrom + index);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -11,8 +11,7 @@ import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {DriverOptionNotSetError} from "../error/DriverOptionNotSetError";
|
||||
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.
|
||||
@ -24,25 +23,6 @@ declare function openDatabase(...params: any[]): any;
|
||||
*/
|
||||
export class WebsqlDriver implements Driver {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection where this driver is used.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Used to wrap lazy relations to be able to perform lazy loadings.
|
||||
*/
|
||||
lazyRelationsWrapper: LazyRelationsWrapper;
|
||||
|
||||
/**
|
||||
* Driver connection options.
|
||||
*/
|
||||
readonly options: DriverOptions;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Properties
|
||||
// -------------------------------------------------------------------------
|
||||
@ -52,26 +32,20 @@ export class WebsqlDriver implements Driver {
|
||||
*/
|
||||
protected databaseConnection: DatabaseConnection|undefined;
|
||||
|
||||
/**
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DriverOptions, logger: Logger) {
|
||||
constructor(protected connection: Connection) {
|
||||
|
||||
this.options = DriverUtils.buildDriverOptions(options);
|
||||
this.logger = logger;
|
||||
Object.assign(connection.options, DriverUtils.buildDriverOptions(connection.options)); // todo: do it better way
|
||||
|
||||
// validate options to make sure everything is set
|
||||
// if (!this.options.host)
|
||||
// throw new DriverOptionNotSetError("host");
|
||||
// if (!this.options.username)
|
||||
// throw new DriverOptionNotSetError("username");
|
||||
if (!this.options.database)
|
||||
if (!connection.options.database)
|
||||
throw new DriverOptionNotSetError("database");
|
||||
// todo: what about extra options: version, description, size
|
||||
}
|
||||
@ -89,8 +63,8 @@ export class WebsqlDriver implements Driver {
|
||||
|
||||
// build connection options for the driver
|
||||
const options = Object.assign({}, {
|
||||
database: this.options.database,
|
||||
}, this.options.extra || {});
|
||||
database: this.connection.options.database,
|
||||
}, this.connection.options.extra || {});
|
||||
|
||||
return new Promise<void>((ok, fail) => {
|
||||
const connection = openDatabase(
|
||||
@ -130,7 +104,7 @@ export class WebsqlDriver implements Driver {
|
||||
return Promise.reject(new ConnectionIsNotSetError("websql"));
|
||||
|
||||
const databaseConnection = await this.retrieveDatabaseConnection();
|
||||
return new WebsqlQueryRunner(databaseConnection, this, this.logger);
|
||||
return new WebsqlQueryRunner(this.connection, databaseConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -13,6 +13,7 @@ import {IndexSchema} from "../../schema-builder/schema/IndexSchema";
|
||||
import {QueryRunnerAlreadyReleasedError} from "../../query-runner/error/QueryRunnerAlreadyReleasedError";
|
||||
import {WebsqlDriver} from "./WebsqlDriver";
|
||||
import {ColumnType} from "../../metadata/types/ColumnTypes";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
|
||||
/**
|
||||
* Runs queries on a single websql database connection.
|
||||
@ -33,9 +34,8 @@ export class WebsqlQueryRunner implements QueryRunner {
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected databaseConnection: DatabaseConnection,
|
||||
protected driver: WebsqlDriver,
|
||||
protected logger: Logger) {
|
||||
constructor(protected connection: Connection,
|
||||
protected databaseConnection: DatabaseConnection) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -139,7 +139,7 @@ export class WebsqlQueryRunner implements QueryRunner {
|
||||
|
||||
return new Promise((ok, fail) => {
|
||||
|
||||
this.logger.logQuery(query, parameters);
|
||||
this.connection.logger.logQuery(query, parameters);
|
||||
const db = this.databaseConnection.connection;
|
||||
// todo: check if transaction is not active
|
||||
db.transaction((tx: any) => {
|
||||
@ -151,8 +151,8 @@ export class WebsqlQueryRunner implements QueryRunner {
|
||||
ok(rows);
|
||||
|
||||
}, (tx: any, err: any) => {
|
||||
this.logger.logFailedQuery(query, parameters);
|
||||
this.logger.logQueryError(err);
|
||||
this.connection.logger.logFailedQuery(query, parameters);
|
||||
this.connection.logger.logQueryError(err);
|
||||
return fail(err);
|
||||
});
|
||||
});
|
||||
@ -167,13 +167,13 @@ export class WebsqlQueryRunner implements QueryRunner {
|
||||
throw new QueryRunnerAlreadyReleasedError();
|
||||
|
||||
const keys = Object.keys(keyValues);
|
||||
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
|
||||
const columns = keys.map(key => this.connection.driver.escapeColumnName(key)).join(", ");
|
||||
const values = keys.map((key, index) => "$" + (index + 1)).join(",");
|
||||
const sql = columns.length > 0 ? (`INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`) : `INSERT INTO ${this.driver.escapeTableName(tableName)} DEFAULT VALUES`;
|
||||
const sql = columns.length > 0 ? (`INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`) : `INSERT INTO ${this.connection.driver.escapeTableName(tableName)} DEFAULT VALUES`;
|
||||
const parameters = keys.map(key => keyValues[key]);
|
||||
|
||||
return new Promise<any[]>((ok, fail) => {
|
||||
this.logger.logQuery(sql, parameters);
|
||||
this.connection.logger.logQuery(sql, parameters);
|
||||
|
||||
const db = this.databaseConnection.connection;
|
||||
// todo: check if transaction is not active
|
||||
@ -184,8 +184,8 @@ export class WebsqlQueryRunner implements QueryRunner {
|
||||
ok();
|
||||
|
||||
}, (tx: any, err: any) => {
|
||||
this.logger.logFailedQuery(sql, parameters);
|
||||
this.logger.logQueryError(err);
|
||||
this.connection.logger.logFailedQuery(sql, parameters);
|
||||
this.connection.logger.logQueryError(err);
|
||||
return fail(err);
|
||||
});
|
||||
});
|
||||
@ -201,7 +201,7 @@ export class WebsqlQueryRunner implements QueryRunner {
|
||||
|
||||
const updateValues = this.parametrize(valuesMap).join(", ");
|
||||
const conditionString = this.parametrize(conditions, Object.keys(valuesMap).length).join(" AND ");
|
||||
const query = `UPDATE ${this.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
const query = `UPDATE ${this.connection.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
|
||||
const updateParams = Object.keys(valuesMap).map(key => valuesMap[key]);
|
||||
const conditionParams = Object.keys(conditions).map(key => conditions[key]);
|
||||
const allParameters = updateParams.concat(conditionParams);
|
||||
@ -228,7 +228,7 @@ export class WebsqlQueryRunner implements QueryRunner {
|
||||
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
|
||||
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
|
||||
|
||||
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
const sql = `DELETE FROM ${this.connection.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
|
||||
await this.query(sql, parameters);
|
||||
}
|
||||
|
||||
@ -241,12 +241,12 @@ export class WebsqlQueryRunner implements QueryRunner {
|
||||
|
||||
let sql = "";
|
||||
if (hasLevel) {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
|
||||
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
|
||||
} else {
|
||||
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
sql = `INSERT INTO ${this.connection.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
|
||||
`SELECT ancestor, ${newEntityId} FROM ${this.connection.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
|
||||
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
|
||||
}
|
||||
await this.query(sql);
|
||||
@ -821,7 +821,7 @@ export class WebsqlQueryRunner implements QueryRunner {
|
||||
* Truncates table.
|
||||
*/
|
||||
async truncate(tableName: string): Promise<void> {
|
||||
await this.query(`DELETE FROM ${this.driver.escapeTableName(tableName)}`);
|
||||
await this.query(`DELETE FROM ${this.connection.driver.escapeTableName(tableName)}`);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -832,7 +832,7 @@ export class WebsqlQueryRunner implements QueryRunner {
|
||||
* Parametrizes given object of values. Used to create column=value queries.
|
||||
*/
|
||||
protected parametrize(objectLiteral: ObjectLiteral, startIndex: number = 0): string[] {
|
||||
return Object.keys(objectLiteral).map((key, index) => this.driver.escapeColumnName(key) + "=$" + (startIndex + index + 1));
|
||||
return Object.keys(objectLiteral).map((key, index) => this.connection.driver.escapeColumnName(key) + "=$" + (startIndex + index + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -8,13 +8,11 @@ import {FindOneOptions} from "../find-options/FindOneOptions";
|
||||
import {DeepPartial} from "../common/DeepPartial";
|
||||
import {RemoveOptions} from "../repository/RemoveOptions";
|
||||
import {SaveOptions} from "../repository/SaveOptions";
|
||||
import {RepositoryAggregator} from "../repository/RepositoryAggregator";
|
||||
import {NoNeedToReleaseEntityManagerError} from "./error/NoNeedToReleaseEntityManagerError";
|
||||
import {SpecificRepository} from "../repository/SpecificRepository";
|
||||
import {MongoRepository} from "../repository/MongoRepository";
|
||||
import {TreeRepository} from "../repository/TreeRepository";
|
||||
import {Repository} from "../repository/Repository";
|
||||
import {RepositoryNotTreeError} from "../connection/error/RepositoryNotTreeError";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {FindOptionsUtils} from "../find-options/FindOptionsUtils";
|
||||
import {SubjectBuilder} from "../persistence/SubjectBuilder";
|
||||
@ -42,11 +40,6 @@ export class EntityManager {
|
||||
*/
|
||||
private data: ObjectLiteral = {};
|
||||
|
||||
/**
|
||||
* Stores all registered repositories.
|
||||
*/
|
||||
private repositoryAggregators: RepositoryAggregator[] = [];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
@ -63,6 +56,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 +150,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 +551,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).
|
||||
*/
|
||||
@ -602,8 +575,12 @@ export class EntityManager {
|
||||
getRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): Repository<Entity> {
|
||||
|
||||
// if single db connection is used then create its own repository with reused query runner
|
||||
if (this.queryRunnerProvider)
|
||||
return this.obtainRepositoryAggregator(entityClassOrName as any).repository;
|
||||
if (this.queryRunnerProvider) {
|
||||
if (this.queryRunnerProvider.isReleased)
|
||||
throw new QueryRunnerProviderAlreadyReleasedError();
|
||||
|
||||
return this.connection.createIsolatedRepository(entityClassOrName, this.queryRunnerProvider);
|
||||
}
|
||||
|
||||
return this.connection.getRepository<Entity>(entityClassOrName as any);
|
||||
}
|
||||
@ -618,11 +595,10 @@ export class EntityManager {
|
||||
|
||||
// if single db connection is used then create its own repository with reused query runner
|
||||
if (this.queryRunnerProvider) {
|
||||
const treeRepository = this.obtainRepositoryAggregator(entityClassOrName).treeRepository;
|
||||
if (!treeRepository)
|
||||
throw new RepositoryNotTreeError(entityClassOrName);
|
||||
if (this.queryRunnerProvider.isReleased)
|
||||
throw new QueryRunnerProviderAlreadyReleasedError();
|
||||
|
||||
return treeRepository;
|
||||
return this.connection.createIsolatedRepository(entityClassOrName, this.queryRunnerProvider) as TreeRepository<Entity>;
|
||||
}
|
||||
|
||||
return this.connection.getTreeRepository<Entity>(entityClassOrName as any);
|
||||
@ -644,8 +620,12 @@ export class EntityManager {
|
||||
getMongoRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): MongoRepository<Entity> {
|
||||
|
||||
// if single db connection is used then create its own repository with reused query runner
|
||||
if (this.queryRunnerProvider)
|
||||
return this.obtainRepositoryAggregator(entityClassOrName as any).repository as MongoRepository<Entity>;
|
||||
if (this.queryRunnerProvider) {
|
||||
if (this.queryRunnerProvider.isReleased)
|
||||
throw new QueryRunnerProviderAlreadyReleasedError();
|
||||
|
||||
return this.connection.createIsolatedRepository(entityClassOrName, this.queryRunnerProvider) as MongoRepository<Entity>;
|
||||
}
|
||||
|
||||
return this.connection.getMongoRepository<Entity>(entityClassOrName as any);
|
||||
}
|
||||
@ -681,8 +661,12 @@ export class EntityManager {
|
||||
getSpecificRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificRepository<Entity> {
|
||||
|
||||
// if single db connection is used then create its own repository with reused query runner
|
||||
if (this.queryRunnerProvider)
|
||||
return this.obtainRepositoryAggregator(entityClassOrName).specificRepository;
|
||||
if (this.queryRunnerProvider) {
|
||||
if (this.queryRunnerProvider.isReleased)
|
||||
throw new QueryRunnerProviderAlreadyReleasedError();
|
||||
|
||||
return this.connection.createIsolatedSpecificRepository(entityClassOrName, this.queryRunnerProvider);
|
||||
}
|
||||
|
||||
return this.connection.getSpecificRepository<Entity>(entityClassOrName as any);
|
||||
}
|
||||
@ -741,7 +725,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 +747,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);
|
||||
@ -777,25 +761,4 @@ export class EntityManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets, or if does not exist yet, creates and returns a repository aggregator for a particular entity target.
|
||||
*/
|
||||
protected obtainRepositoryAggregator<Entity>(entityClassOrName: ObjectType<Entity>|string): RepositoryAggregator {
|
||||
if (this.queryRunnerProvider && this.queryRunnerProvider.isReleased)
|
||||
throw new QueryRunnerProviderAlreadyReleasedError();
|
||||
|
||||
const metadata = this.connection.getMetadata(entityClassOrName);
|
||||
let repositoryAggregator = this.repositoryAggregators.find(repositoryAggregate => repositoryAggregate.metadata === metadata);
|
||||
if (!repositoryAggregator) {
|
||||
repositoryAggregator = new RepositoryAggregator(
|
||||
this.connection,
|
||||
this.connection.getMetadata(entityClassOrName as any),
|
||||
this.queryRunnerProvider
|
||||
);
|
||||
this.repositoryAggregators.push(repositoryAggregator); // todo: check isnt memory leak here?
|
||||
}
|
||||
|
||||
return repositoryAggregator;
|
||||
}
|
||||
|
||||
}
|
||||
22
src/entity-manager/EntityManagerFactory.ts
Normal file
22
src/entity-manager/EntityManagerFactory.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {EntityManager} from "./EntityManager";
|
||||
import {MongoEntityManager} from "./MongoEntityManager";
|
||||
import {MongoDriver} from "../driver/mongodb/MongoDriver";
|
||||
import {QueryRunnerProvider} from "../query-runner/QueryRunnerProvider";
|
||||
|
||||
/**
|
||||
* Helps to create entity managers.
|
||||
*/
|
||||
export class EntityManagerFactory {
|
||||
|
||||
/**
|
||||
* Creates a new entity manager depend on a given connection's driver.
|
||||
*/
|
||||
create(connection: Connection, queryRunnerProvider?: QueryRunnerProvider): EntityManager {
|
||||
if (connection.driver instanceof MongoDriver)
|
||||
return new MongoEntityManager(connection, queryRunnerProvider);
|
||||
|
||||
return new EntityManager(connection, queryRunnerProvider);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -65,7 +65,6 @@ export * from "./decorator/tree/TreeLevelColumn";
|
||||
export * from "./decorator/tree/TreeParent";
|
||||
export * from "./decorator/tree/TreeChildren";
|
||||
export * from "./decorator/Index";
|
||||
export * from "./decorator/NamingStrategy";
|
||||
export * from "./decorator/Embedded";
|
||||
export * from "./decorator/DiscriminatorValue";
|
||||
export * from "./decorator/EntityRepository";
|
||||
|
||||
@ -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 "))
|
||||
|
||||
16
src/logger/LoggerFactory.ts
Normal file
16
src/logger/LoggerFactory.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {LoggerOptions} from "./LoggerOptions";
|
||||
import {Logger} from "./Logger";
|
||||
|
||||
/**
|
||||
* Helps to create logger instances.
|
||||
*/
|
||||
export class LoggerFactory {
|
||||
|
||||
/**
|
||||
* Creates a new logger depend on a given connection's driver.
|
||||
*/
|
||||
create(options: LoggerOptions): Logger {
|
||||
return new Logger(options);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
@ -157,7 +160,7 @@ export class EntityMetadataBuilder {
|
||||
|
||||
// build all indices (need to do it after relations and their join columns are built)
|
||||
entityMetadatas.forEach(entityMetadata => {
|
||||
entityMetadata.indices.forEach(index => index.build(this.connection.driver.namingStrategy));
|
||||
entityMetadata.indices.forEach(index => index.build(this.connection.namingStrategy));
|
||||
});
|
||||
|
||||
entityMetadatas
|
||||
@ -165,7 +168,7 @@ export class EntityMetadataBuilder {
|
||||
.forEach(metadata => {
|
||||
const parentPrimaryColumns = metadata.parentEntityMetadata.primaryColumns;
|
||||
const parentRelationColumns = parentPrimaryColumns.map(parentPrimaryColumn => {
|
||||
const columnName = this.connection.driver.namingStrategy.classTableInheritanceParentColumnName(metadata.parentEntityMetadata.tableName, parentPrimaryColumn.propertyPath);
|
||||
const columnName = this.connection.namingStrategy.classTableInheritanceParentColumnName(metadata.parentEntityMetadata.tableName, parentPrimaryColumn.propertyPath);
|
||||
const column = new ColumnMetadata({
|
||||
entityMetadata: metadata,
|
||||
referencedColumn: parentPrimaryColumn,
|
||||
@ -183,7 +186,7 @@ export class EntityMetadataBuilder {
|
||||
}
|
||||
});
|
||||
metadata.registerColumn(column);
|
||||
column.build(this.connection.driver.namingStrategy);
|
||||
column.build(this.connection.namingStrategy);
|
||||
return column;
|
||||
});
|
||||
|
||||
@ -191,7 +194,7 @@ export class EntityMetadataBuilder {
|
||||
new ForeignKeyMetadata({
|
||||
entityMetadata: metadata,
|
||||
referencedEntityMetadata: metadata.parentEntityMetadata,
|
||||
namingStrategy: this.connection.driver.namingStrategy,
|
||||
namingStrategy: this.connection.namingStrategy,
|
||||
columns: parentRelationColumns,
|
||||
referencedColumns: parentPrimaryColumns,
|
||||
onDelete: "CASCADE"
|
||||
@ -206,7 +209,8 @@ export class EntityMetadataBuilder {
|
||||
entityMetadata.relations
|
||||
.filter(relation => relation.isLazy)
|
||||
.forEach(relation => {
|
||||
this.connection.driver.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;
|
||||
@ -307,12 +314,12 @@ export class EntityMetadataBuilder {
|
||||
* Computes all entity metadata's computed properties, and all its sub-metadatas (relations, columns, embeds, etc).
|
||||
*/
|
||||
protected computeEntityMetadata(entityMetadata: EntityMetadata) {
|
||||
entityMetadata.embeddeds.forEach(embedded => embedded.build(this.connection.driver.namingStrategy));
|
||||
entityMetadata.embeddeds.forEach(embedded => embedded.build(this.connection.namingStrategy));
|
||||
entityMetadata.embeddeds.forEach(embedded => {
|
||||
embedded.columnsFromTree.forEach(column => column.build(this.connection.driver.namingStrategy));
|
||||
embedded.columnsFromTree.forEach(column => column.build(this.connection.namingStrategy));
|
||||
embedded.relationsFromTree.forEach(relation => relation.build());
|
||||
});
|
||||
entityMetadata.ownColumns.forEach(column => column.build(this.connection.driver.namingStrategy));
|
||||
entityMetadata.ownColumns.forEach(column => column.build(this.connection.namingStrategy));
|
||||
entityMetadata.ownRelations.forEach(relation => relation.build());
|
||||
entityMetadata.relations = entityMetadata.embeddeds.reduce((relations, embedded) => relations.concat(embedded.relationsFromTree), entityMetadata.ownRelations);
|
||||
entityMetadata.oneToOneRelations = entityMetadata.relations.filter(relation => relation.isOneToOne);
|
||||
@ -334,7 +341,7 @@ export class EntityMetadataBuilder {
|
||||
entityMetadata.treeLevelColumn = entityMetadata.columns.find(column => column.isTreeLevel);
|
||||
entityMetadata.parentIdColumns = entityMetadata.columns.filter(column => column.isParentId);
|
||||
entityMetadata.objectIdColumn = entityMetadata.columns.find(column => column.isObjectId);
|
||||
entityMetadata.foreignKeys.forEach(foreignKey => foreignKey.build(this.connection.driver.namingStrategy));
|
||||
entityMetadata.foreignKeys.forEach(foreignKey => foreignKey.build(this.connection.namingStrategy));
|
||||
entityMetadata.propertiesMap = entityMetadata.createPropertiesMap();
|
||||
entityMetadata.relationIds.forEach(relationId => relationId.build());
|
||||
entityMetadata.relationCounts.forEach(relationCount => relationCount.build());
|
||||
|
||||
@ -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.
|
||||
@ -30,7 +31,7 @@ export class JunctionEntityMetadataBuilder {
|
||||
const referencedColumns = this.collectReferencedColumns(relation, joinTable);
|
||||
const inverseReferencedColumns = this.collectInverseReferencedColumns(relation, joinTable);
|
||||
|
||||
const joinTableName = joinTable.name || this.connection.driver.namingStrategy.joinTableName(
|
||||
const joinTableName = joinTable.name || this.connection.namingStrategy.joinTableName(
|
||||
relation.entityMetadata.tableNameWithoutPrefix,
|
||||
relation.inverseEntityMetadata.tableNameWithoutPrefix,
|
||||
relation.propertyPath,
|
||||
@ -52,7 +53,7 @@ export class JunctionEntityMetadataBuilder {
|
||||
return (!joinColumnArgs.referencedColumnName || joinColumnArgs.referencedColumnName === referencedColumn.propertyName) &&
|
||||
!!joinColumnArgs.name;
|
||||
}) : undefined;
|
||||
const columnName = joinColumn && joinColumn.name ? joinColumn.name : this.connection.driver.namingStrategy.joinTableColumnName(relation.entityMetadata.tableNameWithoutPrefix, referencedColumn.propertyName, referencedColumn.databaseName);
|
||||
const columnName = joinColumn && joinColumn.name ? joinColumn.name : this.connection.namingStrategy.joinTableColumnName(relation.entityMetadata.tableNameWithoutPrefix, referencedColumn.propertyName, referencedColumn.databaseName);
|
||||
|
||||
return new ColumnMetadata({
|
||||
entityMetadata: entityMetadata,
|
||||
@ -78,7 +79,7 @@ export class JunctionEntityMetadataBuilder {
|
||||
return (!joinColumnArgs.referencedColumnName || joinColumnArgs.referencedColumnName === inverseReferencedColumn.propertyName) &&
|
||||
!!joinColumnArgs.name;
|
||||
}) : undefined;
|
||||
const columnName = joinColumn && joinColumn.name ? joinColumn.name : this.connection.driver.namingStrategy.joinTableColumnName(relation.inverseEntityMetadata.tableNameWithoutPrefix, inverseReferencedColumn.propertyName, inverseReferencedColumn.databaseName);
|
||||
const columnName = joinColumn && joinColumn.name ? joinColumn.name : this.connection.namingStrategy.joinTableColumnName(relation.inverseEntityMetadata.tableNameWithoutPrefix, inverseReferencedColumn.propertyName, inverseReferencedColumn.databaseName);
|
||||
|
||||
return new ColumnMetadata({
|
||||
entityMetadata: entityMetadata,
|
||||
|
||||
@ -59,7 +59,7 @@ export class RelationJoinColumnBuilder {
|
||||
return new ForeignKeyMetadata({
|
||||
entityMetadata: relation.entityMetadata,
|
||||
referencedEntityMetadata: relation.inverseEntityMetadata,
|
||||
namingStrategy: this.connection.driver.namingStrategy,
|
||||
namingStrategy: this.connection.namingStrategy,
|
||||
columns: columns,
|
||||
referencedColumns: referencedColumns,
|
||||
onDelete: relation.onDelete,
|
||||
@ -102,7 +102,7 @@ export class RelationJoinColumnBuilder {
|
||||
return (!joinColumn.referencedColumnName || joinColumn.referencedColumnName === referencedColumn.propertyName) &&
|
||||
!!joinColumn.name;
|
||||
});
|
||||
const joinColumnName = joinColumnMetadataArg ? joinColumnMetadataArg.name : this.connection.driver.namingStrategy.joinColumnName(relation.propertyName, referencedColumn.propertyName);
|
||||
const joinColumnName = joinColumnMetadataArg ? joinColumnMetadataArg.name : this.connection.namingStrategy.joinColumnName(relation.propertyName, referencedColumn.propertyName);
|
||||
|
||||
let relationalColumn = relation.entityMetadata.ownColumns.find(column => column.databaseName === joinColumnName);
|
||||
if (!relationalColumn) {
|
||||
@ -125,7 +125,7 @@ export class RelationJoinColumnBuilder {
|
||||
relationalColumn.referencedColumn = referencedColumn; // its important to set it here because we need to set referenced column for user defined join column
|
||||
relationalColumn.type = referencedColumn.type; // also since types of relational column and join column must be equal we override user defined column type
|
||||
relationalColumn.relationMetadata = relation;
|
||||
relationalColumn.build(this.connection.driver.namingStrategy);
|
||||
relationalColumn.build(this.connection.namingStrategy);
|
||||
return relationalColumn;
|
||||
});
|
||||
}
|
||||
|
||||
@ -14,6 +14,8 @@ import {TableMetadataArgs} from "../metadata-args/TableMetadataArgs";
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {EntityListenerMetadata} from "./EntityListenerMetadata";
|
||||
import {PropertyTypeFactory} from "./types/PropertyTypeInFunction";
|
||||
import {Repository} from "../repository/Repository";
|
||||
import {SpecificRepository} from "../repository/SpecificRepository";
|
||||
|
||||
/**
|
||||
* Contains all entity metadata.
|
||||
@ -24,6 +26,16 @@ export class EntityMetadata {
|
||||
// Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Repository used for this entity metadata.
|
||||
*/
|
||||
repository: Repository<any>;
|
||||
|
||||
/**
|
||||
* Specific repository used for this entity metadata.
|
||||
*/
|
||||
specificRepository: SpecificRepository<any>;
|
||||
|
||||
/**
|
||||
* Used to wrap lazy relations.
|
||||
*/
|
||||
@ -342,9 +354,9 @@ export class EntityMetadata {
|
||||
parentClosureEntityMetadata?: EntityMetadata,
|
||||
args: TableMetadataArgs
|
||||
}) {
|
||||
const namingStrategy = options.connection.driver.namingStrategy;
|
||||
const tablesPrefix = options.connection.driver.options.tablesPrefix;
|
||||
this.lazyRelationsWrapper = options.connection.driver.lazyRelationsWrapper;
|
||||
const namingStrategy = options.connection.namingStrategy;
|
||||
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)
|
||||
|
||||
@ -13,7 +13,7 @@ export class DefaultNamingStrategy implements NamingStrategyInterface {
|
||||
* @param targetName Name of the target entity that can be used to generate a table name.
|
||||
* @param userSpecifiedName For example if user specified a table name in a decorator, e.g. @Entity("name")
|
||||
*/
|
||||
tableName(targetName: string, userSpecifiedName: string): string {
|
||||
tableName(targetName: string, userSpecifiedName: string|undefined): string {
|
||||
return userSpecifiedName ? userSpecifiedName : snakeCase(targetName);
|
||||
}
|
||||
|
||||
|
||||
@ -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,7 +25,8 @@ 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 { OracleDriver } from "../driver/oracle/OracleDriver";
|
||||
import {OracleDriver} from "../driver/oracle/OracleDriver";
|
||||
import {Broadcaster} from "../subscriber/Broadcaster";
|
||||
|
||||
|
||||
// todo: fix problem with long aliases eg getMaxIdentifierLength
|
||||
@ -197,31 +198,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;
|
||||
@ -904,6 +893,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);
|
||||
@ -1000,8 +990,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 {
|
||||
@ -1019,7 +1010,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,
|
||||
@ -1156,7 +1147,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;
|
||||
}
|
||||
@ -1412,11 +1403,11 @@ export class QueryBuilder<Entity> {
|
||||
const updateSet = Object.keys(this.expressionMap.updateSet).map(key => key + "=:updateSet__" + key);
|
||||
const params = Object.keys(this.expressionMap.updateSet).reduce((object, key) => {
|
||||
// todo: map propertyNames to names ?
|
||||
object["updateSet_" + key] = this.expressionMap.updateSet![key];
|
||||
object["updateSet__" + key] = this.expressionMap.updateSet![key];
|
||||
return object;
|
||||
}, {} as ObjectLiteral);
|
||||
this.setParameters(params);
|
||||
return "UPDATE " + tableName + " " + (aliasName ? ea(aliasName) : "") + " SET " + updateSet;
|
||||
return "UPDATE " + this.escapeTable(tableName) + " " + (aliasName ? ea(aliasName) : "") + " SET " + updateSet;
|
||||
}
|
||||
|
||||
throw new Error("No query builder type is specified.");
|
||||
|
||||
@ -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 {
|
||||
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
import {Repository} from "./Repository";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {SpecificRepository} from "./SpecificRepository";
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {TreeRepository} from "./TreeRepository";
|
||||
import {QueryRunnerProvider} from "../query-runner/QueryRunnerProvider";
|
||||
import {RepositoryFactory} from "./RepositoryFactory";
|
||||
import {getFromContainer} from "../container";
|
||||
|
||||
/**
|
||||
* Aggregates all repositories of the specific metadata.
|
||||
*/
|
||||
export class RepositoryAggregator {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Readonly properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Entity metadata which owns repositories.
|
||||
*/
|
||||
public readonly metadata: EntityMetadata;
|
||||
|
||||
/**
|
||||
* Ordinary repository.
|
||||
*/
|
||||
public readonly repository: Repository<any>;
|
||||
|
||||
/**
|
||||
* Tree repository.
|
||||
*/
|
||||
public readonly treeRepository?: TreeRepository<any>;
|
||||
|
||||
/**
|
||||
* Repository with specific functions.
|
||||
*/
|
||||
public readonly specificRepository: SpecificRepository<any>;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider) {
|
||||
this.metadata = metadata;
|
||||
|
||||
const factory = getFromContainer(RepositoryFactory);
|
||||
|
||||
if (metadata.isClosure) {
|
||||
this.repository = this.treeRepository = factory.createTreeRepository(connection.manager, metadata, queryRunnerProvider);
|
||||
} else {
|
||||
this.repository = factory.createRepository(connection.manager, metadata, queryRunnerProvider);
|
||||
}
|
||||
|
||||
this.specificRepository = factory.createSpecificRepository(connection, metadata, queryRunnerProvider);
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,34 +20,32 @@ export class RepositoryFactory {
|
||||
/**
|
||||
* Creates a regular repository.
|
||||
*/
|
||||
createRepository(manager: EntityManager, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider): Repository<any> {
|
||||
createRepository(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider): Repository<any> {
|
||||
|
||||
if (metadata.isClosure) {
|
||||
// NOTE: dynamic access to protected properties. We need this to prevent unwanted properties in those classes to be exposed,
|
||||
// however we need these properties for internal work of the class
|
||||
const repository = new TreeRepository<any>();
|
||||
(repository as any)["manager"] = connection.manager;
|
||||
(repository as any)["metadata"] = metadata;
|
||||
(repository as any)["queryRunnerProvider"] = queryRunnerProvider;
|
||||
return repository;
|
||||
|
||||
// NOTE: dynamic access to protected properties. We need this to prevent unwanted properties in those classes to be exposed,
|
||||
// however we need these properties for internal work of the class
|
||||
let repository: Repository<any>;
|
||||
if (manager.connection.driver instanceof MongoDriver) {
|
||||
repository = new MongoRepository();
|
||||
} else {
|
||||
repository = new Repository<any>();
|
||||
// NOTE: dynamic access to protected properties. We need this to prevent unwanted properties in those classes to be exposed,
|
||||
// however we need these properties for internal work of the class
|
||||
let repository: Repository<any>;
|
||||
if (connection.driver instanceof MongoDriver) {
|
||||
repository = new MongoRepository();
|
||||
} else {
|
||||
repository = new Repository<any>();
|
||||
}
|
||||
(repository as any)["manager"] = connection.manager;
|
||||
(repository as any)["metadata"] = metadata;
|
||||
(repository as any)["queryRunnerProvider"] = queryRunnerProvider;
|
||||
|
||||
return repository;
|
||||
}
|
||||
(repository as any)["manager"] = manager;
|
||||
(repository as any)["metadata"] = metadata;
|
||||
(repository as any)["queryRunnerProvider"] = queryRunnerProvider;
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tree repository.
|
||||
*/
|
||||
createTreeRepository(manager: EntityManager, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider): TreeRepository<any> {
|
||||
|
||||
// NOTE: dynamic access to protected properties. We need this to prevent unwanted properties in those classes to be exposed,
|
||||
// however we need these properties for internal work of the class
|
||||
const repository = new TreeRepository<any>();
|
||||
(repository as any)["manager"] = manager;
|
||||
(repository as any)["metadata"] = metadata;
|
||||
(repository as any)["queryRunnerProvider"] = queryRunnerProvider;
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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 splitClassesAndStrings<T>(clsesAndStrings: T[]|string[]): [T[], string[]] {
|
||||
return [
|
||||
(clsesAndStrings as T[]).filter(cls => typeof cls !== "string"),
|
||||
(clsesAndStrings as string[]).filter(str => typeof str === "string"),
|
||||
];
|
||||
}
|
||||
|
||||
static groupBy<T, R>(array: T[], propertyCallback: (item: T) => R): { id: R, items: T[] }[] {
|
||||
return array.reduce((groupedArray, value) => {
|
||||
const key = propertyCallback(value);
|
||||
|
||||
@ -22,6 +22,7 @@ import {Blog} from "./modules/blog/entity/Blog";
|
||||
import {Question} from "./modules/question/entity/Question";
|
||||
import {Video} from "./modules/video/entity/Video";
|
||||
import {ConnectionOptions} from "../../../src/connection/ConnectionOptions";
|
||||
import {DefaultNamingStrategy} from "../../../src/naming-strategy/DefaultNamingStrategy";
|
||||
|
||||
describe("Connection", () => {
|
||||
const resourceDir = __dirname + "/../../../../../test/functional/connection/";
|
||||
@ -122,26 +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.importNamingStrategies([])).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
|
||||
expect(() => connection.importNamingStrategiesFromDirectories([])).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("something")).to.throw(Error); // CannotUseNamingStrategyNotConnectedError
|
||||
}));
|
||||
|
||||
it("should be able to close a connection", async () => Promise.all(connections.map(connection => {
|
||||
return connection.close();
|
||||
})));
|
||||
@ -229,149 +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.importNamingStrategiesFromDirectories([__dirname + "/naming-strategy/*"]);
|
||||
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.importNamingStrategiesFromDirectories([__dirname + "/modules/**/naming-strategy/*"]);
|
||||
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("using naming strategy", function() {
|
||||
|
||||
let connection: Connection;
|
||||
beforeEach(async () => {
|
||||
connection = getConnectionManager().create(setupSingleTestingConnection("mysql", {
|
||||
name: "default",
|
||||
entities: []
|
||||
}));
|
||||
});
|
||||
afterEach(() => connection.isConnected ? connection.close() : {});
|
||||
|
||||
it("should use naming strategy when its class passed to useNamingStrategy method", async () => {
|
||||
connection.importEntities([Post]);
|
||||
connection.importNamingStrategies([FirstCustomNamingStrategy]);
|
||||
connection.useNamingStrategy(FirstCustomNamingStrategy);
|
||||
await connection.connect();
|
||||
connection.getMetadata(Post).tableName.should.be.equal("POST");
|
||||
});
|
||||
|
||||
it("should use naming strategy when its name passed to useNamingStrategy method", async () => {
|
||||
connection.importEntities([Category]);
|
||||
connection.importNamingStrategies([SecondCustomNamingStrategy]);
|
||||
connection.useNamingStrategy("secondCustomNamingStrategy");
|
||||
await connection.connect();
|
||||
connection.getMetadata(Category).tableName.should.be.equal("category");
|
||||
});
|
||||
|
||||
it("should throw an error if not registered naming strategy was used (assert by name)", () => {
|
||||
connection.importEntities([Category]);
|
||||
connection.importNamingStrategies([FirstCustomNamingStrategy]);
|
||||
connection.useNamingStrategy("secondCustomNamingStrategy");
|
||||
return connection.connect().should.be.rejected; // NamingStrategyNotFoundError
|
||||
});
|
||||
|
||||
it("should throw an error if not registered naming strategy was used (assert by Function)", () => {
|
||||
connection.importEntities([Category]);
|
||||
connection.importNamingStrategies([SecondCustomNamingStrategy]);
|
||||
connection.useNamingStrategy(FirstCustomNamingStrategy);
|
||||
return connection.connect().should.be.rejected; // NamingStrategyNotFoundError
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("skip schema generation when skipSchemaSync option is used", function() {
|
||||
|
||||
let connections: Connection[];
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import {DefaultNamingStrategy} from "../../../../src/naming-strategy/DefaultNamingStrategy";
|
||||
import {NamingStrategy} from "../../../../src/decorator/NamingStrategy";
|
||||
import {NamingStrategyInterface} from "../../../../src/naming-strategy/NamingStrategyInterface";
|
||||
|
||||
@NamingStrategy("firstCustomNamingStrategy")
|
||||
export class FirstCustomNamingStrategy extends DefaultNamingStrategy implements NamingStrategyInterface {
|
||||
|
||||
tableName(className: string, customName: string): string {
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import {DefaultNamingStrategy} from "../../../../src/naming-strategy/DefaultNamingStrategy";
|
||||
import {NamingStrategy} from "../../../../src/decorator/NamingStrategy";
|
||||
import {NamingStrategyInterface} from "../../../../src/naming-strategy/NamingStrategyInterface";
|
||||
|
||||
@NamingStrategy("secondCustomNamingStrategy")
|
||||
export class SecondCustomNamingStrategy extends DefaultNamingStrategy implements NamingStrategyInterface {
|
||||
|
||||
tableName(className: string, customName: string): string {
|
||||
|
||||
@ -4,19 +4,26 @@ import {expect} from "chai";
|
||||
import {setupTestingConnections} from "../../utils/test-utils";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
import {getConnectionManager} from "../../../src/index";
|
||||
import {ConnectionMetadataBuilder} from "../../../src/connection/ConnectionMetadataBuilder";
|
||||
import {EntityMetadataValidator} from "../../../src/metadata-builder/EntityMetadataValidator";
|
||||
|
||||
const should = chai.should();
|
||||
|
||||
describe("entity-metadata-validator", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(() => {
|
||||
connections = setupTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"] })
|
||||
.map(options => getConnectionManager().create(options));
|
||||
it("should throw error if relation count decorator used with ManyToOne or OneToOne relations", () => {
|
||||
const connection = new Connection({ // dummy connection options, connection won't be established anyway
|
||||
type: "mysql",
|
||||
host: "localhost",
|
||||
username: "test",
|
||||
password: "test",
|
||||
database: "test",
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"]
|
||||
});
|
||||
const connectionMetadataBuilder = new ConnectionMetadataBuilder(connection);
|
||||
const entityMetadatas = connectionMetadataBuilder.buildEntityMetadatas([__dirname + "/entity/*{.js,.ts}"], []);
|
||||
const entityMetadataValidator = new EntityMetadataValidator();
|
||||
expect(() => entityMetadataValidator.validateMany(entityMetadatas)).to.throw(Error);
|
||||
});
|
||||
|
||||
it("should throw error if relation count decorator used with ManyToOne or OneToOne relations", () => Promise.all(connections.map(async connection => {
|
||||
expect(() => connection.buildMetadatas()).to.throw(Error);
|
||||
})));
|
||||
|
||||
});
|
||||
@ -3,7 +3,10 @@ import {Post} from "./entity/Post";
|
||||
import {Counters} from "./entity/Counters";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
import {expect} from "chai";
|
||||
import {setupTestingConnections} from "../../utils/test-utils";
|
||||
import {
|
||||
closeTestingConnections, createTestingConnections, reloadTestingDatabases,
|
||||
setupTestingConnections
|
||||
} from "../../utils/test-utils";
|
||||
import {Subcounters} from "./entity/Subcounters";
|
||||
import {User} from "./entity/User";
|
||||
import {getConnectionManager} from "../../../src/index";
|
||||
@ -11,14 +14,13 @@ import {getConnectionManager} from "../../../src/index";
|
||||
describe("entity-metadata > property-map", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(() => {
|
||||
connections = setupTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"] })
|
||||
.map(options => getConnectionManager().create(options))
|
||||
.map(connection => {
|
||||
connection.buildMetadatas();
|
||||
return connection;
|
||||
});
|
||||
});
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should create correct property map object", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import "reflect-metadata";
|
||||
import * as chai from "chai";
|
||||
import {expect} from "chai";
|
||||
import {setupTestingConnections} from "../../../utils/test-utils";
|
||||
import {
|
||||
closeTestingConnections, createTestingConnections, reloadTestingDatabases,
|
||||
setupTestingConnections
|
||||
} from "../../../utils/test-utils";
|
||||
import {Connection} from "../../../../src/connection/Connection";
|
||||
import {Post} from "./entity/Post";
|
||||
import {Counters} from "./entity/Counters";
|
||||
@ -11,16 +14,15 @@ import {getConnectionManager} from "../../../../src/index";
|
||||
const should = chai.should();
|
||||
|
||||
describe("metadata-builder > ColumnMetadata", () => {
|
||||
|
||||
|
||||
let connections: Connection[];
|
||||
before(() => {
|
||||
connections = setupTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"] })
|
||||
.map(options => getConnectionManager().create(options))
|
||||
.map(connection => {
|
||||
connection.buildMetadatas();
|
||||
return connection;
|
||||
});
|
||||
});
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("getValue", () => Promise.all(connections.map(async connection => {
|
||||
const post = new Post();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -1,22 +1,25 @@
|
||||
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({
|
||||
type: "postgres",
|
||||
url: "postgres://test:test@localhost:5432/test",
|
||||
}, new Logger({})));
|
||||
let driver: PostgresDriver, connection: Connection;
|
||||
before(() => {
|
||||
connection = new Connection({
|
||||
type: "postgres",
|
||||
url: "postgres://test:test@localhost:5432/test",
|
||||
});
|
||||
driver = new PostgresDriver(connection);
|
||||
});
|
||||
|
||||
it("should not fail in url parser", () => {
|
||||
expect(driver.options.username).to.be.eq("test");
|
||||
expect(driver.options.password).to.be.eq("test");
|
||||
expect(driver.options.host).to.be.eq("localhost");
|
||||
expect(driver.options.port).to.be.eq(5432);
|
||||
expect(driver.options.database).to.be.eq("test");
|
||||
expect(connection.options.username).to.be.eq("test");
|
||||
expect(connection.options.password).to.be.eq("test");
|
||||
expect(connection.options.host).to.be.eq("localhost");
|
||||
expect(connection.options.port).to.be.eq(5432);
|
||||
expect(connection.options.database).to.be.eq("test");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
17
test/github-issues/512/entity/Post.ts
Normal file
17
test/github-issues/512/entity/Post.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import {Entity} from "../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
|
||||
@Entity("Posts")
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column("date")
|
||||
date: string;
|
||||
|
||||
}
|
||||
30
test/github-issues/512/issue-512.ts
Normal file
30
test/github-issues/512/issue-512.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import "reflect-metadata";
|
||||
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
import {Post} from "./entity/Post";
|
||||
import {expect} from "chai";
|
||||
|
||||
describe("github issues > #512 Table name escaping in UPDATE in QueryBuilder", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should escape table name using driver's escape function in UPDATE", () => Promise.all(connections.map(async connection => {
|
||||
const driver = connection.driver;
|
||||
const queryBuilder = connection.entityManager.createQueryBuilder(Post, "post");
|
||||
const query = queryBuilder
|
||||
.update({
|
||||
title: "Some Title",
|
||||
})
|
||||
.getSql();
|
||||
|
||||
return query.should.contain(driver.escapeTableName("Posts"));
|
||||
})));
|
||||
|
||||
});
|
||||
12
test/github-issues/521/entity/Car.ts
Normal file
12
test/github-issues/521/entity/Car.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Entity } from "../../../../src/decorator/entity/Entity";
|
||||
import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import { Column } from "../../../../src/decorator/columns/Column";
|
||||
|
||||
@Entity()
|
||||
export class Car {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
}
|
||||
32
test/github-issues/521/issue-521.ts
Normal file
32
test/github-issues/521/issue-521.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import "reflect-metadata";
|
||||
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
|
||||
import {Connection} from "../../../src/connection/Connection";
|
||||
import {expect} from "chai";
|
||||
import {Car} from "./entity/Car";
|
||||
|
||||
describe("github issues > #521 Attributes in UPDATE in QB arent getting replaced", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should replace parameters", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const qb = connection.getRepository(Car).createQueryBuilder("car");
|
||||
const [query, parameters] = qb
|
||||
.update({
|
||||
name: "Honda",
|
||||
})
|
||||
.where("name = :name", {
|
||||
name: "Toyota",
|
||||
})
|
||||
.getSqlWithParameters();
|
||||
return parameters.length.should.eql(2);
|
||||
})));
|
||||
|
||||
});
|
||||
@ -1,8 +1,8 @@
|
||||
import {ConnectionOptions} from "../../src/connection/ConnectionOptions";
|
||||
import {createConnection, createConnections} from "../../src/index";
|
||||
import {Connection} from "../../src/connection/Connection";
|
||||
import {DriverType} from "../../src/driver/DriverOptions";
|
||||
import {EntitySchema} from "../../src/entity-schema/EntitySchema";
|
||||
import {DriverType} from "../../src/driver/DriverType";
|
||||
|
||||
/**
|
||||
* Interface in which data is stored in ormconfig.json of the project.
|
||||
@ -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;
|
||||
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