DJG
DJG

Reputation: 75

Hibernate @OneToOne Unidirectional Mapping...Cascade Delete

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

Answers (2)

Christian Beikov
Christian Beikov

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

Stefan Golubović
Stefan Golubović

Reputation: 1275

There are many ways to solve your problem. Some are less, some are more efficient.

Bidirectional with foreign key

@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
}

Bidirectional with principal/parent's primary key as foreign key

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
}

Unidirectional

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

Related Questions