mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
added basic many-to-many tests
This commit is contained in:
parent
33aa1a2fdc
commit
ec13a23c0c
@ -22,14 +22,14 @@ export class Post {
|
||||
})
|
||||
text: string;
|
||||
|
||||
@OneToOne<PostDetails>(true, () => PostDetails, details => details.post, {
|
||||
@OneToOne<PostDetails>(true, () => PostDetails, details => details.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
})
|
||||
details: PostDetails;
|
||||
|
||||
@OneToMany<Image>(type => Image, image => image.post, {
|
||||
@OneToMany<Image>(type => Image, image => image.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
|
||||
@ -30,27 +30,27 @@ export class Post {
|
||||
|
||||
// post has relation with details. cascade inserts here means if new PostDetails instance will be set to this
|
||||
// relation it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToOne<PostDetails>(() => PostDetails, details => details.post, {
|
||||
@ManyToOne<PostDetails>(() => PostDetails, details => details.posts, {
|
||||
cascadeInsert: true
|
||||
})
|
||||
details: PostDetails;
|
||||
|
||||
// post has relation with details. cascade update here means if new PostDetail instance will be set to this relation
|
||||
// it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToOne<PostImage>(() => PostImage, image => image.post, {
|
||||
@ManyToOne<PostImage>(() => PostImage, image => image.posts, {
|
||||
cascadeUpdate: true
|
||||
})
|
||||
image: PostImage;
|
||||
|
||||
// post has relation with details. cascade update here means if new PostDetail instance will be set to this relation
|
||||
// it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToOne<PostMetadata>(() => PostMetadata, metadata => metadata.post, {
|
||||
@ManyToOne<PostMetadata>(() => PostMetadata, metadata => metadata.posts, {
|
||||
cascadeRemove: true
|
||||
})
|
||||
metadata: PostMetadata;
|
||||
|
||||
// post has relation with details. full cascades here
|
||||
@ManyToOne<PostInformation>(() => PostInformation, information => information.post, {
|
||||
@ManyToOne<PostInformation>(() => PostInformation, information => information.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
@ -58,7 +58,7 @@ export class Post {
|
||||
information: PostInformation;
|
||||
|
||||
// post has relation with details. not cascades here. means cannot be persisted, updated or removed
|
||||
@ManyToOne<PostAuthor>(() => PostAuthor, author => author.post)
|
||||
@ManyToOne<PostAuthor>(() => PostAuthor, author => author.posts)
|
||||
author: PostAuthor;
|
||||
|
||||
}
|
||||
@ -13,6 +13,6 @@ export class PostAuthor {
|
||||
name: string;
|
||||
|
||||
@OneToMany<Post>(() => Post, post => post.author)
|
||||
post: Post[];
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
@ -9,13 +9,19 @@ export class PostDetails {
|
||||
@PrimaryColumn("int", { autoIncrement: true })
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
@Column({
|
||||
nullable: true
|
||||
})
|
||||
authorName: string;
|
||||
|
||||
@Column()
|
||||
@Column({
|
||||
nullable: true
|
||||
})
|
||||
comment: string;
|
||||
|
||||
@Column()
|
||||
@Column({
|
||||
nullable: true
|
||||
})
|
||||
metadata: string;
|
||||
|
||||
@OneToMany<Post>(() => Post, post => post.details, {
|
||||
@ -23,6 +29,6 @@ export class PostDetails {
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
})
|
||||
post: Post[];
|
||||
posts: Post[] = [];
|
||||
|
||||
}
|
||||
@ -13,6 +13,6 @@ export class PostImage {
|
||||
url: string;
|
||||
|
||||
@OneToMany<Post>(() => Post, post => post.image)
|
||||
post: Post[];
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
@ -15,6 +15,6 @@ export class PostInformation {
|
||||
@OneToMany<Post>(() => Post, post => post.information, {
|
||||
cascadeUpdate: true,
|
||||
})
|
||||
post: Post[];
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
@ -13,6 +13,6 @@ export class PostMetadata {
|
||||
description: string;
|
||||
|
||||
@OneToMany<Post>(() => Post, post => post.metadata)
|
||||
post: Post[];
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
@ -1,6 +1,11 @@
|
||||
import {TypeORM} from "../../src/TypeORM";
|
||||
import {Post} from "./entity/Post";
|
||||
import {Category} from "./entity/Category";
|
||||
import {PostDetails} from "./entity/PostDetails";
|
||||
import {PostCategory} from "./entity/PostCategory";
|
||||
import {PostMetadata} from "./entity/PostMetadata";
|
||||
import {PostImage} from "./entity/PostImage";
|
||||
import {PostInformation} from "./entity/PostInformation";
|
||||
import {PostAuthor} from "./entity/PostAuthor";
|
||||
|
||||
// first create a connection
|
||||
let options = {
|
||||
@ -12,25 +17,27 @@ let options = {
|
||||
autoSchemaCreate: true
|
||||
};
|
||||
|
||||
TypeORM.createMysqlConnection(options, [Post, Category]).then(connection => {
|
||||
TypeORM.createMysqlConnection(options, [Post, PostDetails, PostCategory, PostMetadata, PostImage, PostInformation, PostAuthor]).then(connection => {
|
||||
|
||||
let category1 = new Category();
|
||||
category1.name = "People";
|
||||
|
||||
let category2 = new Category();
|
||||
category2.name = "Human";
|
||||
|
||||
let post = new Post();
|
||||
post.text = "Hello how are you?";
|
||||
post.title = "hello";
|
||||
post.categories = [category1, category2];
|
||||
/*
|
||||
let category1 = new Category();
|
||||
category1.name = "People";
|
||||
|
||||
let category2 = new Category();
|
||||
category2.name = "Human";
|
||||
|
||||
let post = new Post();
|
||||
post.text = "Hello how are you?";
|
||||
post.title = "hello";
|
||||
post.categories = [category1, category2];
|
||||
*/
|
||||
|
||||
// finally save it
|
||||
let postRepository = connection.getRepository<Post>(Post);
|
||||
/*let postRepository = connection.getRepository<Post>(Post);
|
||||
|
||||
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));*/
|
||||
|
||||
}, error => console.log("Cannot connect: ", error));
|
||||
@ -1,12 +1,17 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
import {PostDetails} from "./PostDetails";
|
||||
import {ManyToMany} from "../../../src/decorator/Relations";
|
||||
import {Category} from "./Category";
|
||||
import {PostCategory} from "./PostCategory";
|
||||
import {PostAuthor} from "./PostAuthor";
|
||||
import {PostInformation} from "./PostInformation";
|
||||
import {PostImage} from "./PostImage";
|
||||
import {PostMetadata} from "./PostMetadata";
|
||||
|
||||
@Table("sample4-post")
|
||||
@Table("sample4_post")
|
||||
export class Post {
|
||||
|
||||
@PrimaryColumn()
|
||||
@PrimaryColumn("int", { autoIncrement: true })
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
@ -15,7 +20,45 @@ export class Post {
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
@ManyToMany<Category>(true, _ => Category, category => category.posts)
|
||||
categories: Category[];
|
||||
// post has relation with category, however inverse relation is not set (category does not have relation with post set)
|
||||
@ManyToMany<PostCategory>(true, () => PostCategory, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
})
|
||||
category: PostCategory[] = [];
|
||||
|
||||
// post has relation with details. cascade inserts here means if new PostDetails instance will be set to this
|
||||
// relation it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToMany<PostDetails>(true, () => PostDetails, details => details.posts, {
|
||||
cascadeInsert: true
|
||||
})
|
||||
details: PostDetails[] = [];
|
||||
|
||||
// post has relation with details. cascade update here means if new PostDetail instance will be set to this relation
|
||||
// it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToMany<PostImage>(true, () => PostImage, image => image.posts, {
|
||||
cascadeUpdate: true
|
||||
})
|
||||
image: PostImage[] = [];
|
||||
|
||||
// post has relation with details. cascade update here means if new PostDetail instance will be set to this relation
|
||||
// it will be inserted automatically to the db when you save this Post entity
|
||||
@ManyToMany<PostMetadata>(true, () => PostMetadata, metadata => metadata.posts, {
|
||||
cascadeRemove: true
|
||||
})
|
||||
metadata: PostMetadata[] = [];
|
||||
|
||||
// post has relation with details. full cascades here
|
||||
@ManyToMany<PostInformation>(true, () => PostInformation, information => information.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
})
|
||||
information: PostInformation[] = [];
|
||||
|
||||
// post has relation with details. not cascades here. means cannot be persisted, updated or removed
|
||||
@ManyToMany<PostAuthor>(true, () => PostAuthor, author => author.posts)
|
||||
author: PostAuthor[] = [];
|
||||
|
||||
}
|
||||
@ -1,18 +1,18 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
import {ManyToMany} from "../../../src/decorator/Relations";
|
||||
import {Post} from "./Post";
|
||||
import {ManyToMany} from "../../../src/decorator/Relations";
|
||||
|
||||
@Table("sample4-category")
|
||||
export class Category {
|
||||
@Table("sample4_post_author")
|
||||
export class PostAuthor {
|
||||
|
||||
@PrimaryColumn()
|
||||
@PrimaryColumn("int", { autoIncrement: true })
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ManyToMany<Post>(false, _ => Post, post => post.categories)
|
||||
@ManyToMany<Post>(false, () => Post, post => post.author)
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
13
sample/sample4-many-to-many/entity/PostCategory.ts
Normal file
13
sample/sample4-many-to-many/entity/PostCategory.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
|
||||
@Table("sample4_post_category")
|
||||
export class PostCategory {
|
||||
|
||||
@PrimaryColumn("int", { autoIncrement: true })
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
}
|
||||
34
sample/sample4-many-to-many/entity/PostDetails.ts
Normal file
34
sample/sample4-many-to-many/entity/PostDetails.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
import {ManyToMany} from "../../../src/decorator/Relations";
|
||||
import {Post} from "./Post";
|
||||
|
||||
@Table("sample4_post_details")
|
||||
export class PostDetails {
|
||||
|
||||
@PrimaryColumn("int", { autoIncrement: true })
|
||||
id: number;
|
||||
|
||||
@Column({
|
||||
nullable: true
|
||||
})
|
||||
authorName: string;
|
||||
|
||||
@Column({
|
||||
nullable: true
|
||||
})
|
||||
comment: string;
|
||||
|
||||
@Column({
|
||||
nullable: true
|
||||
})
|
||||
metadata: string;
|
||||
|
||||
@ManyToMany<Post>(false, () => Post, post => post.details, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
})
|
||||
posts: Post[] = [];
|
||||
|
||||
}
|
||||
18
sample/sample4-many-to-many/entity/PostImage.ts
Normal file
18
sample/sample4-many-to-many/entity/PostImage.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
import {Post} from "./Post";
|
||||
import {ManyToMany} from "../../../src/decorator/Relations";
|
||||
|
||||
@Table("sample4_post_image")
|
||||
export class PostImage {
|
||||
|
||||
@PrimaryColumn("int", { autoIncrement: true })
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
url: string;
|
||||
|
||||
@ManyToMany<Post>(false, () => Post, post => post.image)
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
20
sample/sample4-many-to-many/entity/PostInformation.ts
Normal file
20
sample/sample4-many-to-many/entity/PostInformation.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
import {ManyToMany} from "../../../src/decorator/Relations";
|
||||
import {Post} from "./Post";
|
||||
|
||||
@Table("sample4_post_information")
|
||||
export class PostInformation {
|
||||
|
||||
@PrimaryColumn("int", { autoIncrement: true })
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
@ManyToMany<Post>(false, () => Post, post => post.information, {
|
||||
cascadeUpdate: true,
|
||||
})
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
18
sample/sample4-many-to-many/entity/PostMetadata.ts
Normal file
18
sample/sample4-many-to-many/entity/PostMetadata.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/decorator/Columns";
|
||||
import {Table} from "../../../src/decorator/Tables";
|
||||
import {Post} from "./Post";
|
||||
import {ManyToMany} from "../../../src/decorator/Relations";
|
||||
|
||||
@Table("sample4_post_metadata")
|
||||
export class PostMetadata {
|
||||
|
||||
@PrimaryColumn("int", { autoIncrement: true })
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
description: string;
|
||||
|
||||
@ManyToMany<Post>(false, () => Post, post => post.metadata)
|
||||
posts: Post[];
|
||||
|
||||
}
|
||||
@ -88,8 +88,15 @@ export class MysqlDriver implements Driver {
|
||||
*/
|
||||
query<T>(query: string): Promise<T> {
|
||||
if (!this.connection) throw new Error("Connection is not established, cannot execute a query.");
|
||||
console.info("executing:", query);
|
||||
return new Promise<any>((ok, fail) => this.connection.query(query, (err: any, result: any) => err ? fail(err) : ok(result)));
|
||||
// console.info("executing:", query);
|
||||
return new Promise<any>((ok, fail) => this.connection.query(query, (err: any, result: any) => {
|
||||
if (err) {
|
||||
console.error("query failed: ", query);
|
||||
fail(err);
|
||||
return;
|
||||
}
|
||||
ok(result);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -155,7 +155,8 @@ export class EntityMetadataBuilder {
|
||||
const junctionEntityMetadata = new EntityMetadata(tableMetadata, columns, [], [], [], foreignKeys);
|
||||
junctionEntityMetadatas.push(junctionEntityMetadata);
|
||||
relation.junctionEntityMetadata = junctionEntityMetadata;
|
||||
relation.inverseRelation.junctionEntityMetadata = junctionEntityMetadata;
|
||||
if (relation.inverseRelation)
|
||||
relation.inverseRelation.junctionEntityMetadata = junctionEntityMetadata;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -253,11 +253,7 @@ export class QueryBuilder<Entity> {
|
||||
}
|
||||
|
||||
getSingleResult(): Promise<Entity> {
|
||||
return this.getResults().then(entities => {
|
||||
console.log(this.getSql());
|
||||
console.log(entities);
|
||||
return entities[0];
|
||||
});
|
||||
return this.getResults().then(entities => entities[0]);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -56,8 +56,8 @@ export class AliasMap {
|
||||
const parentEntityMetadata = this.getEntityMetadataByAlias(parentAlias);
|
||||
const relation = parentEntityMetadata.findRelationWithDbName(alias.parentPropertyName);
|
||||
if (!relation)
|
||||
throw new Error("Related entity metadata was not found.");
|
||||
|
||||
throw new Error("Relation metadata for " + alias.parentAliasName + "#" + alias.parentPropertyName + " was not found.");
|
||||
|
||||
return relation.relatedEntityMetadata;
|
||||
}
|
||||
|
||||
|
||||
@ -121,7 +121,7 @@ export class EntityPersistOperationsBuilder {
|
||||
const junctionRemoveOperations = this.findJunctionRemoveOperations(metadata, entity1, allEntities);
|
||||
const updatesByRelationsOperations = this.updateRelations(insertOperations, entity2);
|
||||
//const insertJunctionOperations = ;//this.a();
|
||||
console.log("---------------------------------------------------------");
|
||||
/*console.log("---------------------------------------------------------");
|
||||
console.log("DB ENTITIES");
|
||||
console.log("---------------------------------------------------------");
|
||||
console.log(dbEntities);
|
||||
@ -153,7 +153,7 @@ export class EntityPersistOperationsBuilder {
|
||||
console.log("UPDATES BY RELATIONS");
|
||||
console.log("---------------------------------------------------------");
|
||||
console.log(updatesByRelationsOperations);
|
||||
console.log("---------------------------------------------------------");
|
||||
console.log("---------------------------------------------------------");*/
|
||||
|
||||
// now normalize inserted entities
|
||||
// no need probably, since we cant rely on deepness because of recursion: insertOperations.sort((a, b) => a.deepness + b.deepness);
|
||||
|
||||
@ -179,6 +179,7 @@ export class Repository<Entity> {
|
||||
}
|
||||
|
||||
private updateDeletedRelations(removeOperation: RemoveOperation) { // todo: check if both many-to-one deletions work too
|
||||
if (removeOperation.relation.isManyToMany || removeOperation.relation.isOneToMany) return;
|
||||
const value = removeOperation.relation.name + "=NULL";
|
||||
const query = `UPDATE ${removeOperation.metadata.table.name} SET ${value} WHERE ${removeOperation.metadata.primaryColumn.name}='${removeOperation.fromEntityId}'` ;
|
||||
return this.connection.driver.query(query);
|
||||
|
||||
@ -135,7 +135,7 @@ describe("many-to-one", function() {
|
||||
.should.eventually.eql(expectedPost);
|
||||
});
|
||||
|
||||
/* it("should load details and its post if left join used (from reverse side)", function() {
|
||||
it("should load details and its post if left join used (from reverse side)", function() {
|
||||
|
||||
const expectedDetails = new PostDetails();
|
||||
expectedDetails.id = savedPost.details.id;
|
||||
@ -143,19 +143,21 @@ describe("many-to-one", function() {
|
||||
expectedDetails.comment = savedPost.details.comment;
|
||||
expectedDetails.metadata = savedPost.details.metadata;
|
||||
|
||||
expectedDetails.post = new Post();
|
||||
expectedDetails.post.id = savedPost.id;
|
||||
expectedDetails.post.text = savedPost.text;
|
||||
expectedDetails.post.title = savedPost.title;
|
||||
const expectedPost = new Post();
|
||||
expectedPost.id = savedPost.id;
|
||||
expectedPost.text = savedPost.text;
|
||||
expectedPost.title = savedPost.title;
|
||||
|
||||
expectedDetails.posts.push(expectedPost);
|
||||
|
||||
return postDetailsRepository
|
||||
.createQueryBuilder("details")
|
||||
.leftJoinAndSelect("details.post", "post")
|
||||
.leftJoinAndSelect("details.posts", "posts")
|
||||
.where("details.id=:id")
|
||||
.setParameter("id", savedPost.id)
|
||||
.getSingleResult()
|
||||
.should.eventually.eql(expectedDetails);
|
||||
});*/
|
||||
});
|
||||
|
||||
it("should load saved post without details if left joins are not specified", function() {
|
||||
const expectedPost = new Post();
|
||||
@ -328,7 +330,7 @@ describe("many-to-one", function() {
|
||||
.leftJoinAndSelect("post.details", "details")
|
||||
.where("post.id=:id")
|
||||
.setParameter("id", updatedPost.id)
|
||||
.getSingleResult()
|
||||
.getSingleResult();
|
||||
}).then(updatedPostReloaded => {
|
||||
updatedPostReloaded.details.comment.should.be.equal("this is post");
|
||||
});
|
||||
@ -433,4 +435,68 @@ describe("many-to-one", function() {
|
||||
|
||||
});
|
||||
|
||||
describe("insert post details from reverse side", function() {
|
||||
let newPost: Post, details: PostDetails, savedDetails: PostDetails;
|
||||
|
||||
before(reloadDatabase);
|
||||
|
||||
before(function() {
|
||||
newPost = new Post();
|
||||
newPost.text = "Hello post";
|
||||
newPost.title = "this is post title";
|
||||
|
||||
details = new PostDetails();
|
||||
details.comment = "post details comment";
|
||||
details.posts.push(newPost);
|
||||
|
||||
return postDetailsRepository.persist(details).then(details => savedDetails = details);
|
||||
});
|
||||
|
||||
it("should return the same post instance after its created", function () {
|
||||
savedDetails.posts[0].should.be.equal(newPost);
|
||||
});
|
||||
|
||||
it("should return the same post details instance after its created", function () {
|
||||
savedDetails.should.be.equal(details);
|
||||
});
|
||||
|
||||
it("should have a new generated id after post is created", function () {
|
||||
expect(savedDetails.id).not.to.be.empty;
|
||||
expect(details.id).not.to.be.empty;
|
||||
});
|
||||
|
||||
it("should have inserted post in the database", function() {
|
||||
const expectedPost = new Post();
|
||||
expectedPost.id = newPost.id;
|
||||
expectedPost.text = newPost.text;
|
||||
expectedPost.title = newPost.title;
|
||||
return postRepository.findById(savedDetails.id).should.eventually.eql(expectedPost);
|
||||
});
|
||||
|
||||
it("should have inserted details in the database", function() {
|
||||
const expectedDetails = new PostDetails();
|
||||
expectedDetails.id = details.id;
|
||||
expectedDetails.comment = details.comment;
|
||||
return postDetailsRepository.findById(details.id).should.eventually.eql(expectedDetails);
|
||||
});
|
||||
|
||||
it("should load post and its details if left join used", function() {
|
||||
const expectedDetails = new PostDetails();
|
||||
expectedDetails.id = savedDetails.id;
|
||||
expectedDetails.comment = savedDetails.comment;
|
||||
expectedDetails.posts.push(new Post());
|
||||
expectedDetails.posts[0].id = newPost.id;
|
||||
expectedDetails.posts[0].text = newPost.text;
|
||||
expectedDetails.posts[0].title = newPost.title;
|
||||
|
||||
return postDetailsRepository
|
||||
.createQueryBuilder("details")
|
||||
.leftJoinAndSelect("details.posts", "posts")
|
||||
.where("details.id=:id", { id: savedDetails.id })
|
||||
.getSingleResult()
|
||||
.should.eventually.eql(expectedDetails);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
504
test/integration/sample4-many-to-many.ts
Normal file
504
test/integration/sample4-many-to-many.ts
Normal file
@ -0,0 +1,504 @@
|
||||
import * as chai from "chai";
|
||||
import {expect} from "chai";
|
||||
import {Connection} from "../../src/connection/Connection";
|
||||
import {TypeORM} from "../../src/TypeORM";
|
||||
import {ConnectionOptions} from "../../src/connection/ConnectionOptions";
|
||||
import {Repository} from "../../src/repository/Repository";
|
||||
import {SchemaCreator} from "../../src/schema-creator/SchemaCreator";
|
||||
import {PostDetails} from "../../sample/sample4-many-to-many/entity/PostDetails";
|
||||
import {Post} from "../../sample/sample4-many-to-many/entity/Post";
|
||||
import {PostCategory} from "../../sample/sample4-many-to-many/entity/PostCategory";
|
||||
import {PostAuthor} from "../../sample/sample4-many-to-many/entity/PostAuthor";
|
||||
import {PostMetadata} from "../../sample/sample4-many-to-many/entity/PostMetadata";
|
||||
import {PostImage} from "../../sample/sample4-many-to-many/entity/PostImage";
|
||||
import {PostInformation} from "../../sample/sample4-many-to-many/entity/PostInformation";
|
||||
|
||||
chai.should();
|
||||
describe("many-to-many", function() {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Configuration
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
let options: ConnectionOptions = {
|
||||
host: "192.168.99.100",
|
||||
port: 3306,
|
||||
username: "root",
|
||||
password: "admin",
|
||||
database: "test",
|
||||
autoSchemaCreate: true
|
||||
};
|
||||
|
||||
// connect to db
|
||||
let connection: Connection;
|
||||
before(function() {
|
||||
return TypeORM.createMysqlConnection(options, [Post, PostDetails, PostCategory, PostMetadata, PostImage, PostInformation, PostAuthor]).then(conn => {
|
||||
connection = conn;
|
||||
}).catch(e => console.log("Error during connection to db: " + e));
|
||||
});
|
||||
|
||||
after(function() {
|
||||
connection.close();
|
||||
});
|
||||
|
||||
// clean up database before each test
|
||||
function reloadDatabase() {
|
||||
return connection.driver
|
||||
.clearDatabase()
|
||||
.then(() => new SchemaCreator(connection).create());
|
||||
}
|
||||
|
||||
let postRepository: Repository<Post>,
|
||||
postDetailsRepository: Repository<PostDetails>,
|
||||
postCategoryRepository: Repository<PostCategory>,
|
||||
postImageRepository: Repository<PostImage>,
|
||||
postMetadataRepository: Repository<PostMetadata>;
|
||||
before(function() {
|
||||
postRepository = connection.getRepository<Post>(Post);
|
||||
postDetailsRepository = connection.getRepository<PostDetails>(PostDetails);
|
||||
postCategoryRepository = connection.getRepository<PostCategory>(PostCategory);
|
||||
postImageRepository = connection.getRepository<PostImage>(PostImage);
|
||||
postMetadataRepository = connection.getRepository<PostMetadata>(PostMetadata);
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Specifications
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
describe("insert post and details (has inverse relation + full cascade options)", function() {
|
||||
let newPost: Post, details: PostDetails, savedPost: Post;
|
||||
|
||||
before(reloadDatabase);
|
||||
|
||||
before(function() {
|
||||
details = new PostDetails();
|
||||
details.authorName = "Umed";
|
||||
details.comment = "this is post";
|
||||
details.metadata = "post,posting,postman";
|
||||
|
||||
newPost = new Post();
|
||||
newPost.text = "Hello post";
|
||||
newPost.title = "this is post title";
|
||||
newPost.details.push(details);
|
||||
|
||||
return postRepository.persist(newPost).then(post => savedPost = post);
|
||||
});
|
||||
|
||||
it("should return the same post instance after its created", function () {
|
||||
savedPost.should.be.equal(newPost);
|
||||
});
|
||||
|
||||
it("should return the same post details instance after its created", function () {
|
||||
savedPost.details[0].should.be.equal(newPost.details[0]);
|
||||
});
|
||||
|
||||
it("should have a new generated id after post is created", function () {
|
||||
expect(savedPost.id).not.to.be.empty;
|
||||
expect(savedPost.details[0].id).not.to.be.empty;
|
||||
});
|
||||
|
||||
it("should have inserted post in the database", function() {
|
||||
const expectedPost = new Post();
|
||||
expectedPost.id = savedPost.id;
|
||||
expectedPost.text = savedPost.text;
|
||||
expectedPost.title = savedPost.title;
|
||||
|
||||
return postRepository.findById(savedPost.id).should.eventually.eql(expectedPost);
|
||||
});
|
||||
|
||||
it("should have inserted post details in the database", function() {
|
||||
const expectedDetails = new PostDetails();
|
||||
expectedDetails.id = savedPost.details[0].id;
|
||||
expectedDetails.authorName = savedPost.details[0].authorName;
|
||||
expectedDetails.comment = savedPost.details[0].comment;
|
||||
expectedDetails.metadata = savedPost.details[0].metadata;
|
||||
|
||||
return postDetailsRepository.findById(savedPost.details[0].id).should.eventually.eql(expectedDetails);
|
||||
});
|
||||
|
||||
it("should load post and its details if left join used", function() {
|
||||
const expectedPost = new Post();
|
||||
expectedPost.id = savedPost.id;
|
||||
expectedPost.text = savedPost.text;
|
||||
expectedPost.title = savedPost.title;
|
||||
expectedPost.details.push(new PostDetails());
|
||||
expectedPost.details[0].id = savedPost.details[0].id;
|
||||
expectedPost.details[0].authorName = savedPost.details[0].authorName;
|
||||
expectedPost.details[0].comment = savedPost.details[0].comment;
|
||||
expectedPost.details[0].metadata = savedPost.details[0].metadata;
|
||||
|
||||
return postRepository
|
||||
.createQueryBuilder("post")
|
||||
.leftJoinAndSelect("post.details", "details")
|
||||
.where("post.id=:id")
|
||||
.setParameter("id", savedPost.id)
|
||||
.getSingleResult()
|
||||
.should.eventually.eql(expectedPost);
|
||||
});
|
||||
|
||||
it("should load details and its post if left join used (from reverse side)", function() {
|
||||
|
||||
const expectedDetails = new PostDetails();
|
||||
expectedDetails.id = savedPost.details[0].id;
|
||||
expectedDetails.authorName = savedPost.details[0].authorName;
|
||||
expectedDetails.comment = savedPost.details[0].comment;
|
||||
expectedDetails.metadata = savedPost.details[0].metadata;
|
||||
|
||||
const expectedPost = new Post();
|
||||
expectedPost.id = savedPost.id;
|
||||
expectedPost.text = savedPost.text;
|
||||
expectedPost.title = savedPost.title;
|
||||
|
||||
expectedDetails.posts.push(expectedPost);
|
||||
|
||||
return postDetailsRepository
|
||||
.createQueryBuilder("details")
|
||||
.leftJoinAndSelect("details.posts", "posts")
|
||||
.where("details.id=:id")
|
||||
.setParameter("id", savedPost.id)
|
||||
.getSingleResult()
|
||||
.should.eventually.eql(expectedDetails);
|
||||
});
|
||||
|
||||
it("should load saved post without details if left joins are not specified", function() {
|
||||
const expectedPost = new Post();
|
||||
expectedPost.id = savedPost.id;
|
||||
expectedPost.text = savedPost.text;
|
||||
expectedPost.title = savedPost.title;
|
||||
|
||||
return postRepository
|
||||
.createQueryBuilder("post")
|
||||
.where("post.id=:id", { id: savedPost.id })
|
||||
.getSingleResult()
|
||||
.should.eventually.eql(expectedPost);
|
||||
});
|
||||
|
||||
it("should load saved post without details if left joins are not specified", function() {
|
||||
const expectedDetails = new PostDetails();
|
||||
expectedDetails.id = savedPost.details[0].id;
|
||||
expectedDetails.authorName = savedPost.details[0].authorName;
|
||||
expectedDetails.comment = savedPost.details[0].comment;
|
||||
expectedDetails.metadata = savedPost.details[0].metadata;
|
||||
|
||||
return postDetailsRepository
|
||||
.createQueryBuilder("details")
|
||||
.where("details.id=:id", { id: savedPost.id })
|
||||
.getSingleResult()
|
||||
.should.eventually.eql(expectedDetails);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("insert post and category (one-side relation)", function() {
|
||||
let newPost: Post, category: PostCategory, savedPost: Post;
|
||||
|
||||
before(reloadDatabase);
|
||||
|
||||
before(function() {
|
||||
category = new PostCategory();
|
||||
category.name = "technology";
|
||||
|
||||
newPost = new Post();
|
||||
newPost.text = "Hello post";
|
||||
newPost.title = "this is post title";
|
||||
newPost.category.push(category);
|
||||
|
||||
return postRepository.persist(newPost).then(post => savedPost = post);
|
||||
});
|
||||
|
||||
it("should return the same post instance after its created", function () {
|
||||
savedPost.should.be.equal(newPost);
|
||||
});
|
||||
|
||||
it("should return the same post category instance after its created", function () {
|
||||
savedPost.category.should.be.equal(newPost.category);
|
||||
});
|
||||
|
||||
it("should have a new generated id after post is created", function () {
|
||||
expect(savedPost.id).not.to.be.empty;
|
||||
expect(savedPost.category[0].id).not.to.be.empty;
|
||||
});
|
||||
|
||||
it("should have inserted post in the database", function() {
|
||||
const expectedPost = new Post();
|
||||
expectedPost.id = savedPost.id;
|
||||
expectedPost.text = savedPost.text;
|
||||
expectedPost.title = savedPost.title;
|
||||
return postRepository.findById(savedPost.id).should.eventually.eql(expectedPost);
|
||||
});
|
||||
|
||||
it("should have inserted category in the database", function() {
|
||||
const expectedPost = new PostCategory();
|
||||
expectedPost.id = savedPost.category[0].id;
|
||||
expectedPost.name = "technology";
|
||||
return postCategoryRepository.findById(savedPost.category[0].id).should.eventually.eql(expectedPost);
|
||||
});
|
||||
|
||||
it("should load post and its category if left join used", function() {
|
||||
const expectedPost = new Post();
|
||||
expectedPost.id = savedPost.id;
|
||||
expectedPost.title = savedPost.title;
|
||||
expectedPost.text = savedPost.text;
|
||||
expectedPost.category.push(new PostCategory());
|
||||
expectedPost.category[0].id = savedPost.category[0].id;
|
||||
expectedPost.category[0].name = savedPost.category[0].name;
|
||||
|
||||
return postRepository
|
||||
.createQueryBuilder("post")
|
||||
.leftJoinAndSelect("post.category", "category")
|
||||
.where("post.id=:id", { id: savedPost.id })
|
||||
.getSingleResult()
|
||||
.should.eventually.eql(expectedPost);
|
||||
});
|
||||
|
||||
it("should load details and its post if left join used (from reverse side)", function() {
|
||||
// later need to specify with what exception we reject it
|
||||
/*return postCategoryRepository
|
||||
.createQueryBuilder("category")
|
||||
.leftJoinAndSelect("category.post", "post")
|
||||
.where("category.id=:id", { id: savedPost.id })
|
||||
.getSingleResult()
|
||||
.should.be.rejectedWith(Error);*/ // not working, find fix
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("cascade updates should not be executed when cascadeUpdate option is not set", function() {
|
||||
let newPost: Post, details: PostDetails, savedPost: Post;
|
||||
|
||||
before(reloadDatabase);
|
||||
|
||||
before(function() {
|
||||
|
||||
details = new PostDetails();
|
||||
details.authorName = "Umed";
|
||||
details.comment = "this is post";
|
||||
details.metadata = "post,posting,postman";
|
||||
|
||||
newPost = new Post();
|
||||
newPost.text = "Hello post";
|
||||
newPost.title = "this is post title";
|
||||
newPost.details.push(details);
|
||||
|
||||
return postRepository
|
||||
.persist(newPost)
|
||||
.then(post => savedPost = post);
|
||||
});
|
||||
|
||||
it("should ignore updates in the model and do not update the db when entity is updated", function () {
|
||||
newPost.details[0].comment = "i am updated comment";
|
||||
return postRepository.persist(newPost).then(updatedPost => {
|
||||
updatedPost.details[0].comment.should.be.equal("i am updated comment");
|
||||
return postRepository
|
||||
.createQueryBuilder("post")
|
||||
.leftJoinAndSelect("post.details", "details")
|
||||
.where("post.id=:id")
|
||||
.setParameter("id", updatedPost.id)
|
||||
.getSingleResult()
|
||||
}).then(updatedPostReloaded => {
|
||||
console.log("updatedPost: ", updatedPostReloaded);
|
||||
updatedPostReloaded.details[0].comment.should.be.equal("this is post");
|
||||
});
|
||||
}); // todo: also check that updates throw exception in strict cascades mode
|
||||
});
|
||||
|
||||
describe("cascade remove should not be executed when cascadeRemove option is not set", function() {
|
||||
let newPost: Post, details: PostDetails, savedPost: Post;
|
||||
|
||||
before(reloadDatabase);
|
||||
|
||||
before(function() {
|
||||
|
||||
details = new PostDetails();
|
||||
details.authorName = "Umed";
|
||||
details.comment = "this is post";
|
||||
details.metadata = "post,posting,postman";
|
||||
|
||||
newPost = new Post();
|
||||
newPost.text = "Hello post";
|
||||
newPost.title = "this is post title";
|
||||
newPost.details.push(details);
|
||||
|
||||
return postRepository
|
||||
.persist(newPost)
|
||||
.then(post => savedPost = post);
|
||||
});
|
||||
|
||||
it("should ignore updates in the model and do not update the db when entity is updated", function () {
|
||||
newPost.details = null;
|
||||
return postRepository.persist(newPost).then(updatedPost => {
|
||||
return postRepository
|
||||
.createQueryBuilder("post")
|
||||
.leftJoinAndSelect("post.details", "details")
|
||||
.where("post.id=:id")
|
||||
.setParameter("id", updatedPost.id)
|
||||
.getSingleResult();
|
||||
}).then(updatedPostReloaded => {
|
||||
// todo fix updatedPostReloaded.details[0].comment.should.be.equal("this is post");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("cascade updates should be executed when cascadeUpdate option is set", function() {
|
||||
let newPost: Post, newImage: PostImage, savedImage: PostImage;
|
||||
|
||||
before(reloadDatabase);
|
||||
|
||||
it("should update a relation successfully when updated", function () {
|
||||
|
||||
newImage = new PostImage();
|
||||
newImage.url = "logo.png";
|
||||
|
||||
newPost = new Post();
|
||||
newPost.text = "Hello post";
|
||||
newPost.title = "this is post title";
|
||||
|
||||
return postImageRepository
|
||||
.persist(newImage)
|
||||
.then(image => {
|
||||
savedImage = image;
|
||||
newPost.image.push(image);
|
||||
return postRepository.persist(newPost);
|
||||
|
||||
}).then(post => {
|
||||
newPost = post;
|
||||
return postRepository
|
||||
.createQueryBuilder("post")
|
||||
.leftJoinAndSelect("post.image", "image")
|
||||
.where("post.id=:id")
|
||||
.setParameter("id", post.id)
|
||||
.getSingleResult();
|
||||
|
||||
}).then(loadedPost => {
|
||||
loadedPost.image[0].url = "new-logo.png";
|
||||
return postRepository.persist(loadedPost);
|
||||
|
||||
}).then(() => {
|
||||
return postRepository
|
||||
.createQueryBuilder("post")
|
||||
.leftJoinAndSelect("post.image", "image")
|
||||
.where("post.id=:id")
|
||||
.setParameter("id", newPost.id)
|
||||
.getSingleResult();
|
||||
|
||||
}).then(reloadedPost => {
|
||||
reloadedPost.image[0].url.should.be.equal("new-logo.png");
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("cascade remove should be executed when cascadeRemove option is set", function() {
|
||||
let newPost: Post, newMetadata: PostMetadata, savedMetadata: PostMetadata;
|
||||
|
||||
before(reloadDatabase);
|
||||
|
||||
it("should remove a relation entity successfully when removed", function () {
|
||||
|
||||
newMetadata = new PostMetadata();
|
||||
newMetadata.description = "this is post metadata";
|
||||
|
||||
newPost = new Post();
|
||||
newPost.text = "Hello post";
|
||||
newPost.title = "this is post title";
|
||||
|
||||
return postMetadataRepository
|
||||
.persist(newMetadata)
|
||||
.then(metadata => {
|
||||
savedMetadata = metadata;
|
||||
newPost.metadata.push(metadata);
|
||||
return postRepository.persist(newPost);
|
||||
|
||||
}).then(post => {
|
||||
newPost = post;
|
||||
return postRepository
|
||||
.createQueryBuilder("post")
|
||||
.leftJoinAndSelect("post.metadata", "metadata")
|
||||
.where("post.id=:id")
|
||||
.setParameter("id", post.id)
|
||||
.getSingleResult();
|
||||
|
||||
}).then(loadedPost => {
|
||||
loadedPost.metadata = null;
|
||||
return postRepository.persist(loadedPost);
|
||||
|
||||
}).then(() => {
|
||||
return postRepository
|
||||
.createQueryBuilder("post")
|
||||
.leftJoinAndSelect("post.metadata", "metadata")
|
||||
.where("post.id=:id")
|
||||
.setParameter("id", newPost.id)
|
||||
.getSingleResult();
|
||||
|
||||
}).then(reloadedPost => {
|
||||
expect(reloadedPost.metadata).to.be.empty;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("insert post details from reverse side", function() {
|
||||
let newPost: Post, details: PostDetails, savedDetails: PostDetails;
|
||||
|
||||
before(reloadDatabase);
|
||||
|
||||
before(function() {
|
||||
newPost = new Post();
|
||||
newPost.text = "Hello post";
|
||||
newPost.title = "this is post title";
|
||||
|
||||
details = new PostDetails();
|
||||
details.comment = "post details comment";
|
||||
details.posts.push(newPost);
|
||||
|
||||
return postDetailsRepository.persist(details).then(details => savedDetails = details);
|
||||
});
|
||||
|
||||
it("should return the same post instance after its created", function () {
|
||||
savedDetails.posts[0].should.be.equal(newPost);
|
||||
});
|
||||
|
||||
it("should return the same post details instance after its created", function () {
|
||||
savedDetails.should.be.equal(details);
|
||||
});
|
||||
|
||||
it("should have a new generated id after post is created", function () {
|
||||
expect(savedDetails.id).not.to.be.empty;
|
||||
expect(details.id).not.to.be.empty;
|
||||
});
|
||||
|
||||
it("should have inserted post in the database", function() {
|
||||
const expectedPost = new Post();
|
||||
expectedPost.id = newPost.id;
|
||||
expectedPost.text = newPost.text;
|
||||
expectedPost.title = newPost.title;
|
||||
return postRepository.findById(savedDetails.id).should.eventually.eql(expectedPost);
|
||||
});
|
||||
|
||||
it("should have inserted details in the database", function() {
|
||||
const expectedDetails = new PostDetails();
|
||||
expectedDetails.id = details.id;
|
||||
expectedDetails.comment = details.comment;
|
||||
return postDetailsRepository.findById(details.id).should.eventually.eql(expectedDetails);
|
||||
});
|
||||
|
||||
it("should load post and its details if left join used", function() {
|
||||
const expectedDetails = new PostDetails();
|
||||
expectedDetails.id = savedDetails.id;
|
||||
expectedDetails.comment = savedDetails.comment;
|
||||
expectedDetails.posts.push(new Post());
|
||||
expectedDetails.posts[0].id = newPost.id;
|
||||
expectedDetails.posts[0].text = newPost.text;
|
||||
expectedDetails.posts[0].title = newPost.title;
|
||||
|
||||
return postDetailsRepository
|
||||
.createQueryBuilder("details")
|
||||
.leftJoinAndSelect("details.posts", "posts")
|
||||
.where("details.id=:id", { id: savedDetails.id })
|
||||
.getSingleResult()
|
||||
.should.eventually.eql(expectedDetails);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user