fixed many to many when using lazy loading

This commit is contained in:
Umed Khudoiberdiev 2016-05-04 02:04:45 +05:00
parent 60fc079f85
commit 37c2731f89
5 changed files with 118 additions and 47 deletions

View File

@ -1,6 +1,7 @@
import {createConnection, CreateConnectionOptions} from "../../src/typeorm";
import {Post} from "./entity/Post";
import {Author} from "./entity/Author";
import {Category} from "./entity/Category";
const options: CreateConnectionOptions = {
driver: "mysql",
@ -16,13 +17,14 @@ const options: CreateConnectionOptions = {
logFailedQueryError: true
}
},
entities: [Post, Author]
entities: [Post, Author, Category]
};
createConnection(options).then(connection => {
let postRepository = connection.getRepository(Post);
let authorRepository = connection.getRepository(Author);
let categoryRepository = connection.getRepository(Category);
let author = authorRepository.create();
author.name = "Umed";
@ -67,6 +69,40 @@ createConnection(options).then(connection => {
})
.then(posts => {
console.log("Two post's author has been removed.");
console.log("Now lets check many-to-many relations");
let category1 = categoryRepository.create();
category1.name = "Hello category1";
let category2 = categoryRepository.create();
category2.name = "Bye category2";
let post = postRepository.create();
post.title = "Post & Categories";
post.text = "Post with many categories";
post.categories = Promise.resolve([
category1,
category2
]);
return postRepository.persist(post);
})
.then(posts => {
console.log("Post has been saved with its categories. ");
console.log("Lets find it now. ");
return postRepository.find({ alias: "post", innerJoinAndSelect: { categories: "post.categories" } });
})
.then(posts => {
console.log("Post with categories are loaded: ", posts);
console.log("Lets remove one of the categories: ");
return posts[0].categories.then(categories => {
categories.splice(0, 1);
// console.log(posts[0]);
return postRepository.persist(posts[0]);
});
})
.then(posts => {
console.log("One of the post category has been removed.");
})
.catch(error => console.log(error.stack));

View File

@ -0,0 +1,18 @@
import {PrimaryColumn, Column} from "../../../src/columns";
import {Table} from "../../../src/tables";
import {ManyToMany} from "../../../src/decorator/relations/ManyToMany";
import {Post} from "./Post";
@Table("sample18_category")
export class Category {
@PrimaryColumn("int", { generated: true })
id: number;
@Column()
name: string;
@ManyToMany(type => Post, post => post.categories)
posts: Promise<Post[]>;
}

View File

@ -2,6 +2,9 @@ import {PrimaryColumn, Column} from "../../../src/columns";
import {Table} from "../../../src/tables";
import {Author} from "./Author";
import {ManyToOne} from "../../../src/decorator/relations/ManyToOne";
import {Category} from "./Category";
import {ManyToMany} from "../../../src/decorator/relations/ManyToMany";
import {JoinTable} from "../../../src/decorator/relations/JoinTable";
@Table("sample18_post")
export class Post {
@ -22,4 +25,10 @@ export class Post {
})
author: Promise<Author|null>;
@ManyToMany(type => Category, category => category.posts, {
cascadeAll: true
})
@JoinTable()
categories: Promise<Category[]>;
}

View File

@ -273,15 +273,19 @@ export class EntityPersistOperationBuilder {
});
return metadata.relations
.filter(relation => relation.isManyToMany)
.filter(relation => newEntity[relation.propertyName] instanceof Array)
// .filter(relation => newEntity[relation.propertyName] instanceof Array)
.reduce((operations, relation) => {
const relationMetadata = relation.relatedEntityMetadata;
const relationIdProperty = relationMetadata.primaryColumn.name;
newEntity[relation.propertyName].map((subEntity: any) => {
const value = this.getEntityRelationValue(relation, newEntity);
const dbValue = dbEntity ? this.getEntityRelationValue(relation, dbEntity.entity) : null;
if (!(value instanceof Array))
return operations;
value.forEach((subEntity: any) => {
const has = !dbEntity ||
!dbEntity.entity[relation.propertyName] ||
!dbEntity.entity[relation.propertyName].find((e: any) => e[relationIdProperty] === subEntity[relationIdProperty]);
const has = !dbValue || !dbValue.find((e: any) => e[relationIdProperty] === subEntity[relationIdProperty]);
if (has) {
operations.push({
@ -307,15 +311,19 @@ export class EntityPersistOperationBuilder {
});
return metadata.relations
.filter(relation => relation.isManyToMany)
.filter(relation => dbEntity[relation.propertyName] instanceof Array)
// .filter(relation => dbEntity[relation.propertyName] instanceof Array)
.reduce((operations, relation) => {
const relationMetadata = relation.relatedEntityMetadata;
const relationIdProperty = relationMetadata.primaryColumn.name;
dbEntity[relation.propertyName].map((subEntity: any) => {
const value = newEntity ? this.getEntityRelationValue(relation, newEntity.entity) : null;
const dbValue = this.getEntityRelationValue(relation, dbEntity);
const has = !newEntity ||
!newEntity.entity[relation.propertyName] ||
!newEntity.entity[relation.propertyName].find((e: any) => e[relationIdProperty] === subEntity[relationIdProperty]);
if (!(dbValue instanceof Array))
return operations;
dbValue.forEach((subEntity: any) => {
const has = !value || !value.find((e: any) => e[relationIdProperty] === subEntity[relationIdProperty]);
if (has) {
operations.push({

View File

@ -77,42 +77,6 @@ export class Repository<Entity> {
return <Entity> this.addLazyProperties(this.metadata.create());
}
// todo: duplication
private addLazyProperties(entity: any) {
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
metadata.relations
.filter(relation => relation.isLazy)
.forEach(relation => {
const index = "__" + relation.propertyName + "__";
Object.defineProperty(entity, relation.propertyName, {
get: () => {
if (entity[index])
return Promise.resolve(entity[index]);
// find object metadata and try to load
return new QueryBuilder(this.driver, this.entityMetadatas, this.broadcaster)
.select(relation.propertyName)
.from(relation.target, relation.propertyName) // todo: change `id` after join column implemented
.where(relation.propertyName + ".id=:" + relation.propertyName + "Id")
.setParameter(relation.propertyName + "Id", entity[index])
.getSingleResult()
.then(result => {
entity[index] = result;
return entity[index];
});
},
set: (promise: Promise<any>) => {
if (promise instanceof Promise) {
promise.then(result => entity[index] = result);
} else {
entity[index] = promise;
}
}
});
});
return entity;
}
/**
* Creates entities from a given array of plain javascript objects.
*/
@ -412,4 +376,40 @@ export class Repository<Entity> {
});
}
// todo: duplication
private addLazyProperties(entity: any) {
const metadata = this.entityMetadatas.findByTarget(entity.constructor);
metadata.relations
.filter(relation => relation.isLazy)
.forEach(relation => {
const index = "__" + relation.propertyName + "__";
Object.defineProperty(entity, relation.propertyName, {
get: () => {
if (entity[index])
return Promise.resolve(entity[index]);
// find object metadata and try to load
return new QueryBuilder(this.driver, this.entityMetadatas, this.broadcaster)
.select(relation.propertyName)
.from(relation.target, relation.propertyName) // todo: change `id` after join column implemented
.where(relation.propertyName + ".id=:" + relation.propertyName + "Id")
.setParameter(relation.propertyName + "Id", entity[index])
.getSingleResult()
.then(result => {
entity[index] = result;
return entity[index];
});
},
set: (promise: Promise<any>) => {
if (promise instanceof Promise) {
promise.then(result => entity[index] = result);
} else {
entity[index] = promise;
}
}
});
});
return entity;
}
}