mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
new way of converting raw db results to object
This commit is contained in:
parent
7d34424eec
commit
d01d2b6cea
@ -43,6 +43,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"fs": "^0.0.2",
|
||||
"lodash": "^4.5.0",
|
||||
"mongodb": ">=2.0.0",
|
||||
"path": "^0.11.14",
|
||||
"reflect-metadata": "^0.1.3",
|
||||
|
||||
@ -3,6 +3,7 @@ import {Post} from "./entity/Post";
|
||||
import {PostDetails} from "./entity/PostDetails";
|
||||
import {Image} from "./entity/Image";
|
||||
import {ImageDetails} from "./entity/ImageDetails";
|
||||
import {Cover} from "./entity/Cover";
|
||||
|
||||
// first create a connection
|
||||
let options = {
|
||||
@ -14,7 +15,7 @@ let options = {
|
||||
autoSchemaCreate: true
|
||||
};
|
||||
|
||||
TypeORM.createMysqlConnection(options, [Post, PostDetails, Image, ImageDetails]).then(connection => {
|
||||
TypeORM.createMysqlConnection(options, [Post, PostDetails, Image, ImageDetails, Cover]).then(connection => {
|
||||
|
||||
const postJson = {
|
||||
id: 1,
|
||||
@ -26,11 +27,26 @@ TypeORM.createMysqlConnection(options, [Post, PostDetails, Image, ImageDetails])
|
||||
meta: "about-hello"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let postRepository = connection.getRepository<Post>(Post);
|
||||
return postRepository.findById(1).then(post => {
|
||||
console.log(post);
|
||||
}, err => console.log(err));
|
||||
let qb = postRepository
|
||||
.createQueryBuilder("post")
|
||||
.addSelect("image")
|
||||
.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")
|
||||
//.leftJoin(Image, "image", "on", "image.post=post.id")
|
||||
//.where("post.id=:id")
|
||||
.setParameter("id", 1);
|
||||
|
||||
return postRepository
|
||||
.queryMany(qb.getSql(), qb.generateAliasMap())
|
||||
.then(result => console.log(JSON.stringify(result, null, 4)))
|
||||
.catch(err => console.log(err));
|
||||
|
||||
return;
|
||||
|
||||
@ -41,11 +57,11 @@ TypeORM.createMysqlConnection(options, [Post, PostDetails, Image, ImageDetails])
|
||||
const post = new Post();
|
||||
post.text = "Hello how are you?";
|
||||
post.title = "hello";
|
||||
post.details = details;
|
||||
//post.details = details;
|
||||
|
||||
postRepository
|
||||
.persist(post)
|
||||
.then(post => console.log("Post has been saved"))
|
||||
.catch(error => console.log("Cannot save. Error: ", error));
|
||||
|
||||
}, error => console.log("Cannot connect: ", error));
|
||||
}).catch(error => console.log("Cannot connect: ", error));
|
||||
18
sample/sample2-one-to-one/entity/Cover.ts
Normal file
18
sample/sample2-one-to-one/entity/Cover.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
import {OneToMany} from "../../../src/decorator/Relations";
|
||||
import {Post} from "./Post";
|
||||
|
||||
@Table("sample2_cover")
|
||||
export class Cover {
|
||||
|
||||
@PrimaryColumn("int", { isAutoIncrement: true })
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
url: string;
|
||||
|
||||
@OneToMany<Post>(() => Post, post => post.cover)
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
@ -2,6 +2,7 @@ import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
import {ManyToOne, OneToMany, OneToOne} from "../../../src/decorator/Relations";
|
||||
import {Post} from "./Post";
|
||||
import {ImageDetails} from "./ImageDetails";
|
||||
|
||||
@Table("sample2_image")
|
||||
export class Image {
|
||||
@ -12,21 +13,13 @@ export class Image {
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToOne<Post>(() => Post, post => post.images, {
|
||||
//isAlwaysLeftJoin: true
|
||||
})
|
||||
@ManyToOne<Post>(() => Post, post => post.images)
|
||||
post: Post;
|
||||
|
||||
@ManyToOne<Post>(() => Post, post => post.secondaryImages, {
|
||||
// isAlwaysLeftJoin: true
|
||||
})
|
||||
@ManyToOne<Post>(() => Post, post => post.secondaryImages)
|
||||
secondaryPost: Post;
|
||||
|
||||
/*
|
||||
|
||||
@OneToOne<ImageDetails>(true, () => ImageDetails, details => details.image, {
|
||||
isAlwaysInnerJoin: true
|
||||
})
|
||||
details: ImageDetails;*/
|
||||
@OneToOne<ImageDetails>(true, () => ImageDetails, details => details.image)
|
||||
details: ImageDetails;
|
||||
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
import {OneToOne, OneToMany} from "../../../src/decorator/Relations";
|
||||
import {PostDetails} from "./PostDetails";
|
||||
import {OneToMany, ManyToOne} from "../../../src/decorator/Relations";
|
||||
import {Image} from "./Image";
|
||||
import {Cover} from "./Cover";
|
||||
|
||||
@Table("sample2_post")
|
||||
export class Post {
|
||||
@ -20,19 +20,16 @@ export class Post {
|
||||
})
|
||||
text: string;
|
||||
|
||||
/* @OneToOne<PostDetails>(true, () => PostDetails, details => details.post, {
|
||||
//isAlwaysInnerJoin: true
|
||||
})
|
||||
/* @OneToOne<PostDetails>(true, () => PostDetails, details => details.post)
|
||||
details: PostDetails;*/
|
||||
|
||||
@OneToMany<Image>(() => Image, image => image.post, {
|
||||
isAlwaysLeftJoin: true
|
||||
})
|
||||
@OneToMany<Image>(() => Image, image => image.post)
|
||||
images: Image[];
|
||||
|
||||
@OneToMany<Image>(() => Image, image => image.secondaryPost, {
|
||||
isAlwaysLeftJoin: true
|
||||
})
|
||||
@OneToMany<Image>(() => Image, image => image.secondaryPost)
|
||||
secondaryImages: Image[];
|
||||
|
||||
@ManyToOne<Cover>(() => Cover, cover => cover.posts)
|
||||
cover: Cover;
|
||||
|
||||
}
|
||||
@ -1,5 +1,52 @@
|
||||
import {EntityMetadata} from "../../metadata-builder/metadata/EntityMetadata";
|
||||
import {ColumnMetadata} from "../../metadata-builder/metadata/ColumnMetadata";
|
||||
|
||||
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 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>
|
||||
*/
|
||||
@ -18,9 +65,9 @@ export class QueryBuilder {
|
||||
|
||||
private type: "select"|"update"|"delete";
|
||||
private selects: string[] = [];
|
||||
private froms: { entity: Function, alias: string };
|
||||
private leftJoins: { join: Function, alias: string, conditionType: string, condition: string }[] = [];
|
||||
private innerJoins: { join: Function, alias: string, conditionType: string, condition: string }[] = [];
|
||||
private froms: { alias: Alias };
|
||||
private leftJoins: { alias: Alias, conditionType: string, condition: string }[] = [];
|
||||
private innerJoins: { alias: Alias, conditionType: string, condition: string }[] = [];
|
||||
private groupBys: string[] = [];
|
||||
private wheres: { type: "simple"|"and"|"or", condition: string }[] = [];
|
||||
private havings: { type: "simple"|"and"|"or", condition: string }[] = [];
|
||||
@ -29,6 +76,8 @@ export class QueryBuilder {
|
||||
private limit: number;
|
||||
private offset: number;
|
||||
|
||||
private aliasMap = new AliasMap();
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
@ -74,17 +123,49 @@ export class QueryBuilder {
|
||||
//from(tableName: string, alias: string): this;
|
||||
from(entity: Function, alias?: string): this {
|
||||
//from(entityOrTableName: Function|string, alias: string): this {
|
||||
this.froms = { entity: entity, alias: alias };
|
||||
const aliasObj = new Alias(alias, this.findMetadata(entity));
|
||||
this.aliasMap.addMainAlias(aliasObj);
|
||||
this.froms = { alias: aliasObj };
|
||||
return this;
|
||||
}
|
||||
|
||||
innerJoin(target: Function, alias: string, conditionType: string, condition: string): this {
|
||||
this.innerJoins.push({ join: target, alias: alias, conditionType: conditionType, condition: condition });
|
||||
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;
|
||||
}
|
||||
|
||||
leftJoin(target: Function, alias: string, conditionType: string, condition: string): this {
|
||||
this.leftJoins.push({ join: target, alias: alias, conditionType: conditionType, condition: 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;
|
||||
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;
|
||||
}
|
||||
|
||||
const aliasObj = new Alias(alias, entityMetadata, parentAliasName, parentPropertyName);
|
||||
this.aliasMap.addAlias(aliasObj);
|
||||
this.leftJoins.push({ alias: aliasObj, conditionType: conditionType, condition: condition });
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -148,7 +229,7 @@ export class QueryBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
setParameter(key: string, value: string): this {
|
||||
setParameter(key: string, value: any): this {
|
||||
this.parameters[key] = value;
|
||||
return this;
|
||||
}
|
||||
@ -171,6 +252,13 @@ export class QueryBuilder {
|
||||
sql = this.replaceParameters(sql);
|
||||
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]);*/
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
@ -196,7 +284,7 @@ export class QueryBuilder {
|
||||
protected findMetadata(target: Function) {
|
||||
const metadata = this.entityMetadatas.find(metadata => metadata.target === target);
|
||||
if (!metadata)
|
||||
throw new Error("Metadata for " + this.froms.entity + " was not found.");
|
||||
throw new Error("Metadata for " + (<any>target).name + " was not found.");
|
||||
|
||||
return metadata;
|
||||
}
|
||||
@ -204,24 +292,24 @@ export class QueryBuilder {
|
||||
protected createSelectExpression() {
|
||||
// todo throw exception if selects or from is missing
|
||||
|
||||
const metadata = this.findMetadata(this.froms.entity);
|
||||
const metadata = this.froms.alias.entityMetadata;
|
||||
const tableName = metadata.table.name;
|
||||
const alias = this.froms.alias ? this.froms.alias : metadata.table.name;
|
||||
const alias = this.froms.alias.name;
|
||||
const columns: string[] = [];
|
||||
|
||||
// add select from the main table
|
||||
if (this.selects.indexOf(this.froms.alias) !== -1)
|
||||
if (this.selects.indexOf(alias) !== -1)
|
||||
metadata.columns.forEach(column => {
|
||||
columns.push(this.froms.alias + "." + column.name + " AS " + this.froms.alias + "_" + column.name);
|
||||
columns.push(alias + "." + column.name + " AS " + alias + "_" + column.name);
|
||||
});
|
||||
|
||||
// add selects from left and inner joins
|
||||
this.leftJoins.concat(this.innerJoins)
|
||||
.filter(join => this.selects.indexOf(join.alias) !== -1)
|
||||
.filter(join => this.selects.indexOf(join.alias.name) !== -1)
|
||||
.forEach(join => {
|
||||
const joinMetadata = this.findMetadata(join.join);
|
||||
const joinMetadata = join.alias.entityMetadata;
|
||||
joinMetadata.columns.forEach(column => {
|
||||
columns.push(join.alias + "." + column.name + " AS " + join.alias + "_" + column.name);
|
||||
columns.push(join.alias.name + "." + column.name + " AS " + join.alias.name + "_" + column.name);
|
||||
});
|
||||
});
|
||||
|
||||
@ -255,9 +343,9 @@ export class QueryBuilder {
|
||||
if (!this.innerJoins || !this.innerJoins.length) return "";
|
||||
|
||||
return this.innerJoins.map(join => {
|
||||
const joinMetadata = this.entityMetadatas.find(metadata => metadata.target === join.join); // todo: throw exception if not found
|
||||
const joinMetadata = join.alias.entityMetadata; // todo: throw exception if not found
|
||||
const relationTable = joinMetadata.table.name;
|
||||
return " INNER JOIN " + relationTable + " " + join.alias + " " + join.conditionType + " " + join.condition;
|
||||
return " INNER JOIN " + relationTable + " " + join.alias.name + " " + join.conditionType + " " + join.condition;
|
||||
}).join(" ");
|
||||
}
|
||||
|
||||
@ -265,9 +353,9 @@ export class QueryBuilder {
|
||||
if (!this.leftJoins || !this.leftJoins.length) return "";
|
||||
|
||||
return this.leftJoins.map(join => {
|
||||
const joinMetadata = this.findMetadata(join.join);
|
||||
const joinMetadata = join.alias.entityMetadata;
|
||||
const relationTable = joinMetadata.table.name;
|
||||
return " LEFT JOIN " + relationTable + " " + join.alias + " " + join.conditionType + " " + join.condition;
|
||||
return " LEFT JOIN " + relationTable + " " + join.alias.name + " " + join.conditionType + " " + join.condition;
|
||||
}).join(" ");
|
||||
}
|
||||
|
||||
@ -311,9 +399,5 @@ export class QueryBuilder {
|
||||
});
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected replaceTableNames(sql: string) {
|
||||
return sql.replace("\$\$", "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -71,16 +71,6 @@ export class RelationMetadata extends PropertyMetadata {
|
||||
*/
|
||||
private _isCascadeRemove: boolean;
|
||||
|
||||
/**
|
||||
* If set to true then it means that related object always will be left-joined when this object is being loaded.
|
||||
*/
|
||||
private _isAlwaysLeftJoin: boolean;
|
||||
|
||||
/**
|
||||
* If set to true then it means that related object always will be inner-joined when this object is being loaded.
|
||||
*/
|
||||
private _isAlwaysInnerJoin: boolean;
|
||||
|
||||
/**
|
||||
* Indicates if relation column value can be nullable or not.
|
||||
*/
|
||||
@ -115,10 +105,6 @@ export class RelationMetadata extends PropertyMetadata {
|
||||
|
||||
if (options.name)
|
||||
this._name = options.name;
|
||||
if (options.isAlwaysInnerJoin)
|
||||
this._isAlwaysInnerJoin = options.isAlwaysInnerJoin;
|
||||
if (options.isAlwaysLeftJoin)
|
||||
this._isAlwaysLeftJoin = options.isAlwaysLeftJoin;
|
||||
if (options.isCascadeInsert)
|
||||
this._isCascadeInsert = options.isCascadeInsert;
|
||||
if (options.isCascadeUpdate)
|
||||
@ -178,14 +164,6 @@ export class RelationMetadata extends PropertyMetadata {
|
||||
return this._isCascadeRemove;
|
||||
}
|
||||
|
||||
get isAlwaysLeftJoin(): boolean {
|
||||
return this._isAlwaysLeftJoin;
|
||||
}
|
||||
|
||||
get isAlwaysInnerJoin(): boolean {
|
||||
return this._isAlwaysInnerJoin;
|
||||
}
|
||||
|
||||
get isOneToOne(): boolean {
|
||||
return this.relationType === RelationTypes.ONE_TO_ONE;
|
||||
}
|
||||
|
||||
@ -20,16 +20,6 @@ export interface RelationOptions {
|
||||
*/
|
||||
isCascadeRemove?: boolean;
|
||||
|
||||
/**
|
||||
* If set to true then it means that related object always will be left-joined when this object is being loaded.
|
||||
*/
|
||||
isAlwaysLeftJoin?: boolean;
|
||||
|
||||
/**
|
||||
* If set to true then it means that related object always will be inner-joined when this object is being loaded.
|
||||
*/
|
||||
isAlwaysInnerJoin?: boolean;
|
||||
|
||||
/**
|
||||
* Old column name. Used to make safe schema updates.
|
||||
*/
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {EntityMetadata} from "../metadata-builder/metadata/EntityMetadata";
|
||||
import {OrmBroadcaster} from "../subscriber/OrmBroadcaster";
|
||||
import {QueryBuilder} from "../driver/query-builder/QueryBuilder";
|
||||
import {QueryBuilder, AliasMap} from "../driver/query-builder/QueryBuilder";
|
||||
import {DynamicCascadeOptions} from "./cascade/CascadeOption";
|
||||
import {EntityCreator} from "./creator/EntityCreator";
|
||||
|
||||
@ -87,34 +87,33 @@ export class Repository<Entity> {
|
||||
/**
|
||||
* Creates a new query builder that can be used to build an sql query.
|
||||
*/
|
||||
createQueryBuilder(alias?: string): QueryBuilder {
|
||||
const queryBuilder = this.connection.driver.createQueryBuilder(this.connection.metadatas);
|
||||
if (alias)
|
||||
queryBuilder.select(alias).from(this.metadata.target, alias);
|
||||
|
||||
return queryBuilder;
|
||||
createQueryBuilder(alias: string): QueryBuilder {
|
||||
return this.connection.driver
|
||||
.createQueryBuilder(this.connection.metadatas)
|
||||
.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): Promise<Entity> {
|
||||
queryOne(query: string, aliasMap: AliasMap): Promise<Entity> {
|
||||
return this.connection.driver
|
||||
.query<any[]>(query)
|
||||
.then(results => this.createFromJson(results[0]))
|
||||
.then(entity => {
|
||||
this.broadcaster.broadcastAfterLoaded(entity);
|
||||
return entity;
|
||||
.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): Promise<Entity[]> {
|
||||
queryMany(query: string, aliasMap: AliasMap): Promise<Entity[]> {
|
||||
return this.connection.driver
|
||||
.query<any[]>(query)
|
||||
.then(results => this.createManyFromJson(results))
|
||||
.then(results => this.objectToEntity(results, aliasMap))
|
||||
.then(entities => {
|
||||
this.broadcaster.broadcastAfterLoadedAll(entities);
|
||||
return entities;
|
||||
@ -141,34 +140,37 @@ export class Repository<Entity> {
|
||||
* Finds entities that match given conditions.
|
||||
*/
|
||||
find(conditions?: Object): Promise<Entity[]> {
|
||||
const builder = this.createQueryBuilder("entity");
|
||||
const alias = this.metadata.table.name;
|
||||
const builder = this.createQueryBuilder(alias);
|
||||
Object.keys(conditions).forEach(key => {
|
||||
builder.where("entity." + key + "=:" + key).setParameter(key, (<any> conditions)[key]);
|
||||
builder.where(alias + "." + key + "=:" + key).setParameter(key, (<any> conditions)[key]);
|
||||
});
|
||||
return this.queryMany(builder.getSql());
|
||||
return this.queryMany(builder.getSql(), builder.generateAliasMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds one entity that matches given condition.
|
||||
*/
|
||||
findOne(conditions: Object): Promise<Entity> {
|
||||
const builder = this.createQueryBuilder("entity");
|
||||
const alias = this.metadata.table.name;
|
||||
const builder = this.createQueryBuilder(alias);
|
||||
Object.keys(conditions).forEach(key => {
|
||||
builder.where("entity." + key + "=:" + key).setParameter(key, (<any> conditions)[key]);
|
||||
builder.where(alias + "." + key + "=:" + key).setParameter(key, (<any> conditions)[key]);
|
||||
});
|
||||
return this.queryOne(builder.getSql());
|
||||
return this.queryOne(builder.getSql(), builder.generateAliasMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entity with given id.
|
||||
*/
|
||||
findById(id: any): Promise<Entity> {
|
||||
const builder = this.createQueryBuilder("entity")
|
||||
.where("entity." + this.metadata.primaryColumn.name + "=:id")
|
||||
const alias = this.metadata.table.name;
|
||||
const builder = this.createQueryBuilder(alias)
|
||||
.where(alias + "." + this.metadata.primaryColumn.name + "=:id")
|
||||
.setParameter("id", id);
|
||||
|
||||
|
||||
return this.queryOne(builder.getSql());
|
||||
return this.queryOne(builder.getSql(), builder.generateAliasMap());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -268,6 +270,16 @@ export class Repository<Entity> {
|
||||
// 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,6 +1,8 @@
|
||||
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 * as _ from "lodash";
|
||||
|
||||
export class EntityCreator {
|
||||
|
||||
@ -25,14 +27,24 @@ export class EntityCreator {
|
||||
createFromJson<Entity>(object: any, metadata: EntityMetadata, fetchProperty?: boolean): Promise<Entity>;
|
||||
createFromJson<Entity>(object: any, metadata: EntityMetadata, fetchProperty?: Object): Promise<Entity>;
|
||||
createFromJson<Entity>(object: any, metadata: EntityMetadata, fetchOption?: boolean|Object): Promise<Entity> {
|
||||
return this.objectToEntity(object, metadata, fetchOption);
|
||||
|
||||
return Promise.resolve(this.objectToEntity(object, metadata, fetchOption));
|
||||
//return this.objectToEntity(object, metadata, fetchOption);
|
||||
}
|
||||
|
||||
objectToEntity<Entity>(objects: any[], metadata: EntityMetadata, aliasMap: AliasMap, fetchProperty?: boolean): Entity;
|
||||
objectToEntity<Entity>(objects: any[], metadata: EntityMetadata, aliasMap: AliasMap,fetchProperty?: Object): Entity;
|
||||
objectToEntity<Entity>(objects: any[], metadata: EntityMetadata, aliasMap: AliasMap, fetchOption?: boolean|Object): Entity {
|
||||
|
||||
return this.toEntity(objects, metadata, aliasMap.getMainAlias(), aliasMap);
|
||||
//return this.objectToEntity(object, metadata, fetchOption);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
getLoadMap(metadata: EntityMetadata) {
|
||||
/*getLoadMap(metadata: EntityMetadata) {
|
||||
|
||||
const postId = 1;
|
||||
const postJson = {
|
||||
@ -59,7 +71,7 @@ export class EntityCreator {
|
||||
const joinRelations = (parentTableAlias: string, entityMetadata: EntityMetadata) => {
|
||||
if (visitedMetadatas.find(metadata => metadata === entityMetadata))
|
||||
return;
|
||||
|
||||
|
||||
visitedMetadatas.push(metadata);
|
||||
entityMetadata.relations.filter(relation => relation.isAlwaysLeftJoin || relation.isAlwaysInnerJoin).forEach(relation => {
|
||||
let relationAlias = relation.relatedEntityMetadata.table.name;
|
||||
@ -81,7 +93,7 @@ export class EntityCreator {
|
||||
} else { // else can be only always inner join
|
||||
qb.addSelect(relationAlias).innerJoin(relation.type, relationAlias, "ON", condition);
|
||||
}
|
||||
|
||||
|
||||
// now recursively go throw its relations
|
||||
joinRelations(relationAlias, relation.relatedEntityMetadata);
|
||||
});
|
||||
@ -101,15 +113,73 @@ export class EntityCreator {
|
||||
|
||||
private convertTableResultToJsonTree() {
|
||||
|
||||
}*/
|
||||
|
||||
|
||||
private toEntity(sqlResult: any[], metadata: EntityMetadata, mainAlias: Alias, aliasMap: AliasMap): any[] {
|
||||
|
||||
const objects = _.groupBy(sqlResult, result => {
|
||||
return result[mainAlias.name + "_" + metadata.primaryColumn.name];
|
||||
});
|
||||
|
||||
return Object.keys(objects).map(key => {
|
||||
//if (id && id != key) return null;
|
||||
|
||||
let isAnythingLoaded = false;
|
||||
const object = objects[key][0];
|
||||
//const entity = metadata.create();
|
||||
const jsonObject: any = {};
|
||||
|
||||
metadata.columns.forEach(column => {
|
||||
const valueInObject = object[mainAlias.name + "_" + column.name];
|
||||
if (valueInObject && column.propertyName) { // todo: add check for property relation with id as a column
|
||||
jsonObject[column.propertyName] = valueInObject;
|
||||
isAnythingLoaded = true;
|
||||
}
|
||||
});
|
||||
|
||||
metadata.relations.forEach(relation => {
|
||||
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;
|
||||
//}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return isAnythingLoaded ? jsonObject : null;
|
||||
|
||||
}).filter(res => res !== null);
|
||||
|
||||
//return id ? final[0] : final;
|
||||
}
|
||||
|
||||
private objectToEntity(object: any, metadata: EntityMetadata, doFetchProperties?: boolean): Promise<any>;
|
||||
private objectToEntity(object: any, metadata: EntityMetadata, fetchConditions?: Object): Promise<any>;
|
||||
private objectToEntity(object: any, metadata: EntityMetadata, fetchOption?: boolean|Object): Promise<any> {
|
||||
private objectToEntity2(object: any, metadata: EntityMetadata, doFetchProperties?: boolean): Promise<any>;
|
||||
private objectToEntity2(object: any, metadata: EntityMetadata, fetchConditions?: Object): Promise<any>;
|
||||
private objectToEntity2(object: any, metadata: EntityMetadata, fetchOption?: boolean|Object): Promise<any> {
|
||||
if (!object)
|
||||
throw new Error("Given object is empty, cannot initialize empty object.");
|
||||
|
||||
this.getLoadMap(metadata);
|
||||
//this.getLoadMap(metadata);
|
||||
|
||||
const doFetch = !!fetchOption;
|
||||
const entityPromise = this.loadDependOnFetchOption(object, metadata, fetchOption);
|
||||
|
||||
@ -62,7 +62,7 @@ export class DocumentHydrator<Document> {
|
||||
|
||||
let relationId = dbObject[relation.name];
|
||||
let canLoadRelation = this.canLoadRelation(relation, joinFields);
|
||||
let isLoadInnerTyped = this.isInnerJoin(joinFields, relation.name) || relation.isAlwaysInnerJoin;
|
||||
let isLoadInnerTyped = this.isInnerJoin(joinFields, relation.name);
|
||||
|
||||
if (!canLoadRelation)
|
||||
return;
|
||||
@ -90,7 +90,7 @@ export class DocumentHydrator<Document> {
|
||||
schema.relationWithManies.forEach(relation => {
|
||||
|
||||
let canLoadRelation = this.canLoadRelation(relation, joinFields);
|
||||
let isLoadInnerTyped = this.isInnerJoin(joinFields, relation.name) || relation.isAlwaysInnerJoin;
|
||||
let isLoadInnerTyped = this.isInnerJoin(joinFields, relation.name);
|
||||
|
||||
if (!canLoadRelation)
|
||||
return;
|
||||
@ -129,9 +129,7 @@ export class DocumentHydrator<Document> {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private canLoadRelation(relation: RelationSchema, joinFields?: JoinFieldOption[]|any[]): boolean {
|
||||
return this.hasKey(joinFields, relation.propertyName)
|
||||
|| relation.isAlwaysLeftJoin
|
||||
|| relation.isAlwaysInnerJoin;
|
||||
return this.hasKey(joinFields, relation.propertyName);
|
||||
}
|
||||
|
||||
private hasKey(joinFields: JoinFieldOption[]|any[], key: string) {
|
||||
|
||||
11
typings.json
11
typings.json
@ -1,13 +1,14 @@
|
||||
{
|
||||
"ambientDevDependencies": {
|
||||
"mocha": "github:DefinitelyTyped/DefinitelyTyped/mocha/mocha.d.ts#7a3ca1f0b8a0960af9fc1838f3234cc9d6ce0645",
|
||||
"assertion-error": "github:DefinitelyTyped/DefinitelyTyped/assertion-error/assertion-error.d.ts#7a3ca1f0b8a0960af9fc1838f3234cc9d6ce0645",
|
||||
"chai": "github:DefinitelyTyped/DefinitelyTyped/chai/chai.d.ts#7a3ca1f0b8a0960af9fc1838f3234cc9d6ce0645",
|
||||
"sinon": "github:DefinitelyTyped/DefinitelyTyped/sinon/sinon.d.ts#7a3ca1f0b8a0960af9fc1838f3234cc9d6ce0645",
|
||||
"mockery": "github:DefinitelyTyped/DefinitelyTyped/mockery/mockery.d.ts#6f6e5c7dd9effe21fee14eb65fe340ecbbc8580a"
|
||||
"mocha": "github:DefinitelyTyped/DefinitelyTyped/mocha/mocha.d.ts#7a3ca1f0b8a0960af9fc1838f3234cc9d6ce0645",
|
||||
"mockery": "github:DefinitelyTyped/DefinitelyTyped/mockery/mockery.d.ts#6f6e5c7dd9effe21fee14eb65fe340ecbbc8580a",
|
||||
"sinon": "github:DefinitelyTyped/DefinitelyTyped/sinon/sinon.d.ts#7a3ca1f0b8a0960af9fc1838f3234cc9d6ce0645"
|
||||
},
|
||||
"ambientDependencies": {
|
||||
"node": "github:DefinitelyTyped/DefinitelyTyped/node/node.d.ts#cf7f980ba6cf09f75aaa9b5e53010db3c00b9aaf",
|
||||
"es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#6697d6f7dadbf5773cb40ecda35a76027e0783b2"
|
||||
"es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#6697d6f7dadbf5773cb40ecda35a76027e0783b2",
|
||||
"lodash": "github:DefinitelyTyped/DefinitelyTyped/lodash/lodash.d.ts#7b7aa2027a8fb6219a8bcf1b6bb12bcd0ff9539d",
|
||||
"node": "github:DefinitelyTyped/DefinitelyTyped/node/node.d.ts#cf7f980ba6cf09f75aaa9b5e53010db3c00b9aaf"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user