mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
implemented query builder and many-to-many queries
This commit is contained in:
parent
d01d2b6cea
commit
a13ee96ba2
@ -12,3 +12,6 @@ Take a look on samples in [./sample](https://github.com/pleerock/typeorm/tree/ma
|
||||
usages.
|
||||
|
||||
## Todos
|
||||
|
||||
* add partial selection support
|
||||
* in query builder should we use property names or table names? (right now its mixed)
|
||||
@ -1,7 +1,7 @@
|
||||
import {Connection} from "../../connection/Connection";
|
||||
import {EntityMetadata} from "../../metadata-builder/metadata/EntityMetadata";
|
||||
import {RelationMetadata} from "../../metadata-builder/metadata/RelationMetadata";
|
||||
import {AliasMap, Alias} from "../../driver/query-builder/QueryBuilder";
|
||||
import {AliasMap, Alias} from "../../query-builder/QueryBuilder";
|
||||
import * as _ from "lodash";
|
||||
|
||||
export class EntityCreator {
|
||||
@ -142,26 +142,12 @@ export class EntityCreator {
|
||||
const alias = aliasMap.findAliasByParent(mainAlias.name, relation.propertyName);
|
||||
if (alias) {
|
||||
//const id = relation.isManyToOne || relation.isOneToOne ? object[mainAlias.name + "_" + relation.name] : null;
|
||||
const relatedEntities = this.toEntity(sqlResult, relation.relatedEntityMetadata, alias, aliasMap);
|
||||
if (relation.isManyToOne || relation.isOneToOne) {
|
||||
const relatedObject = relatedEntities.find(obj => {
|
||||
return obj[relation.relatedEntityMetadata.primaryColumn.name] === object[mainAlias.name + "_" + relation.name];
|
||||
});
|
||||
|
||||
if (relatedObject) {
|
||||
jsonObject[relation.propertyName] = relatedObject;
|
||||
isAnythingLoaded = true;
|
||||
}
|
||||
|
||||
} else if (relation.isOneToMany) {
|
||||
const relatedObjects = relatedEntities.filter(obj => {
|
||||
return obj[relation.inverseSideProperty] === object[mainAlias.name + "_" + metadata.primaryColumn.name];
|
||||
});
|
||||
|
||||
//if (relatedObjects) {
|
||||
jsonObject[relation.propertyName] = relatedObjects;
|
||||
isAnythingLoaded = true;
|
||||
//}
|
||||
const subSqlResult = sqlResult.filter(result => String(result[mainAlias.name + "_" + metadata.primaryColumn.name]) === key);
|
||||
const relatedEntities = this.toEntity(subSqlResult, relation.relatedEntityMetadata, alias, aliasMap);
|
||||
const res = relation.isManyToOne || relation.isOneToOne ? relatedEntities[0] : relatedEntities;
|
||||
if (res) {
|
||||
jsonObject[relation.propertyName] = res;
|
||||
isAnythingLoaded = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -169,8 +155,6 @@ export class EntityCreator {
|
||||
return isAnythingLoaded ? jsonObject : null;
|
||||
|
||||
}).filter(res => res !== null);
|
||||
|
||||
//return id ? final[0] : final;
|
||||
}
|
||||
|
||||
private objectToEntity2(object: any, metadata: EntityMetadata, doFetchProperties?: boolean): Promise<any>;
|
||||
@ -2,9 +2,9 @@ import {Connection} from "../../connection/Connection";
|
||||
import {CascadeOption, DynamicCascadeOptions} from "./../cascade/CascadeOption";
|
||||
import {RelationMetadata} from "../../metadata-builder/metadata/RelationMetadata";
|
||||
import {DocumentToDbObjectTransformer} from "./DocumentToDbObjectTransformer";
|
||||
import {PersistOperation} from "./../operation/PersistOperation";
|
||||
import {InverseSideUpdateOperation} from "./../operation/InverseSideUpdateOperation";
|
||||
import {PersistOperationGrouppedByDeepness} from "../operation/PersistOperationGrouppedByDeepness";
|
||||
import {PersistOperation} from "../../../odmhelpers/operation/PersistOperation";
|
||||
import {InverseSideUpdateOperation} from "../../../odmhelpers/operation/InverseSideUpdateOperation";
|
||||
import {PersistOperationGrouppedByDeepness} from "../../../odmhelpers/operation/PersistOperationGrouppedByDeepness";
|
||||
|
||||
export class EntityPersister {
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import {Connection} from "../../connection/Connection";
|
||||
import {CascadeOption, DynamicCascadeOptions} from "./../cascade/CascadeOption";
|
||||
import {DbObjectColumnValidator} from "./DbObjectColumnValidator";
|
||||
import {ColumnTypeNotSupportedError} from "../error/ColumnTypeNotSupportedError";
|
||||
import {PersistOperation} from "./../operation/PersistOperation";
|
||||
import {ColumnTypeNotSupportedError} from "../../../odmhelpers/error/ColumnTypeNotSupportedError";
|
||||
import {PersistOperation} from "../../../odmhelpers/operation/PersistOperation";
|
||||
import {CascadeOptionUtils} from "../cascade/CascadeOptionUtils";
|
||||
import {InverseSideUpdateOperation} from "../operation/InverseSideUpdateOperation";
|
||||
import {InverseSideUpdateOperation} from "../../../odmhelpers/operation/InverseSideUpdateOperation";
|
||||
import {EntityMetadata} from "../../metadata-builder/metadata/EntityMetadata";
|
||||
import {RelationMetadata} from "../../metadata-builder/metadata/RelationMetadata";
|
||||
|
||||
@ -2,10 +2,10 @@ import {DocumentSchema} from "../../schema/DocumentSchema";
|
||||
import {Connection} from "../../connection/Connection";
|
||||
import {RelationSchema} from "../../schema/RelationSchema";
|
||||
import {CascadeOption, DynamicCascadeOptions} from "./../cascade/CascadeOption";
|
||||
import {RemoveOperation} from "./../operation/RemoveOperation";
|
||||
import {InverseSideUpdateOperation} from "./../operation/InverseSideUpdateOperation";
|
||||
import {RemoveOperation} from "../../../odmhelpers/operation/RemoveOperation";
|
||||
import {InverseSideUpdateOperation} from "../../../odmhelpers/operation/InverseSideUpdateOperation";
|
||||
import {CascadeOptionUtils} from "../cascade/CascadeOptionUtils";
|
||||
import {NoDocumentWithSuchIdError} from "../error/NoDocumentWithSuchIdError";
|
||||
import {NoDocumentWithSuchIdError} from "../../../odmhelpers/error/NoDocumentWithSuchIdError";
|
||||
import {ObjectID} from "mongodb";
|
||||
|
||||
/**
|
||||
@ -4,6 +4,7 @@ import {PostDetails} from "./entity/PostDetails";
|
||||
import {Image} from "./entity/Image";
|
||||
import {ImageDetails} from "./entity/ImageDetails";
|
||||
import {Cover} from "./entity/Cover";
|
||||
import {Category} from "./entity/Category";
|
||||
|
||||
// first create a connection
|
||||
let options = {
|
||||
@ -15,7 +16,7 @@ let options = {
|
||||
autoSchemaCreate: true
|
||||
};
|
||||
|
||||
TypeORM.createMysqlConnection(options, [Post, PostDetails, Image, ImageDetails, Cover]).then(connection => {
|
||||
TypeORM.createMysqlConnection(options, [Post, PostDetails, Image, ImageDetails, Cover, Category]).then(connection => {
|
||||
|
||||
const postJson = {
|
||||
id: 1,
|
||||
@ -35,22 +36,24 @@ TypeORM.createMysqlConnection(options, [Post, PostDetails, Image, ImageDetails,
|
||||
.addSelect("imageDetails")
|
||||
.addSelect("secondaryImage")
|
||||
.addSelect("cover")
|
||||
.leftJoin("post.images", "image", "on", "image.post=post.id")
|
||||
.leftJoin("post.secondaryImages", "secondaryImage", "on", "secondaryImage.secondaryPost=post.id")
|
||||
.leftJoin("image.details", "imageDetails", "on", "imageDetails.id=image.details")
|
||||
.innerJoin("post.cover", "cover", "on", "cover.id=post.cover")
|
||||
.addSelect("category")
|
||||
.leftJoin("post.images", "image")
|
||||
.leftJoin("post.secondaryImages", "secondaryImage")
|
||||
.leftJoin("image.details", "imageDetails", "on", "imageDetails.meta=:meta")
|
||||
.innerJoin("post.cover", "cover")
|
||||
.leftJoin("post.categories", "category", "on", "category.description=:description")
|
||||
//.leftJoin(Image, "image", "on", "image.post=post.id")
|
||||
//.where("post.id=:id")
|
||||
.setParameter("id", 1);
|
||||
.setParameter("id", 1)
|
||||
.setParameter("description", "cat2")
|
||||
.setParameter("meta", "sec image");
|
||||
|
||||
return postRepository
|
||||
.queryMany(qb.getSql(), qb.generateAliasMap())
|
||||
return qb
|
||||
.getSingleResult()
|
||||
.then(result => console.log(JSON.stringify(result, null, 4)))
|
||||
.catch(err => console.log(err));
|
||||
.catch(error => console.log(error.stack ? error.stack : error));
|
||||
|
||||
return;
|
||||
|
||||
let details = new PostDetails();
|
||||
/*let details = new PostDetails();
|
||||
details.comment = "This is post about hello";
|
||||
details.meta = "about-hello";
|
||||
|
||||
@ -62,6 +65,6 @@ TypeORM.createMysqlConnection(options, [Post, PostDetails, Image, ImageDetails,
|
||||
postRepository
|
||||
.persist(post)
|
||||
.then(post => console.log("Post has been saved"))
|
||||
.catch(error => console.log("Cannot save. Error: ", error));
|
||||
.catch(error => console.log("Cannot save. Error: ", error));*/
|
||||
|
||||
}).catch(error => console.log("Cannot connect: ", error));
|
||||
}).catch(error => console.log(error.stack ? error.stack : error));
|
||||
18
sample/sample2-one-to-one/entity/Category.ts
Normal file
18
sample/sample2-one-to-one/entity/Category.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
import {OneToMany, ManyToMany} from "../../../src/decorator/Relations";
|
||||
import {Post} from "./Post";
|
||||
|
||||
@Table("sample2_category")
|
||||
export class Category {
|
||||
|
||||
@PrimaryColumn("int", { isAutoIncrement: true })
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
description: string;
|
||||
|
||||
@ManyToMany<Post>(false, type => Post, post => post.categories)
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
import {OneToMany, ManyToOne} from "../../../src/decorator/Relations";
|
||||
import {OneToMany, ManyToOne, ManyToMany, OneToOne} from "../../../src/decorator/Relations";
|
||||
import {Image} from "./Image";
|
||||
import {Cover} from "./Cover";
|
||||
import {Category} from "./Category";
|
||||
import {PostDetails} from "./PostDetails";
|
||||
|
||||
@Table("sample2_post")
|
||||
export class Post {
|
||||
@ -20,16 +22,19 @@ export class Post {
|
||||
})
|
||||
text: string;
|
||||
|
||||
/* @OneToOne<PostDetails>(true, () => PostDetails, details => details.post)
|
||||
details: PostDetails;*/
|
||||
@OneToOne<PostDetails>(true, () => PostDetails, details => details.post)
|
||||
details: PostDetails;
|
||||
|
||||
@OneToMany<Image>(() => Image, image => image.post)
|
||||
@OneToMany<Image>(type => Image, image => image.post)
|
||||
images: Image[];
|
||||
|
||||
@OneToMany<Image>(() => Image, image => image.secondaryPost)
|
||||
@OneToMany<Image>(type => Image, image => image.secondaryPost)
|
||||
secondaryImages: Image[];
|
||||
|
||||
@ManyToOne<Cover>(() => Cover, cover => cover.posts)
|
||||
@ManyToOne<Cover>(type => Cover, cover => cover.posts)
|
||||
cover: Cover;
|
||||
|
||||
@ManyToMany<Category>(true, type => Category, category => category.posts)
|
||||
categories: Category;
|
||||
|
||||
}
|
||||
@ -8,6 +8,7 @@ export interface ConnectionOptions {
|
||||
*/
|
||||
url?: string;
|
||||
host?: string;
|
||||
port?: number;
|
||||
username?: string;
|
||||
password?: string;
|
||||
database?: string;
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import {ConnectionOptions} from "../connection/ConnectionOptions";
|
||||
import {SchemaBuilder} from "./schema-builder/SchemaBuilder";
|
||||
import {QueryBuilder} from "./query-builder/QueryBuilder";
|
||||
import {SchemaBuilder} from "../schema-builder/SchemaBuilder";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {EntityMetadata} from "../metadata-builder/metadata/EntityMetadata";
|
||||
import {Connection} from "../connection/Connection";
|
||||
|
||||
/**
|
||||
* Driver communicates with specific database.
|
||||
@ -21,7 +22,7 @@ export interface Driver {
|
||||
/**
|
||||
* Creates a query builder which can be used to build an sql queries.
|
||||
*/
|
||||
createQueryBuilder(entityMetadatas: EntityMetadata[]): QueryBuilder;
|
||||
createQueryBuilder<Entity>(connection: Connection): QueryBuilder<Entity>;
|
||||
|
||||
/**
|
||||
* Creates a schema builder which can be used to build database/table schemas.
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import {Driver} from "./Driver";
|
||||
import {ConnectionOptions} from "../connection/ConnectionOptions";
|
||||
import {SchemaBuilder} from "./schema-builder/SchemaBuilder";
|
||||
import {QueryBuilder} from "./query-builder/QueryBuilder";
|
||||
import {MysqlSchemaBuilder} from "./schema-builder/MysqlSchemaBuilder";
|
||||
import {SchemaBuilder} from "../schema-builder/SchemaBuilder";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {MysqlSchemaBuilder} from "../schema-builder/MysqlSchemaBuilder";
|
||||
import {EntityMetadata} from "../metadata-builder/metadata/EntityMetadata";
|
||||
import {Connection} from "../connection/Connection";
|
||||
|
||||
/**
|
||||
* This driver organizes work with mongodb database.
|
||||
@ -45,8 +46,8 @@ export class MysqlDriver implements Driver {
|
||||
/**
|
||||
* Creates a query builder which can be used to build an sql queries.
|
||||
*/
|
||||
createQueryBuilder(entityMetadatas: EntityMetadata[]): QueryBuilder {
|
||||
return new QueryBuilder(entityMetadatas);
|
||||
createQueryBuilder<Entity>(connection: Connection): QueryBuilder<Entity> {
|
||||
return new QueryBuilder<Entity>(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -118,6 +118,13 @@ export class EntityMetadataBuilder {
|
||||
});
|
||||
});
|
||||
|
||||
// set inverse side (related) entity metadatas for all relation metadatas
|
||||
entityMetadatas.forEach(entityMetadata => {
|
||||
entityMetadata.relations.forEach(relation => {
|
||||
relation.relatedEntityMetadata = entityMetadatas.find(m => m.target === relation.type);
|
||||
});
|
||||
});
|
||||
|
||||
// generate junction tables with its columns and foreign keys
|
||||
const junctionEntityMetadatas: EntityMetadata[] = [];
|
||||
entityMetadatas.forEach(metadata => {
|
||||
@ -130,7 +137,7 @@ export class EntityMetadataBuilder {
|
||||
const column1options: ColumnOptions = {
|
||||
length: metadata.primaryColumn.length,
|
||||
type: metadata.primaryColumn.type,
|
||||
name: metadata.table.name + "_" + relation.name
|
||||
name: metadata.table.name + "_" + metadata.primaryColumn.name
|
||||
};
|
||||
const column2options: ColumnOptions = {
|
||||
length: inverseSideMetadata.primaryColumn.length,
|
||||
@ -145,20 +152,14 @@ export class EntityMetadataBuilder {
|
||||
new ForeignKeyMetadata(tableMetadata, [columns[0]], metadata.table, [metadata.primaryColumn]),
|
||||
new ForeignKeyMetadata(tableMetadata, [columns[1]], inverseSideMetadata.table, [inverseSideMetadata.primaryColumn]),
|
||||
];
|
||||
junctionEntityMetadatas.push(new EntityMetadata(tableMetadata, columns, [], [], [], foreignKeys));
|
||||
const junctionEntityMetadata = new EntityMetadata(tableMetadata, columns, [], [], [], foreignKeys);
|
||||
junctionEntityMetadatas.push(junctionEntityMetadata);
|
||||
relation.junctionEntityMetadata = junctionEntityMetadata;
|
||||
relation.inverseRelation.junctionEntityMetadata = junctionEntityMetadata;
|
||||
});
|
||||
});
|
||||
|
||||
const allEntityMetadatas = entityMetadatas.concat(junctionEntityMetadatas);
|
||||
|
||||
// set inverse side (related) entity metadatas for all relation metadatas
|
||||
allEntityMetadatas.forEach(entityMetadata => {
|
||||
entityMetadata.relations.forEach(relation => {
|
||||
relation.relatedEntityMetadata = allEntityMetadatas.find(m => m.target === relation.type);
|
||||
})
|
||||
});
|
||||
|
||||
return allEntityMetadatas;
|
||||
return entityMetadatas.concat(junctionEntityMetadatas);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -108,6 +108,10 @@ export class EntityMetadata {
|
||||
return this._columns.find(column => column.isUpdateDate);
|
||||
}
|
||||
|
||||
get hasPrimaryKey(): boolean {
|
||||
return !!this.primaryColumn;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -86,6 +86,11 @@ export class RelationMetadata extends PropertyMetadata {
|
||||
*/
|
||||
private _relatedEntityMetadata: EntityMetadata;
|
||||
|
||||
/**
|
||||
* Junction entity metadata.
|
||||
*/
|
||||
private _junctionEntityMetadata: EntityMetadata;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
@ -136,6 +141,14 @@ export class RelationMetadata extends PropertyMetadata {
|
||||
this._relatedEntityMetadata = metadata;
|
||||
}
|
||||
|
||||
get junctionEntityMetadata(): EntityMetadata {
|
||||
return this._junctionEntityMetadata;
|
||||
}
|
||||
|
||||
set junctionEntityMetadata(metadata: EntityMetadata) {
|
||||
this._junctionEntityMetadata = metadata;
|
||||
}
|
||||
|
||||
get relationType(): RelationTypes {
|
||||
return this._relationType;
|
||||
}
|
||||
@ -148,6 +161,10 @@ export class RelationMetadata extends PropertyMetadata {
|
||||
return this.computeInverseSide(this._inverseSideProperty);
|
||||
}
|
||||
|
||||
get inverseRelation(): RelationMetadata {
|
||||
return this._relatedEntityMetadata.findRelationByPropertyName(this.computeInverseSide(this._inverseSideProperty));
|
||||
}
|
||||
|
||||
get isOwning(): boolean {
|
||||
return this._isOwning;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ export class ColumnTypes {
|
||||
if (!type)
|
||||
return false;
|
||||
|
||||
if (typeof type === "string" && !ColumnTypes.isTypeSupported(type))
|
||||
if (typeof type === "string" && !ColumnTypes.isTypeSupported(String(type)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@ -1,73 +1,26 @@
|
||||
import {EntityMetadata} from "../../metadata-builder/metadata/EntityMetadata";
|
||||
import {Alias} from "./alias/Alias";
|
||||
import {AliasMap} from "./alias/AliasMap";
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {RawSqlResultsToObjectTransformer} from "./transformer/RawSqlResultsToObjectTransformer";
|
||||
|
||||
export class Alias {
|
||||
isMain: boolean;
|
||||
entityMetadata: EntityMetadata;
|
||||
name: string;
|
||||
parentPropertyName: string;
|
||||
parentAliasName: string;
|
||||
|
||||
constructor(name: string, entityMetadata: EntityMetadata, parentAliasName?: string, parentPropertyName?: string) {
|
||||
this.name = name;
|
||||
this.entityMetadata = entityMetadata;
|
||||
this.parentAliasName = parentAliasName;
|
||||
this.parentPropertyName = parentPropertyName;
|
||||
}
|
||||
export interface Join {
|
||||
alias: Alias;
|
||||
type: "left"|"inner";
|
||||
conditionType: "on"|"with";
|
||||
condition: string;
|
||||
}
|
||||
|
||||
export class AliasMap {
|
||||
constructor(public aliases: Alias[] = []) {
|
||||
}
|
||||
|
||||
addMainAlias(alias: Alias) {
|
||||
const mainAlias = this.getMainAlias();
|
||||
if (mainAlias)
|
||||
this.aliases.splice(this.aliases.indexOf(mainAlias), 1);
|
||||
|
||||
alias.isMain = true;
|
||||
this.aliases.push(alias);
|
||||
}
|
||||
|
||||
addAlias(alias: Alias) {
|
||||
this.aliases.push(alias);
|
||||
}
|
||||
|
||||
getMainAlias() {
|
||||
return this.aliases.find(alias => alias.isMain);
|
||||
}
|
||||
|
||||
findAliasByName(name: string) {
|
||||
return this.aliases.find(alias => alias.name === name);
|
||||
}
|
||||
|
||||
findAliasByParent(parentAliasName: string, parentPropertyName: string) {
|
||||
return this.aliases.find(alias => {
|
||||
return alias.parentAliasName === parentAliasName && alias.parentPropertyName === parentPropertyName;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Umed Khudoiberdiev <info@zar.tj>
|
||||
*/
|
||||
export class QueryBuilder {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(private entityMetadatas: EntityMetadata[]) {
|
||||
}
|
||||
export class QueryBuilder<Entity> {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Pirvate properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private aliasMap: AliasMap;
|
||||
private type: "select"|"update"|"delete";
|
||||
private selects: string[] = [];
|
||||
private froms: { alias: Alias };
|
||||
private leftJoins: { alias: Alias, conditionType: string, condition: string }[] = [];
|
||||
private innerJoins: { alias: Alias, conditionType: string, condition: string }[] = [];
|
||||
private joins: Join[] = [];
|
||||
private groupBys: string[] = [];
|
||||
private wheres: { type: "simple"|"and"|"or", condition: string }[] = [];
|
||||
private havings: { type: "simple"|"and"|"or", condition: string }[] = [];
|
||||
@ -76,41 +29,46 @@ export class QueryBuilder {
|
||||
private limit: number;
|
||||
private offset: number;
|
||||
|
||||
private aliasMap = new AliasMap();
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(private connection: Connection) {
|
||||
this.aliasMap = new AliasMap(connection.metadatas);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
//delete(selection: string): this;
|
||||
//delete(selection: string[]): this;
|
||||
//delete(selection: string|string[]): this {
|
||||
delete(): this {
|
||||
this.type = "delete";
|
||||
// this.addSelection(selection);
|
||||
return this;
|
||||
}
|
||||
|
||||
// update(selection: string): this;
|
||||
// update(selection: string[]): this;
|
||||
// update(selection: string|string[]): this {
|
||||
update(): this {
|
||||
this.type = "update";
|
||||
// this.addSelection(selection);
|
||||
return this;
|
||||
}
|
||||
|
||||
select(selection?: string): this;
|
||||
select(selection?: string[]): this;
|
||||
select(...selection: string[]): this;
|
||||
select(selection?: string|string[]): this {
|
||||
this.type = "select";
|
||||
if (selection)
|
||||
this.addSelection(selection);
|
||||
if (selection) {
|
||||
if (selection instanceof Array) {
|
||||
this.selects = selection;
|
||||
} else {
|
||||
this.selects = [selection];
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
addSelect(selection: string): this;
|
||||
addSelect(selection: string[]): this;
|
||||
addSelect(...selection: string[]): this;
|
||||
addSelect(selection: string|string[]): this {
|
||||
if (selection instanceof Array)
|
||||
this.selects = this.selects.concat(selection);
|
||||
@ -123,49 +81,42 @@ export class QueryBuilder {
|
||||
//from(tableName: string, alias: string): this;
|
||||
from(entity: Function, alias?: string): this {
|
||||
//from(entityOrTableName: Function|string, alias: string): this {
|
||||
const aliasObj = new Alias(alias, this.findMetadata(entity));
|
||||
const aliasObj = new Alias(alias);
|
||||
aliasObj.target = entity;
|
||||
this.aliasMap.addMainAlias(aliasObj);
|
||||
this.froms = { alias: aliasObj };
|
||||
return this;
|
||||
}
|
||||
|
||||
innerJoin(property: string, alias: string, conditionType: string, condition: string): this;
|
||||
innerJoin(entity: Function, alias: string, conditionType: string, condition: string): this;
|
||||
innerJoin(entityOrProperty: Function|string, alias: string, conditionType: string, condition: string): this {
|
||||
let parentPropertyName = "", parentAliasName = "";
|
||||
let entityMetadata: EntityMetadata;
|
||||
if (entityOrProperty instanceof Function) {
|
||||
entityMetadata = this.findMetadata(entityOrProperty);
|
||||
} else {
|
||||
parentAliasName = (<string> entityOrProperty).split(".")[0];
|
||||
parentPropertyName = (<string> entityOrProperty).split(".")[1];
|
||||
const parentAliasMetadata = this.aliasMap.findAliasByName(parentAliasName).entityMetadata;
|
||||
entityMetadata = parentAliasMetadata.findRelationWithDbName(parentPropertyName).relatedEntityMetadata;
|
||||
}
|
||||
|
||||
const aliasObj = new Alias(alias, entityMetadata, parentAliasName, parentPropertyName);
|
||||
this.aliasMap.addAlias(aliasObj);
|
||||
this.innerJoins.push({ alias: aliasObj, conditionType: conditionType, condition: condition });
|
||||
return this;
|
||||
innerJoin(property: string, alias: string, conditionType?: "on"|"with", condition?: string): this;
|
||||
innerJoin(entity: Function, alias: string, conditionType?: "on"|"with", condition?: string): this;
|
||||
innerJoin(entityOrProperty: Function|string, alias: string, conditionType?: "on"|"with", condition?: string): this {
|
||||
return this.join("inner", entityOrProperty, alias, conditionType, condition);
|
||||
}
|
||||
|
||||
leftJoin(property: string, alias: string, conditionType: string, condition: string): this;
|
||||
leftJoin(entity: Function, alias: string, conditionType: string, condition: string): this;
|
||||
leftJoin(entityOrProperty: Function|string, alias: string, conditionType: string, condition: string): this {
|
||||
let parentPropertyName = "", parentAliasName = "";
|
||||
let entityMetadata: EntityMetadata;
|
||||
leftJoin(property: string, alias: string, conditionType?: "on"|"with", condition?: string): this;
|
||||
leftJoin(entity: Function, alias: string, conditionType?: "on"|"with", condition?: string): this;
|
||||
leftJoin(entityOrProperty: Function|string, alias: string, conditionType: "on"|"with" = "on", condition?: string): this {
|
||||
return this.join("left", entityOrProperty, alias, conditionType, condition);
|
||||
}
|
||||
|
||||
join(joinType: "inner"|"left", property: string, alias: string, conditionType?: "on"|"with", condition?: string): this;
|
||||
join(joinType: "inner"|"left", entity: Function, alias: string, conditionType?: "on"|"with", condition?: string): this;
|
||||
join(joinType: "inner"|"left", entityOrProperty: Function|string, alias: string, conditionType: "on"|"with", condition: string): this;
|
||||
join(joinType: "inner"|"left", entityOrProperty: Function|string, alias: string, conditionType: "on"|"with" = "on", condition?: string): this {
|
||||
|
||||
const aliasObj = new Alias(alias);
|
||||
this.aliasMap.addAlias(aliasObj);
|
||||
if (entityOrProperty instanceof Function) {
|
||||
entityMetadata = this.findMetadata(entityOrProperty);
|
||||
} else {
|
||||
parentAliasName = (<string> entityOrProperty).split(".")[0];
|
||||
parentPropertyName = (<string> entityOrProperty).split(".")[1];
|
||||
const parentAliasMetadata = this.aliasMap.findAliasByName(parentAliasName).entityMetadata;
|
||||
entityMetadata = parentAliasMetadata.findRelationByPropertyName(parentPropertyName).relatedEntityMetadata;
|
||||
aliasObj.target = entityOrProperty;
|
||||
|
||||
} else if (typeof entityOrProperty === "string") {
|
||||
aliasObj.parentAliasName = entityOrProperty.split(".")[0];
|
||||
aliasObj.parentPropertyName = entityOrProperty.split(".")[1];
|
||||
}
|
||||
|
||||
const aliasObj = new Alias(alias, entityMetadata, parentAliasName, parentPropertyName);
|
||||
this.aliasMap.addAlias(aliasObj);
|
||||
this.leftJoins.push({ alias: aliasObj, conditionType: conditionType, condition: condition });
|
||||
const join: Join = { type: joinType, alias: aliasObj, conditionType: conditionType, condition: condition };
|
||||
this.joins.push(join);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -240,9 +191,9 @@ export class QueryBuilder {
|
||||
}
|
||||
|
||||
getSql(): string {
|
||||
// joins are before because their many-to-many relations can add aliases
|
||||
let sql = this.createSelectExpression();
|
||||
sql += this.createLeftJoinExpression();
|
||||
sql += this.createInnerJoinExpression();
|
||||
sql += this.createJoinExpression();
|
||||
sql += this.createWhereExpression();
|
||||
sql += this.createGroupByExpression();
|
||||
sql += this.createHavingExpression();
|
||||
@ -253,69 +204,71 @@ export class QueryBuilder {
|
||||
return sql;
|
||||
}
|
||||
|
||||
generateAliasMap(): AliasMap {
|
||||
return this.aliasMap;
|
||||
/* const aliasesFromInnerJoins = this.innerJoins.map(join => join.alias);
|
||||
const aliasesFromLeftJoins = this.leftJoins.map(join => join.alias);
|
||||
return new AliasMap([this.froms.alias, ...aliasesFromLeftJoins, ...aliasesFromInnerJoins]);*/
|
||||
execute(): Promise<any[]> {
|
||||
return this.connection.driver.query<any[]>(this.getSql())
|
||||
}
|
||||
|
||||
getScalarResults(): Promise<any[]> {
|
||||
return this.execute().then(results => this.rawResultsToObjects(results));
|
||||
|
||||
}
|
||||
|
||||
getSingleScalarResult(): Promise<any> {
|
||||
return this.getScalarResults().then(results => results[0]);
|
||||
}
|
||||
|
||||
getResults(): Promise<Entity[]> {
|
||||
return this.getScalarResults().then(objects => this.objectsToEntities(objects));
|
||||
}
|
||||
|
||||
getSingleResult(): Promise<Entity> {
|
||||
return this.getResults().then(entities => entities[0]);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
protected addSelection(selection: string|string[]) {
|
||||
|
||||
if (selection instanceof Array)
|
||||
this.selects = selection;
|
||||
else
|
||||
this.selects = [selection];
|
||||
/*if (typeof selection === 'function') {
|
||||
this.selects = this.selects.concat(selection(this.generatePropertyValuesEntity()));
|
||||
|
||||
} else if (typeof selection === 'string') {
|
||||
this.selects.push(selection);
|
||||
|
||||
} else if (selection instanceof Array) {
|
||||
this.selects = this.selects.concat(selection);
|
||||
}*/
|
||||
protected rawResultsToObjects(results: any[]) {
|
||||
const transformer = new RawSqlResultsToObjectTransformer(this.aliasMap);
|
||||
return transformer.transform(results);
|
||||
}
|
||||
|
||||
protected findMetadata(target: Function) {
|
||||
const metadata = this.entityMetadatas.find(metadata => metadata.target === target);
|
||||
if (!metadata)
|
||||
throw new Error("Metadata for " + (<any>target).name + " was not found.");
|
||||
|
||||
return metadata;
|
||||
protected objectsToEntities(entities: any[]) {
|
||||
return entities;
|
||||
}
|
||||
|
||||
|
||||
protected createSelectExpression() {
|
||||
// todo throw exception if selects or from is missing
|
||||
|
||||
const metadata = this.froms.alias.entityMetadata;
|
||||
const metadata = this.aliasMap.getEntityMetadataByAlias(this.froms.alias);
|
||||
const tableName = metadata.table.name;
|
||||
const alias = this.froms.alias.name;
|
||||
const columns: string[] = [];
|
||||
const allSelects: string[] = [];
|
||||
|
||||
// add select from the main table
|
||||
if (this.selects.indexOf(alias) !== -1)
|
||||
metadata.columns.forEach(column => {
|
||||
columns.push(alias + "." + column.name + " AS " + alias + "_" + column.name);
|
||||
allSelects.push(alias + "." + column.name + " AS " + alias + "_" + column.name);
|
||||
});
|
||||
|
||||
// add selects from left and inner joins
|
||||
this.leftJoins.concat(this.innerJoins)
|
||||
// add selects from joins
|
||||
this.joins
|
||||
.filter(join => this.selects.indexOf(join.alias.name) !== -1)
|
||||
.forEach(join => {
|
||||
const joinMetadata = join.alias.entityMetadata;
|
||||
const joinMetadata = this.aliasMap.getEntityMetadataByAlias(join.alias);
|
||||
joinMetadata.columns.forEach(column => {
|
||||
columns.push(join.alias.name + "." + column.name + " AS " + join.alias.name + "_" + column.name);
|
||||
allSelects.push(join.alias.name + "." + column.name + " AS " + join.alias.name + "_" + column.name);
|
||||
});
|
||||
});
|
||||
|
||||
// add all other selects
|
||||
this.selects.filter(select => {
|
||||
return select !== alias && !this.joins.find(join => join.alias.name === select);
|
||||
}).forEach(select => allSelects.push(select));
|
||||
|
||||
switch (this.type) {
|
||||
case "select":
|
||||
return "SELECT " + columns.join(", ") + " FROM " + tableName + " " + alias;
|
||||
return "SELECT " + allSelects.join(", ") + " FROM " + tableName + " " + alias;
|
||||
case "update":
|
||||
return "UPDATE " + tableName + " " + alias;
|
||||
case "delete":
|
||||
@ -339,23 +292,41 @@ export class QueryBuilder {
|
||||
}).join(" ");
|
||||
}
|
||||
|
||||
protected createInnerJoinExpression() {
|
||||
if (!this.innerJoins || !this.innerJoins.length) return "";
|
||||
protected createJoinExpression() {
|
||||
return this.joins.map(join => {
|
||||
const joinType = join.type === "inner" ? "INNER" : "LEFT";
|
||||
const appendedCondition = join.condition ? " AND " + join.condition : "";
|
||||
const parentAlias = join.alias.parentAliasName;
|
||||
const parentMetadata = this.aliasMap.getEntityMetadataByAlias(this.aliasMap.findAliasByName(parentAlias));
|
||||
const parentTable = parentMetadata.table.name;
|
||||
const parentTableColumn = parentMetadata.primaryColumn.name;
|
||||
const relation = parentMetadata.findRelationByPropertyName(join.alias.parentPropertyName);
|
||||
const junctionMetadata = relation.junctionEntityMetadata;
|
||||
const joinMetadata = this.aliasMap.getEntityMetadataByAlias(join.alias);
|
||||
const joinTable = joinMetadata.table.name;
|
||||
const joinTableColumn = joinMetadata.primaryColumn.name;
|
||||
|
||||
if (relation.isManyToMany) {
|
||||
const junctionTable = junctionMetadata.table.name;
|
||||
const junctionAlias = join.alias.parentAliasName + "_" + join.alias.name;
|
||||
const joinAlias = join.alias.name;
|
||||
const condition1 = junctionAlias + "." + parentTable + "_" + parentTableColumn + "=" + parentAlias + "." + joinTableColumn; // todo: use column names from junction table somehow
|
||||
const condition2 = joinAlias + "." + joinTableColumn + "=" + junctionAlias + "." + joinTable + "_" + joinTableColumn;
|
||||
|
||||
return " " + joinType + " JOIN " + junctionTable + " " + junctionAlias + " " + join.conditionType + " " + condition1 +
|
||||
" " + joinType + " JOIN " + joinTable + " " + joinAlias + " " + join.conditionType + " " + condition2 + appendedCondition;
|
||||
|
||||
} else if (relation.isOneToOne || relation.isManyToOne) {
|
||||
const condition = join.alias.name + "." + joinTableColumn + "=" + parentAlias + "." + join.alias.parentPropertyName;
|
||||
return " " + joinType + " JOIN " + joinTable + " " + join.alias.name + " " + join.conditionType + " " + condition + appendedCondition;
|
||||
|
||||
return this.innerJoins.map(join => {
|
||||
const joinMetadata = join.alias.entityMetadata; // todo: throw exception if not found
|
||||
const relationTable = joinMetadata.table.name;
|
||||
return " INNER JOIN " + relationTable + " " + join.alias.name + " " + join.conditionType + " " + join.condition;
|
||||
}).join(" ");
|
||||
}
|
||||
|
||||
protected createLeftJoinExpression() {
|
||||
if (!this.leftJoins || !this.leftJoins.length) return "";
|
||||
|
||||
return this.leftJoins.map(join => {
|
||||
const joinMetadata = join.alias.entityMetadata;
|
||||
const relationTable = joinMetadata.table.name;
|
||||
return " LEFT JOIN " + relationTable + " " + join.alias.name + " " + join.conditionType + " " + join.condition;
|
||||
} else if (relation.isOneToMany) {
|
||||
const condition = join.alias.name + "." + relation.inverseSideProperty + "=" + parentAlias + "." + joinTableColumn;
|
||||
return " " + joinType + " JOIN " + joinTable + " " + join.alias.name + " " + join.conditionType + " " + condition + appendedCondition;
|
||||
|
||||
} else {
|
||||
return " " + joinType + " JOIN " + joinTable + " " + join.alias.name + " " + join.conditionType + " " + join.condition;
|
||||
}
|
||||
}).join(" ");
|
||||
}
|
||||
|
||||
22
src/query-builder/alias/Alias.ts
Normal file
22
src/query-builder/alias/Alias.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {ColumnMetadata} from "../../metadata-builder/metadata/ColumnMetadata";
|
||||
|
||||
export class Alias {
|
||||
isMain: boolean;
|
||||
name: string;
|
||||
target: Function;
|
||||
parentPropertyName: string;
|
||||
parentAliasName: string;
|
||||
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
getPrimaryKeyValue(result: any, primaryColumn: ColumnMetadata): any {
|
||||
return result[this.name + "_" + primaryColumn.name];
|
||||
}
|
||||
|
||||
getColumnValue(result: any, column: ColumnMetadata) {
|
||||
return result[this.name + "_" + column.name];
|
||||
}
|
||||
|
||||
}
|
||||
76
src/query-builder/alias/AliasMap.ts
Normal file
76
src/query-builder/alias/AliasMap.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import {EntityMetadata} from "../../metadata-builder/metadata/EntityMetadata";
|
||||
import {Alias} from "./Alias";
|
||||
|
||||
export class AliasMap {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
aliases: Alias[] = [];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(private entityMetadatas: EntityMetadata[]) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
addMainAlias(alias: Alias) {
|
||||
const mainAlias = this.getMainAlias();
|
||||
if (mainAlias)
|
||||
this.aliases.splice(this.aliases.indexOf(mainAlias), 1);
|
||||
|
||||
alias.isMain = true;
|
||||
this.aliases.push(alias);
|
||||
}
|
||||
|
||||
addAlias(alias: Alias) {
|
||||
this.aliases.push(alias);
|
||||
}
|
||||
|
||||
getMainAlias() {
|
||||
return this.aliases.find(alias => alias.isMain);
|
||||
}
|
||||
|
||||
findAliasByName(name: string) {
|
||||
return this.aliases.find(alias => alias.name === name);
|
||||
}
|
||||
|
||||
findAliasByParent(parentAliasName: string, parentPropertyName: string) {
|
||||
return this.aliases.find(alias => {
|
||||
return alias.parentAliasName === parentAliasName && alias.parentPropertyName === parentPropertyName;
|
||||
});
|
||||
}
|
||||
|
||||
getEntityMetadataByAlias(alias: Alias): EntityMetadata {
|
||||
if (alias.target) {
|
||||
return this.findMetadata(alias.target);
|
||||
|
||||
} else if (alias.parentAliasName && alias.parentPropertyName) {
|
||||
const parentAlias = this.findAliasByName(alias.parentAliasName); // todo: throw exceptions everywhere
|
||||
const parentEntityMetadata = this.getEntityMetadataByAlias(parentAlias);
|
||||
const relation = parentEntityMetadata.findRelationByPropertyName(alias.parentPropertyName);
|
||||
return relation.relatedEntityMetadata;
|
||||
}
|
||||
|
||||
throw new Error("Cannot get entity metadata for the given alias " + alias.name);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private findMetadata(target: Function) {
|
||||
const metadata = this.entityMetadatas.find(metadata => metadata.target === target);
|
||||
if (!metadata)
|
||||
throw new Error("Metadata for " + (<any>target).name + " was not found.");
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
import {AliasMap} from "../alias/AliasMap";
|
||||
import {Alias} from "../alias/Alias";
|
||||
import * as _ from "lodash";
|
||||
import {EntityMetadata} from "../../metadata-builder/metadata/EntityMetadata";
|
||||
|
||||
/**
|
||||
* Transforms raw sql results returned from the database into object. Object is constructed for entity
|
||||
* based on the entity metadata.
|
||||
*/
|
||||
export class RawSqlResultsToObjectTransformer {
|
||||
|
||||
// todo: add check for property relation with id as a column
|
||||
// todo: create metadata or do it later?
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(private aliasMap: AliasMap) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
transform(rawSqlResults: any[]): any[] {
|
||||
return this.groupAndTransform(rawSqlResults, this.aliasMap.getMainAlias());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Since db returns a duplicated rows of the data where accuracies of the same object can be duplicated
|
||||
* we need to group our result and we must have some unique id (primary key in our case)
|
||||
*/
|
||||
private groupAndTransform(rawSqlResults: any[], alias: Alias) {
|
||||
|
||||
const metadata = this.aliasMap.getEntityMetadataByAlias(alias);
|
||||
if (!metadata.hasPrimaryKey)
|
||||
throw new Error("Metadata does not have primary key. You must have it to make convertation to object possible.");
|
||||
|
||||
const groupedRawResults = _.groupBy(rawSqlResults, result => alias.getPrimaryKeyValue(result, metadata.primaryColumn));
|
||||
return Object.keys(groupedRawResults)
|
||||
.map(key => this.transformIntoSingleResult(groupedRawResults[key], alias, metadata))
|
||||
.filter(res => !!res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms set of data results of the single value.
|
||||
*/
|
||||
private transformIntoSingleResult(rawSqlResults: any[], alias: Alias, metadata: EntityMetadata) {
|
||||
const jsonObject: any = {};
|
||||
|
||||
// get value from columns selections and put them into object
|
||||
metadata.columns.forEach(column => {
|
||||
const valueInObject = alias.getColumnValue(rawSqlResults[0], column); // we use zero index since its grouped data
|
||||
if (valueInObject && column.propertyName)
|
||||
jsonObject[column.propertyName] = valueInObject;
|
||||
});
|
||||
|
||||
// if relation is loaded then go into it recursively and transform its values too
|
||||
metadata.relations.forEach(relation => {
|
||||
const relationAlias = this.aliasMap.findAliasByParent(alias.name, relation.propertyName);
|
||||
if (relationAlias) {
|
||||
const relatedObjects = this.groupAndTransform(rawSqlResults, relationAlias);
|
||||
const result = (relation.isManyToOne || relation.isOneToOne) ? relatedObjects[0] : relatedObjects;
|
||||
if (result)
|
||||
jsonObject[relation.propertyName] = result;
|
||||
}
|
||||
});
|
||||
|
||||
return Object.keys(jsonObject).length > 0 ? jsonObject : null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,9 +1,10 @@
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {EntityMetadata} from "../metadata-builder/metadata/EntityMetadata";
|
||||
import {OrmBroadcaster} from "../subscriber/OrmBroadcaster";
|
||||
import {QueryBuilder, AliasMap} from "../driver/query-builder/QueryBuilder";
|
||||
import {DynamicCascadeOptions} from "./cascade/CascadeOption";
|
||||
import {EntityCreator} from "./creator/EntityCreator";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
|
||||
// todo: think how we can implement queryCount, queryManyAndCount
|
||||
// todo: extract non safe methods from repository (removeById, removeByConditions)
|
||||
|
||||
/**
|
||||
* Repository is supposed to work with your entity objects. Find entities, insert, update, delete, etc.
|
||||
@ -67,8 +68,9 @@ export class Repository<Entity> {
|
||||
createFromJson(json: any, fetchProperty?: boolean): Promise<Entity>;
|
||||
createFromJson(json: any, fetchConditions?: Object): Promise<Entity>;
|
||||
createFromJson(json: any, fetchOption?: boolean|Object): Promise<Entity> {
|
||||
const creator = new EntityCreator(this.connection);
|
||||
return creator.createFromJson<Entity>(json, this.metadata, fetchOption);
|
||||
return Promise.resolve<Entity>(null); // todo
|
||||
/* const creator = new EntityCreator(this.connection);
|
||||
return creator.createFromJson<Entity>(json, this.metadata, fetchOption);*/
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,74 +80,38 @@ export class Repository<Entity> {
|
||||
createManyFromJson(objects: any[], fetchProperties?: boolean[]): Promise<Entity[]>;
|
||||
createManyFromJson(objects: any[], fetchConditions?: Object[]): Promise<Entity[]>;
|
||||
createManyFromJson(objects: any[], fetchOption?: boolean[]|Object[]): Promise<Entity[]> {
|
||||
return Promise.all(objects.map((object, key) => {
|
||||
return Promise.resolve<Entity[]>(null); // todo
|
||||
/*return Promise.all(objects.map((object, key) => {
|
||||
const fetchConditions = (fetchOption && fetchOption[key]) ? fetchOption[key] : undefined;
|
||||
return this.createFromJson(object, fetchConditions);
|
||||
}));
|
||||
}));*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new query builder that can be used to build an sql query.
|
||||
*/
|
||||
createQueryBuilder(alias: string): QueryBuilder {
|
||||
createQueryBuilder(alias: string): QueryBuilder<Entity> {
|
||||
return this.connection.driver
|
||||
.createQueryBuilder(this.connection.metadatas)
|
||||
.createQueryBuilder<Entity>(this.connection)
|
||||
.select(alias)
|
||||
.from(this.metadata.target, alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query. Expects query will return object in Entity format and creates Entity object from that result.
|
||||
*/
|
||||
queryOne(query: string, aliasMap: AliasMap): Promise<Entity> {
|
||||
return this.connection.driver
|
||||
.query<any[]>(query)
|
||||
.then(results => this.objectToEntity(results, aliasMap))
|
||||
.then(entities => {
|
||||
this.broadcaster.broadcastAfterLoaded(entities[0]);
|
||||
return entities[0];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query. Expects query will return objects in Entity format and creates Entity objects from that result.
|
||||
*/
|
||||
queryMany(query: string, aliasMap: AliasMap): Promise<Entity[]> {
|
||||
return this.connection.driver
|
||||
.query<any[]>(query)
|
||||
.then(results => this.objectToEntity(results, aliasMap))
|
||||
.then(entities => {
|
||||
this.broadcaster.broadcastAfterLoadedAll(entities);
|
||||
return entities;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes query and returns raw result.
|
||||
* Executes query and returns raw database results.
|
||||
*/
|
||||
query(query: string): Promise<any> {
|
||||
return this.connection.driver.query(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives number of rows found by a given query.
|
||||
*/
|
||||
queryCount(query: any): Promise<number> {
|
||||
return this.connection.driver
|
||||
.query(query)
|
||||
.then(result => parseInt(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
*/
|
||||
find(conditions?: Object): Promise<Entity[]> {
|
||||
const alias = this.metadata.table.name;
|
||||
const builder = this.createQueryBuilder(alias);
|
||||
Object.keys(conditions).forEach(key => {
|
||||
builder.where(alias + "." + key + "=:" + key).setParameter(key, (<any> conditions)[key]);
|
||||
});
|
||||
return this.queryMany(builder.getSql(), builder.generateAliasMap());
|
||||
Object.keys(conditions).forEach(key => builder.where(alias + "." + key + "=:" + key));
|
||||
return builder.setParameters(conditions).getResults();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,10 +120,8 @@ export class Repository<Entity> {
|
||||
findOne(conditions: Object): Promise<Entity> {
|
||||
const alias = this.metadata.table.name;
|
||||
const builder = this.createQueryBuilder(alias);
|
||||
Object.keys(conditions).forEach(key => {
|
||||
builder.where(alias + "." + key + "=:" + key).setParameter(key, (<any> conditions)[key]);
|
||||
});
|
||||
return this.queryOne(builder.getSql(), builder.generateAliasMap());
|
||||
Object.keys(conditions).forEach(key => builder.where(alias + "." + key + "=:" + key));
|
||||
return builder.setParameters(conditions).getSingleResult();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -165,12 +129,10 @@ export class Repository<Entity> {
|
||||
*/
|
||||
findById(id: any): Promise<Entity> {
|
||||
const alias = this.metadata.table.name;
|
||||
const builder = this.createQueryBuilder(alias)
|
||||
return this.createQueryBuilder(alias)
|
||||
.where(alias + "." + this.metadata.primaryColumn.name + "=:id")
|
||||
.setParameter("id", id);
|
||||
|
||||
|
||||
return this.queryOne(builder.getSql(), builder.generateAliasMap());
|
||||
.setParameter("id", id)
|
||||
.getSingleResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -181,9 +143,9 @@ export class Repository<Entity> {
|
||||
* Saves a given entity. If entity is not inserted yet then it inserts a new entity.
|
||||
* If entity already inserted then performs its update.
|
||||
*/
|
||||
persist(entity: Entity, dynamicCascadeOptions?: DynamicCascadeOptions<Entity>): Promise<Entity> {
|
||||
|
||||
|
||||
persist(entity: Entity/*, dynamicCascadeOptions?: DynamicCascadeOptions<Entity>*/): Promise<Entity> {
|
||||
// todo
|
||||
return Promise.resolve<Entity>(null);
|
||||
|
||||
// if (!this.schema.isEntityTypeCorrect(entity))
|
||||
// throw new BadEntityInstanceException(entity, this.schema.entityClass);
|
||||
@ -191,14 +153,14 @@ export class Repository<Entity> {
|
||||
// const remover = new EntityRemover<Entity>(this.connection);
|
||||
// const persister = new EntityPersister<Entity>(this.connection);
|
||||
|
||||
return remover.computeRemovedRelations(this.metadata, entity, dynamicCascadeOptions)
|
||||
/* return remover.computeRemovedRelations(this.metadata, entity, dynamicCascadeOptions)
|
||||
.then(result => persister.persist(this.metadata, entity, dynamicCascadeOptions))
|
||||
.then(result => remover.executeRemoveOperations())
|
||||
.then(result => remover.executeUpdateInverseSideRelationRemoveIds())
|
||||
.then(result => entity);
|
||||
.then(result => entity);*/
|
||||
}
|
||||
|
||||
computeChangeSet(entity: Entity) {
|
||||
/*computeChangeSet(entity: Entity) {
|
||||
// if there is no primary key - there is no way to determine if object needs to be updated or insert
|
||||
// since we cannot get the target object without identifier, that's why we always do insert for such objects
|
||||
if (!this.metadata.primaryColumn)
|
||||
@ -208,11 +170,7 @@ export class Repository<Entity> {
|
||||
this.findById(this.metadata.getEntityId(entity)).then(dbEntity => {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
insert(entity: Entity) {
|
||||
|
||||
}
|
||||
}*/
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Persist ends
|
||||
@ -221,68 +179,45 @@ export class Repository<Entity> {
|
||||
/**
|
||||
* Removes a given entity.
|
||||
*/
|
||||
remove(entity: Entity, dynamicCascadeOptions?: DynamicCascadeOptions<Entity>): Promise<void> {
|
||||
remove(entity: Entity/*, dynamicCascadeOptions?: DynamicCascadeOptions<Entity>*/): Promise<void> {
|
||||
// todo
|
||||
return Promise.resolve();
|
||||
// if (!this.schema.isEntityTypeCorrect(entity))
|
||||
// throw new BadEntityInstanceException(entity, this.schema.entityClass);
|
||||
|
||||
const remover = new EntityRemover<Entity>(this.connection);
|
||||
/*const remover = new EntityRemover<Entity>(this.connection);
|
||||
return remover.registerEntityRemoveOperation(this.metadata, this.metadata.getEntityId(entity), dynamicCascadeOptions)
|
||||
.then(results => remover.executeRemoveOperations())
|
||||
.then(results => remover.executeUpdateInverseSideRelationRemoveIds());
|
||||
.then(results => remover.executeUpdateInverseSideRelationRemoveIds());*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entity by a given id.
|
||||
* Removes entity by a given id. Does not take care about cascade remove operations.
|
||||
*/
|
||||
removeById(id: string): Promise<void> {
|
||||
const builder = this.createQueryBuilder("entity")
|
||||
const alias = this.metadata.table.name;
|
||||
return this.createQueryBuilder(alias)
|
||||
.delete()
|
||||
.where("entity." + this.metadata.primaryColumn.name + "=:id")
|
||||
.setParameter("id", id);
|
||||
|
||||
return this.query(builder.getSql());
|
||||
.where(alias + "." + this.metadata.primaryColumn.name + "=:id")
|
||||
.setParameter("id", id)
|
||||
.execute()
|
||||
.then(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes entities by a given simple conditions.
|
||||
* Removes entities by a given simple conditions. Does not take care about cascade remove operations.
|
||||
*/
|
||||
removeByConditions(conditions: Object): Promise<any> {
|
||||
const builder = this.createQueryBuilder("entity").delete();
|
||||
Object.keys(conditions).forEach(key => {
|
||||
builder.where("entity." + key + "=:" + key).setParameter(key, (<any> conditions)[key]);
|
||||
});
|
||||
return this.query(builder.getSql());
|
||||
removeByConditions(conditions: Object): Promise<void> {
|
||||
const alias = this.metadata.table.name;
|
||||
const builder = this.createQueryBuilder(alias).delete();
|
||||
Object.keys(conditions).forEach(key => builder.where(alias + "." + key + "=:" + key));
|
||||
return builder
|
||||
.setParameters(conditions)
|
||||
.execute()
|
||||
.then(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities by given criteria and returns them with the total number of
|
||||
*/
|
||||
queryManyAndCount(query: string, countQuery: string): Promise<{ entities: Entity[] }> {
|
||||
return Promise.all<any>([
|
||||
this.queryMany(query),
|
||||
this.queryCount(countQuery)
|
||||
]).then(([entities, count]) => {
|
||||
return { entities: <Entity[]> entities, count: <number> count };
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates entity from the given json data. If fetchAllData param is specified then entity data will be
|
||||
* loaded from the database first, then filled with given json data.
|
||||
*/
|
||||
private objectToEntity(objects: any, aliasMap: AliasMap) {
|
||||
const creator = new EntityCreator(this.connection);
|
||||
return creator.objectToEntity<Entity>(objects, this.metadata, aliasMap);
|
||||
}
|
||||
|
||||
|
||||
/*private dbObjectToEntity(dbObject: any): Promise<Entity> {
|
||||
const hydrator = new EntityHydrator<Entity>(this.connection);
|
||||
return hydrator.hydrate(this.metadata, dbObject, joinFields);
|
||||
}*/
|
||||
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import {SchemaBuilder} from "./SchemaBuilder";
|
||||
import {MysqlDriver} from "../MysqlDriver";
|
||||
import {ColumnMetadata} from "../../metadata-builder/metadata/ColumnMetadata";
|
||||
import {ForeignKeyMetadata} from "../../metadata-builder/metadata/ForeignKeyMetadata";
|
||||
import {TableMetadata} from "../../metadata-builder/metadata/TableMetadata";
|
||||
import {MysqlDriver} from "../driver/MysqlDriver";
|
||||
import {ColumnMetadata} from "../metadata-builder/metadata/ColumnMetadata";
|
||||
import {ForeignKeyMetadata} from "../metadata-builder/metadata/ForeignKeyMetadata";
|
||||
import {TableMetadata} from "../metadata-builder/metadata/TableMetadata";
|
||||
|
||||
export class MysqlSchemaBuilder extends SchemaBuilder {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {ColumnMetadata} from "../../metadata-builder/metadata/ColumnMetadata";
|
||||
import {ForeignKeyMetadata} from "../../metadata-builder/metadata/ForeignKeyMetadata";
|
||||
import {TableMetadata} from "../../metadata-builder/metadata/TableMetadata";
|
||||
import {ColumnMetadata} from "../metadata-builder/metadata/ColumnMetadata";
|
||||
import {ForeignKeyMetadata} from "../metadata-builder/metadata/ForeignKeyMetadata";
|
||||
import {TableMetadata} from "../metadata-builder/metadata/TableMetadata";
|
||||
|
||||
export abstract class SchemaBuilder {
|
||||
|
||||
@ -3,7 +3,7 @@ import {TableMetadata} from "../metadata-builder/metadata/TableMetadata";
|
||||
import {ColumnMetadata} from "../metadata-builder/metadata/ColumnMetadata";
|
||||
import {ForeignKeyMetadata} from "../metadata-builder/metadata/ForeignKeyMetadata";
|
||||
import {EntityMetadata} from "../metadata-builder/metadata/EntityMetadata";
|
||||
import {SchemaBuilder} from "../driver/schema-builder/SchemaBuilder";
|
||||
import {SchemaBuilder} from "../schema-builder/SchemaBuilder";
|
||||
|
||||
/**
|
||||
* Creates indexes based on the given metadata
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"exclude": [
|
||||
"build",
|
||||
"node_modules",
|
||||
"odmhelpers",
|
||||
"typings/browser.d.ts",
|
||||
"typings/browser"
|
||||
]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user