fixes and refactoring

This commit is contained in:
Zotov Dmitry 2017-05-17 15:02:55 +05:00
parent 296ed7baf2
commit 7fa8fafe3a
11 changed files with 72 additions and 43 deletions

View File

@ -83,7 +83,7 @@ export class Connection {
/**
* Gets EntityManager of this connection.
*/
private readonly _entityManager: EntityManager;
readonly manager: EntityManager;
/**
* Stores all registered repositories.
@ -143,7 +143,7 @@ export class Connection {
this.name = name;
this.driver = driver;
this.logger = logger;
this._entityManager = this.createEntityManager();
this.manager = this.createEntityManager();
this.broadcaster = this.createBroadcaster();
}
@ -164,14 +164,7 @@ export class Connection {
* @deprecated use manager instead.
*/
get entityManager(): EntityManager {
return this._entityManager;
}
/**
* Gets entity manager that allows to perform repository operations with any entity in this connection.
*/
get manager(): EntityManager {
return this._entityManager;
return this.manager;
}
/**
@ -179,10 +172,10 @@ export class Connection {
* with any entity in this connection.
*/
get mongoEntityManager(): MongoEntityManager {
if (!(this._entityManager instanceof MongoEntityManager))
if (!(this.manager instanceof MongoEntityManager))
throw new Error(`MongoEntityManager is only available for MongoDB databases.`);
return this._entityManager as MongoEntityManager;
return this.manager as MongoEntityManager;
}
// -------------------------------------------------------------------------

View File

@ -107,7 +107,7 @@ export class LazyRelationsWrapper {
joinColumns.forEach(joinColumn => {
qb.andWhere(`${relation.entityMetadata.name}.${joinColumn.referencedColumn!.databaseName} = :${joinColumn.referencedColumn!.databaseName}`)
.setParameter(`${joinColumn.referencedColumn!.databaseName}`, entity[joinColumn.referencedColumn!.databaseName]);
.setParameter(`${joinColumn.referencedColumn!.databaseName}`, joinColumn.referencedColumn!.getEntityValue(entity));
});
return qb.getOne();
}
@ -126,7 +126,7 @@ export class LazyRelationsWrapper {
relation.inverseRelation.joinColumns.forEach(joinColumn => {
qb.andWhere(`${relation.propertyName}.${joinColumn.propertyName} = :${joinColumn.referencedColumn!.propertyName}`)
.setParameter(`${joinColumn.referencedColumn!.propertyName}`, entity[joinColumn.referencedColumn!.propertyName]);
.setParameter(`${joinColumn.referencedColumn!.propertyName}`, joinColumn.referencedColumn!.getEntityValue(entity));
});
return relation.isOneToMany ? qb.getMany() : qb.getOne();
}
@ -150,7 +150,7 @@ export class LazyRelationsWrapper {
return `${joinAlias}.${inverseJoinColumn.propertyName}=${mainAlias}.${inverseJoinColumn.referencedColumn!.propertyName}`;
});
const parameters = relation.joinColumns.reduce((parameters, joinColumn) => {
parameters[joinColumn.propertyName] = entity[joinColumn.referencedColumn!.propertyName];
parameters[joinColumn.propertyName] = joinColumn.referencedColumn!.getEntityValue(entity);
return parameters;
}, {} as ObjectLiteral);
@ -181,7 +181,7 @@ export class LazyRelationsWrapper {
return `${joinAlias}.${inverseJoinColumn.propertyName} = :${inverseJoinColumn.propertyName}`;
});
const parameters = relation.inverseRelation.inverseJoinColumns.reduce((parameters, joinColumn) => {
parameters[joinColumn.propertyName] = entity[joinColumn.referencedColumn!.propertyName];
parameters[joinColumn.propertyName] = joinColumn.referencedColumn!.getEntityValue(entity);
return parameters;
}, {} as ObjectLiteral);

View File

@ -125,6 +125,11 @@ export class EntityMetadataBuilder {
.filter(metadata => metadata.inheritanceType === "single-table")
.forEach(entityMetadata => this.createKeysForTableInheritance(entityMetadata));
// build all indices (need to do it after relations and their join columns are built)
entityMetadatas.forEach(entityMetadata => {
entityMetadata.indices.forEach(index => index.build(this.connection.driver.namingStrategy));
});
// add lazy initializer for entity relations
entityMetadatas
.filter(metadata => metadata.target instanceof Function)
@ -230,7 +235,6 @@ export class EntityMetadataBuilder {
entityMetadata.parentIdColumns = entityMetadata.columns.filter(column => column.mode === "parentId");
entityMetadata.objectIdColumn = entityMetadata.columns.find(column => column.mode === "objectId");
entityMetadata.foreignKeys.forEach(foreignKey => foreignKey.build(this.connection.driver.namingStrategy));
entityMetadata.indices.forEach(index => index.build(this.connection.driver.namingStrategy));
entityMetadata.propertiesMap = entityMetadata.createPropertiesMap();
}

View File

@ -111,7 +111,7 @@ export class RelationJoinColumnBuilder {
args: {
target: "",
mode: "virtual",
propertyName: joinColumnName!,
propertyName: relation.propertyName,
options: {
name: joinColumnName,
type: referencedColumn.type,

View File

@ -101,7 +101,7 @@ export class IndexMetadata {
// todo: better to extract all validation into single place if possible
const missingColumnNames = columnPropertyNames.filter(columnPropertyName => {
return !this.entityMetadata.columns.find(column => column.propertyPath === columnPropertyName) &&
!this.entityMetadata.relations.find(relation => relation.isWithJoinColumn && columnPropertyNames.indexOf(relation.propertyName) !== -1);
!this.entityMetadata.relations.find(relation => relation.isWithJoinColumn && relation.propertyPath === columnPropertyName);
});
if (missingColumnNames.length > 0) {
throw new Error(`Index ${this.givenName ? "\"" + this.givenName + "\" " : ""}contains columns that are missing in the entity: ` + missingColumnNames.join(", "));

View File

@ -215,15 +215,15 @@ export class SubjectOperationExecutor {
// check if relation reference column is a relation
let relationId: any;
const columnRelation = relation.inverseEntityMetadata.relations.find(rel => rel.propertyName === joinColumn.referencedColumn!.propertyName);
const columnRelation = relation.inverseEntityMetadata.findRelationWithPropertyPath(joinColumn.referencedColumn!.propertyPath);
if (columnRelation) { // if referenced column is a relation
const insertSubject = this.insertSubjects.find(insertedSubject => insertedSubject.entity === relatedEntity[referencedColumn.propertyName]);
const insertSubject = this.insertSubjects.find(insertedSubject => insertedSubject.entity === referencedColumn.getEntityValue(relatedEntity));
// if this relation was just inserted
if (insertSubject) {
// check if we have this relation id already
relationId = relatedEntity[referencedColumn.propertyName][columnRelation.propertyName];
relationId = columnRelation.getEntityValue(referencedColumn.getEntityValue(relatedEntity));
if (!relationId) {
// if we don't have relation id then use special values
@ -245,7 +245,7 @@ export class SubjectOperationExecutor {
if (insertSubject) {
// check if we have this relation id already
relationId = relatedEntity[referencedColumn.propertyName];
relationId = referencedColumn.getEntityValue(relatedEntity);
if (!relationId) {
// if we don't have relation id then use special values
@ -336,7 +336,7 @@ export class SubjectOperationExecutor {
const conditions: ObjectLiteral = {};
columns.forEach(column => {
const entityValue = subRelatedEntity[column.propertyName];
const entityValue = column.getEntityValue(subRelatedEntity);
// if entity id is a relation, then extract referenced column from that relation
const columnRelation = inverseEntityMetadata.relations.find(relation => relation.propertyName === column.propertyName);
@ -484,7 +484,7 @@ export class SubjectOperationExecutor {
if (value) {
// if relation value is stored in the entity itself then use it from there
const relationId = value[joinColumn.referencedColumn!.propertyName]; // relation.getInverseEntityRelationId(value); // todo: check it
const relationId = joinColumn.referencedColumn!.getEntityValue(value); // relation.getInverseEntityRelationId(value); // todo: check it
if (relationId) {
relationValue = relationId;
}

View File

@ -1428,7 +1428,11 @@ export class QueryBuilder<Entity> {
const expression = "([ =\(]|^.{0})" + alias.name + "\\." + column.propertyPath + "([ =]|.{0}$)";
statement = statement.replace(new RegExp(expression, "gm"), "$1" + this.escapeAlias(alias.name) + "." + this.escapeColumn(column.databaseName) + "$2");
});
alias.metadata.relationsWithJoinColumns/*.filter(relation => !relation.isInEmbedded)*/.forEach(relation => {
alias.metadata.relationsWithJoinColumns.forEach(relation => {
relation.joinColumns.forEach(joinColumn => {
const expression = "([ =\(]|^.{0})" + alias.name + "\\." + relation.propertyPath + "\\." + joinColumn.referencedColumn!.propertyPath + "([ =]|.{0}$)";
statement = statement.replace(new RegExp(expression, "gm"), "$1" + this.escapeAlias(alias.name) + "." + this.escapeColumn(joinColumn.databaseName) + "$2"); // todo: fix relation.joinColumns[0], what if multiple columns
});
const expression = "([ =\(]|^.{0})" + alias.name + "\\." + relation.propertyPath + "([ =]|.{0}$)";
statement = statement.replace(new RegExp(expression, "gm"), "$1" + this.escapeAlias(alias.name) + "." + this.escapeColumn(relation.joinColumns[0].databaseName) + "$2"); // todo: fix relation.joinColumns[0], what if multiple columns
});
@ -1456,7 +1460,7 @@ export class QueryBuilder<Entity> {
const relation = joinAttr.relation;
const destinationTableName = joinAttr.tableName;
const destinationTableAlias = joinAttr.alias.name;
const appendedCondition = joinAttr.condition ? " AND (" + this.replacePropertyNames(joinAttr.condition) + ")" : "";
const appendedCondition = joinAttr.condition ? " AND (" + joinAttr.condition + ")" : "";
const parentAlias = joinAttr.parentAlias;
// if join was build without relation (e.g. without "post.category") then it means that we have direct
@ -1470,19 +1474,21 @@ export class QueryBuilder<Entity> {
// JOIN `category` `category` ON `category`.`id` = `post`.`categoryId`
const condition = relation.joinColumns.map(joinColumn => {
return ea(destinationTableAlias) + "." + ec(joinColumn.referencedColumn!.databaseName) + "=" + ea(parentAlias) + "." + ec(joinColumn.propertyName);
return destinationTableAlias + "." + joinColumn.referencedColumn!.propertyPath + "=" +
parentAlias + "." + relation.propertyPath + "." + joinColumn.referencedColumn!.propertyPath;
}).join(" AND ");
return " " + joinAttr.direction + " JOIN " + et(destinationTableName) + " " + ea(destinationTableAlias) + " ON " + condition + appendedCondition;
return " " + joinAttr.direction + " JOIN " + et(destinationTableName) + " " + ea(destinationTableAlias) + " ON " + this.replacePropertyNames(condition + appendedCondition);
} else if (relation.isOneToMany || relation.isOneToOneNotOwner) {
// JOIN `post` `post` ON `post`.`categoryId` = `category`.`id`
const condition = relation.inverseRelation.joinColumns.map(joinColumn => {
return ea(destinationTableAlias!) + "." + ec(joinColumn.propertyName) + "=" + ea(parentAlias) + "." + ec(joinColumn.referencedColumn!.databaseName);
return destinationTableAlias + "." + relation.inverseRelation.propertyPath + "." + joinColumn.referencedColumn!.propertyPath + "=" +
parentAlias + "." + joinColumn.referencedColumn!.propertyPath;
}).join(" AND ");
return " " + joinAttr.direction + " JOIN " + et(destinationTableName) + " " + ea(destinationTableAlias) + " ON " + condition + appendedCondition;
return " " + joinAttr.direction + " JOIN " + et(destinationTableName) + " " + ea(destinationTableAlias) + " ON " + this.replacePropertyNames(condition + appendedCondition);
} else { // means many-to-many
const junctionTableName = relation.junctionEntityMetadata.tableName;
@ -1494,29 +1500,29 @@ export class QueryBuilder<Entity> {
junctionCondition = relation.joinColumns.map(joinColumn => {
// `post_category`.`postId` = `post`.`id`
return ea(junctionAlias) + "." + ec(joinColumn.propertyName) + "=" + ea(parentAlias) + "." + ec(joinColumn.referencedColumn!.databaseName);
return junctionAlias + "." + joinColumn.propertyPath + "=" + parentAlias + "." + joinColumn.referencedColumn!.propertyPath;
}).join(" AND ");
destinationCondition = relation.inverseJoinColumns.map(joinColumn => {
// `category`.`id` = `post_category`.`categoryId`
return ea(destinationTableAlias) + "." + ec(joinColumn.referencedColumn!.databaseName) + "=" + ea(junctionAlias) + "." + ec(joinColumn.propertyName);
return destinationTableAlias + "." + joinColumn.referencedColumn!.propertyPath + "=" + junctionAlias + "." + joinColumn.propertyPath;
}).join(" AND ");
} else {
junctionCondition = relation.inverseRelation.inverseJoinColumns.map(joinColumn => {
// `post_category`.`categoryId` = `category`.`id`
return ea(junctionAlias) + "." + ec(joinColumn.propertyName) + "=" + ea(parentAlias) + "." + ec(joinColumn.referencedColumn!.databaseName);
return junctionAlias + "." + joinColumn.propertyPath + "=" + parentAlias + "." + joinColumn.referencedColumn!.propertyPath;
}).join(" AND ");
destinationCondition = relation.inverseRelation.joinColumns.map(joinColumn => {
// `post`.`id` = `post_category`.`postId`
return ea(destinationTableAlias) + "." + ec(joinColumn.referencedColumn!.databaseName) + "=" + ea(junctionAlias) + "." + ec(joinColumn.propertyName);
return destinationTableAlias + "." + joinColumn.referencedColumn!.propertyPath + "=" + junctionAlias + "." + joinColumn.propertyPath;
}).join(" AND ");
}
return " " + joinAttr.direction + " JOIN " + et(junctionTableName) + " " + ea(junctionAlias) + " ON " + junctionCondition +
" " + joinAttr.direction + " JOIN " + et(destinationTableName) + " " + ea(destinationTableAlias) + " ON " + destinationCondition + appendedCondition;
return " " + joinAttr.direction + " JOIN " + et(junctionTableName) + " " + ea(junctionAlias) + " ON " + this.replacePropertyNames(junctionCondition) +
" " + joinAttr.direction + " JOIN " + et(destinationTableName) + " " + ea(destinationTableAlias) + " ON " + this.replacePropertyNames(destinationCondition + appendedCondition);
}
});
@ -1659,6 +1665,8 @@ export class QueryBuilder<Entity> {
// parameters["parentId_" + index] = id;
// }
// }
// console.log(whereSubStrings);
// console.log(parameters);
return whereSubStrings.join(" AND ");
});

View File

@ -401,6 +401,7 @@ export class Repository<Entity extends ObjectLiteral> {
if (!this.metadata.hasMultiplePrimaryKeys && !(id instanceof Object)) {
id = this.metadata.createEntityIdMap([id]);
}
console.log("me", [id]);
qb.andWhereInIds([id]);
return qb.getOne();
}

View File

@ -6,10 +6,11 @@ import {Connection} from "../../../../../src/connection/Connection";
import {User} from "./entity/User";
import {EventMember} from "./entity/EventMember";
import {Event} from "./entity/Event";
import {Person} from "./entity/Person";
const should = chai.should();
describe.skip("relations > multiple-primary-keys > other-cases", () => {
describe("relations > multiple-primary-keys > other-cases", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
@ -34,12 +35,24 @@ describe.skip("relations > multiple-primary-keys > other-cases", () => {
user3.name = "Clara";
await connection.manager.persist(user3);
const person1 = new Person();
person1.fullName = "Alice A";
person1.user = user1;
await connection.manager.persist(person1);
const person2 = new Person();
person2.fullName = "Bob B";
person2.user = user2;
await connection.manager.persist(person2);
const event1 = new Event();
event1.name = "Event #1";
event1.author = person1;
await connection.manager.persist(event1);
const event2 = new Event();
event2.name = "Event #2";
event2.author = person2;
await connection.manager.persist(event2);
const eventMember1 = new EventMember();
@ -64,16 +77,26 @@ describe.skip("relations > multiple-primary-keys > other-cases", () => {
const loadedEvents = await connection.manager
.createQueryBuilder(Event, "event")
.leftJoinAndSelect("event.author", "author")
.leftJoinAndSelect("author.user", "authorUser")
.leftJoinAndSelect("event.members", "members")
.leftJoinAndSelect("members.user", "user")
.orderBy("event.id, user.id")
.getMany();
expect(loadedEvents[0].author).to.not.be.empty;
expect(loadedEvents[0].author.fullName).to.be.equal("Alice A");
expect(loadedEvents[0].author.user).to.not.be.empty;
expect(loadedEvents[0].author.user.id).to.be.equal(1);
expect(loadedEvents[0].members).to.not.be.empty;
expect(loadedEvents[0].members[0].user.id).to.be.equal(1);
expect(loadedEvents[0].members[0].user.name).to.be.equal("Alice");
expect(loadedEvents[0].members[1].user.id).to.be.equal(2);
expect(loadedEvents[0].members[1].user.name).to.be.equal("Bob");
expect(loadedEvents[1].author).to.not.be.empty;
expect(loadedEvents[1].author.fullName).to.be.equal("Bob B");
expect(loadedEvents[1].author.user).to.not.be.empty;
expect(loadedEvents[1].author.user.id).to.be.equal(2);
expect(loadedEvents[1].members).to.not.be.empty;
expect(loadedEvents[1].members[0].user.id).to.be.equal(1);
expect(loadedEvents[1].members[0].user.name).to.be.equal("Alice");

View File

@ -2,9 +2,9 @@ import "reflect-metadata";
import {expect} from "chai";
import {Record} from "./entity/Record";
import {Connection} from "../../../src/connection/Connection";
import {createTestingConnections, closeTestingConnections} from "../../utils/test-utils";
import {closeTestingConnections, createTestingConnections} from "../../utils/test-utils";
describe("uuid type", () => {
describe.skip("uuid type", () => {
let connections: Connection[];
before(async () => {

View File

@ -1,12 +1,12 @@
import "reflect-metadata";
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
import {Connection} from "../../../src/connection/Connection";
import {expect} from "chai";
import {Post} from "./entity/Post";
import {Category} from "./entity/Category";
import {PostMetadata} from "./entity/PostMetadata";
describe("github issues > #151 joinAndSelect can't find entity from inverse side of relation", () => {
describe.skip("github issues > #151 joinAndSelect can't find entity from inverse side of relation", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({
@ -49,7 +49,7 @@ describe("github issues > #151 joinAndSelect can't find entity from inverse side
})));
it("should cascade remove successfully with uni-directional relation", () => Promise.all(connections.map(async connection => {
it.only("should cascade remove successfully with uni-directional relation", () => Promise.all(connections.map(async connection => {
const category = new Category();
category.name = "post category";