mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
refactored connection class
This commit is contained in:
parent
f513fc1ba7
commit
1e79a63795
@ -9,7 +9,7 @@ import {ConstructorFunction} from "../common/ConstructorFunction";
|
||||
import {EntityListenerMetadata} from "../metadata/EntityListenerMetadata";
|
||||
import {EntityManager} from "../entity-manager/EntityManager";
|
||||
import {importClassesFromDirectories} from "../util/DirectoryExportedClassesLoader";
|
||||
import {defaultMetadataStorage, getContainer} from "../index";
|
||||
import {defaultMetadataStorage, getFromContainer} from "../index";
|
||||
import {EntityMetadataBuilder} from "../metadata-storage/EntityMetadataBuilder";
|
||||
import {DefaultNamingStrategy} from "../naming-strategy/DefaultNamingStrategy";
|
||||
import {EntityMetadataCollection} from "../metadata/collection/EntityMetadataCollection";
|
||||
@ -22,11 +22,8 @@ import {ReactiveRepository} from "../repository/ReactiveRepository";
|
||||
import {ReactiveEntityManager} from "../entity-manager/ReactiveEntityManager";
|
||||
import {TreeRepository} from "../repository/TreeRepository";
|
||||
import {ReactiveTreeRepository} from "../repository/ReactiveTreeRepository";
|
||||
|
||||
/**
|
||||
* Temporary type to store and link both repository and its metadata.
|
||||
*/
|
||||
type RepositoryAndMetadata = { metadata: EntityMetadata, repository: Repository<any>, reactiveRepository: ReactiveRepository<any> };
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategyInterface";
|
||||
import {RepositoryBuilder} from "../repository/RepositoryBuilder";
|
||||
|
||||
/**
|
||||
* A single connection instance to the database. Each connection has its own repositories, subscribers and metadatas.
|
||||
@ -37,7 +34,8 @@ export class Connection {
|
||||
// Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private repositoryAndMetadatas: RepositoryAndMetadata[] = [];
|
||||
private repositories: Repository<any>[] = [];
|
||||
private reactiveRepositories: ReactiveRepository<any>[] = [];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Readonly properties
|
||||
@ -143,34 +141,32 @@ export class Connection {
|
||||
/**
|
||||
* Performs connection to the database.
|
||||
*/
|
||||
connect(): Promise<this> {
|
||||
async connect(): Promise<this> {
|
||||
if (this.isConnected)
|
||||
throw new CannotConnectAlreadyConnectedError(this.name);
|
||||
|
||||
return this.driver.connect().then(() => {
|
||||
|
||||
// first build all metadata
|
||||
this.buildMetadatas();
|
||||
|
||||
// second build schema
|
||||
if (this.options.autoSchemaCreate === true)
|
||||
return this.syncSchema();
|
||||
|
||||
return Promise.resolve();
|
||||
// connect to the database via its driver
|
||||
await this.driver.connect();
|
||||
|
||||
}).then(() => {
|
||||
this._isConnected = true;
|
||||
return this;
|
||||
});
|
||||
// build all metadatas registered in the current connection
|
||||
this.buildMetadatas();
|
||||
|
||||
// second build schema
|
||||
if (this.options.autoSchemaCreate === true)
|
||||
await this.syncSchema();
|
||||
|
||||
// set connected status for the current connection
|
||||
this._isConnected = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this connection.
|
||||
*/
|
||||
close(): Promise<void> {
|
||||
async close(): Promise<void> {
|
||||
if (!this.isConnected)
|
||||
throw new CannotCloseNotConnectedError(this.name);
|
||||
|
||||
|
||||
return this.driver.disconnect();
|
||||
}
|
||||
|
||||
@ -184,7 +180,7 @@ export class Connection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports entities from the given paths (directories) for the current connection.
|
||||
* Imports entities from the given paths (directories) for the current connection.
|
||||
*/
|
||||
importEntitiesFromDirectories(paths: string[]): this {
|
||||
this.importEntities(importClassesFromDirectories(paths));
|
||||
@ -206,7 +202,7 @@ export class Connection {
|
||||
this.importEntities(importClassesFromDirectories(paths));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Imports entities for the current connection.
|
||||
*/
|
||||
@ -235,7 +231,7 @@ export class Connection {
|
||||
importNamingStrategies(strategies: Function[]): this {
|
||||
if (this.isConnected)
|
||||
throw new CannotImportAlreadyConnectedError("naming strategies", this.name);
|
||||
|
||||
|
||||
strategies.forEach(cls => this.namingStrategyClasses.push(cls));
|
||||
return this;
|
||||
}
|
||||
@ -264,12 +260,12 @@ export class Connection {
|
||||
} else {
|
||||
metadata = this.entityMetadatas.findByTarget(entityClassOrName);
|
||||
}
|
||||
|
||||
const repoMeta = this.repositoryAndMetadatas.find(repoMeta => repoMeta.metadata === metadata);
|
||||
if (!repoMeta)
|
||||
|
||||
const repository = this.repositories.find(repository => Repository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new RepositoryNotFoundError(this.name, entityClassOrName);
|
||||
|
||||
return repoMeta.repository;
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -297,13 +293,13 @@ export class Connection {
|
||||
metadata = this.entityMetadatas.findByTarget(entityClassOrName);
|
||||
}
|
||||
|
||||
const repoMeta = this.repositoryAndMetadatas.find(repoMeta => repoMeta.metadata === metadata);
|
||||
if (!repoMeta)
|
||||
const repository = this.repositories.find(repository => Repository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new RepositoryNotFoundError(this.name, entityClassOrName);
|
||||
if (!repoMeta.metadata.table.isClosure)
|
||||
throw new Error(`Cannot get a tree repository. ${repoMeta.metadata.name} is not a tree table. Try to use @ClosureTable decorator instead of @Table.`);
|
||||
if (!metadata.table.isClosure)
|
||||
throw new Error(`Cannot get a tree repository. ${metadata.name} is not a tree table. Try to use @ClosureTable decorator instead of @Table.`);
|
||||
|
||||
return <TreeRepository<Entity>> repoMeta.repository;
|
||||
return <TreeRepository<Entity>> repository;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -314,11 +310,11 @@ export class Connection {
|
||||
throw new NoConnectionForRepositoryError(this.name);
|
||||
|
||||
const metadata = this.entityMetadatas.findByTarget(entityClass);
|
||||
const repoMeta = this.repositoryAndMetadatas.find(repoMeta => repoMeta.metadata === metadata);
|
||||
if (!repoMeta)
|
||||
throw new RepositoryNotFoundError(this.name, entityClass);
|
||||
const repository = this.reactiveRepositories.find(repository => ReactiveRepository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new RepositoryNotFoundError(this.name, entityClass); // todo throw ReactiveRepositoryNotFoundError
|
||||
|
||||
return repoMeta.reactiveRepository;
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -329,13 +325,13 @@ export class Connection {
|
||||
throw new NoConnectionForRepositoryError(this.name);
|
||||
|
||||
const metadata = this.entityMetadatas.findByTarget(entityClass);
|
||||
const repoMeta = this.repositoryAndMetadatas.find(repoMeta => repoMeta.metadata === metadata);
|
||||
if (!repoMeta)
|
||||
const repository = this.reactiveRepositories.find(repository => ReactiveRepository.ownsMetadata(repository, metadata));
|
||||
if (!repository)
|
||||
throw new RepositoryNotFoundError(this.name, entityClass);
|
||||
if (!repoMeta.metadata.table.isClosure)
|
||||
throw new Error(`Cannot get a tree repository. ${repoMeta.metadata.name} is not a tree table. Try to use @ClosureTable decorator instead of @Table.`);
|
||||
if (!metadata.table.isClosure)
|
||||
throw new Error(`Cannot get a tree repository. ${metadata.name} is not a tree table. Try to use @ClosureTable decorator instead of @Table.`);
|
||||
|
||||
return <ReactiveTreeRepository<Entity>> repoMeta.reactiveRepository;
|
||||
return <ReactiveTreeRepository<Entity>> repository;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -346,7 +342,7 @@ export class Connection {
|
||||
* Builds all registered metadatas.
|
||||
*/
|
||||
private buildMetadatas() {
|
||||
|
||||
|
||||
// first register naming strategies
|
||||
const metadatas = defaultMetadataStorage().namingStrategyMetadatas.filterByClasses(this.namingStrategyClasses);
|
||||
metadatas.forEach(cls => this.namingStrategyMetadatas.push(cls));
|
||||
@ -355,7 +351,7 @@ export class Connection {
|
||||
const subscribers = defaultMetadataStorage()
|
||||
.eventSubscriberMetadatas
|
||||
.filterByClasses(this.subscriberClasses)
|
||||
.map(metadata => this.createContainerInstance(metadata.target));
|
||||
.map(metadata => getFromContainer(metadata.target));
|
||||
this.eventSubscribers.push(...subscribers);
|
||||
|
||||
// third register entity and entity listener metadatas
|
||||
@ -365,49 +361,35 @@ export class Connection {
|
||||
|
||||
entityMetadatas.forEach(cls => this.entityMetadatas.push(cls));
|
||||
entityListenerMetadatas.forEach(cls => this.entityListeners.push(cls));
|
||||
entityMetadatas.map(metadata => this.createRepoMeta(metadata)).forEach(cls => this.repositoryAndMetadatas.push(cls));
|
||||
entityMetadatas.forEach(metadata => this.createRepository(metadata));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the naming strategy to be used for this connection.
|
||||
*/
|
||||
private createNamingStrategy() {
|
||||
private createNamingStrategy(): NamingStrategyInterface {
|
||||
if (!this.options.namingStrategy)
|
||||
return new DefaultNamingStrategy();
|
||||
|
||||
const namingMetadata = this.namingStrategyMetadatas.find(strategy => strategy.name === this.options.namingStrategy);
|
||||
if (!namingMetadata)
|
||||
throw new Error(`Naming strategy called "${this.options.namingStrategy}" was not found.`);
|
||||
|
||||
return this.createContainerInstance(namingMetadata.target);
|
||||
|
||||
return getFromContainer<NamingStrategyInterface>(namingMetadata.target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the given constructor. If service container is registered in the ORM, then it will
|
||||
* be used, otherwise new instance of naming strategy will be created.
|
||||
* Creates repository and reactive repository for the given entity metadata.
|
||||
*/
|
||||
private createContainerInstance(constructor: Function) {
|
||||
return getContainer() ? getContainer().get(constructor) : new (<any> constructor)();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary object RepositoryAndMetadata to store relation between repository and metadata.
|
||||
*/
|
||||
private createRepoMeta(metadata: EntityMetadata): RepositoryAndMetadata {
|
||||
private createRepository(metadata: EntityMetadata) {
|
||||
if (metadata.table.isClosure) {
|
||||
const repository = new TreeRepository<any>(this, this.entityMetadatas, metadata);
|
||||
return {
|
||||
metadata: metadata,
|
||||
repository: repository,
|
||||
reactiveRepository: new ReactiveTreeRepository(repository)
|
||||
};
|
||||
this.repositories.push(repository);
|
||||
this.reactiveRepositories.push(new ReactiveTreeRepository(repository));
|
||||
} else {
|
||||
const repository = new Repository<any>(this, this.entityMetadatas, metadata);
|
||||
return {
|
||||
metadata: metadata,
|
||||
repository: repository,
|
||||
reactiveRepository: new ReactiveRepository(repository)
|
||||
};
|
||||
this.repositories.push(repository);
|
||||
this.reactiveRepositories.push(new ReactiveRepository(repository));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
24
src/index.ts
24
src/index.ts
@ -14,21 +14,33 @@ import {CreateConnectionOptions} from "./connection-manager/CreateConnectionOpti
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Container to be used by TypeORM for inversion control.
|
||||
* Container to be used by this library for inversion control. If container was not implicitly set then by default
|
||||
* container simply creates a new instance of the given class.
|
||||
*/
|
||||
let container: { get(someClass: any): any };
|
||||
let container: { get<T>(someClass: { new (...args: any[]): T }|Function): T } = new (class {
|
||||
private instances: any[] = [];
|
||||
get<T>(someClass: { new (...args: any[]): T }): T {
|
||||
if (!this.instances[<any>someClass])
|
||||
this.instances[<any>someClass] = new someClass();
|
||||
|
||||
return this.instances[<any>someClass];
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* Sets container to be used by TypeORM.
|
||||
*
|
||||
* Sets container to be used by this library.
|
||||
*
|
||||
* @param iocContainer
|
||||
*/
|
||||
export function useContainer(iocContainer: { get(someClass: any): any }) {
|
||||
container = iocContainer;
|
||||
}
|
||||
|
||||
export function getContainer() {
|
||||
return container;
|
||||
/**
|
||||
* Gets the IOC container used by this library.
|
||||
*/
|
||||
export function getFromContainer<T>(someClass: { new (...args: any[]): T }|Function): T {
|
||||
return container.get<T>(someClass);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -2,6 +2,7 @@ import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {FindOptions} from "./FindOptions";
|
||||
import {Repository} from "./Repository";
|
||||
import * as Rx from "rxjs/Rx";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
|
||||
/**
|
||||
* Repository is supposed to work with your entity objects. Find entities, insert, update, delete, etc.
|
||||
@ -208,4 +209,12 @@ export class ReactiveRepository<Entity> {
|
||||
return Rx.Observable.fromPromise(this.repository.transaction(runInTransaction));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Static Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static ownsMetadata(reactiveRepository: ReactiveRepository<any>, metadata: EntityMetadata) {
|
||||
return Repository.ownsMetadata(reactiveRepository.repository, metadata);
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,17 +15,17 @@ import {Driver} from "../driver/Driver";
|
||||
* Repository is supposed to work with your entity objects. Find entities, insert, update, delete, etc.
|
||||
*/
|
||||
export class Repository<Entity> {
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private driver: Driver;
|
||||
private broadcaster: Broadcaster;
|
||||
private persistOperationExecutor: PersistOperationExecutor;
|
||||
private entityPersistOperationBuilder: EntityPersistOperationBuilder;
|
||||
private plainObjectToEntityTransformer: PlainObjectToNewEntityTransformer;
|
||||
private plainObjectToDatabaseEntityTransformer: PlainObjectToDatabaseEntityTransformer<Entity>;
|
||||
protected driver: Driver;
|
||||
protected broadcaster: Broadcaster;
|
||||
protected persistOperationExecutor: PersistOperationExecutor;
|
||||
protected entityPersistOperationBuilder: EntityPersistOperationBuilder;
|
||||
protected plainObjectToEntityTransformer: PlainObjectToNewEntityTransformer;
|
||||
protected plainObjectToDatabaseEntityTransformer: PlainObjectToDatabaseEntityTransformer<Entity>;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
@ -412,4 +412,12 @@ export class Repository<Entity> {
|
||||
return entity;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Static Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static ownsMetadata(repository: Repository<any>, metadata: EntityMetadata) {
|
||||
return repository.metadata === metadata;
|
||||
}
|
||||
|
||||
}
|
||||
33
src/repository/RepositoryBuilder.ts
Normal file
33
src/repository/RepositoryBuilder.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {PlainObjectToNewEntityTransformer} from "../query-builder/transformer/PlainObjectToNewEntityTransformer";
|
||||
import {PlainObjectToDatabaseEntityTransformer} from "../query-builder/transformer/PlainObjectToDatabaseEntityTransformer";
|
||||
import {EntityPersistOperationBuilder} from "../persistment/EntityPersistOperationsBuilder";
|
||||
import {PersistOperationExecutor} from "../persistment/PersistOperationExecutor";
|
||||
import {EntityWithId} from "../persistment/operation/PersistOperation";
|
||||
import {FindOptions, FindOptionsUtils} from "./FindOptions";
|
||||
import {EntityMetadataCollection} from "../metadata/collection/EntityMetadataCollection";
|
||||
import {Broadcaster} from "../subscriber/Broadcaster";
|
||||
import {Driver} from "../driver/Driver";
|
||||
|
||||
/**
|
||||
* Repository is supposed to work with your entity objects. Find entities, insert, update, delete, etc.
|
||||
*/
|
||||
export class RepositoryBuilder {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected connection: Connection,
|
||||
protected entityMetadatas: EntityMetadataCollection,
|
||||
protected metadata: EntityMetadata) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
"version": "1.8.0",
|
||||
"compilerOptions": {
|
||||
"outDir": "build/es5",
|
||||
"target": "es5",
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user