mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
fixes #151 + removed cascade remove options where they should not be possible
This commit is contained in:
parent
df3b3cf294
commit
11817adc2e
@ -8,6 +8,8 @@
|
||||
not only with tables, but also with documents and other database-specific "tables".
|
||||
Previous decorator names are deprecated and will be removed in the future.
|
||||
* added custom repositories support. Example in samples directory.
|
||||
* cascade remove options has been removed from `@ManyToMany`, `@OneToMany` decorators. Also cascade remove is not possible
|
||||
from two sides of `@OneToOne` relationship now.
|
||||
|
||||
# 0.0.6 (latest)
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "typeorm",
|
||||
"private": true,
|
||||
"version": "0.0.7-alpha.15",
|
||||
"version": "0.0.7-alpha.18",
|
||||
"description": "Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL databases.",
|
||||
"license": "MIT",
|
||||
"readmeFilename": "README.md",
|
||||
|
||||
@ -32,8 +32,7 @@ export class Post {
|
||||
|
||||
@OneToMany(type => Image, image => image.post, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
images: Image[] = [];
|
||||
|
||||
@ -54,8 +53,7 @@ export class Post {
|
||||
|
||||
@ManyToMany(type => Category, category => category.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: Category[];
|
||||
|
||||
@ -19,8 +19,7 @@ export class PostDetails {
|
||||
post: Post;
|
||||
|
||||
@OneToMany(type => Category, category => category.details, {
|
||||
cascadeInsert: true,
|
||||
cascadeRemove: true
|
||||
cascadeInsert: true
|
||||
})
|
||||
categories: Category[];
|
||||
|
||||
|
||||
@ -12,8 +12,7 @@ export class Blog extends BaseObject {
|
||||
|
||||
@ManyToMany(type => PostCategory, category => category.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: PostCategory[] = [];
|
||||
|
||||
@ -12,8 +12,7 @@ export class Post extends BaseObject {
|
||||
|
||||
@ManyToMany(type => PostCategory, category => category.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: PostCategory[] = [];
|
||||
|
||||
@ -13,8 +13,7 @@ export class PostCategory {
|
||||
|
||||
@ManyToMany(type => Post, post => post.categories, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
posts: Post[] = [];
|
||||
|
||||
|
||||
@ -28,8 +28,7 @@ export class Post {
|
||||
|
||||
@OneToMany(type => PostAuthor, author => author.editedPost, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
// @JoinColumn() // uncomment this and you'll get an error, because JoinColumn is not allowed here (only many-to-one/one-to-one)
|
||||
// @JoinTable() // uncomment this and you'll get an error because JoinTable is not allowed here (only many-to-many)
|
||||
|
||||
@ -25,7 +25,8 @@ export class Post {
|
||||
author: Promise<Author|null>;
|
||||
|
||||
@ManyToMany(type => Category, category => category.posts, {
|
||||
cascadeAll: true
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: Promise<Category[]>;
|
||||
|
||||
@ -23,7 +23,10 @@ export class Post {
|
||||
@ManyToOne(type => Author, { cascadeAll: true })
|
||||
author: Author;
|
||||
|
||||
@ManyToMany(type => Category, { cascadeAll: true })
|
||||
@ManyToMany(type => Category, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: Category[];
|
||||
|
||||
|
||||
@ -27,7 +27,8 @@ export class Post {
|
||||
author: Author;
|
||||
|
||||
@ManyToMany(type => Category, category => category.posts, {
|
||||
cascadeAll: true
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable({
|
||||
name: "_post_categories"
|
||||
|
||||
@ -18,7 +18,8 @@ export class Post {
|
||||
text: string;
|
||||
|
||||
@ManyToMany(type => Category, {
|
||||
cascadeAll: true
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: Category[];
|
||||
|
||||
@ -24,8 +24,7 @@ export class PostDetails {
|
||||
|
||||
@OneToMany(type => Post, post => post.details, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
posts: Post[];
|
||||
|
||||
|
||||
@ -22,8 +22,7 @@ export class Post {
|
||||
// post has relation with category, however inverse relation is not set (category does not have relation with post set)
|
||||
@ManyToMany(type => PostCategory, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: PostCategory[];
|
||||
@ -46,17 +45,14 @@ export class Post {
|
||||
|
||||
// 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(type => PostMetadata, metadata => metadata.posts, {
|
||||
cascadeRemove: true
|
||||
})
|
||||
@ManyToMany(type => PostMetadata, metadata => metadata.posts)
|
||||
@JoinTable()
|
||||
metadatas: PostMetadata[];
|
||||
|
||||
// post has relation with details. full cascades here
|
||||
@ManyToMany(type => PostInformation, information => information.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
informations: PostInformation[];
|
||||
|
||||
@ -24,8 +24,7 @@ export class PostDetails {
|
||||
|
||||
@ManyToMany(type => Post, post => post.details, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
posts: Post[];
|
||||
|
||||
|
||||
@ -25,8 +25,7 @@ export class Post {
|
||||
|
||||
@ManyToMany(type => PostCategory, category => category.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: PostCategory[] = [];
|
||||
|
||||
@ -13,8 +13,7 @@ export class PostCategory {
|
||||
|
||||
@ManyToMany(type => Post, post => post.categories, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
posts: Post[] = [];
|
||||
|
||||
|
||||
@ -21,8 +21,7 @@ export class Blog extends BasePost {
|
||||
|
||||
@ManyToMany(type => PostCategory, category => category.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: PostCategory[] = [];
|
||||
|
||||
@ -21,8 +21,7 @@ export class Post extends BasePost {
|
||||
|
||||
@ManyToMany(type => PostCategory, category => category.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: PostCategory[] = [];
|
||||
|
||||
@ -13,8 +13,7 @@ export class PostCategory {
|
||||
|
||||
@ManyToMany(type => Post, post => post.categories, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
posts: Post[] = [];
|
||||
|
||||
|
||||
@ -25,8 +25,7 @@ export class Post {
|
||||
|
||||
@ManyToMany(type => PostCategory, category => category.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: PostCategory[] = [];
|
||||
|
||||
@ -13,8 +13,7 @@ export class PostCategory {
|
||||
|
||||
@ManyToMany(type => Post, post => post.categories, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
posts: Post[] = [];
|
||||
|
||||
|
||||
@ -38,23 +38,20 @@ export class Category {
|
||||
|
||||
@OneToMany(type => Category, category => category.oneManyCategory, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
oneManyCategories: Category[] = [];
|
||||
|
||||
@ManyToMany(type => Category, category => category.manyInverseCategories, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
manyCategories: Category[] = [];
|
||||
|
||||
@ManyToMany(type => Category, category => category.manyCategories, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
manyInverseCategories: Category[] = [];
|
||||
|
||||
|
||||
@ -31,8 +31,7 @@ export class Post {
|
||||
|
||||
@ManyToMany(type => PostCategory, category => category.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
categories: PostCategory[] = [];
|
||||
|
||||
@ -19,8 +19,7 @@ export class PostCategory {
|
||||
|
||||
@ManyToMany(type => Post, post => post.categories, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
posts: Post[] = [];
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs";
|
||||
* multiple instances of Entity1. To achieve it, this type of relation creates a junction table, where it storage
|
||||
* entity1 and entity2 ids. This is owner side of the relationship.
|
||||
*/
|
||||
export function ManyToMany<T>(typeFunction: (type?: any) => ObjectType<T>, options?: RelationOptions): Function;
|
||||
export function ManyToMany<T>(typeFunction: (type?: any) => ObjectType<T>, options?: { cascadeInsert?: boolean, cascadeUpdate?: boolean }): Function;
|
||||
|
||||
/**
|
||||
* Many-to-many is a type of relationship when Entity1 can have multiple instances of Entity2, and Entity2 can have
|
||||
@ -18,7 +18,7 @@ export function ManyToMany<T>(typeFunction: (type?: any) => ObjectType<T>, optio
|
||||
*/
|
||||
export function ManyToMany<T>(typeFunction: (type?: any) => ObjectType<T>,
|
||||
inverseSide?: string|((object: T) => any),
|
||||
options?: RelationOptions): Function;
|
||||
options?: { cascadeInsert?: boolean, cascadeUpdate?: boolean }): Function;
|
||||
|
||||
/**
|
||||
* Many-to-many is a type of relationship when Entity1 can have multiple instances of Entity2, and Entity2 can have
|
||||
@ -26,8 +26,8 @@ export function ManyToMany<T>(typeFunction: (type?: any) => ObjectType<T>,
|
||||
* entity1 and entity2 ids. This is owner side of the relationship.
|
||||
*/
|
||||
export function ManyToMany<T>(typeFunction: (type?: any) => ObjectType<T>,
|
||||
inverseSideOrOptions?: string|((object: T) => any)|RelationOptions,
|
||||
options?: RelationOptions): Function {
|
||||
inverseSideOrOptions?: string|((object: T) => any)|{ cascadeInsert?: boolean, cascadeUpdate?: boolean },
|
||||
options?: { cascadeInsert?: boolean, cascadeUpdate?: boolean }): Function {
|
||||
let inverseSideProperty: string|((object: T) => any);
|
||||
if (typeof inverseSideOrOptions === "object") {
|
||||
options = <RelationOptions> inverseSideOrOptions;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import {RelationOptions} from "../options/RelationOptions";
|
||||
import {RelationTypes} from "../../metadata/types/RelationTypes";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ObjectType} from "../../common/ObjectType";
|
||||
import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs";
|
||||
import {RelationOptions} from "../options/RelationOptions";
|
||||
|
||||
// todo: make decorators which use inverse side string separate
|
||||
|
||||
@ -10,36 +10,9 @@ import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs";
|
||||
* One-to-many relation allows to create type of relation when Entity2 can have multiple instances of Entity1.
|
||||
* Entity1 have only one Entity2. Entity1 is an owner of the relationship, and storages Entity2 id on its own side.
|
||||
*/
|
||||
// export function OneToMany<T>(typeFunction: (type?: any) => ConstructorFunction<T>, options?: RelationOptions): Function;
|
||||
|
||||
/**
|
||||
* One-to-many relation allows to create type of relation when Entity2 can have multiple instances of Entity1.
|
||||
* Entity1 have only one Entity2. Entity1 is an owner of the relationship, and storages Entity2 id on its own side.
|
||||
*/
|
||||
export function OneToMany<T>(typeFunction: (type?: any) => ObjectType<T>,
|
||||
inverseSide: string|((object: T) => any),
|
||||
options?: RelationOptions): Function;
|
||||
|
||||
/**
|
||||
* One-to-many relation allows to create type of relation when Entity2 can have multiple instances of Entity1.
|
||||
* Entity1 have only one Entity2. Entity1 is an owner of the relationship, and storages Entity2 id on its own side.
|
||||
*/
|
||||
export function OneToMany<T>(typeFunction: (type?: any) => ObjectType<T>,
|
||||
inverseSideOrOptions: string|((object: T) => any)|RelationOptions,
|
||||
options?: RelationOptions): Function {
|
||||
let inverseSideProperty: string|((object: T) => any);
|
||||
if (typeof inverseSideOrOptions === "object") {
|
||||
options = <RelationOptions> inverseSideOrOptions;
|
||||
} else {
|
||||
inverseSideProperty = <string|((object: T) => any)> inverseSideOrOptions;
|
||||
}
|
||||
|
||||
// todo: for OneToMany having inverse side is required because otherwise its not possible to do anything (selections/persisment)
|
||||
// todo: validate it somehow?
|
||||
|
||||
export function OneToMany<T>(typeFunction: (type?: any) => ObjectType<T>, inverseSide: string|((object: T) => any), options?: { cascadeInsert?: boolean, cascadeUpdate?: boolean }): Function {
|
||||
return function (object: Object, propertyName: string) {
|
||||
if (!options) options = {} as RelationOptions;
|
||||
|
||||
const reflectedType = (Reflect as any).getMetadata("design:type", object, propertyName);
|
||||
const isLazy = reflectedType && typeof reflectedType.name === "string" && reflectedType.name.toLowerCase() === "promise";
|
||||
|
||||
@ -50,7 +23,7 @@ export function OneToMany<T>(typeFunction: (type?: any) => ObjectType<T>,
|
||||
isLazy: isLazy,
|
||||
relationType: RelationTypes.ONE_TO_MANY,
|
||||
type: typeFunction,
|
||||
inverseSideProperty: inverseSideProperty,
|
||||
inverseSideProperty: inverseSide,
|
||||
options: options
|
||||
};
|
||||
getMetadataArgsStorage().relations.add(args);
|
||||
|
||||
@ -106,6 +106,14 @@ export class EntityMetadataValidator {
|
||||
// todo: check if there are multiple columns on the same column applied.
|
||||
|
||||
});
|
||||
|
||||
// make sure cascade remove is not set for both sides of relationships (can be set in OneToOne decorators)
|
||||
entityMetadata.relations.forEach(relation => {
|
||||
const isCircularCascadeRemove = relation.isCascadeRemove && relation.hasInverseSide && relation.inverseRelation.isCascadeRemove;
|
||||
if (isCircularCascadeRemove)
|
||||
throw new Error(`Relation ${entityMetadata.name}#${relation.propertyName} and ${relation.inverseRelation.entityMetadata.name}#${relation.inverseRelation.propertyName} both has cascade remove set. ` +
|
||||
`This may lead to unexpected circular removals. Please set cascade remove only from one side of relationship.`);
|
||||
}); // todo: maybe better just deny removal from one to one relation without join column?
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -335,6 +335,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
let persistValueRelationId: any = undefined;
|
||||
if (subject.hasEntity) {
|
||||
const persistValue = relation.getEntityValue(subject.entity);
|
||||
if (persistValue === null) persistValueRelationId = null;
|
||||
if (persistValue) persistValueRelationId = persistValue[relation.joinColumn.referencedColumn.propertyName];
|
||||
if (persistValueRelationId === undefined) return; // skip undefined properties
|
||||
}
|
||||
@ -381,6 +382,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
if (alreadyLoadedRelatedDatabaseSubject.mustBeRemoved)
|
||||
return;
|
||||
|
||||
console.log("marked as removed: ", alreadyLoadedRelatedDatabaseSubject);
|
||||
alreadyLoadedRelatedDatabaseSubject.mustBeRemoved = true;
|
||||
await this.buildCascadeRemovedAndRelationUpdateOperateSubjects(alreadyLoadedRelatedDatabaseSubject);
|
||||
}
|
||||
|
||||
@ -67,6 +67,13 @@ export class SubjectOperationExecutor {
|
||||
*/
|
||||
async execute(subjects: Subject[]): Promise<void> {
|
||||
|
||||
/*subjects.forEach(subject => {
|
||||
console.log(subject.entity);
|
||||
console.log("mustBeInserted: ", subject.mustBeInserted);
|
||||
console.log("mustBeUpdated: ", subject.mustBeUpdated);
|
||||
console.log("mustBeRemoved: ", subject.mustBeRemoved);
|
||||
});*/
|
||||
|
||||
// validate all subjects first
|
||||
subjects.forEach(subject => subject.validate());
|
||||
|
||||
@ -114,10 +121,6 @@ export class SubjectOperationExecutor {
|
||||
await this.executeUpdateRelations();
|
||||
await this.executeRemoveOperations();
|
||||
|
||||
// finally broadcast "after" events
|
||||
// note that we are broadcasting events after we release connection to make sure its free if someone reuse connection inside listeners
|
||||
await this.connection.broadcaster.broadcastAfterEventsForAll(this.transactionEntityManager, this.insertSubjects, this.updateSubjects, this.removeSubjects);
|
||||
|
||||
// commit transaction if it was started by us
|
||||
if (isTransactionStartedByItself === true)
|
||||
await this.queryRunner.commitTransaction();
|
||||
@ -125,6 +128,10 @@ export class SubjectOperationExecutor {
|
||||
// update all special columns in persisted entities, like inserted id or remove ids from the removed entities
|
||||
await this.updateSpecialColumnsInPersistedEntities();
|
||||
|
||||
// finally broadcast "after" events
|
||||
// note that we are broadcasting events after commit because we want to have ids of the entities inside them to be available in subscribers
|
||||
await this.connection.broadcaster.broadcastAfterEventsForAll(this.transactionEntityManager, this.insertSubjects, this.updateSubjects, this.removeSubjects);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
// rollback transaction if it was started by us
|
||||
|
||||
@ -29,15 +29,13 @@ export class Category {
|
||||
|
||||
@OneToMany(type => Post, post => post.category, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
posts: Post[];
|
||||
|
||||
@ManyToMany(type => Photo, photo => photo.categories, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
photos: Photo[];
|
||||
|
||||
@ -25,8 +25,7 @@ export class Photo {
|
||||
|
||||
@ManyToMany(type => Category, category => category.photos, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
categories: Category[];
|
||||
|
||||
|
||||
@ -26,8 +26,7 @@ export class Post {
|
||||
|
||||
@ManyToMany(type => Photo, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
photos: Photo[];
|
||||
|
||||
@ -21,15 +21,13 @@ export class Category {
|
||||
|
||||
@OneToMany(type => Post, post => post.manyToOneCategory, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true,
|
||||
cascadeUpdate: true
|
||||
})
|
||||
oneToManyPosts: Post[];
|
||||
|
||||
@OneToMany(type => Post, post => post.noCascadeManyToOneCategory, {
|
||||
cascadeInsert: false,
|
||||
cascadeUpdate: false,
|
||||
cascadeRemove: false,
|
||||
cascadeUpdate: false
|
||||
})
|
||||
noCascadeOneToManyPosts: Post[];
|
||||
|
||||
@ -52,7 +50,6 @@ export class Category {
|
||||
@ManyToMany(type => Post, post => post.manyToManyOwnerCategories, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true,
|
||||
})
|
||||
@JoinTable()
|
||||
manyToManyPosts: Post[];
|
||||
@ -60,15 +57,13 @@ export class Category {
|
||||
@ManyToMany(type => Post, post => post.noCascadeManyToManyOwnerCategories, {
|
||||
cascadeInsert: false,
|
||||
cascadeUpdate: false,
|
||||
cascadeRemove: false,
|
||||
})
|
||||
@JoinTable()
|
||||
noCascadeManyToManyPosts: Post[];
|
||||
|
||||
@ManyToMany(type => Photo, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
@JoinTable()
|
||||
photos: Photo[];
|
||||
|
||||
@ -25,7 +25,6 @@ export class Photo {
|
||||
@ManyToMany(type => Post, photo => photo.photos, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true,
|
||||
})
|
||||
posts: Post[];
|
||||
|
||||
|
||||
@ -48,7 +48,6 @@ export class Post {
|
||||
@ManyToMany(type => Category, category => category.manyToManyPosts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true,
|
||||
})
|
||||
@JoinTable()
|
||||
manyToManyOwnerCategories: Category[];
|
||||
@ -56,7 +55,6 @@ export class Post {
|
||||
@ManyToMany(type => Category, category => category.noCascadeManyToManyPosts, {
|
||||
cascadeInsert: false,
|
||||
cascadeUpdate: false,
|
||||
cascadeRemove: false,
|
||||
})
|
||||
@JoinTable()
|
||||
noCascadeManyToManyOwnerCategories: Category[];
|
||||
@ -64,7 +62,6 @@ export class Post {
|
||||
@ManyToMany(type => Photo, photo => photo.posts, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true,
|
||||
})
|
||||
@JoinTable()
|
||||
photos: Photo[];
|
||||
@ -72,7 +69,6 @@ export class Post {
|
||||
@ManyToMany(type => Photo, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true,
|
||||
})
|
||||
@JoinTable()
|
||||
noInversePhotos: Photo[];
|
||||
|
||||
14
test/github-issues/151/entity/Category.ts
Normal file
14
test/github-issues/151/entity/Category.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import {Entity} from "../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
|
||||
@Entity()
|
||||
export class Category {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
}
|
||||
26
test/github-issues/151/entity/Post.ts
Normal file
26
test/github-issues/151/entity/Post.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import {Entity} from "../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
import {Category} from "./Category";
|
||||
import {JoinColumn} from "../../../../src/decorator/relations/JoinColumn";
|
||||
import {OneToOne} from "../../../../src/decorator/relations/OneToOne";
|
||||
import {PostMetadata} from "./PostMetadata";
|
||||
|
||||
@Entity()
|
||||
export class Post {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@OneToOne(type => Category, { cascadeAll: true })
|
||||
@JoinColumn()
|
||||
category: Category|null;
|
||||
|
||||
@OneToOne(type => PostMetadata, metadata => metadata.post, { cascadeAll: true })
|
||||
@JoinColumn()
|
||||
metadata: PostMetadata|null;
|
||||
|
||||
}
|
||||
19
test/github-issues/151/entity/PostMetadata.ts
Normal file
19
test/github-issues/151/entity/PostMetadata.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Entity} from "../../../../src/decorator/entity/Entity";
|
||||
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
|
||||
import {Column} from "../../../../src/decorator/columns/Column";
|
||||
import {OneToOne} from "../../../../src/decorator/relations/OneToOne";
|
||||
import {Post} from "./Post";
|
||||
|
||||
@Entity()
|
||||
export class PostMetadata {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@OneToOne(type => Post, post => post.metadata)
|
||||
post: Post|null;
|
||||
|
||||
}
|
||||
128
test/github-issues/151/issue-151.ts
Normal file
128
test/github-issues/151/issue-151.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import "reflect-metadata";
|
||||
import {createTestingConnections, closeTestingConnections, 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", () => {
|
||||
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchemaOnConnection: true,
|
||||
}));
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should cascade persist successfully", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const category = new Category();
|
||||
category.name = "post category";
|
||||
|
||||
const post = new Post();
|
||||
post.title = "Hello post";
|
||||
post.category = category;
|
||||
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
const loadedPost = await connection.entityManager.findOneById(Post, 1, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
loadedPost!.should.be.eql({
|
||||
id: 1,
|
||||
title: "Hello post",
|
||||
category: {
|
||||
id: 1,
|
||||
name: "post category"
|
||||
}
|
||||
});
|
||||
|
||||
})));
|
||||
|
||||
it("should cascade remove successfully with uni-directional relation", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const category = new Category();
|
||||
category.name = "post category";
|
||||
|
||||
const post = new Post();
|
||||
post.title = "Hello post";
|
||||
post.category = category;
|
||||
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
post.category = null;
|
||||
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
const loadedPostWithCategory = await connection.entityManager.findOneById(Post, 1, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedPostWithCategory).to.be.empty;
|
||||
|
||||
const loadedPostWithoutCategory = await connection.entityManager.findOneById(Post, 1, {
|
||||
alias: "post"
|
||||
});
|
||||
|
||||
expect(loadedPostWithoutCategory).not.to.be.empty;
|
||||
loadedPostWithoutCategory!.should.be.eql({
|
||||
id: 1,
|
||||
title: "Hello post"
|
||||
});
|
||||
|
||||
const loadedCategory = await connection.entityManager.findOneById(Category, 1);
|
||||
expect(loadedCategory).to.be.empty;
|
||||
|
||||
})));
|
||||
|
||||
it("should cascade remove successfully with bi-directional relation from owner side", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const metadata = new PostMetadata();
|
||||
metadata.name = "post metadata";
|
||||
|
||||
const post = new Post();
|
||||
post.title = "Hello post";
|
||||
post.metadata = metadata;
|
||||
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
post.metadata = null;
|
||||
|
||||
console.log("-------------------------------------------------");
|
||||
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
const loadedPostWithMetadata = await connection.entityManager.findOneById(Post, 1, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
metadata: "post.metadata"
|
||||
}
|
||||
});
|
||||
expect(loadedPostWithMetadata).to.be.empty;
|
||||
|
||||
const loadedPostWithoutMetadata = await connection.entityManager.findOneById(Post, 1, {
|
||||
alias: "post"
|
||||
});
|
||||
expect(loadedPostWithoutMetadata).not.to.be.empty;
|
||||
loadedPostWithoutMetadata!.should.be.eql({
|
||||
id: 1,
|
||||
title: "Hello post"
|
||||
});
|
||||
|
||||
const loadedMetadata = await connection.entityManager.findOneById(PostMetadata, 1);
|
||||
expect(loadedMetadata).to.be.empty;
|
||||
|
||||
})));
|
||||
|
||||
});
|
||||
@ -21,8 +21,7 @@ export class Request {
|
||||
|
||||
@OneToOne(type => Ticket, ticket => ticket.request, {
|
||||
cascadeInsert: true,
|
||||
cascadeUpdate: true,
|
||||
cascadeRemove: true
|
||||
cascadeUpdate: true
|
||||
})
|
||||
ticket: Ticket;
|
||||
|
||||
|
||||
@ -32,7 +32,6 @@ export class Game {
|
||||
@ManyToMany(type => Platform, platform => platform.games, {
|
||||
cascadeInsert: true, // allow to insert a new platform on game save
|
||||
cascadeUpdate: true, // allow to update a platform on game save
|
||||
cascadeRemove: true // allow to remove a platform on game remove
|
||||
})
|
||||
@JoinTable()
|
||||
platforms: Platform[];
|
||||
|
||||
@ -25,7 +25,6 @@ export class Platform {
|
||||
@ManyToMany(type => Game, game => game.platforms, {
|
||||
cascadeInsert: true, // allow to insert a new game on platform save
|
||||
cascadeUpdate: true, // allow to update an game on platform save
|
||||
cascadeRemove: true // allow to remove an game on platform remove
|
||||
})
|
||||
games: Game[];
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user