From d870654fc888728a53bde61cca4a8bc471d49627 Mon Sep 17 00:00:00 2001 From: Kononnable Date: Mon, 5 Jun 2017 15:33:34 +0200 Subject: [PATCH] better handling of OneToMany/OneToOne relations --- src/drivers/MssqlDriver.ts | 10 +-- .../sample2-one-to-one/entity/Post.ts | 8 ++- .../sample3-many-to-one/entity/Post.ts | 62 +++++++++++++++++++ .../sample3-many-to-one/entity/PostAuthor.ts | 16 +++++ .../entity/PostCategory.ts | 12 ++++ .../sample3-many-to-one/entity/PostDetails.ts | 31 ++++++++++ .../sample3-many-to-one/entity/PostImage.ts | 16 +++++ .../entity/PostInformation.ts | 18 ++++++ .../entity/PostMetadata.ts | 16 +++++ test/utils/EntityFileToJson.ts | 19 ++++++ 10 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 test/integration/examples/sample3-many-to-one/entity/Post.ts create mode 100644 test/integration/examples/sample3-many-to-one/entity/PostAuthor.ts create mode 100644 test/integration/examples/sample3-many-to-one/entity/PostCategory.ts create mode 100644 test/integration/examples/sample3-many-to-one/entity/PostDetails.ts create mode 100644 test/integration/examples/sample3-many-to-one/entity/PostImage.ts create mode 100644 test/integration/examples/sample3-many-to-one/entity/PostInformation.ts create mode 100644 test/integration/examples/sample3-many-to-one/entity/PostMetadata.ts diff --git a/src/drivers/MssqlDriver.ts b/src/drivers/MssqlDriver.ts index 41a548d..88f7e19 100644 --- a/src/drivers/MssqlDriver.ts +++ b/src/drivers/MssqlDriver.ts @@ -273,10 +273,10 @@ order by let ownColumn: ColumnInfo = ownerColumn; let isOneToMany: boolean; isOneToMany = false; - let index = referencedEntity.Indexes.find( + let index = ownerEntity.Indexes.find( (index) => { return index.isUnique && index.columns.some(col => { - return col.name == relatedColumn!.name + return col.name == ownerColumn!.name }) } ) @@ -293,11 +293,11 @@ order by ownerRelation.relatedTable = relationTmp.referencedTable ownerRelation.ownerTable = relationTmp.ownerTable ownerRelation.ownerColumn = ownerEntity.EntityName.toLowerCase() - ownerRelation.relationType = isOneToMany ? "OneToMany" : "OneToOne" + ownerRelation.relationType = isOneToMany ? "ManyToOne" : "OneToOne" ownerColumn.relations.push(ownerRelation) if (isOneToMany) { let col = new ColumnInfo() - col.name = ownerEntity.EntityName.toLowerCase() //+ 's' + col.name = ownerEntity.EntityName.toLowerCase() +'s' let referencedRelation = new RelationInfo(); col.relations.push(referencedRelation) referencedRelation.actionOnDelete = relationTmp.actionOnDelete @@ -307,7 +307,7 @@ order by referencedRelation.relatedTable = relationTmp.ownerTable referencedRelation.ownerTable = relationTmp.referencedTable referencedRelation.ownerColumn = relatedColumn.name.toLowerCase() - referencedRelation.relationType = "ManyToOne" + referencedRelation.relationType = "OneToMany" referencedEntity.Columns.push(col) } else { let col = new ColumnInfo() diff --git a/test/integration/examples/sample2-one-to-one/entity/Post.ts b/test/integration/examples/sample2-one-to-one/entity/Post.ts index c557b6e..7a8efe0 100644 --- a/test/integration/examples/sample2-one-to-one/entity/Post.ts +++ b/test/integration/examples/sample2-one-to-one/entity/Post.ts @@ -1,4 +1,4 @@ -import {PrimaryGeneratedColumn, Column, Entity, OneToOne,JoinColumn} from "typeorm"; +import {PrimaryGeneratedColumn, Column, Entity, OneToOne,JoinColumn,Index} from "typeorm"; import {PostDetails} from "./PostDetails"; import {PostCategory} from "./PostCategory"; import {PostAuthor} from "./PostAuthor"; @@ -25,6 +25,7 @@ export class Post { cascadeRemove: true }) @JoinColumn() + @Index({ unique: true }) category: PostCategory; // post has relation with details. cascade inserts here means if new PostDetails instance will be set to this @@ -33,6 +34,7 @@ export class Post { cascadeInsert: true }) @JoinColumn() + @Index({ unique: true }) details: PostDetails; // post has relation with details. cascade update here means if new PostDetail instance will be set to this relation @@ -41,6 +43,7 @@ export class Post { cascadeUpdate: true }) @JoinColumn() + @Index({ unique: true }) image: PostImage; // post has relation with details. cascade update here means if new PostDetail instance will be set to this relation @@ -49,6 +52,7 @@ export class Post { cascadeRemove: true }) @JoinColumn() + @Index({ unique: true }) metadata: PostMetadata|null; // post has relation with details. full cascades here @@ -58,11 +62,13 @@ export class Post { cascadeRemove: true }) @JoinColumn() + @Index({ unique: true }) information: PostInformation; // post has relation with details. not cascades here. means cannot be persisted, updated or removed @OneToOne(type => PostAuthor, author => author.post) @JoinColumn() + @Index({ unique: true }) author: PostAuthor; } \ No newline at end of file diff --git a/test/integration/examples/sample3-many-to-one/entity/Post.ts b/test/integration/examples/sample3-many-to-one/entity/Post.ts new file mode 100644 index 0000000..89a3aa5 --- /dev/null +++ b/test/integration/examples/sample3-many-to-one/entity/Post.ts @@ -0,0 +1,62 @@ +import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm"; +import {PostDetails} from "./PostDetails"; +import {PostCategory} from "./PostCategory"; +import {PostAuthor} from "./PostAuthor"; +import {PostInformation} from "./PostInformation"; +import {PostImage} from "./PostImage"; +import {PostMetadata} from "./PostMetadata"; + +@Entity("Post") +export class Post { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + title: string; + + @Column() + text: string; + + // post has relation with category, however inverse relation is not set (category does not have relation with post set) + @ManyToOne(type => 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 + @ManyToOne(type => 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(type => 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(type => PostMetadata, metadata => metadata.posts, { + cascadeRemove: true + }) + metadata: PostMetadata|null; + + // post has relation with details. full cascades here + @ManyToOne(type => 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 + @ManyToOne(type => PostAuthor, author => author.posts) + author: PostAuthor; + +} \ No newline at end of file diff --git a/test/integration/examples/sample3-many-to-one/entity/PostAuthor.ts b/test/integration/examples/sample3-many-to-one/entity/PostAuthor.ts new file mode 100644 index 0000000..90b0d38 --- /dev/null +++ b/test/integration/examples/sample3-many-to-one/entity/PostAuthor.ts @@ -0,0 +1,16 @@ +import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm"; +import {Post} from "./Post"; + +@Entity("PostAuthor") +export class PostAuthor { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + + @OneToMany(type => Post, post => post.author) + posts: Post[]; + +} \ No newline at end of file diff --git a/test/integration/examples/sample3-many-to-one/entity/PostCategory.ts b/test/integration/examples/sample3-many-to-one/entity/PostCategory.ts new file mode 100644 index 0000000..bae28e4 --- /dev/null +++ b/test/integration/examples/sample3-many-to-one/entity/PostCategory.ts @@ -0,0 +1,12 @@ +import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm"; + +@Entity("PostCategory") +export class PostCategory { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + +} \ No newline at end of file diff --git a/test/integration/examples/sample3-many-to-one/entity/PostDetails.ts b/test/integration/examples/sample3-many-to-one/entity/PostDetails.ts new file mode 100644 index 0000000..0f96397 --- /dev/null +++ b/test/integration/examples/sample3-many-to-one/entity/PostDetails.ts @@ -0,0 +1,31 @@ +import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm"; +import {Post} from "./Post"; + +@Entity("PostDetails") +export class PostDetails { + + @PrimaryGeneratedColumn() + id: number; + + @Column({ + nullable: true + }) + authorName: string; + + @Column({ + nullable: true + }) + comment: string; + + @Column({ + nullable: true + }) + metadata: string; + + @OneToMany(type => Post, post => post.details, { + cascadeInsert: true, + cascadeUpdate: true + }) + posts: Post[]; + +} \ No newline at end of file diff --git a/test/integration/examples/sample3-many-to-one/entity/PostImage.ts b/test/integration/examples/sample3-many-to-one/entity/PostImage.ts new file mode 100644 index 0000000..2965712 --- /dev/null +++ b/test/integration/examples/sample3-many-to-one/entity/PostImage.ts @@ -0,0 +1,16 @@ +import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm"; +import {Post} from "./Post"; + +@Entity("PostImage") +export class PostImage { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + url: string; + + @OneToMany(type => Post, post => post.image) + posts: Post[]; + +} \ No newline at end of file diff --git a/test/integration/examples/sample3-many-to-one/entity/PostInformation.ts b/test/integration/examples/sample3-many-to-one/entity/PostInformation.ts new file mode 100644 index 0000000..a2b9b20 --- /dev/null +++ b/test/integration/examples/sample3-many-to-one/entity/PostInformation.ts @@ -0,0 +1,18 @@ +import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm"; +import {Post} from "./Post"; + +@Entity("PostInformation") +export class PostInformation { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + text: string; + + @OneToMany(type => Post, post => post.information, { + cascadeUpdate: true, + }) + posts: Post[]; + +} \ No newline at end of file diff --git a/test/integration/examples/sample3-many-to-one/entity/PostMetadata.ts b/test/integration/examples/sample3-many-to-one/entity/PostMetadata.ts new file mode 100644 index 0000000..ce724db --- /dev/null +++ b/test/integration/examples/sample3-many-to-one/entity/PostMetadata.ts @@ -0,0 +1,16 @@ +import { PrimaryGeneratedColumn, Column, Entity, OneToOne, OneToMany, ManyToOne, JoinColumn } from "typeorm"; +import {Post} from "./Post"; + +@Entity("PostMetadata") +export class PostMetadata { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + description: string; + + @OneToMany(type => Post, post => post.metadata) + posts: Post[]; + +} \ No newline at end of file diff --git a/test/utils/EntityFileToJson.ts b/test/utils/EntityFileToJson.ts index 7cd1629..b1c5e09 100644 --- a/test/utils/EntityFileToJson.ts +++ b/test/utils/EntityFileToJson.ts @@ -61,6 +61,15 @@ export class EntityFileToJson { retVal.entityName = trimmedLine.substring(trimmedLine.indexOf('class') + 5, trimmedLine.lastIndexOf('{')).trim().toLowerCase() isInClassBody = true; continue; + } else if (trimmedLine.startsWith('@Index')) { + if (this.isPartOfMultilineStatement(trimmedLine)) { + isMultilineStatement = true; + priorPartOfMultilineStatement = trimmedLine; + continue; + } else { + //TODO:Add indicies to comparision model + continue; + } } } else { if (trimmedLine.startsWith('@Column')) { @@ -113,6 +122,7 @@ export class EntityFileToJson { let column = new EntityColumn() retVal.columns.push(column) column.relationType = "ManyToOne" + column.isOwnerOfRelation=true; continue; } } else if (trimmedLine.startsWith('@OneToMany')) { @@ -149,6 +159,15 @@ export class EntityFileToJson { retVal.columns[retVal.columns.length - 1].isOwnerOfRelation = true; continue; } + } else if (trimmedLine.startsWith('@Index')) { + if (this.isPartOfMultilineStatement(trimmedLine)) { + isMultilineStatement = true; + priorPartOfMultilineStatement = trimmedLine; + continue; + } else { + //TODO:Add indicies to comparision model + continue; + } } else if (trimmedLine.split(':').length - 1 > 0) { retVal.columns[retVal.columns.length - 1].columnName = trimmedLine.split(':')[0].trim(); //TODO:Should check if null only column is nullable?