Reputation: 75
I'm working on a Spring Boot Application with Hibernate and I'm just trying to understand the correct way to approach a OneToOne mapping when it comes to using cascade delete.
So, we have a User table and a PasswordResetToken table. A user has standard user columns: id, username, password, email.
A password reset token has an id, a FK to userId, and a string for a token.
So, my question now is: how do I correctly model this so we can properly cascade delete?
My thought process is that we have a unidirectional mapping since password reset token has a FK to user, and user does NOT have a FK to password reset token.
So I would think that we would place the @OneToOne on our PasswordResetToken class in Java and not have a reference to PasswordResetToken in our User class, but then the PasswordResetToken class will have a reference to a User object.
But, through some stackoverflowing, I found that people would have the child object (PasswordResetToken) inside the parent object (User) despite the parent object's table not having a reference to the child object's table (since the User table doesn't have a PasswordResetToken in it) which allows for adding the cascade remove to the @OneToOne annotation which means that when a User gets deleted, all children will get deleted as well.
So, which way is the right way to model this relationship?
Thanks for your time
Upvotes: 2
Views: 2669
Reputation: 16400
I don't know how big the token is, but what is wrong with storing the token in the User
entity as simple column? You can abstract some parts by using an @Embeddable
but really this should IMO be in the same table. If you are concerned with the amount of data fetched, you should be using DTOs to reduce the amount of data.
Upvotes: 0
Reputation: 1275
There are many ways to solve your problem. Some are less, some are more efficient.
@Entity
public class PasswordResetToken {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User User;
// other fields
}
@Entity
public class User {
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL,
fetch = FetchType.LAZY, optional = false)
private PasswordResetToken passwordResetToken;
// other fields
}
Since it's 1-1 relationship, you could use User
's ID as a primary key for PasswordResetToken
table.
@Entity
public class PasswordResetToken {
@OneToOne(fetch = FetchType.LAZY)
@MapsId
private User User;
// other fields
}
@Entity
public class User {
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL,
fetch = FetchType.LAZY, optional = false)
private PasswordResetToken passwordResetToken;
// other fields
}
If you want to have unidirectional mapping, and to have PasswordResetToken
entity as part of User
entity, you'll have to move the foreign key to User
table, since @JoinColumn
has to be applied on entity owning the foreign key.
@Entity
public class User {
@OneToOne(cascade = CascadeType.ALL,
fetch = FetchType.LAZY)
@JoinColumn("password_reset_token_id") // FK in User table
private PasswordResetToken passwordResetToken;
// other fields
}
As for performance, the most efficient is bidirectional with @MapsId
. Bidirectional with @JoinColumn
is less efficient, and I'm not sure about unidirectional mapping. One to one mappings are not that common in practice, and I'm not sure how often people use unidirectional mapping. Probably not at all, since the foreign key is usually on dependent side.
Upvotes: 2