refactored connection manager and added methods to create entity manager with its own single query runner

This commit is contained in:
Umed Khudoiberdiev 2016-09-19 18:16:15 +05:00
parent 2a59f621ee
commit 7e45f70f15
13 changed files with 449 additions and 205 deletions

View File

@ -31,7 +31,7 @@ import {CannotGetEntityManagerNotConnectedError} from "./error/CannotGetEntityMa
import {LazyRelationsWrapper} from "../repository/LazyRelationsWrapper";
import {SpecificRepository} from "../repository/SpecificRepository";
import {SpecificReactiveRepository} from "../repository/ReactiveSpecificRepository";
import {RepositoryForMetadata} from "../repository/RepositoryForMetadata";
import {RepositoryAggregator} from "../repository/RepositoryAggregator";
import {EntityMetadata} from "../metadata/EntityMetadata";
import {SchemaBuilder} from "../schema-builder/SchemaBuilder";
import {Logger} from "../logger/Logger";
@ -86,9 +86,9 @@ export class Connection {
private readonly _reactiveEntityManager: ReactiveEntityManager;
/**
* Stores all registered metadatas with their repositories.
* Stores all registered repositories.
*/
private readonly repositoryForMetadatas: RepositoryForMetadata[] = [];
private readonly repositoryAggregators: RepositoryAggregator[] = [];
/**
* Entity listeners that are registered for this connection.
@ -377,8 +377,7 @@ export class Connection {
* Gets repository for the given entity class or name.
*/
getRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): Repository<Entity> {
const repositoryForMetadata = this.findRepositoryForMetadata(entityClassOrName);
return repositoryForMetadata.repository;
return this.findRepositoryAggregator(entityClassOrName).repository;
}
/**
@ -401,8 +400,10 @@ export class Connection {
* like ones decorated with @ClosureTable decorator.
*/
getTreeRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): TreeRepository<Entity> {
const repositoryForMetadata = this.findRepositoryForMetadata(entityClassOrName, { checkIfTreeTable: true });
return repositoryForMetadata.repository as TreeRepository<Entity>;
const repository = this.findRepositoryAggregator(entityClassOrName).treeRepository;
if (!repository)
throw new RepositoryNotTreeError(entityClassOrName);
return repository;
}
/**
@ -422,8 +423,7 @@ export class Connection {
* SpecificRepository is a special repository that contains specific and non standard repository methods.
*/
getSpecificRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificRepository<Entity> {
const repositoryForMetadata = this.findRepositoryForMetadata(entityClassOrName);
return repositoryForMetadata.specificRepository;
return this.findRepositoryAggregator(entityClassOrName).specificRepository;
}
/**
@ -446,8 +446,7 @@ export class Connection {
* the only difference is that reactive repository methods return Observable instead of Promise.
*/
getReactiveRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): ReactiveRepository<Entity> {
const repositoryForMetadata = this.findRepositoryForMetadata(entityClassOrName);
return repositoryForMetadata.reactiveRepository;
return this.findRepositoryAggregator(entityClassOrName).reactiveRepository;
}
/**
@ -476,8 +475,10 @@ export class Connection {
* the only difference is that reactive repository methods return Observable instead of Promise.
*/
getReactiveTreeRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): TreeReactiveRepository<Entity> {
const repositoryForMetadata = this.findRepositoryForMetadata(entityClassOrName, { checkIfTreeTable: true });
return repositoryForMetadata.reactiveRepository as TreeReactiveRepository<Entity>;
const repository = this.findRepositoryAggregator(entityClassOrName).treeReactiveRepository;
if (!repository)
throw new RepositoryNotTreeError(entityClassOrName);
return repository;
}
/**
@ -503,8 +504,26 @@ export class Connection {
* the only difference is that reactive repository methods return Observable instead of Promise.
*/
getSpecificReactiveRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificReactiveRepository<Entity> {
const repositoryForMetadata = this.findRepositoryForMetadata(entityClassOrName);
return repositoryForMetadata.specificReactiveRepository;
const repositoryAggregator = this.findRepositoryAggregator(entityClassOrName);
return repositoryAggregator.specificReactiveRepository;
}
/**
* 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() {
return new EntityManager(this, true);
}
/**
* Creates a new reactive 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.
*/
createReactiveEntityManagerWithSingleDatabaseConnection() {
return new ReactiveEntityManager(this, true);
}
// -------------------------------------------------------------------------
@ -512,10 +531,9 @@ export class Connection {
// -------------------------------------------------------------------------
/**
* Finds repository with metadata of the given entity class or name.
* Finds repository aggregator of the given entity class or name.
*/
private findRepositoryForMetadata(entityClassOrName: ObjectType<any>|string,
options?: { checkIfTreeTable: boolean }): RepositoryForMetadata {
private findRepositoryAggregator(entityClassOrName: ObjectType<any>|string): RepositoryAggregator {
if (!this.isConnected)
throw new NoConnectionForRepositoryError(this.name);
@ -523,14 +541,11 @@ export class Connection {
throw new RepositoryNotFoundError(this.name, entityClassOrName);
const metadata = this.entityMetadatas.findByTarget(entityClassOrName);
const repositoryForMetadata = this.repositoryForMetadatas.find(metadataRepository => metadataRepository.metadata === metadata);
if (!repositoryForMetadata)
const repositoryAggregator = this.repositoryAggregators.find(repositoryAggregate => repositoryAggregate.metadata === metadata);
if (!repositoryAggregator)
throw new RepositoryNotFoundError(this.name, entityClassOrName);
if (options && options.checkIfTreeTable && !metadata.table.isClosure)
throw new RepositoryNotTreeError(entityClassOrName);
return repositoryForMetadata;
return repositoryAggregator;
}
/**
@ -540,7 +555,7 @@ export class Connection {
this.entitySubscribers.length = 0;
this.entityListeners.length = 0;
this.repositoryForMetadatas.length = 0;
this.repositoryAggregators.length = 0;
this.entityMetadatas.length = 0;
const namingStrategy = this.createNamingStrategy();
@ -569,7 +584,7 @@ export class Connection {
.buildFromMetadataArgsStorage(this.driver, lazyRelationsWrapper, namingStrategy, this.entityClasses)
.forEach(metadata => {
this.entityMetadatas.push(metadata);
this.repositoryForMetadatas.push(new RepositoryForMetadata(this, metadata));
this.repositoryAggregators.push(new RepositoryAggregator(this, metadata));
});
}
@ -579,7 +594,7 @@ export class Connection {
.buildFromSchemas(this.driver, lazyRelationsWrapper, namingStrategy, this.entitySchemas)
.forEach(metadata => {
this.entityMetadatas.push(metadata);
this.repositoryForMetadatas.push(new RepositoryForMetadata(this, metadata));
this.repositoryAggregators.push(new RepositoryAggregator(this, metadata));
});
}
}

View File

@ -8,17 +8,56 @@ import {ObjectLiteral} from "../common/ObjectLiteral";
import {TreeReactiveRepository} from "../repository/TreeReactiveRepository";
import {SpecificRepository} from "../repository/SpecificRepository";
import {SpecificReactiveRepository} from "../repository/ReactiveSpecificRepository";
import {QueryRunnerProvider} from "../repository/QueryRunnerProvider";
import {RepositoryAggregator} from "../repository/RepositoryAggregator";
import {RepositoryNotTreeError} from "../connection/error/RepositoryNotTreeError";
import {NoNeedToReleaseEntityManagerError} from "./error/NoNeedToReleaseEntityManagerError";
import {EntityManagerAlreadyReleasedError} from "./error/EntityManagerAlreadyReleasedError";
/**
* Common functions shared between different manager types.
* Common functions shared between different entity manager types.
*/
export abstract class BaseEntityManager {
// -------------------------------------------------------------------------
// Protected Properties
// -------------------------------------------------------------------------
/**
* Provides single query runner for the all repositories retrieved from this entity manager.
* Works only when useSingleDatabaseConnection is enabled.
*/
protected queryRunnerProvider?: QueryRunnerProvider;
/**
* Indicates if this entity manager is released.
* Entity manager can be released only if useSingleDatabaseConnection is enabled.
* Once entity manager is released, its repositories and some other methods can't be used anymore.
*/
protected isReleased: boolean;
// -------------------------------------------------------------------------
// Private Properties
// -------------------------------------------------------------------------
/**
* Stores all registered repositories.
* Used when useSingleDatabaseConnection is enabled.
*/
private readonly repositoryAggregators: RepositoryAggregator[] = [];
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(protected connection: Connection) {
/**
* @param connection Connection to be used in this entity manager
* @param useSingleDatabaseConnection Indicates if single database connection should be used for all queries execute
*/
constructor(protected connection: Connection,
protected useSingleDatabaseConnection: boolean) {
if (useSingleDatabaseConnection === true)
this.queryRunnerProvider = new QueryRunnerProvider(connection.driver, true);
}
// -------------------------------------------------------------------------
@ -27,104 +66,198 @@ export abstract class BaseEntityManager {
/**
* Gets repository for the given entity class.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getRepository<Entity>(entityClass: ObjectType<Entity>): Repository<Entity>;
/**
* Gets repository for the given entity name.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getRepository<Entity>(entityName: string): Repository<Entity>;
/**
* Gets repository for the given entity class or name.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): Repository<Entity> {
return this.obtainRepository(entityClassOrName);
// if single db connection is used then create its own repository with reused query runner
if (this.useSingleDatabaseConnection)
return this.obtainRepositoryAggregator(entityClassOrName as any).repository;
return this.connection.getRepository<Entity>(entityClassOrName as any);
}
/**
* Gets tree repository for the given entity class.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getTreeRepository<Entity>(entityClass: ObjectType<Entity>): TreeRepository<Entity>;
/**
* Gets tree repository for the given entity name.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getTreeRepository<Entity>(entityName: string): TreeRepository<Entity>;
/**
* Gets tree repository for the given entity class or name.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getTreeRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): TreeRepository<Entity> {
return this.obtainTreeRepository(entityClassOrName);
// if single db connection is used then create its own repository with reused query runner
if (this.useSingleDatabaseConnection) {
const treeRepository = this.obtainRepositoryAggregator(entityClassOrName).treeRepository;
if (!treeRepository)
throw new RepositoryNotTreeError(entityClassOrName);
return treeRepository;
}
return this.connection.getTreeRepository<Entity>(entityClassOrName as any);
}
/**
* Gets reactive repository for the given entity class.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getReactiveRepository<Entity>(entityClass: ObjectType<Entity>): ReactiveRepository<Entity>;
/**
* Gets reactive repository for the given entity name.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getReactiveRepository<Entity>(entityName: string): ReactiveRepository<Entity>;
/**
* Gets reactive repository of the given entity.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getReactiveRepository<Entity>(entityClass: ObjectType<Entity>|string): ReactiveRepository<Entity> {
return this.obtainReactiveRepository(entityClass);
getReactiveRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): ReactiveRepository<Entity> {
// if single db connection is used then create its own repository with reused query runner
if (this.useSingleDatabaseConnection)
return this.obtainRepositoryAggregator(entityClassOrName as any).reactiveRepository;
return this.connection.getReactiveRepository<Entity>(entityClassOrName as any);
}
/**
* Gets specific repository for the given entity class.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getSpecificRepository<Entity>(entityClass: ObjectType<Entity>): SpecificRepository<Entity>;
/**
* Gets specific repository for the given entity name.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getSpecificRepository<Entity>(entityName: string): SpecificRepository<Entity>;
/**
* Gets specific repository of the given entity.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getSpecificRepository<Entity>(entityClass: ObjectType<Entity>|string): SpecificRepository<Entity> {
return this.obtainSpecificRepository(entityClass);
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.useSingleDatabaseConnection)
return this.obtainRepositoryAggregator(entityClassOrName as any).specificRepository;
return this.connection.getSpecificRepository<Entity>(entityClassOrName as any);
}
/**
* Gets specific reactive repository for the given entity class.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getSpecificReactiveRepository<Entity>(entityClass: ObjectType<Entity>): SpecificReactiveRepository<Entity>;
/**
* Gets specific reactive repository for the given entity name.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getSpecificReactiveRepository<Entity>(entityName: string): SpecificReactiveRepository<Entity>;
/**
* Gets specific reactive repository of the given entity.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getSpecificReactiveRepository<Entity>(entityClass: ObjectType<Entity>|string): SpecificReactiveRepository<Entity> {
return this.obtainSpecificReactiveRepository(entityClass);
getSpecificReactiveRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificReactiveRepository<Entity> {
// if single db connection is used then create its own repository with reused query runner
if (this.useSingleDatabaseConnection)
return this.obtainRepositoryAggregator(entityClassOrName).specificReactiveRepository;
return this.connection.getSpecificReactiveRepository<Entity>(entityClassOrName as any);
}
/**
* Gets reactive tree repository for the given entity class.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getTreeReactiveRepository<Entity>(entityClass: ObjectType<Entity>): TreeReactiveRepository<Entity>;
/**
* Gets reactive tree repository for the given entity name.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getTreeReactiveRepository<Entity>(entityName: string): TreeReactiveRepository<Entity>;
/**
* Gets reactive tree repository of the given entity.
* If single database connection mode is used, then repository is obtained from the
* repository aggregator, where each repository is individually created for this entity manager.
* When single database connection is not used, repository is being obtained from the connection.
*/
getTreeReactiveRepository<Entity>(entityClass: ObjectType<Entity>|string): TreeReactiveRepository<Entity> {
return this.obtainTreeReactiveRepository(entityClass);
getTreeReactiveRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): TreeReactiveRepository<Entity> {
// if single db connection is used then create its own repository with reused query runner
if (this.useSingleDatabaseConnection) {
const treeRepository = this.obtainRepositoryAggregator(entityClassOrName).treeReactiveRepository;
if (!treeRepository)
throw new RepositoryNotTreeError(entityClassOrName);
return treeRepository;
}
return this.connection.getReactiveTreeRepository<Entity>(entityClassOrName as any);
}
// todo: add methods for getSpecificRepository and getReactiveSpecificRepository
@ -145,14 +278,14 @@ export abstract class BaseEntityManager {
hasId(targetOrEntity: Object|string, maybeEntity?: Object): boolean {
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
const entity = arguments.length === 2 ? <Object> maybeEntity : <Object> targetOrEntity;
return this.obtainRepository(target as any).hasId(entity);
return this.getRepository(target as any).hasId(entity);
}
/**
* Creates a new query builder that can be used to build an sql query.
*/
createQueryBuilder<Entity>(entityClass: ObjectType<Entity>, alias: string): QueryBuilder<Entity> {
return this.obtainRepository(entityClass).createQueryBuilder(alias);
return this.getRepository(entityClass).createQueryBuilder(alias);
}
/**
@ -178,11 +311,13 @@ export abstract class BaseEntityManager {
*/
create<Entity>(entityClass: ObjectType<Entity>, plainObjectOrObjects?: Object|Object[]): Entity|Entity[] {
if (plainObjectOrObjects instanceof Array) {
return this.obtainRepository(entityClass).create(plainObjectOrObjects);
return this.getRepository(entityClass).create(plainObjectOrObjects);
} else if (plainObjectOrObjects) {
return this.obtainRepository(entityClass).create(plainObjectOrObjects);
return this.getRepository(entityClass).create(plainObjectOrObjects);
} else {
return this.obtainRepository(entityClass).create();
return this.getRepository(entityClass).create();
}
}
@ -193,96 +328,54 @@ export abstract class BaseEntityManager {
* replaced from the new object.
*/
preload<Entity>(entityClass: ObjectType<Entity>, object: Object): Promise<Entity> {
return this.obtainRepository(entityClass).preload(object);
return this.getRepository(entityClass).preload(object);
}
/**
* Merges two entities into one new entity.
*/
merge<Entity>(entityClass: ObjectType<Entity>, ...objects: ObjectLiteral[]): Entity {
return <Entity> this.obtainRepository(entityClass).merge(...objects);
merge<Entity>(entityClass: ObjectType<Entity>, ...objects: ObjectLiteral[]): Entity { // todo: throw exception ie tntity manager is released
return <Entity> this.getRepository(entityClass).merge(...objects);
}
/**
* Releases all resources used by entity manager.
* This is used when entity manager is created with a single query runner,
* and this single query runner needs to be released after job with repository is done.
*/
async release(): Promise<void> {
if (this.useSingleDatabaseConnection)
throw new NoNeedToReleaseEntityManagerError();
this.isReleased = true;
if (this.queryRunnerProvider)
return this.queryRunnerProvider.releaseReused();
}
// -------------------------------------------------------------------------
// Private Methods
// Protected Methods
// -------------------------------------------------------------------------
/**
* We are using a private function to avoid type problems, because we are sending a Function everywhere in the
* code, and obtainRepository does not accept a Function, and only ObjectType it can accept - it brings
* us type problems. To avoid this we are using private function here.
* Gets, or if does not exist yet, creates and returns a repository aggregator for a particular entity target.
*/
protected obtainRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): Repository<Entity> {
if (typeof entityClassOrName === "string") {
return this.connection.getRepository<Entity>(entityClassOrName);
} else {
return this.connection.getRepository(entityClassOrName);
}
}
protected obtainRepositoryAggregator<Entity>(entityClassOrName: ObjectType<Entity>|string): RepositoryAggregator {
if (this.isReleased)
throw new EntityManagerAlreadyReleasedError();
/**
* We are using a private function to avoid type problems, because we are sending a Function everywhere in the
* code, and obtainTreeRepository does not accept a Function, and only ObjectType it can accept - it brings
* us type problems. To avoid this we are using private function here.
*/
protected obtainTreeRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): TreeRepository<Entity> {
if (typeof entityClassOrName === "string") {
return this.connection.getTreeRepository<Entity>(entityClassOrName);
} else {
return this.connection.getTreeRepository(entityClassOrName);
const metadata = this.connection.entityMetadatas.findByTarget(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);
}
}
/**
* We are using a private function to avoid type problems, because we are sending a Function everywhere in the
* code, and obtainTreeRepository does not accept a Function, and only ObjectType it can accept - it brings
* us type problems. To avoid this we are using private function here.
*/
protected obtainSpecificRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificRepository<Entity> {
if (typeof entityClassOrName === "string") {
return this.connection.getSpecificRepository<Entity>(entityClassOrName);
} else {
return this.connection.getSpecificRepository(entityClassOrName);
}
}
/**
* We are using a private function to avoid type problems, because we are sending a Function everywhere in the
* code, and obtainReactiveRepository does not accept a Function, and only ObjectType it can accept - it brings
* us type problems. To avoid this we are using private function here.
*/
protected obtainReactiveRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): ReactiveRepository<Entity> {
if (typeof entityClassOrName === "string") {
return this.connection.getReactiveRepository<Entity>(entityClassOrName);
} else {
return this.connection.getReactiveRepository(entityClassOrName);
}
}
/**
* We are using a private function to avoid type problems, because we are sending a Function everywhere in the
* code, and obtainReactiveTreeRepository does not accept a Function, and only ObjectType it can accept - it brings
* us type problems. To avoid this we are using private function here.
*/
protected obtainTreeReactiveRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): TreeReactiveRepository<Entity> {
if (typeof entityClassOrName === "string") {
return this.connection.getReactiveTreeRepository<Entity>(entityClassOrName);
} else {
return this.connection.getReactiveTreeRepository(entityClassOrName);
}
}
/**
* We are using a private function to avoid type problems, because we are sending a Function everywhere in the
* code, and obtainReactiveRepository does not accept a Function, and only ObjectType it can accept - it brings
* us type problems. To avoid this we are using private function here.
*/
protected obtainSpecificReactiveRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificReactiveRepository<Entity> {
if (typeof entityClassOrName === "string") {
return this.connection.getSpecificReactiveRepository<Entity>(entityClassOrName);
} else {
return this.connection.getSpecificReactiveRepository(entityClassOrName);
}
return repositoryAggregator;
}
}

View File

@ -2,6 +2,8 @@ import {Connection} from "../connection/Connection";
import {FindOptions} from "../repository/FindOptions";
import {ObjectType} from "../common/ObjectType";
import {BaseEntityManager} from "./BaseEntityManager";
import {EntityManagerAlreadyReleasedError} from "./error/EntityManagerAlreadyReleasedError";
import {QueryRunnerProvider} from "../repository/QueryRunnerProvider";
/**
* Entity manager supposed to work with any entity, automatically find its repository and call its methods, whatever
@ -13,8 +15,8 @@ export class EntityManager extends BaseEntityManager {
// Constructor
// -------------------------------------------------------------------------
constructor(connection: Connection) {
super(connection);
constructor(connection: Connection, useSingleDatabaseConnection: boolean) {
super(connection, useSingleDatabaseConnection);
}
// -------------------------------------------------------------------------
@ -27,10 +29,9 @@ export class EntityManager extends BaseEntityManager {
persist<Entity>(entity: Entity): Promise<Entity>;
persist<Entity>(targetOrEntity: Function|string, entity: Entity): Promise<Entity>;
persist<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Promise<Entity> {
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
return <any> this.obtainRepository(<any> target).persist(entity);
const entity = arguments.length === 2 ? maybeEntity as Entity : targetOrEntity as Entity;
return this.getRepository(target as any).persist(entity);
}
/**
@ -39,10 +40,9 @@ export class EntityManager extends BaseEntityManager {
remove<Entity>(entity: Entity): Promise<Entity>;
remove<Entity>(targetOrEntity: Function|string, entity: Entity): Promise<Entity>;
remove<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Promise<Entity> {
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
return <any> this.obtainRepository(<any> target).remove(entity);
const entity = arguments.length === 2 ? maybeEntity as Entity : targetOrEntity as Entity;
return this.getRepository(target as any).remove(entity);
}
/**
* Finds entities that match given conditions.
@ -69,13 +69,13 @@ export class EntityManager extends BaseEntityManager {
*/
find<Entity>(entityClass: ObjectType<Entity>, conditionsOrFindOptions?: Object|FindOptions, options?: FindOptions): Promise<Entity[]> {
if (conditionsOrFindOptions && options) {
return this.obtainRepository(entityClass).find(conditionsOrFindOptions, options);
return this.getRepository(entityClass).find(conditionsOrFindOptions, options);
} else if (conditionsOrFindOptions) {
return this.obtainRepository(entityClass).find(conditionsOrFindOptions);
return this.getRepository(entityClass).find(conditionsOrFindOptions);
} else {
return this.obtainRepository(entityClass).find();
return this.getRepository(entityClass).find();
}
}
@ -104,13 +104,13 @@ export class EntityManager extends BaseEntityManager {
*/
findAndCount<Entity>(entityClass: ObjectType<Entity>, conditionsOrFindOptions?: Object|FindOptions, options?: FindOptions): Promise<[Entity[], number]> {
if (conditionsOrFindOptions && options) {
return this.obtainRepository(entityClass).findAndCount(conditionsOrFindOptions, options);
return this.getRepository(entityClass).findAndCount(conditionsOrFindOptions, options);
} else if (conditionsOrFindOptions) {
return this.obtainRepository(entityClass).findAndCount(conditionsOrFindOptions);
return this.getRepository(entityClass).findAndCount(conditionsOrFindOptions);
} else {
return this.obtainRepository(entityClass).findAndCount();
return this.getRepository(entityClass).findAndCount();
}
}
@ -139,13 +139,13 @@ export class EntityManager extends BaseEntityManager {
*/
findOne<Entity>(entityClass: ObjectType<Entity>, conditionsOrFindOptions?: Object|FindOptions, options?: FindOptions): Promise<Entity> {
if (conditionsOrFindOptions && options) {
return this.obtainRepository(entityClass).findOne(conditionsOrFindOptions, options);
return this.getRepository(entityClass).findOne(conditionsOrFindOptions, options);
} else if (conditionsOrFindOptions) {
return this.obtainRepository(entityClass).findOne(conditionsOrFindOptions);
return this.getRepository(entityClass).findOne(conditionsOrFindOptions);
} else {
return this.obtainRepository(entityClass).findOne();
return this.getRepository(entityClass).findOne();
}
}
@ -153,19 +153,24 @@ export class EntityManager extends BaseEntityManager {
* Finds entity with given id.
*/
findOneById<Entity>(entityClass: ObjectType<Entity>, id: any, options?: FindOptions): Promise<Entity> {
return this.obtainRepository(entityClass).findOneById(id, options);
return this.getRepository(entityClass).findOneById(id, options);
}
/**
* Executes raw SQL query and returns raw database results.
*/
async query(query: string): Promise<any> {
const queryRunner = await this.connection.driver.createQueryRunner();
if (this.useSingleDatabaseConnection && this.isReleased)
throw new EntityManagerAlreadyReleasedError();
const queryRunnerProvider = this.queryRunnerProvider || new QueryRunnerProvider(this.connection.driver);
const queryRunner = await queryRunnerProvider.provide();
try {
return queryRunner.query(query);
} finally {
await queryRunner.release();
await queryRunnerProvider.release(queryRunner);
}
}
@ -173,7 +178,11 @@ export class EntityManager extends BaseEntityManager {
* Wraps given function execution (and all operations made there) in a transaction.
*/
async transaction(runInTransaction: () => Promise<any>): Promise<any> {
const queryRunner = await this.connection.driver.createQueryRunner();
if (this.useSingleDatabaseConnection && this.isReleased)
throw new EntityManagerAlreadyReleasedError();
const queryRunnerProvider = this.queryRunnerProvider || new QueryRunnerProvider(this.connection.driver);
const queryRunner = await queryRunnerProvider.provide();
try {
await queryRunner.beginTransaction();
@ -186,7 +195,7 @@ export class EntityManager extends BaseEntityManager {
throw err;
} finally {
await queryRunner.release();
await queryRunnerProvider.release(queryRunner);
}
}

View File

@ -13,11 +13,11 @@ export class EntityManagerFactory {
// -------------------------------------------------------------------------
createEntityManager(connection: Connection) {
return new EntityManager(connection);
return new EntityManager(connection, false);
}
createReactiveEntityManager(connection: Connection) {
return new ReactiveEntityManager(connection);
return new ReactiveEntityManager(connection, false);
}
}

View File

@ -3,6 +3,8 @@ import {Connection} from "../connection/Connection";
import {FindOptions} from "../repository/FindOptions";
import {ObjectType} from "../common/ObjectType";
import {BaseEntityManager} from "./BaseEntityManager";
import {QueryRunnerProvider} from "../repository/QueryRunnerProvider";
import {EntityManagerAlreadyReleasedError} from "./error/EntityManagerAlreadyReleasedError";
/**
* Entity manager supposed to work with any entity, automatically find its repository and call its method, whatever
@ -14,8 +16,8 @@ export class ReactiveEntityManager extends BaseEntityManager {
// Constructor
// -------------------------------------------------------------------------
constructor(connection: Connection) {
super(connection);
constructor(connection: Connection, useSingleDatabaseConnection: boolean) {
super(connection, useSingleDatabaseConnection);
}
// -------------------------------------------------------------------------
@ -28,10 +30,9 @@ export class ReactiveEntityManager extends BaseEntityManager {
persist<Entity>(entity: Entity): Rx.Observable<Entity>;
persist<Entity>(targetOrEntity: Function|string, entity: Entity): Rx.Observable<Entity>;
persist<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Rx.Observable<Entity> {
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
return <any> this.getReactiveRepository(<any> target).persist(entity);
const entity = arguments.length === 2 ? maybeEntity as Entity : targetOrEntity as Entity;
return this.getReactiveRepository<Entity>(target as any).persist(entity);
}
/**
@ -40,10 +41,9 @@ export class ReactiveEntityManager extends BaseEntityManager {
remove<Entity>(entity: Entity): Rx.Observable<Entity>;
remove<Entity>(targetOrEntity: Function|string, entity: Entity): Rx.Observable<Entity>;
remove<Entity>(targetOrEntity: Entity|Function|string, maybeEntity?: Entity): Rx.Observable<Entity> {
// todo: extra casting is used strange tsc error here, check later maybe typescript bug
const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor;
const entity = arguments.length === 2 ? <Entity> maybeEntity : <Entity> targetOrEntity;
return <any> this.getReactiveRepository(<any> target).remove(entity);
const entity = arguments.length === 2 ? maybeEntity as Entity : targetOrEntity as Entity;
return this.getReactiveRepository<Entity>(target as any).remove(entity);
}
/**
@ -163,13 +163,18 @@ export class ReactiveEntityManager extends BaseEntityManager {
*/
query(query: string): Rx.Observable<any> {
const promiseFn = async () => {
const queryRunner = await this.connection.driver.createQueryRunner();
if (this.useSingleDatabaseConnection && this.isReleased)
throw new EntityManagerAlreadyReleasedError();
const queryRunnerProvider = this.queryRunnerProvider || new QueryRunnerProvider(this.connection.driver);
const queryRunner = await queryRunnerProvider.provide();
try {
const result = await queryRunner.query(query);
return Promise.resolve(result);
} finally {
await queryRunner.release();
await queryRunnerProvider.release(queryRunner);
}
};
return Rx.Observable.fromPromise(promiseFn as any);
@ -180,7 +185,11 @@ export class ReactiveEntityManager extends BaseEntityManager {
*/
transaction(runInTransaction: () => Promise<any>): Rx.Observable<any> {
const promiseFn = async () => {
const queryRunner = await this.connection.driver.createQueryRunner();
if (this.useSingleDatabaseConnection && this.isReleased)
throw new EntityManagerAlreadyReleasedError();
const queryRunnerProvider = this.queryRunnerProvider || new QueryRunnerProvider(this.connection.driver);
const queryRunner = await queryRunnerProvider.provide();
try {
await queryRunner.beginTransaction();
@ -193,7 +202,7 @@ export class ReactiveEntityManager extends BaseEntityManager {
throw err;
} finally {
await queryRunner.release();
await queryRunnerProvider.release(queryRunner);
}
};
return Rx.Observable.fromPromise(promiseFn as any);

View File

@ -0,0 +1,15 @@
/**
* Thrown when consumer tries to use entity manager after it was released.
*
* @internal
*/
export class EntityManagerAlreadyReleasedError extends Error {
name = "EntityManagerAlreadyReleaseError";
constructor() {
super();
this.message = `Entity manager was already released, cannot continue to work with its repositories and querying methods anymore.`;
this.stack = new Error().stack;
}
}

View File

@ -0,0 +1,18 @@
/**
* Thrown when consumer tries to release entity manager that does not use single database connection.
*
* @internal
*/
export class NoNeedToReleaseEntityManagerError extends Error {
name = "NoNeedToReleaseEntityManagerError";
constructor() {
super();
this.message = `Entity manager is not using single database connection and cannot be released. ` +
`Only entity managers created by connection#createEntityManagerWithSingleDatabaseConnection and ` +
`connection#createReactiveEntityManagerWithSingleDatabaseConnection methods have a single database connection ` +
`and they should be released.`;
this.stack = new Error().stack;
}
}

View File

@ -0,0 +1,64 @@
import {QueryRunner} from "../driver/QueryRunner";
import {Driver} from "../driver/Driver";
/**
* Represents functionality to provide a new query runners, and release old ones.
* Also can provide always same query runner.
*/
export class QueryRunnerProvider {
// -------------------------------------------------------------------------
// Protected Properties
// -------------------------------------------------------------------------
protected reusableQueryRunner: QueryRunner;
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(protected driver: Driver,
protected useSingleQueryRunner: boolean = false) {
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
/**
* Provides a new query runner used to run repository queries.
* If use useSingleQueryRunner mode is enabled then reusable query runner will be provided instead.
*/
async provide(): Promise<QueryRunner> {
if (this.useSingleQueryRunner) {
if (!this.reusableQueryRunner)
this.reusableQueryRunner = await this.driver.createQueryRunner();
return this.reusableQueryRunner;
}
return this.driver.createQueryRunner();
}
/**
* Query runner release logic extracted into separated methods intently,
* to make possible to create a subclass with its own release query runner logic.
* Note: release only query runners that provided by a provideQueryRunner() method.
* This is important and by design.
*/
async release(queryRunner: QueryRunner): Promise<void> {
if (queryRunner === this.reusableQueryRunner)
return;
return queryRunner.release();
}
/**
* Releases reused query runner.
*/
async releaseReused(): Promise<void> {
if (this.reusableQueryRunner)
return this.reusableQueryRunner.release();
}
}

View File

@ -11,7 +11,7 @@ import {ObjectLiteral} from "../common/ObjectLiteral";
* @experimental
*/
export class ReactiveRepository<Entity> {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------

View File

@ -9,6 +9,7 @@ import {EntityWithId} from "../persistment/operation/PersistOperation";
import {FindOptions, FindOptionsUtils} from "./FindOptions";
import {ObjectLiteral} from "../common/ObjectLiteral";
import {QueryRunner} from "../driver/QueryRunner";
import {QueryRunnerProvider} from "./QueryRunnerProvider";
/**
* Repository is supposed to work with your entity objects. Find entities, insert, update, delete, etc.
@ -22,13 +23,22 @@ export class Repository<Entity extends ObjectLiteral> {
protected entityPersistOperationBuilder: EntityPersistOperationBuilder;
protected plainObjectToEntityTransformer: PlainObjectToNewEntityTransformer;
protected plainObjectToDatabaseEntityTransformer: PlainObjectToDatabaseEntityTransformer<Entity>;
protected queryRunnerProvider: QueryRunnerProvider;
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(protected connection: Connection,
protected metadata: EntityMetadata) {
protected metadata: EntityMetadata,
queryRunnerProvider?: QueryRunnerProvider) {
if (queryRunnerProvider) {
this.queryRunnerProvider = queryRunnerProvider;
} else {
this.queryRunnerProvider = new QueryRunnerProvider(connection.driver);
}
this.entityPersistOperationBuilder = new EntityPersistOperationBuilder(connection.entityMetadatas);
this.plainObjectToEntityTransformer = new PlainObjectToNewEntityTransformer();
this.plainObjectToDatabaseEntityTransformer = new PlainObjectToDatabaseEntityTransformer();
@ -156,7 +166,7 @@ export class Repository<Entity extends ObjectLiteral> {
if (entityOrEntities instanceof Array)
return Promise.all(entityOrEntities.map(entity => this.persist(entity)));
const queryRunner = await this.provideQueryRunner();
const queryRunner = await this.queryRunnerProvider.provide();
try {
const allPersistedEntities = await this.extractObjectsById(entityOrEntities, this.metadata);
let loadedDbEntity: Entity|null = null;
@ -182,7 +192,7 @@ export class Repository<Entity extends ObjectLiteral> {
return entityOrEntities;
} finally {
await this.releaseProvidedQueryRunner(queryRunner);
await this.queryRunnerProvider.release(queryRunner);
}
}
@ -204,7 +214,7 @@ export class Repository<Entity extends ObjectLiteral> {
if (entityOrEntities instanceof Array) // todo: make it in transaction, like in persist
return Promise.all(entityOrEntities.map(entity => this.remove(entity)));
const queryRunner = await this.provideQueryRunner();
const queryRunner = await this.queryRunnerProvider.provide();
try {
const queryBuilder = new QueryBuilder(this.connection.driver, this.connection.entityMetadatas, this.connection.broadcaster, queryRunner) // todo: better to pass connection?
.select(this.metadata.table.name)
@ -225,7 +235,7 @@ export class Repository<Entity extends ObjectLiteral> {
return entityOrEntities;
} finally {
await this.releaseProvidedQueryRunner(queryRunner);
await this.queryRunnerProvider.release(queryRunner);
}
}
@ -340,12 +350,12 @@ export class Repository<Entity extends ObjectLiteral> {
* Executes a raw SQL query and returns a raw database results.
*/
async query(query: string): Promise<any> {
const queryRunner = await this.provideQueryRunner();
const queryRunner = await this.queryRunnerProvider.provide();
try {
return queryRunner.query(query);
} finally {
await this.releaseProvidedQueryRunner(queryRunner);
await this.queryRunnerProvider.release(queryRunner);
}
}
@ -353,7 +363,7 @@ export class Repository<Entity extends ObjectLiteral> {
* Wraps given function execution (and all operations made there) in a transaction.
*/
async transaction(runInTransaction: () => Promise<any>|any): Promise<any> {
const queryRunner = await this.provideQueryRunner();
const queryRunner = await this.queryRunnerProvider.provide();
try {
await queryRunner.beginTransaction();
const result = await runInTransaction();
@ -365,7 +375,7 @@ export class Repository<Entity extends ObjectLiteral> {
throw err;
} finally {
await this.releaseProvidedQueryRunner(queryRunner);
await this.queryRunnerProvider.release(queryRunner);
}
}
@ -373,25 +383,7 @@ export class Repository<Entity extends ObjectLiteral> {
// Protected Methods
// -------------------------------------------------------------------------
protected provideQueryRunner(): Promise<QueryRunner> {
return this.connection.driver.createQueryRunner();
}
/**
* Query runner release logic extracted into separated methods intently,
* to make possible to create a subclass with its own release query runner logic.
* Note: release only query runners that provided by a provideQueryRunner() method.
* This is important and by design.
*/
protected releaseProvidedQueryRunner(queryRunner: QueryRunner): Promise<void> {
return queryRunner.release();
}
// -------------------------------------------------------------------------
// Private Methods
// -------------------------------------------------------------------------
private createFindQueryBuilder(conditionsOrFindOptions?: Object|FindOptions, options?: FindOptions) {
protected createFindQueryBuilder(conditionsOrFindOptions?: Object|FindOptions, options?: FindOptions) {
const findOptions = FindOptionsUtils.isFindOptions(conditionsOrFindOptions) ? conditionsOrFindOptions : <FindOptions> options;
const conditions = FindOptionsUtils.isFindOptions(conditionsOrFindOptions) ? undefined : conditionsOrFindOptions;
@ -417,7 +409,7 @@ export class Repository<Entity extends ObjectLiteral> {
* ids, check if we did not load them yet and try to load them. This algorithm will make sure that all dbEntities
* are loaded. Further it will help insert operations to work correctly.
*/
private findNotLoadedIds(queryRunner: QueryRunner, dbEntities: EntityWithId[], persistedEntities: EntityWithId[]): Promise<EntityWithId[]> {
protected findNotLoadedIds(queryRunner: QueryRunner, dbEntities: EntityWithId[], persistedEntities: EntityWithId[]): Promise<EntityWithId[]> {
const missingDbEntitiesLoad = persistedEntities
.filter(entityWithId => entityWithId.id !== null && entityWithId.id !== undefined) // todo: not sure if this condition will work
.filter(entityWithId => !dbEntities.find(dbEntity => dbEntity.entityTarget === entityWithId.entityTarget && dbEntity.compareId(entityWithId.id!)))
@ -460,7 +452,7 @@ export class Repository<Entity extends ObjectLiteral> {
/**
* Extracts unique objects from given entity and all its downside relations.
*/
private extractObjectsById(entity: any, metadata: EntityMetadata, entityWithIds: EntityWithId[] = []): Promise<EntityWithId[]> { // todo: why promises used there?
protected extractObjectsById(entity: any, metadata: EntityMetadata, entityWithIds: EntityWithId[] = []): Promise<EntityWithId[]> { // todo: why promises used there?
const promises = metadata.relations.map(relation => {
const relMetadata = relation.inverseEntityMetadata;

View File

@ -7,11 +7,13 @@ import {Connection} from "../connection/Connection";
import {getFromContainer} from "../index";
import {RepositoryFactory} from "./RepositoryFactory";
import {TreeRepository} from "./TreeRepository";
import {TreeReactiveRepository} from "./TreeReactiveRepository";
import {QueryRunnerProvider} from "./QueryRunnerProvider";
/**
* Aggregates all repositories of the specific metadata.
*/
export class RepositoryForMetadata {
export class RepositoryAggregator {
// -------------------------------------------------------------------------
// Public Readonly properties
@ -23,22 +25,32 @@ export class RepositoryForMetadata {
public readonly metadata: EntityMetadata;
/**
* All connection's repositories.
* Ordinary repository.
*/
public readonly repository: Repository<any>;
/**
* All connection's reactive repositories.
* Reactive version of the repository.
*/
public readonly reactiveRepository: ReactiveRepository<any>;
/**
* All connection's specific repositories.
* Tree repository.
*/
public readonly treeRepository?: TreeRepository<any>;
/**
* Reactive version of the tree repository.
*/
public readonly treeReactiveRepository?: TreeReactiveRepository<any>;
/**
* Repository with specific functions.
*/
public readonly specificRepository: SpecificRepository<any>;
/**
* All connection's specific reactive repositories.
* Reactive version of the repository with specific functions.
*/
public readonly specificReactiveRepository: SpecificReactiveRepository<any>;
@ -46,20 +58,22 @@ export class RepositoryForMetadata {
// Constructor
// -------------------------------------------------------------------------
constructor(connection: Connection, metadata: EntityMetadata) {
constructor(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider) {
const repositoryFactory = getFromContainer(RepositoryFactory);
this.metadata = metadata;
if (metadata.table.isClosure) {
this.repository = repositoryFactory.createTreeRepository(connection, metadata);
this.reactiveRepository = repositoryFactory.createReactiveTreeRepository(this.repository as TreeRepository<any>);
this.treeRepository = repositoryFactory.createTreeRepository(connection, metadata, queryRunnerProvider);
this.repository = this.treeRepository;
this.treeReactiveRepository = repositoryFactory.createReactiveTreeRepository(this.repository as TreeRepository<any>);
this.reactiveRepository = this.treeReactiveRepository;
} else {
this.repository = repositoryFactory.createRepository(connection, metadata);
this.repository = repositoryFactory.createRepository(connection, metadata, queryRunnerProvider);
this.reactiveRepository = repositoryFactory.createReactiveRepository(this.repository);
}
this.specificRepository = repositoryFactory.createSpecificRepository(connection, metadata, this.repository);
this.specificRepository = repositoryFactory.createSpecificRepository(connection, metadata, this.repository, queryRunnerProvider);
this.specificReactiveRepository = repositoryFactory.createSpecificReactiveRepository(this.specificRepository);
}

View File

@ -6,6 +6,7 @@ import {ReactiveRepository} from "./ReactiveRepository";
import {TreeReactiveRepository} from "./TreeReactiveRepository";
import {SpecificRepository} from "./SpecificRepository";
import {SpecificReactiveRepository} from "./ReactiveSpecificRepository";
import {QueryRunnerProvider} from "./QueryRunnerProvider";
/**
*/
@ -15,12 +16,12 @@ export class RepositoryFactory {
// Public Methods
// -------------------------------------------------------------------------
createRepository(connection: Connection, metadata: EntityMetadata) {
return new Repository<any>(connection, metadata);
createRepository(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider) {
return new Repository<any>(connection, metadata, queryRunnerProvider);
}
createTreeRepository(connection: Connection, metadata: EntityMetadata) {
return new TreeRepository<any>(connection, metadata);
createTreeRepository(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider) {
return new TreeRepository<any>(connection, metadata, queryRunnerProvider);
}
createReactiveRepository(repository: Repository<any>) {
@ -31,8 +32,8 @@ export class RepositoryFactory {
return new TreeReactiveRepository(repository);
}
createSpecificRepository(connection: Connection, metadata: EntityMetadata, repository: Repository<any>) {
return new SpecificRepository(connection, metadata, repository);
createSpecificRepository(connection: Connection, metadata: EntityMetadata, repository: Repository<any>, queryRunnerProvider?: QueryRunnerProvider) {
return new SpecificRepository(connection, metadata, repository, queryRunnerProvider);
}
createSpecificReactiveRepository(repository: SpecificRepository<any>) {

View File

@ -5,19 +5,33 @@ import {FindOptions, FindOptionsUtils} from "./FindOptions";
import {ObjectLiteral} from "../common/ObjectLiteral";
import {Repository} from "./Repository";
import {QueryRunner} from "../driver/QueryRunner";
import {QueryRunnerProvider} from "./QueryRunnerProvider";
/**
* Repository for more specific operations.
*/
export class SpecificRepository<Entity extends ObjectLiteral> {
// -------------------------------------------------------------------------
// Protected Properties
// -------------------------------------------------------------------------
protected queryRunnerProvider: QueryRunnerProvider;
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(protected connection: Connection,
protected metadata: EntityMetadata,
protected repository: Repository<Entity>) {
protected repository: Repository<Entity>,
queryRunnerProvider?: QueryRunnerProvider) {
if (queryRunnerProvider) {
this.queryRunnerProvider = queryRunnerProvider;
} else {
this.queryRunnerProvider = new QueryRunnerProvider(connection.driver);
}
}
// -------------------------------------------------------------------------