How to use onDelete: 'CASCADE' on one-to-one relationship - mysql

I try to delete user's profile when user was deleted. But it's delete nothing on profile.
User Entity
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#OneToOne(type => Profile, profile => profile.user,
{ eager: true, cascade: true, onDelete: 'CASCADE' })
#JoinColumn()
profile: Profile;
}
Profile Entity
#Entity()
export class Profile {
#PrimaryGeneratedColumn()
id: number;
#Column()
gender: string;
#Column()
photo: string;
#OneToOne(type => User, user => user.profile)
user: User;
}
How I delete a user.
const userRepository = connection.getRepository(User);
const user = await userRepository.findOne({ where: {id: 'someuserId'}, relations: ["profile"] });
await user.remove()
User was removed but nothing happen on user's profile.
Can anyone explain how to delete relationship on one-to-one?

User
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#OneToOne(type => Profile, profile => profile.user,
{ eager: true, onDelete: 'CASCADE' })
#JoinColumn()
profile: Profile;
}
Profile
#Entity()
export class Profile {
#PrimaryGeneratedColumn()
id: number;
#Column()
gender: string;
#Column()
photo: string;
#OneToOne(type => User, user => user.profile, { onDelete: "CASCADE" })
user: User;
}
I ran this code for removing entries from both the entities.
const userRepository = connection.getRepository(User);
const user = await userRepository.findOne({ where: {id: 'someuserId'} });
const profileRepository = connection.getRepository(Profile);
const profile = await profileRepository.findOne({ where: {id: user.profile.id} });
await profileRepository.remove(profile)
This is the expected behaviour for one-to-one relation. You have to delete referencing side to take cascade deletion to take in effect. discussion link
edit: raised issue on github

Solution
I recently found myself in the same spot. I created a inheritance relationship in my database. Multiple entities reference one common entity which provides basic attributes for each specialized table. For example: Common attribute holder table "Car" and specialized tables "Truck", "Bus". Now I wanted to delete the corresponding "Car"-entity whenever a "Truck" or "Bus" is deleted.
In typeorm the specialized tables should reference the common attribute table. To make the CASCADE DELETE work, you have to specify the side which holds the id using the #JoinColumn() annotation. Then you have to specify the onDelete: "CASCADE" on the One-to-One relationship of the same side as the #JoinColumn() annotation.
To validate the created table you can use SHOW CREATE TABLE table_name in your database. There you will see if the CASCADE DELETE is present on the correct foreign key constraint. (It should be on the table holding the foreign key).
That means, it is not required to set the CASCADE DELETE on both sides.
Code example
(see Gaurav Sharma's answer - modified)
// referenced table
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
// you can use eager on the none-owning side
// this can be very helpful for inheritance
#OneToOne(type => Profile, profile => profile.user, { eager: true })
profile: Profile;
}
// owning side - foreign key in this table
#Entity()
export class Profile {
#PrimaryGeneratedColumn()
id: number;
#Column()
gender: string;
#Column()
photo: string;
// specify onDelete here because owning side FK constraint would fail
// and it needs to know what is going to happen
#OneToOne(type => User, user => user.profile, { onDelete: "CASCADE" })
#JoinColumn()
user: User;
}
From here on you would create a new user. Set the saved user in the profile to save and save the profile.
Deleting the profile will not delete the user.
Deleting the user will delete the profile.
I also looked into CASCADE DELETE from both sides. From what I have found, you would have to specify the #JoinColumn() annotation on both sides, declaring onDelete: "CASCADE" on both sides. This will require at least two saves for new entities and I did not find further information in the docs concerning the bidirectional CASCADE DELETE. I would consider deleting manually in these situations because it might become very unclear what is going to happen and when entities need to be saved.

I solved my problem (for now)
I just manual remove in both entities.
const userRepository = connection.getRepository(User);
const user = await userRepository.findOne({ where: {id: 'someuserId'}, relations: ["profile"] });
const profileRepository = connection.getRepository(Profile);
const profile = await profileRepository.findOne({where: {id: user.profile.id}}).getOne()
await profile.remove()
await user.remove()

Related

Typeorm SQL one to one relation using same user ID in both tables

I wanted to create one to one relation in typeorm (NestJS). Just like in the picture below.
I wanted then to signup user with email and password and create row in custom_auth and user_profile table.
relations
I cant find solution to create that type of relation. I was only able to create typical relation using foreign key (solution in typeorm docs)
export class CustomAuth {
#PrimaryGeneratedColumn()
id: number
#Column({ unique: true })
email: string
#Column()
password: string
#OneToOne(() => UserProfile, (userProfile) => userProfile.customAuth, {
cascade: true,
})
userProfile: UserProfile
}
export class UserProfile {
#PrimaryGeneratedColumn()
userId: number
#Column()
name: string
#Column({ nullable: true })
address: string
#OneToOne(() => CustomAuth, (customAuth) => customAuth.userProfile, {})
#JoinColumn()
customAuth: CustomAuth
}

Typeorm, Define a new entity and populate it with default values and foreign keys

Let's say we have a user entity already defined and it has 3 rows. We create another entity called Usage after sometime and add one-to-one relation with User. Is it possible to prefill the usage table having 3 rows related to user table with default values ?
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
username: string;
#Column()
password: string;
#OneToOne(() => Usage)
usage: Usage;}
#Entity()
export class Usage {
#PrimaryGeneratedColumn()
id: number;
#Column({default:0})
views: number;
#OneToOne(() => User)
#JoinColumn()
User: User; }

How to select a row by many to many relation connection?

Here are my type-orm entities:
#Entity()
export class Conversation {
#PrimaryGeneratedColumn()
id: number;
#ManyToMany((type) => User, (user) => user.conversations)
attendees: User[];
}
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#ManyToMany((type) => Conversation, (conversation) => conversation.attendees)
#JoinTable()
conversations: Conversation[];
}
Having two different user IDs, I want to select a single conversation entity, which contains both of those users as attendees.
What I managed to get so far are the conversations of one user:
const conversationsUsers = await conversationRepository
.createQueryBuilder("c")
.innerJoinAndSelect("c.attendees", "user", "user.id = :authorId", { authorId })
.getMany();
Being far from becoming SQL expert, I think I should create another JOIN with users table (with an alias for e.g. user.id => attende_id, but have no idea how to approach this.
Thanks in advance for any thoughts :)

TypeORM MariaDB Primary Key as UUID

Having a really strange issue with TypeORM and MariaDB. I have two entities with UUIDs as the primary key. The Companies entity is working fine as it scaffolds the schema as CHAR but the users table is being instantiated as an auto increment INT.
User Entity
#Entity()
export class User {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
firstname: string;
#Column()
lastname: string;
#Column({ unique: true })
email: string;
#Column()
password: string;
#ManyToOne(() => Company, (company) => company.users)
company: Company;
}
Companies Entity
#Entity()
export class Company {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
name: string;
#OneToMany(() => User, (user) => user.company)
users: User[];
}
Config
const config: MysqlConnectionOptions = {
type: 'mariadb',
....
entities: ['dist/src/**/*.entity.js'],
synchronize: true,
};
Users Table Screenshot
Companies Table Screenshot
Find it really strange, is it because I'm using MariaDB instead of straight MySQL?
Okay looks like I fixed it, really odd one. Looks like something was cached incorrectly. Deleted dist folder and node modules. reinstalled and total recompile. Fixed...

Two entity have each other

In this case, the store can have many owners, and the owners can have many store.
However, when i save the entity by using Repository and the problem happen:
Maximum call stack size exceeded
I think it need to use Nested Tree annotation? But i dont know how to correct it. Please help!
#Entity('store')
export class StoreEntity {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
name: string;
...
#ManyToMany(type => UserEntity, user => user.ownStores)
#JoinTable()
owners: UserEntity[];
}
#Entity('user')
export class UserEntity {
#PrimaryGeneratedColumn('uuid')
id: string;
#Column({
type: 'varchar',
})
email: string;
...
#ManyToMany(type => StoreEntity, store => store.owners)
ownStores: StoreEntity[];