typeorm: Cascade on save when it is not set
Issue type:
[ ] question [x] bug report [ ] feature request [ ] documentation issue
Database system/driver:
[ ] cordova
[ ] mongodb
[ ] mssql
[x] mysql / mariadb
[ ] oracle
[ ] postgres
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo
TypeORM version:
[ ] latest
[ ] @next
[x] 0.2.7 (or put your version here)
Steps to reproduce or a small repository showing the problem:
Here are my entities:
export class Category {
@PrimaryGeneratedColumn({unsigned: true})
id: number;
@Length(4, 45)
@Column({type: 'varchar', length: 45})
name: string;
@CreateDateColumn({precision: null, type: 'timestamp', default: () => 'CURRENT_TIMESTAMP'})
createdAt: Date;
@IsInt()
@Min(0)
@Max(1)
@Column({type: 'tinyint', width: 1})
isLeaf: number;
@ManyToOne(type => User, creator => creator.createdCategories, {nullable: false})
creator: User;
@ManyToOne(type => Category, parent => parent.children)
@JoinColumn({name: 'parent_category_id'})
parent: Category;
@OneToOne(type => CategoryLeaf, category => category.category)
categoryLeaf: CategoryLeaf;
@OneToMany(type => Category, category => category.parent)
children: Category[];
}
export class CategoryLeaf {
@IsInt()
@PrimaryColumn({unsigned: true})
categoryId: number;
@OneToOne(type => Category)
@JoinColumn()
category: Promise<Category>;
@IsInt()
@Min(0)
@Max(9999)
@Column({type: 'tinyint', width: 4})
sla: number;
@IsEnum(ApprovalMechanism)
@Column({type: 'enum', enum: ApprovalMechanism, nullable: true})
approvalMechanism: ApprovalMechanism;
@IsJSON()
@Column({type: 'simple-json'})
template: JSON;
@OneToMany(type => Request, request => request.category)
requests: Request[];
@OneToMany(type => ApproverCategory, approvers => approvers.categoryData, {cascade: true})
approvers: Promise<ApproverCategory[]>;
@ManyToMany(type => Room, room => room.categories)
@JoinTable({
name: 'categories_rooms',
joinColumns: [{
name: 'category_id'
}],
inverseJoinColumns: [{
name: 'room_id'
}]
})
rooms: Promise<Room[]>;
}
When I try to make a save on Category this way:
const categoryRepository = await getRepository(Category);
let existingCategory = categoryRepository.findOne(1, ['categoryLeaf']);
existingCategory.name = 'New Name';
await categoryRepository.save(existingCategory);
I’m getting this error:
query: SELECT categories_leaf.category_id AS category_id FROM categories_leaf categories_leaf WHERE ((categories_leaf.category_id = ?)) – PARAMETERS: [2]
query: START TRANSACTION
query: UPDATE categories SET name = ? WHERE id = ? – PARAMETERS: [“Cat222”,2]
query: UPDATE categories_leaf SET category_id = ? WHERE category_id = ? – PARAMETERS: [null,2]
query failed: UPDATE categories_leaf SET category_id = ? WHERE category_id = ? – PARAMETERS: [null,2]
error: { Error: ER_BAD_NULL_ERROR: Column ‘category_id’ cannot be null
Why is the save action trying to update categoryLeaf if i didn’t configure cascade on true?
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 10
- Comments: 26 (11 by maintainers)
well… that’s confusing judging by the age of this bug, I think we’ll have to live with it 😆
It’s not completely intuitive to me, but something that might help is if you set
persistence: falsein the relationship:I’m not sure why lacking
cascade: ["update"]doesn’t have the same effect. Would need @pleerock to weigh in on the intent here.This is really frustrating.
Let’s say I have the following example:
User
UserThing
Now simply trying to load a user with it’s things and then saving that user causes TypeORM to try to update all of the user’s things. It actually even does the update twice, once with all the same values the thing already has, and a second time with a null userId which fails.
This makes absolutely no sense. All I’ve done is just load the user and immediately save it without modifying anything and it still fails!
Setting
persistence: falsefor the one-to-many relation somewhat fixes the issue. On save, TypeORM still re-selects all of the user’s things (which is pretty inefficient) and then I can’t make use of any nested update functionality if I wanted to.Basically my only real solution is to not select related entities if I want to save the main entity.
@pleerock that’s exactly what I’d like to do - allow persistence of relations, but unfortunately only the
persistence: falseattribute fixes the original issue in this thread.I originally had a many-to-many setup, however as TypeORM doesn’t allow for additional values on the joining table I created an intermediary entity and made it one-to-many, many-to-one, one-to-many between Site, SiteSpecie and Specie. Perhaps then my schema is incorrect (using Babel / JS):
Site
SIteSpecie
Specie
Without
persistence: falsewhenever I runsite->save()(after, say, changing the site name) TypeORM tries to update every entry insite_speciewith null values, just like the original message in this thread. This obviously fails, and so nothing is updated and the transaction rolls back.I have another example that resulted in a bug that we couldn’t find that easily…
Let’s say you have those two entities:
When you do something like this:
Then you will end up with all products of that pickup-point having everything set to their default value (
null), because you could not tell typeorm to only save the entity without relations…I can confirm I experience the same issue with MySQL and TypeORM v0.2.7. The relation is configured exactly the same way, it’s a
OneToOnerelationship and the entity I’m saving is the inverse side (the side that does not have theJoinColumn). doesn’t matter ifcascadeis omitted or explicitly set tofalseon the inverse relation, TypeORM will try to save the parent when the inverse is saved.@MubashirMalik sadly no. We had to check for those occurrences and optimize them by for example using
updateinstead ofsaveand only passing the changed properties or even decrease performance by not just selecting specific columns and instead select everything…@ORzazade Gotcha. I think the
persistenceoption needs a complete overhaul.@P4sca1 Oh yeah by no means a solution, but seeing as how old this issue is, I don’t foresee us getting a real one anytime soon unless @pleerock chimes back in.