mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
refactored connection manager and added methods to create entity manager with its own single query runner
This commit is contained in:
parent
2a59f621ee
commit
7e45f70f15
@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
64
src/repository/QueryRunnerProvider.ts
Normal file
64
src/repository/QueryRunnerProvider.ts
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -11,7 +11,7 @@ import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
* @experimental
|
||||
*/
|
||||
export class ReactiveRepository<Entity> {
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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>) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user