fixed issues with query runner manager and repo instances

This commit is contained in:
Umed Khudoiberdiev 2017-07-03 17:31:13 +05:00
parent e7c516e7f4
commit 555cd69f46
25 changed files with 229 additions and 168 deletions

View File

@ -29,6 +29,7 @@ import {DriverFactory} from "../driver/DriverFactory";
import {ConnectionMetadataBuilder} from "./ConnectionMetadataBuilder";
import {QueryRunner} from "../query-runner/QueryRunner";
import {SelectQueryBuilder} from "../query-builder/SelectQueryBuilder";
import {SqliteDriver} from "../driver/sqlite/SqliteDriver";
/**
* Connection is a single database ORM connection to a specific DBMS database.
@ -269,7 +270,7 @@ export class Connection {
* Gets repository for the given entity.
*/
getRepository<Entity>(target: ObjectType<Entity>|string): Repository<Entity> {
return this.getMetadata(target).repository;
return this.manager.getRepository(target);
}
/**
@ -277,17 +278,7 @@ export class Connection {
* Only tree-type entities can have a TreeRepository, like ones decorated with @ClosureEntity decorator.
*/
getTreeRepository<Entity>(target: ObjectType<Entity>|string): TreeRepository<Entity> {
if (this.driver instanceof MongoDriver)
throw new Error(`You cannot use getTreeRepository for MongoDB connections.`);
if (!this.hasMetadata(target))
throw new RepositoryNotFoundError(this.name, target);
const repository = this.getMetadata(target).repository;
if (!(repository instanceof TreeRepository))
throw new RepositoryNotTreeError(target);
return repository;
return this.manager.getTreeRepository(target);
}
/**
@ -298,10 +289,7 @@ export class Connection {
if (!(this.driver instanceof MongoDriver))
throw new Error(`You can use getMongoRepository only for MongoDB connections.`);
if (!this.hasMetadata(target))
throw new RepositoryNotFoundError(this.name, target);
return this.getMetadata(target).repository as MongoRepository<Entity>;
return this.manager.getRepository(target) as MongoRepository<Entity>;
}
/**
@ -315,32 +303,8 @@ export class Connection {
* Wraps given function execution (and all operations made there) into a transaction.
* All database operations must be executed using provided entity manager.
*/
async transaction(runInTransaction: (entityManger: EntityManager) => Promise<any>, queryRunner?: QueryRunner): Promise<any> {
if (this instanceof MongoEntityManager)
throw new Error(`Transactions aren't supported by MongoDB.`);
if (queryRunner && queryRunner.isReleased)
throw new QueryRunnerProviderAlreadyReleasedError();
const usedQueryRunner = queryRunner || this.createQueryRunner();
const transactionEntityManager = new EntityManagerFactory().create(this, usedQueryRunner);
try {
await usedQueryRunner.startTransaction();
const result = await runInTransaction(transactionEntityManager);
await usedQueryRunner.commitTransaction();
return result;
} catch (err) {
try { // we throw original error even if rollback thrown an error
await usedQueryRunner.rollbackTransaction();
} catch (rollbackError) { }
throw err;
} finally {
if (!queryRunner) // if we used a new query runner provider then release it
await usedQueryRunner.release();
}
async transaction(runInTransaction: (entityManger: EntityManager) => Promise<any>): Promise<any> {
return this.manager.transaction(runInTransaction);
}
/**
@ -406,26 +370,31 @@ export class Connection {
* 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 database connection back to pool).
*/
createIsolatedManager(queryRunner?: QueryRunner): EntityManager {
if (queryRunner && queryRunner.manager && queryRunner.manager !== this.manager)
return queryRunner.manager;
createIsolatedManager(): EntityManager {
if (this.driver instanceof MongoDriver)
throw new Error(`You can use createIsolatedManager only for non MongoDB connections.`);
if (!queryRunner)
queryRunner = this.createQueryRunner();
// sqlite has a single query runner and does not support isolated managers
if (this.driver instanceof SqliteDriver)
return this.manager;
Object.assign(queryRunner, { manager: new EntityManagerFactory().create(this, queryRunner) });
return queryRunner.manager;
return new EntityManagerFactory().create(this, this.driver.createQueryRunner());
}
/**
* Creates a new repository with a single opened connection to the database.
* This may be useful if you want to perform all db queries within one connection.
* After finishing with entity manager, don't forget to release it (to release database connection back to pool).
* After finishing with repository, don't forget to release its query runner (to release database connection back to pool).
*/
createIsolatedRepository<Entity>(entityClassOrName: ObjectType<Entity>|string, queryRunner?: QueryRunner): Repository<Entity> {
if (!queryRunner)
queryRunner = this.createQueryRunner();
return new RepositoryFactory().create(this, this.getMetadata(entityClassOrName), queryRunner);
createIsolatedRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): Repository<Entity> {
if (this.driver instanceof MongoDriver)
throw new Error(`You can use createIsolatedRepository only for non MongoDB connections.`);
// sqlite has a single query runner and does not support isolated repositories
if (this.driver instanceof SqliteDriver)
return this.manager.getRepository(entityClassOrName);
return this.createIsolatedManager().getRepository(entityClassOrName);
}
// -------------------------------------------------------------------------
@ -465,7 +434,6 @@ export class Connection {
protected buildMetadatas(): void {
const connectionMetadataBuilder = new ConnectionMetadataBuilder(this);
const repositoryFactory = new RepositoryFactory();
const entityMetadataValidator = new EntityMetadataValidator();
// create subscribers instances if they are not disallowed from high-level (for example they can disallowed from migrations run process)
@ -482,11 +450,6 @@ export class Connection {
const migrations = connectionMetadataBuilder.buildMigrations(this.options.migrations || []);
Object.assign(this, { migrations: migrations });
// initialize repositories for all entity metadatas
this.entityMetadatas.forEach(metadata => {
metadata.repository = repositoryFactory.create(this, metadata);
});
// validate all created entity metadatas to make sure user created entities are valid and correct
entityMetadataValidator.validateMany(this.entityMetadatas);
}

View File

@ -4,6 +4,7 @@ import {ObjectLiteral} from "../common/ObjectLiteral";
import {ColumnType} from "./types/ColumnTypes";
import {MappedColumnTypes} from "./types/MappedColumnTypes";
import {SchemaBuilder} from "../schema-builder/SchemaBuilder";
import {EntityManager} from "../entity-manager/EntityManager";
import {DataTypeDefaults} from "./types/DataTypeDefaults";
/**

View File

@ -12,6 +12,7 @@ import {MongoConnectionOptions} from "./MongoConnectionOptions";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ColumnType} from "../types/ColumnTypes";
import {MongoSchemaBuilder} from "../../schema-builder/MongoSchemaBuilder";
import {EntityManager} from "../../entity-manager/EntityManager";
import {DataTypeDefaults} from "../types/DataTypeDefaults";
/**

View File

@ -54,11 +54,6 @@ export class MongoQueryRunner implements QueryRunner {
*/
connection: Connection;
/**
* Entity manager isolated for this query runner.
*/
manager: EntityManager;
/**
* Indicates if connection for this query runner is released.
* Once its released, query runner cannot run queries anymore.
@ -83,7 +78,6 @@ export class MongoQueryRunner implements QueryRunner {
constructor(connection: Connection, databaseConnection: Db) {
this.connection = connection;
this.manager = connection.manager;
this.databaseConnection = databaseConnection;
}

View File

@ -13,6 +13,7 @@ import {RdbmsSchemaBuilder} from "../../schema-builder/RdbmsSchemaBuilder";
import {MysqlConnectionOptions} from "./MysqlConnectionOptions";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ColumnType} from "../types/ColumnTypes";
import {EntityManager} from "../../entity-manager/EntityManager";
import {DataTypeDefaults} from "../types/DataTypeDefaults";
/**

View File

@ -23,16 +23,16 @@ export class MysqlQueryRunner implements QueryRunner {
// Public Implemented Properties
// -------------------------------------------------------------------------
/**
* Database driver used by connection.
*/
driver: MysqlDriver;
/**
* Connection used by this query runner.
*/
connection: Connection;
/**
* Entity manager isolated for this query runner.
*/
manager: EntityManager;
/**
* Indicates if connection for this query runner is released.
* Once its released, query runner cannot run queries anymore.
@ -72,9 +72,9 @@ export class MysqlQueryRunner implements QueryRunner {
// Constructor
// -------------------------------------------------------------------------
constructor(protected driver: MysqlDriver) {
constructor(driver: MysqlDriver) {
this.driver = driver;
this.connection = driver.connection;
this.manager = driver.connection.manager;
}
// -------------------------------------------------------------------------

View File

@ -12,6 +12,7 @@ import {RdbmsSchemaBuilder} from "../../schema-builder/RdbmsSchemaBuilder";
import {OracleConnectionOptions} from "./OracleConnectionOptions";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ColumnType} from "../types/ColumnTypes";
import {EntityManager} from "../../entity-manager/EntityManager";
import {DataTypeDefaults} from "../types/DataTypeDefaults";
/**

View File

@ -25,16 +25,16 @@ export class OracleQueryRunner implements QueryRunner {
// Public Implemented Properties
// -------------------------------------------------------------------------
/**
* Database driver used by connection.
*/
driver: OracleDriver;
/**
* Connection used by this query runner.
*/
connection: Connection;
/**
* Entity manager isolated for this query runner.
*/
manager: EntityManager;
/**
* Indicates if connection for this query runner is released.
* Once its released, query runner cannot run queries anymore.
@ -74,9 +74,9 @@ export class OracleQueryRunner implements QueryRunner {
// Constructor
// -------------------------------------------------------------------------
constructor(protected driver: OracleDriver) {
constructor(driver: OracleDriver) {
this.driver = driver;
this.connection = driver.connection;
this.manager = driver.connection.manager;
}
// -------------------------------------------------------------------------

View File

@ -14,6 +14,7 @@ import {PostgresConnectionOptions} from "./PostgresConnectionOptions";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ColumnType} from "../types/ColumnTypes";
import {QueryRunner} from "../../query-runner/QueryRunner";
import {EntityManager} from "../../entity-manager/EntityManager";
import {DataTypeDefaults} from "../types/DataTypeDefaults";
/**

View File

@ -23,16 +23,16 @@ export class PostgresQueryRunner implements QueryRunner {
// Public Implemented Properties
// -------------------------------------------------------------------------
/**
* Database driver used by connection.
*/
driver: PostgresDriver;
/**
* Connection used by this query runner.
*/
connection: Connection;
/**
* Entity manager isolated for this query runner.
*/
manager: EntityManager;
/**
* Indicates if connection for this query runner is released.
* Once its released, query runner cannot run queries anymore.
@ -77,9 +77,9 @@ export class PostgresQueryRunner implements QueryRunner {
// Constructor
// -------------------------------------------------------------------------
constructor(protected driver: PostgresDriver) {
constructor(driver: PostgresDriver) {
this.driver = driver;
this.connection = driver.connection;
this.manager = driver.connection.manager;
}
// -------------------------------------------------------------------------

View File

@ -12,6 +12,7 @@ import {SqliteConnectionOptions} from "./SqliteConnectionOptions";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ColumnType} from "../types/ColumnTypes";
import {QueryRunner} from "../../query-runner/QueryRunner";
import {EntityManager} from "../../entity-manager/EntityManager";
import {DataTypeDefaults} from "../types/DataTypeDefaults";
/**

View File

@ -27,16 +27,16 @@ export class SqliteQueryRunner implements QueryRunner {
// Public Implemented Properties
// -------------------------------------------------------------------------
/**
* Database driver used by connection.
*/
driver: SqliteDriver;
/**
* Connection used by this query runner.
*/
connection: Connection;
/**
* Entity manager isolated for this query runner.
*/
manager: EntityManager;
/**
* Indicates if connection for this query runner is released.
* Once its released, query runner cannot run queries anymore.
@ -66,9 +66,9 @@ export class SqliteQueryRunner implements QueryRunner {
// Constructor
// -------------------------------------------------------------------------
constructor(protected driver: SqliteDriver) {
constructor(driver: SqliteDriver) {
this.driver = driver;
this.connection = driver.connection;
this.manager = driver.connection.manager;
}
// -------------------------------------------------------------------------

View File

@ -13,6 +13,7 @@ import {RdbmsSchemaBuilder} from "../../schema-builder/RdbmsSchemaBuilder";
import {SqlServerConnectionOptions} from "./SqlServerConnectionOptions";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ColumnType} from "../types/ColumnTypes";
import {EntityManager} from "../../entity-manager/EntityManager";
import {DataTypeDefaults} from "../types/DataTypeDefaults";
/**
@ -112,7 +113,7 @@ export class SqlServerDriver implements Driver {
varchar: { length: 255 },
nvarchar: { length: 255 }
};
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------

View File

@ -23,16 +23,16 @@ export class SqlServerQueryRunner implements QueryRunner {
// Public Implemented Properties
// -------------------------------------------------------------------------
/**
* Database driver used by connection.
*/
driver: SqlServerDriver;
/**
* Connection used by this query runner.
*/
connection: Connection;
/**
* Entity manager isolated for this query runner.
*/
manager: EntityManager;
/**
* Indicates if connection for this query runner is released.
* Once its released, query runner cannot run queries anymore.
@ -76,9 +76,9 @@ export class SqlServerQueryRunner implements QueryRunner {
// Constructor
// -------------------------------------------------------------------------
constructor(protected driver: SqlServerDriver) {
constructor(driver: SqlServerDriver) {
this.driver = driver;
this.connection = driver.connection;
this.manager = driver.connection.manager;
}
// -------------------------------------------------------------------------

View File

@ -10,6 +10,7 @@ import {RdbmsSchemaBuilder} from "../../schema-builder/RdbmsSchemaBuilder";
import {WebSqlConnectionOptions} from "./WebSqlConnectionOptions";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ColumnType} from "../types/ColumnTypes";
import {EntityManager} from "../../entity-manager/EntityManager";
import {DataTypeDefaults} from "../types/DataTypeDefaults";
/**

View File

@ -27,16 +27,16 @@ export class WebsqlQueryRunner implements QueryRunner {
// Public Implemented Properties
// -------------------------------------------------------------------------
/**
* Database driver used by connection.
*/
driver: WebsqlDriver;
/**
* Connection used by this query runner.
*/
connection: Connection;
/**
* Entity manager isolated for this query runner.
*/
manager: EntityManager;
/**
* Indicates if connection for this query runner is released.
* Once its released, query runner cannot run queries anymore.
@ -76,9 +76,9 @@ export class WebsqlQueryRunner implements QueryRunner {
// Constructor
// -------------------------------------------------------------------------
constructor(protected driver: WebsqlDriver) {
constructor(driver: WebsqlDriver) {
this.driver = driver;
this.connection = driver.connection;
this.manager = driver.connection.manager;
}
// -------------------------------------------------------------------------

View File

@ -22,6 +22,12 @@ import {AbstractRepository} from "../repository/AbstractRepository";
import {CustomRepositoryCannotInheritRepositoryError} from "../error/CustomRepositoryCannotInheritRepositoryError";
import {QueryRunner} from "../query-runner/QueryRunner";
import {SelectQueryBuilder} from "../query-builder/SelectQueryBuilder";
import {EntityMetadata} from "../metadata/EntityMetadata";
import {MongoDriver} from "../driver/mongodb/MongoDriver";
import {RepositoryNotFoundError} from "../error/RepositoryNotFoundError";
import {RepositoryNotTreeError} from "../error/RepositoryNotTreeError";
import {RepositoryFactory} from "../repository/RepositoryFactory";
import {EntityManagerFactory} from "./EntityManagerFactory";
/**
* Entity manager supposed to work with any entity, automatically find its repository and call its methods,
@ -36,23 +42,29 @@ export class EntityManager {
/**
* Connection used by this entity manager.
*/
connection: Connection;
readonly connection: Connection;
/**
* Custom query runner to be used for operations in this entity manager.
* Used only in non-global entity manager.
*/
readonly queryRunner?: QueryRunner;
// -------------------------------------------------------------------------
// Protected Properties
// -------------------------------------------------------------------------
/**
* Custom query runner to be used for operations in this entity manager.
*/
protected queryRunner?: QueryRunner;
/**
* Stores temporarily user data.
* Useful for sharing data with subscribers.
*/
protected data: ObjectLiteral = {};
/**
* Once created and then reused by en repositories.
*/
protected repositories: { metadata: EntityMetadata, repository: Repository<any> }[] = [];
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
@ -72,7 +84,35 @@ export class EntityManager {
* All database operations must be executed using provided entity manager.
*/
async transaction(runInTransaction: (entityManger: EntityManager) => Promise<any>): Promise<any> {
return this.connection.transaction(runInTransaction, this.queryRunner);
if (this.connection.driver instanceof MongoDriver)
throw new Error(`Transactions aren't supported by MongoDB.`);
if (this.queryRunner && this.queryRunner.isReleased)
throw new QueryRunnerProviderAlreadyReleasedError();
if (this.queryRunner && this.queryRunner.isTransactionActive)
throw new Error(`Cannot start transaction because its already started`);
const usedQueryRunner = this.queryRunner || this.connection.createQueryRunner();
const transactionEntityManager = new EntityManagerFactory().create(this.connection, usedQueryRunner);
try {
await usedQueryRunner.startTransaction();
const result = await runInTransaction(transactionEntityManager);
await usedQueryRunner.commitTransaction();
return result;
} catch (err) {
try { // we throw original error even if rollback thrown an error
await usedQueryRunner.rollbackTransaction();
} catch (rollbackError) { }
throw err;
} finally {
if (!this.queryRunner) // if we used a new query runner provider then release it
await usedQueryRunner.release();
}
}
/**
@ -255,8 +295,15 @@ export class EntityManager {
return Promise.resolve().then(async () => { // we MUST call "fake" resolve here to make sure all properties of lazily loaded properties are resolved.
// todo: use transaction instead if possible
await this.transaction(async transactionEntityManager => {
if (options && options.data)
transactionEntityManager.data = options.data;
});
const queryRunner = this.queryRunner || this.connection.createQueryRunner();
const transactionEntityManager = this.connection.createIsolatedManager(queryRunner);
const transactionEntityManager = new EntityManagerFactory().create(this.connection, queryRunner);
if (options && options.data)
transactionEntityManager.data = options.data;
@ -453,7 +500,7 @@ export class EntityManager {
return Promise.resolve().then(async () => { // we MUST call "fake" resolve here to make sure all properties of lazily loaded properties are resolved.
const queryRunner = this.queryRunner || this.connection.createQueryRunner();
const transactionEntityManager = this.connection.createIsolatedManager(queryRunner);
const transactionEntityManager = new EntityManagerFactory().create(this.connection, queryRunner);
if (options && options.data)
transactionEntityManager.data = options.data;
@ -720,17 +767,27 @@ export class EntityManager {
* 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> {
getRepository<Entity>(target: ObjectType<Entity>|string): Repository<Entity> {
// if single db connection is used then create its own repository with reused query runner
if (this.queryRunner) {
if (this.queryRunner.isReleased)
throw new QueryRunnerProviderAlreadyReleasedError();
if (!this.connection.hasMetadata(target))
throw new RepositoryNotFoundError(this.connection.name, target);
return this.connection.createIsolatedRepository(entityClassOrName, this.queryRunner);
const metadata = this.connection.getMetadata(target);
let repository = this.repositories.find(repo => repo.metadata === metadata);
if (!repository) {
repository = { metadata: metadata, repository: new RepositoryFactory().create(this, metadata, this.queryRunner) };
this.repositories.push(repository);
}
return this.connection.getRepository<Entity>(entityClassOrName as any);
// if single db connection is used then create its own repository with reused query runner
// if (this.queryRunner) {
// if (this.queryRunner.isReleased)
// throw new QueryRunnerProviderAlreadyReleasedError();
//
// return this.connection.createIsolatedRepository(target, this.queryRunner);
// }
return repository.repository;
}
/**
@ -739,17 +796,15 @@ export class EntityManager {
* 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> {
getTreeRepository<Entity>(target: ObjectType<Entity>|string): TreeRepository<Entity> {
if (this.connection.driver instanceof MongoDriver)
throw new Error(`You cannot use getTreeRepository for MongoDB connections.`);
// if single db connection is used then create its own repository with reused query runner
if (this.queryRunner) {
if (this.queryRunner.isReleased)
throw new QueryRunnerProviderAlreadyReleasedError();
const repository = this.getRepository(target);
if (!(repository instanceof TreeRepository))
throw new RepositoryNotTreeError(target);
return this.connection.createIsolatedRepository(entityClassOrName, this.queryRunner) as TreeRepository<Entity>;
}
return this.connection.getTreeRepository<Entity>(entityClassOrName as any);
return repository;
}
/**
@ -772,7 +827,7 @@ export class EntityManager {
if (this.queryRunner.isReleased)
throw new QueryRunnerProviderAlreadyReleasedError();
return this.connection.createIsolatedRepository(entityClassOrName, this.queryRunner) as MongoRepository<Entity>;
return this.connection.createIsolatedRepository(entityClassOrName) as MongoRepository<Entity>;
}
return this.connection.getMongoRepository<Entity>(entityClassOrName as any);

View File

@ -58,6 +58,17 @@ export class MongoEntityManager extends EntityManager {
super(connection);
}
// -------------------------------------------------------------------------
// Overridden Properties
// -------------------------------------------------------------------------
/**
* Gets query runner used to execute queries.
*/
get queryRunner(): MongoQueryRunner {
return (this.connection.driver as MongoDriver).queryRunner!;
}
// -------------------------------------------------------------------------
// Overridden Methods
// -------------------------------------------------------------------------
@ -495,13 +506,6 @@ export class MongoEntityManager extends EntityManager {
// Protected Methods
// -------------------------------------------------------------------------
/**
* Gets query runner used to execute queries.
*/
protected get queryRunner(): MongoQueryRunner {
return (this.connection.driver as MongoDriver).queryRunner!;
}
/**
* Converts FindManyOptions to mongodb query.
*/

View File

@ -25,11 +25,6 @@ export class EntityMetadata {
// Properties
// -------------------------------------------------------------------------
/**
* Repository used for this entity metadata.
*/
repository: Repository<any>;
/**
* Used to wrap lazy relations.
*/

View File

@ -19,11 +19,6 @@ export interface QueryRunner {
*/
readonly connection: Connection;
/**
* Entity manager isolated for this query runner.
*/
readonly manager: EntityManager;
/**
* Indicates if connection for this query runner is released.
* Once its released, query runner cannot run queries anymore.

View File

@ -42,13 +42,13 @@ import {SelectQueryBuilder} from "../query-builder/SelectQueryBuilder";
export class MongoRepository<Entity extends ObjectLiteral> extends Repository<Entity> {
// -------------------------------------------------------------------------
// Protected Methods Set Dynamically
// Public Properties
// -------------------------------------------------------------------------
/**
* Entity Manager used by this repository.
*/
protected manager: MongoEntityManager;
readonly manager: MongoEntityManager;
// -------------------------------------------------------------------------
// Overridden Methods

View File

@ -15,25 +15,23 @@ import {SelectQueryBuilder} from "../query-builder/SelectQueryBuilder";
export class Repository<Entity extends ObjectLiteral> {
// -------------------------------------------------------------------------
// Protected Methods Set Dynamically
// Public Properties
// -------------------------------------------------------------------------
// todo: wny not to make them public?
/**
* Entity Manager used by this repository.
*/
protected manager: EntityManager;
readonly manager: EntityManager;
/**
* Entity metadata of the entity current repository manages.
*/
protected metadata: EntityMetadata;
readonly metadata: EntityMetadata;
/**
* Query runner provider used for this repository.
*/
protected queryRunner?: QueryRunner;
readonly queryRunner?: QueryRunner;
// -------------------------------------------------------------------------
// Public Methods

View File

@ -5,6 +5,7 @@ import {Repository} from "./Repository";
import {MongoDriver} from "../driver/mongodb/MongoDriver";
import {MongoRepository} from "./MongoRepository";
import {QueryRunner} from "../query-runner/QueryRunner";
import {EntityManager} from "../entity-manager/EntityManager";
/**
* Factory used to create different types of repositories.
@ -18,29 +19,33 @@ export class RepositoryFactory {
/**
* Creates a repository.
*/
create(connection: Connection, metadata: EntityMetadata, queryRunner?: QueryRunner): Repository<any> {
create(manager: EntityManager, metadata: EntityMetadata, queryRunner?: QueryRunner): Repository<any> {
if (metadata.isClosure) {
// NOTE: dynamic access to protected properties. We need this to prevent unwanted properties in those classes to be exposed,
// however we need these properties for internal work of the class
const repository = new TreeRepository<any>();
(repository as any)["manager"] = connection.manager;
(repository as any)["metadata"] = metadata;
(repository as any)["queryRunner"] = queryRunner;
Object.assign(repository, {
manager: manager,
metadata: metadata,
queryRunner: queryRunner,
});
return repository;
} else {
// NOTE: dynamic access to protected properties. We need this to prevent unwanted properties in those classes to be exposed,
// however we need these properties for internal work of the class
let repository: Repository<any>;
if (connection.driver instanceof MongoDriver) {
if (manager.connection.driver instanceof MongoDriver) {
repository = new MongoRepository();
} else {
repository = new Repository<any>();
}
(repository as any)["manager"] = connection.manager;
(repository as any)["metadata"] = metadata;
(repository as any)["queryRunner"] = queryRunner;
Object.assign(repository, {
manager: manager,
metadata: metadata,
queryRunner: queryRunner,
});
return repository;
}

View File

@ -0,0 +1,14 @@
import {Entity} from "../../../../../src/decorator/entity/Entity";
import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn";
import {Column} from "../../../../../src/decorator/columns/Column";
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
}

View File

@ -0,0 +1,30 @@
import "reflect-metadata";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils";
import {Connection} from "../../../../src/connection/Connection";
import {Post} from "./entity/Post";
import {expect} from "chai";
describe("transaction > single query runner", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
schemaCreate: true,
dropSchemaOnConnection: true,
}));
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));
it("should execute all operations in the method in a transaction", () => Promise.all(connections.map(async connection => {
return connection.transaction(async transactionalEntityManager => {
const originalQueryRunner = transactionalEntityManager.queryRunner;
expect(originalQueryRunner).to.exist;
expect(transactionalEntityManager.getRepository(Post).queryRunner).to.exist;
transactionalEntityManager.getRepository(Post).queryRunner!.should.be.equal(originalQueryRunner);
transactionalEntityManager.getRepository(Post).manager.should.be.equal(transactionalEntityManager);
});
})));
});