refactored relations between connection, driver and other classes

This commit is contained in:
Umed Khudoiberdiev 2016-04-25 14:43:47 +05:00
parent 31f0c0b2e8
commit ac5bb23955
23 changed files with 151 additions and 90 deletions

View File

@ -11,7 +11,7 @@ First you need to create a new subscriber class and implement `EventSubscriberIn
```typescript
import {EventSubscriber, UpdateEvent, RemoveEvent, InsertEvent} from "typeorm/listeners"
import {EventSubscriberInterface} from "typeorm/subscriber/EventSubscriberInterface";
import {EventSubscriberInterface} from "typeorm/typeorm";
@EventSubscriber()
export class MySubscriber implements EventSubscriberInterface<any> {
@ -80,13 +80,7 @@ You can also use listeners in your entities. Such listeners can be convenient fo
```typescript
import {Table} from "typeorm/tables";
import {AfterLoad} from "typeorm/decorator/listeners/AfterLoad";
import {AfterInsert} from "typeorm/decorator/listeners/AfterInsert";
import {BeforeInsert} from "typeorm/decorator/listeners/BeforeInsert";
import {BeforeUpdate} from "typeorm/decorator/listeners/BeforeUpdate";
import {AfterUpdate} from "typeorm/decorator/listeners/AfterUpdate";
import {BeforeRemove} from "typeorm/decorator/listeners/BeforeRemove";
import {AfterRemove} from "typeorm/decorator/listeners/AfterRemove";
import {AfterLoad, AfterInsert, BeforeInsert, BeforeUpdate, AfterUpdate, BeforeRemove, AfterRemove} from "typeorm/listeners";
@Table("posts")
export class Post {

View File

@ -1,7 +1,7 @@
{
"name": "typeorm",
"private": true,
"version": "0.0.2-alpha.6",
"version": "0.0.2-alpha.7",
"description": "Data-mapper ORM for Typescript",
"license": "Apache-2.0",
"readmeFilename": "README.md",

View File

@ -69,7 +69,7 @@ export class Connection {
constructor(name: string, driver: Driver, options: ConnectionOptions) {
this.name = name;
this.driver = driver;
this.driver.connection = this;
this.driver.connectionOptions = options;
this.options = options;
this.entityManager = new EntityManager(this);
}
@ -113,7 +113,8 @@ export class Connection {
* Gets repository for the given entity class.
*/
getRepository<Entity>(entityClass: ConstructorFunction<Entity>|Function): Repository<Entity> {
const metadata = this.getEntityMetadata(entityClass);
// const metadata = this.getEntityMetadata(entityClass);
const metadata = this.entityMetadatas.find(metadata => metadata.target === entityClass);
const repoMeta = this.repositoryAndMetadatas.find(repoMeta => repoMeta.metadata === metadata);
if (!repoMeta)
throw new RepositoryNotFoundError(entityClass);
@ -164,7 +165,7 @@ export class Connection {
private createRepoMeta(metadata: EntityMetadata): RepositoryAndMetadata {
return {
metadata: metadata,
repository: new Repository<any>(this, metadata)
repository: new Repository<any>(this, this.entityMetadatas, metadata)
};
}

View File

@ -1,4 +1,4 @@
import {Connection} from "../connection/Connection";
import {ConnectionOptions} from "../connection/ConnectionOptions";
/**
* Provides base functionality for all driver implementations.
@ -10,34 +10,34 @@ export abstract class BaseDriver {
// Properties
// -------------------------------------------------------------------------
connection: Connection;
abstract connectionOptions: ConnectionOptions;
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
protected logQuery(query: string) {
if (this.connection.options.logging && this.connection.options.logging.logQueries)
if (this.connectionOptions.logging && this.connectionOptions.logging.logQueries)
this.log("executing query: " + query, "log");
}
protected logQueryError(error: any) {
if (this.connection.options.logging && this.connection.options.logging.logFailedQueryError) {
if (this.connectionOptions.logging && this.connectionOptions.logging.logFailedQueryError) {
this.log("error during executing query:", "error");
this.log(error, "error");
}
}
protected logFailedQuery(query: string) {
if (this.connection.options.logging &&
(this.connection.options.logging.logQueries || this.connection.options.logging.logOnlyFailedQueries))
if (this.connectionOptions.logging &&
(this.connectionOptions.logging.logQueries || this.connectionOptions.logging.logOnlyFailedQueries))
this.log("query failed: " + query, "error");
}
protected log(message: any, level: "log"|"debug"|"info"|"error") {
if (!this.connection.options.logging) return;
if (this.connection.options && this.connection.options.logging && this.connection.options.logging.logger) {
this.connection.options.logging.logger(message, level);
if (!this.connectionOptions.logging) return;
if (this.connectionOptions && this.connectionOptions.logging && this.connectionOptions.logging.logger) {
this.connectionOptions.logging.logger(message, level);
} else {
switch (level) {
case "log":

View File

@ -1,7 +1,6 @@
import {SchemaBuilder} from "../schema-builder/SchemaBuilder";
import {QueryBuilder} from "../query-builder/QueryBuilder";
import {Connection} from "../connection/Connection";
import {ColumnMetadata} from "../metadata-builder/metadata/ColumnMetadata";
import {ConnectionOptions} from "../connection/ConnectionOptions";
/**
* Driver communicates with specific database.
@ -19,9 +18,9 @@ export interface Driver {
readonly nativeConnection: any;
/**
* Connection used in this driver.
* Connection options used in this driver.
*/
connection: Connection;
connectionOptions: ConnectionOptions;
/**
* Database name to which this connection is made.
@ -31,7 +30,7 @@ export interface Driver {
/**
* Creates a query builder which can be used to build an sql queries.
*/
createQueryBuilder<Entity>(): QueryBuilder<Entity>;
readonly queryBuilderClass: Function;
/**
* Creates a schema builder which can be used to build database/table schemas.

View File

@ -2,12 +2,12 @@ import {Driver} from "./Driver";
import {SchemaBuilder} from "../schema-builder/SchemaBuilder";
import {QueryBuilder} from "../query-builder/QueryBuilder";
import {MysqlSchemaBuilder} from "../schema-builder/MysqlSchemaBuilder";
import {Connection} from "../connection/Connection";
import {ConnectionIsNotSetError} from "./error/ConnectionIsNotSetError";
import {BaseDriver} from "./BaseDriver";
import {ColumnMetadata} from "../metadata-builder/metadata/ColumnMetadata";
import {ColumnTypes} from "../metadata-builder/types/ColumnTypes";
import * as moment from "moment";
import {ConnectionOptions} from "../connection/ConnectionOptions";
/**
* This driver organizes work with mysql database.
@ -21,7 +21,7 @@ export class MysqlDriver extends BaseDriver implements Driver {
/**
* Connection used in this driver.
*/
connection: Connection;
connectionOptions: ConnectionOptions;
// -------------------------------------------------------------------------
// Private Properties
@ -62,8 +62,8 @@ export class MysqlDriver extends BaseDriver implements Driver {
if (this.mysqlConnection && this.mysqlConnection.config.database)
return this.mysqlConnection.config.database;
if (this.connection.options.database)
return this.connection.options.database;
if (this.connectionOptions.database)
return this.connectionOptions.database;
throw new Error("Cannot get the database name. Since database name is not explicitly given in configuration " +
"(maybe connection url is used?), database name cannot be retrieved until connection is made.");
@ -97,8 +97,8 @@ export class MysqlDriver extends BaseDriver implements Driver {
/**
* Creates a query builder which can be used to build an sql queries.
*/
createQueryBuilder<Entity>(): QueryBuilder<Entity> {
return new QueryBuilder<Entity>(this.connection);
get queryBuilderClass() {
return QueryBuilder;
}
/**
@ -113,10 +113,10 @@ export class MysqlDriver extends BaseDriver implements Driver {
*/
connect(): Promise<void> {
this.mysqlConnection = this.mysql.createConnection({
host: this.connection.options.host,
user: this.connection.options.username,
password: this.connection.options.password,
database: this.connection.options.database
host: this.connectionOptions.host,
user: this.connectionOptions.username,
password: this.connectionOptions.password,
database: this.connectionOptions.database
});
return new Promise<void>((ok, fail) => {
this.mysqlConnection.connect((err: any) => err ? fail(err) : ok());
@ -180,9 +180,24 @@ export class MysqlDriver extends BaseDriver implements Driver {
if (!this.mysqlConnection)
throw new ConnectionIsNotSetError("mysql");
const qb = this.createQueryBuilder().update(tableName, valuesMap).from(tableName, "t");
Object.keys(conditions).forEach(key => qb.andWhere(key + "=:" + key, { [key]: (<any> conditions)[key] }));
return qb.execute().then(() => {});
const updateValues = this.escapeObjectMap(valuesMap).join(",");
const conditionString = this.escapeObjectMap(conditions).join(" AND ");
const query = `UPDATE ${tableName} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
return this.query(query).then(() => {});
// const qb = this.createQueryBuilder().update(tableName, valuesMap).from(tableName, "t");
// Object.keys(conditions).forEach(key => qb.andWhere(key + "=:" + key, { [key]: (<any> conditions)[key] }));
// return qb.execute().then(() => {});
}
private escapeObjectMap(objectMap: { [key: string]: any }): string[] {
return Object.keys(objectMap).map(key => {
const value = (<any> objectMap)[key];
if (value === null || value === undefined) {
return key + "=NULL";
} else {
return key + "=" + this.escape(value);
}
});
}
/**
@ -193,9 +208,10 @@ export class MysqlDriver extends BaseDriver implements Driver {
throw new ConnectionIsNotSetError("mysql");
const columns = Object.keys(keyValues).join(",");
const values = Object.keys(keyValues).map(key => (<any> keyValues)[key]).join(",");
// const values = this.escapeObjectMap(keyValues).join(",");
const values = Object.keys(keyValues).map(key => this.escape((<any> keyValues)[key])).join(","); // todo: escape here
const query = `INSERT INTO ${tableName}(${columns}) VALUES (${values})`;
return this.query(query);
return this.query<any>(query).then(result => result.insertId);
}
/**
@ -204,10 +220,13 @@ export class MysqlDriver extends BaseDriver implements Driver {
delete(tableName: string, conditions: Object): Promise<void> {
if (!this.mysqlConnection)
throw new ConnectionIsNotSetError("mysql");
const qb = this.createQueryBuilder().delete(tableName);
Object.keys(conditions).forEach(key => qb.andWhere(key + "=:" + key, { [key]: (<any> conditions)[key] }));
return qb.execute().then(() => {});
const conditionString = this.escapeObjectMap(conditions).join(" AND ");
const query = `DELETE FROM ${tableName} WHERE ${conditionString}`;
return this.query(query).then(() => {});
// const qb = this.createQueryBuilder().delete(tableName);
// Object.keys(conditions).forEach(key => qb.andWhere(key + "=:" + key, { [key]: (<any> conditions)[key] }));
// return qb.execute().then(() => {});
}
/**

View File

@ -5,6 +5,8 @@ import {ColumnType} from "../types/ColumnTypes";
/**
* Constructor arguments for ColumnMetadata class.
*
* @internal
*/
export interface ColumnMetadataArgs {
@ -51,6 +53,8 @@ export interface ColumnMetadataArgs {
/**
* This metadata contains all information about class's column.
*
* @internal
*/
export class ColumnMetadata extends PropertyMetadata {

View File

@ -1,5 +1,7 @@
/**
* This metadata interface contains all information about table's compound index.
*
* @internal
*/
export class CompoundIndexMetadata {

View File

@ -3,6 +3,8 @@ import {EventListenerType} from "../types/EventListenerTypes";
/**
* This metadata interface contains all information about some index on a field.
*
* @internal
*/
export class EntityListenerMetadata extends PropertyMetadata {

View File

@ -8,6 +8,8 @@ import {ForeignKeyMetadata} from "./ForeignKeyMetadata";
/**
* Contains all entity metadata.
*
* @internal
*/
export class EntityMetadata {

View File

@ -1,5 +1,7 @@
/**
* Contains metadata information about ORM event subscribers.
*
* @internal
*/
export class EventSubscriberMetadata {

View File

@ -5,6 +5,8 @@ export type OnDeleteType = "RESTRICT"|"CASCADE"|"SET NULL";
/**
* This metadata interface contains all information foreign keys.
*
* @internal
*/
export class ForeignKeyMetadata {

View File

@ -2,6 +2,8 @@ import {PropertyMetadata} from "./PropertyMetadata";
/**
* This metadata interface contains all information about some index on a field.
*
* @internal
*/
export class IndexMetadata extends PropertyMetadata {

View File

@ -2,6 +2,8 @@ import {TableMetadata} from "./TableMetadata";
/**
* This metadata interface contains all information about junction table.
*
* @internal
*/
export class JunctionTableMetadata extends TableMetadata {

View File

@ -1,5 +1,7 @@
/**
* This represents metadata of some object's property.
*
* @internal
*/
export abstract class PropertyMetadata {

View File

@ -17,6 +17,8 @@ export type PropertyTypeInFunction<T> = string|((t: T) => string|any);
/**
* Relation metadata constructor arguments.
*
* @internal
*/
export interface RelationMetadataArgs {
@ -59,6 +61,8 @@ export interface RelationMetadataArgs {
/**
* This metadata interface contains all information about some document's relation.
*
* @internal
*/
export class RelationMetadata extends PropertyMetadata {

View File

@ -2,6 +2,8 @@ import {NamingStrategy} from "../../naming-strategy/NamingStrategy";
/**
* This metadata interface contains all information about specific table.
*
* @internal
*/
export class TableMetadata {

View File

@ -1,4 +1,5 @@
import {OnDeleteType} from "../metadata/ForeignKeyMetadata";
/**
* Describes all relation's options.
*/

View File

@ -45,7 +45,8 @@ export class EntityPersistOperationBuilder {
// Constructor
// -------------------------------------------------------------------------
constructor(private connection: Connection) {
constructor(private connection: Connection,
private entityMetadatas: EntityMetadata[]) {
}
// -------------------------------------------------------------------------
@ -109,7 +110,8 @@ export class EntityPersistOperationBuilder {
dbEntities: EntityWithId[],
fromRelation?: RelationMetadata,
operations: InsertOperation[] = []): InsertOperation[] {
const metadata = this.connection.getEntityMetadata(newEntity.constructor);
// const metadata = this.connection.getEntityMetadata(newEntity.constructor);
const metadata = this.entityMetadatas.find(metadata => metadata.target === newEntity.constructor);
const isObjectNew = !this.findEntityWithId(dbEntities, metadata.target, newEntity[metadata.primaryColumn.name]);
// if object is new and should be inserted, we check if cascades are allowed before add it to operations list
@ -234,7 +236,8 @@ export class EntityPersistOperationBuilder {
}
private findRelationsWithEntityInside(insertOperation: InsertOperation, entityToSearchIn: any) {
const metadata = this.connection.getEntityMetadata(entityToSearchIn.constructor);
// const metadata = this.connection.getEntityMetadata(entityToSearchIn.constructor);
const metadata = this.entityMetadatas.find(metadata => metadata.target === entityToSearchIn.constructor);
return metadata.relations.reduce((operations, relation) => {
const value = entityToSearchIn[relation.propertyName];

View File

@ -7,6 +7,7 @@ import {InsertOperation} from "./operation/InsertOperation";
import {JunctionRemoveOperation} from "./operation/JunctionRemoveOperation";
import {UpdateByRelationOperation} from "./operation/UpdateByRelationOperation";
import {Broadcaster} from "../subscriber/Broadcaster";
import {EntityMetadata} from "../metadata-builder/metadata/EntityMetadata";
/**
* Executes PersistOperation in the given connection.
@ -18,7 +19,8 @@ export class PersistOperationExecutor {
// Constructor
// -------------------------------------------------------------------------
constructor(private connection: Connection) {
constructor(private connection: Connection,
private entityMetadatas: EntityMetadata[]) {
}
// -------------------------------------------------------------------------
@ -29,7 +31,7 @@ export class PersistOperationExecutor {
* Executes given persist operation.
*/
executePersistOperation(persistOperation: PersistOperation) {
const broadcaster = new Broadcaster(this.connection);
const broadcaster = new Broadcaster(this.connection, this.entityMetadatas);
return Promise.resolve()
.then(() => this.broadcastBeforeEvents(broadcaster, persistOperation))
@ -104,8 +106,8 @@ export class PersistOperationExecutor {
*/
private executeInsertOperations(persistOperation: PersistOperation) {
return Promise.all(persistOperation.inserts.map(operation => {
return this.insert(operation.entity).then((result: any) => {
operation.entityId = result.insertId;
return this.insert(operation.entity).then((insertId: any) => {
operation.entityId = insertId;
});
}));
}
@ -174,7 +176,8 @@ export class PersistOperationExecutor {
*/
private updateIdsOfInsertedEntities(persistOperation: PersistOperation) {
persistOperation.inserts.forEach(insertOperation => {
const metadata = this.connection.getEntityMetadata(insertOperation.entity.constructor);
// const metadata = this.connection.getEntityMetadata(insertOperation.entity.constructor);
const metadata = this.entityMetadatas.find(metadata => metadata.target === insertOperation.entity.constructor);
insertOperation.entity[metadata.primaryColumn.name] = insertOperation.entityId;
});
}
@ -184,7 +187,8 @@ export class PersistOperationExecutor {
*/
private updateIdsOfRemovedEntities(persistOperation: PersistOperation) {
persistOperation.removes.forEach(removeOperation => {
const metadata = this.connection.getEntityMetadata(removeOperation.entity.constructor);
// const metadata = this.connection.getEntityMetadata(removeOperation.entity.constructor);
const metadata = this.entityMetadatas.find(metadata => metadata.target === removeOperation.entity.constructor);
const removedEntity = persistOperation.allPersistedEntities.find(allNewEntity => {
return allNewEntity.entity.constructor === removeOperation.entity.constructor && allNewEntity.id === removeOperation.entity[metadata.primaryColumn.name];
});
@ -197,7 +201,8 @@ export class PersistOperationExecutor {
let tableName: string, relationName: string, relationId: any, idColumn: string, id: any;
const idInInserts = insertOperations.find(o => o.entity === operation.targetEntity).entityId;
if (operation.updatedRelation.isOneToMany) {
const metadata = this.connection.getEntityMetadata(operation.insertOperation.entity.constructor);
// const metadata = this.connection.getEntityMetadata(operation.insertOperation.entity.constructor);
const metadata = this.entityMetadatas.find(metadata => metadata.target === operation.insertOperation.entity.constructor);
tableName = metadata.table.name;
relationName = operation.updatedRelation.inverseRelation.name;
relationId = operation.targetEntity[metadata.primaryColumn.propertyName] || idInInserts;
@ -205,7 +210,8 @@ export class PersistOperationExecutor {
id = operation.insertOperation.entityId;
} else {
const metadata = this.connection.getEntityMetadata(operation.targetEntity.constructor);
// const metadata = this.connection.getEntityMetadata(operation.targetEntity.constructor);
const metadata = this.entityMetadatas.find(metadata => metadata.target === operation.targetEntity.constructor);
tableName = metadata.table.name;
relationName = operation.updatedRelation.name;
relationId = operation.insertOperation.entityId;
@ -217,7 +223,8 @@ export class PersistOperationExecutor {
private update(updateOperation: UpdateOperation) {
const entity = updateOperation.entity;
const metadata = this.connection.getEntityMetadata(entity.constructor);
// const metadata = this.connection.getEntityMetadata(entity.constructor);
const metadata = this.entityMetadatas.find(metadata => metadata.target === entity.constructor);
const values: any = updateOperation.columns.reduce((object, column) => {
const value = this.connection.driver.preparePersistentValue(entity[column.propertyName], column);
(<any> object)[column.name] = value;
@ -243,12 +250,14 @@ export class PersistOperationExecutor {
}
private delete(entity: any) {
const metadata = this.connection.getEntityMetadata(entity.constructor);
// const metadata = this.connection.getEntityMetadata(entity.constructor);
const metadata = this.entityMetadatas.find(metadata => metadata.target === entity.constructor);
return this.connection.driver.delete(metadata.table.name, { [metadata.primaryColumn.name]: entity[metadata.primaryColumn.propertyName] });
}
private insert(entity: any) {
const metadata = this.connection.getEntityMetadata(entity.constructor);
// const metadata = this.connection.getEntityMetadata(entity.constructor);
const metadata = this.entityMetadatas.find(metadata => metadata.target === entity.constructor);
const columns = metadata.columns
.filter(column => !column.isVirtual)
.filter(column => entity.hasOwnProperty(column.propertyName))
@ -256,14 +265,7 @@ export class PersistOperationExecutor {
const values = metadata.columns
.filter(column => !column.isVirtual)
.filter(column => entity.hasOwnProperty(column.propertyName))
.map(column => this.connection.driver.preparePersistentValue(entity[column.propertyName], column))
.map(value => {
if (value === null || value === undefined) {
return "NULL";
} else {
return this.connection.driver.escape(value);
}
});
.map(column => this.connection.driver.preparePersistentValue(entity[column.propertyName], column));
const relationColumns = metadata.relations
.filter(relation => relation.isOwning && !!relation.relatedEntityMetadata)
.filter(relation => entity.hasOwnProperty(relation.propertyName))
@ -274,19 +276,19 @@ export class PersistOperationExecutor {
.filter(relation => relation.isOwning && !!relation.relatedEntityMetadata)
.filter(relation => entity.hasOwnProperty(relation.propertyName))
.filter(relation => entity[relation.propertyName].hasOwnProperty(relation.relatedEntityMetadata.primaryColumn.name))
.map(relation => this.connection.driver.escape(entity[relation.propertyName][relation.relatedEntityMetadata.primaryColumn.name]));
.map(relation => entity[relation.propertyName][relation.relatedEntityMetadata.primaryColumn.name]);
const allColumns = columns.concat(relationColumns);
const allValues = values.concat(relationValues);
if (metadata.createDateColumn) {
allColumns.push(metadata.createDateColumn.name);
allValues.push(this.connection.driver.escape(this.connection.driver.preparePersistentValue(new Date(), metadata.createDateColumn)));
allValues.push(this.connection.driver.preparePersistentValue(new Date(), metadata.createDateColumn));
}
if (metadata.updateDateColumn) {
allColumns.push(metadata.updateDateColumn.name);
allValues.push(this.connection.driver.escape(this.connection.driver.preparePersistentValue(new Date(), metadata.updateDateColumn)));
allValues.push(this.connection.driver.preparePersistentValue(new Date(), metadata.updateDateColumn));
}
return this.connection.driver.insert(metadata.table.name, this.zipObject(allColumns, allValues));
@ -294,8 +296,10 @@ export class PersistOperationExecutor {
private insertJunctions(junctionOperation: JunctionInsertOperation, insertOperations: InsertOperation[]) {
const junctionMetadata = junctionOperation.metadata;
const metadata1 = this.connection.getEntityMetadata(junctionOperation.entity1.constructor);
const metadata2 = this.connection.getEntityMetadata(junctionOperation.entity2.constructor);
// const metadata1 = this.connection.getEntityMetadata(junctionOperation.entity1.constructor);
// const metadata2 = this.connection.getEntityMetadata(junctionOperation.entity2.constructor);
const metadata1 = this.entityMetadatas.find(metadata => metadata.target === junctionOperation.entity1.constructor);
const metadata2 = this.entityMetadatas.find(metadata => metadata.target === junctionOperation.entity2.constructor);
const columns = junctionMetadata.columns.map(column => column.name);
const id1 = junctionOperation.entity1[metadata1.primaryColumn.name] || insertOperations.find(o => o.entity === junctionOperation.entity1).entityId;
const id2 = junctionOperation.entity2[metadata2.primaryColumn.name] || insertOperations.find(o => o.entity === junctionOperation.entity2).entityId;
@ -305,8 +309,10 @@ export class PersistOperationExecutor {
private removeJunctions(junctionOperation: JunctionRemoveOperation) {
const junctionMetadata = junctionOperation.metadata;
const metadata1 = this.connection.getEntityMetadata(junctionOperation.entity1.constructor);
const metadata2 = this.connection.getEntityMetadata(junctionOperation.entity2.constructor);
// const metadata1 = this.connection.getEntityMetadata(junctionOperation.entity1.constructor);
// const metadata2 = this.connection.getEntityMetadata(junctionOperation.entity2.constructor);
const metadata1 = this.entityMetadatas.find(metadata => metadata.target === junctionOperation.entity1.constructor);
const metadata2 = this.entityMetadatas.find(metadata => metadata.target === junctionOperation.entity2.constructor);
const columns = junctionMetadata.columns.map(column => column.name);
const id1 = junctionOperation.entity1[metadata1.primaryColumn.name];
const id2 = junctionOperation.entity2[metadata2.primaryColumn.name];

View File

@ -3,6 +3,7 @@ import {AliasMap} from "./alias/AliasMap";
import {Connection} from "../connection/Connection";
import {RawSqlResultsToEntityTransformer} from "./transformer/RawSqlResultsToEntityTransformer";
import {Broadcaster} from "../subscriber/Broadcaster";
import {EntityMetadata} from "../metadata-builder/metadata/EntityMetadata";
/**
* @internal
@ -43,9 +44,10 @@ export class QueryBuilder<Entity> {
// Constructor
// -------------------------------------------------------------------------
constructor(private connection: Connection) {
constructor(private connection: Connection,
private entityMetadatas: EntityMetadata[]) {
this.aliasMap = new AliasMap(connection.entityMetadatas);
this.broadcaster = new Broadcaster(connection);
this.broadcaster = new Broadcaster(connection, this.entityMetadatas);
}
// -------------------------------------------------------------------------
@ -307,7 +309,8 @@ export class QueryBuilder<Entity> {
getResults(): Promise<Entity[]> {
const mainAlias = this.aliasMap.mainAlias.name;
if (this.firstResult || this.maxResults) {
const metadata = this.connection.getEntityMetadata(this.fromEntity.alias.target);
// const metadata = this.connection.getEntityMetadata(this.fromEntity.alias.target);
const metadata = this.entityMetadatas.find(metadata => metadata.target === this.fromEntity.alias.target);
let idsQuery = `SELECT DISTINCT(distinctAlias.${mainAlias}_${metadata.primaryColumn.name}) as ids`;
if (this.orderBys && this.orderBys.length > 0)
idsQuery += ", " + this.orderBys.map(orderBy => orderBy.sort.replace(".", "_")).join(", ");
@ -346,7 +349,8 @@ export class QueryBuilder<Entity> {
getCount(): Promise<number> {
const mainAlias = this.aliasMap.mainAlias.name;
const metadata = this.connection.getEntityMetadata(this.fromEntity.alias.target);
const metadata = this.entityMetadatas.find(metadata => metadata.target === this.fromEntity.alias.target);
// const metadata = this.connection.getEntityMetadata(this.fromEntity.alias.target);
const countQuery = this.clone({ skipOrderBys: true })
.select(`COUNT(DISTINCT(${mainAlias}.${metadata.primaryColumn.name})) as cnt`)
.getSql();
@ -363,7 +367,7 @@ export class QueryBuilder<Entity> {
}
clone(options?: { skipOrderBys?: boolean }) {
const qb = new QueryBuilder(this.connection);
const qb = new QueryBuilder(this.connection, this.entityMetadatas);
switch (this.type) {
case "select":
@ -594,7 +598,7 @@ export class QueryBuilder<Entity> {
protected replaceParameters(sql: string) {
Object.keys(this.parameters).forEach(key => {
const value = this.parameters[key] !== null && this.parameters[key] !== undefined ? this.connection.driver.escape(this.parameters[key]) : "NULL";
sql = sql.replace(":" + key, value);
sql = sql.replace(":" + key, value); // todo: make replace only in value statements, otherwise problems
});
return sql;
}

View File

@ -17,7 +17,9 @@ export class Repository<Entity> {
// Constructor
// -------------------------------------------------------------------------
constructor(private connection: Connection, private metadata: EntityMetadata) {
constructor(private connection: Connection,
private entityMetadatas: EntityMetadata[],
private metadata: EntityMetadata) {
}
// -------------------------------------------------------------------------
@ -39,8 +41,10 @@ export class Repository<Entity> {
* Creates a new query builder that can be used to build a sql query.
*/
createQueryBuilder(alias: string): QueryBuilder<Entity> {
return this.connection.driver
.createQueryBuilder<Entity>()
// const qb = this.connection.driver.createQueryBuilder<Entity>();
const cls: any = this.connection.driver.queryBuilderClass;
const qb = new cls(this.connection, this.entityMetadatas);
return qb
.select(alias)
.from(this.metadata.target, alias);
}
@ -89,8 +93,8 @@ export class Repository<Entity> {
*/
persist(entity: Entity): Promise<Entity> {
let loadedDbEntity: any;
const persister = new PersistOperationExecutor(this.connection);
const builder = new EntityPersistOperationBuilder(this.connection);
const persister = new PersistOperationExecutor(this.connection, this.entityMetadatas);
const builder = new EntityPersistOperationBuilder(this.connection, this.entityMetadatas);
const allPersistedEntities = this.extractObjectsById(entity, this.metadata);
const promise: Promise<Entity> = !this.hasId(entity) ? Promise.resolve<Entity|null>(null) : this.initialize(entity);
return promise
@ -109,10 +113,10 @@ export class Repository<Entity> {
* Removes a given entity from the database.
*/
remove(entity: Entity): Promise<Entity> {
const persister = new PersistOperationExecutor(this.connection);
const persister = new PersistOperationExecutor(this.connection, this.entityMetadatas);
return this.initialize(entity).then(dbEntity => {
(<any> entity)[this.metadata.primaryColumn.name] = undefined;
const builder = new EntityPersistOperationBuilder(this.connection);
const builder = new EntityPersistOperationBuilder(this.connection, this.entityMetadatas);
const dbEntities = this.extractObjectsById(dbEntity, this.metadata);
const allPersistedEntities = this.extractObjectsById(entity, this.metadata);
const persistOperation = builder.buildOnlyRemovement(this.metadata, dbEntity, entity, dbEntities, allPersistedEntities);
@ -269,7 +273,8 @@ export class Repository<Entity> {
.filter(entityWithId => entityWithId.id !== null && entityWithId.id !== undefined)
.filter(entityWithId => !dbEntities.find(dbEntity => dbEntity.entity.constructor === entityWithId.entity.constructor && dbEntity.id === entityWithId.id))
.map(entityWithId => {
const metadata = this.connection.getEntityMetadata(entityWithId.entity.constructor);
// const metadata = this.connection.getEntityMetadata(entityWithId.entity.constructor);
const metadata = this.entityMetadatas.find(metadata => metadata.target === entityWithId.entity.constructor);
const repository = this.connection.getRepository(entityWithId.entity.constructor);
return repository.findById(entityWithId.id).then(loadedEntity => {
if (!loadedEntity) return undefined;

View File

@ -2,6 +2,7 @@ import {EventSubscriberInterface} from "./EventSubscriberInterface";
import {Connection} from "../connection/Connection";
import {ColumnMetadata} from "../metadata-builder/metadata/ColumnMetadata";
import {EventListenerTypes} from "../metadata-builder/types/EventListenerTypes";
import {EntityMetadata} from "../metadata-builder/metadata/EntityMetadata";
/**
* Broadcaster provides a helper methods to broadcast events to the subscribers.
@ -14,7 +15,8 @@ export class Broadcaster {
// Constructor
// -------------------------------------------------------------------------
constructor(private connection: Connection) {
constructor(private connection: Connection,
private entityMetadatas: EntityMetadata[]) {
}
// -------------------------------------------------------------------------
@ -118,7 +120,8 @@ export class Broadcaster {
}
broadcastLoadEvents(entity: any): Promise<void> {
const metadata = this.connection.getEntityMetadata(entity.constructor);
// const metadata = this.connection.getEntityMetadata(entity.constructor);
const metadata = this.entityMetadatas.find(metadata => metadata.target === entity.constructor);
let promises: Promise<any>[] = [];
metadata