DEV Community

Hunor Vadasz-Perhat
Hunor Vadasz-Perhat

Posted on

hibernate-008: Unidirectional vs Bidirectional @OneToOne Relationship

πŸš€ 1️⃣ Unidirectional @OneToOne Relationship

βœ… In a unidirectional @OneToOne, only one entity knows about the other, and the foreign key is stored in the owning entity.


πŸ“Œ Example: A User has a Profile

  • A User has exactly one Profile.
  • A Profile belongs to exactly one User.
  • The foreign key (profile_id) is stored in the User table.

πŸ”Ή Step 1: Define the Owning Side (@OneToOne with @JoinColumn)

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;

    @OneToOne
    @JoinColumn(name = "profile_id") // βœ… Foreign key stored in User table
    private Profile profile;
}
Enter fullscreen mode Exit fullscreen mode
  • @JoinColumn(name = "profile_id") ensures that the User table stores the foreign key.

πŸ”Ή Step 2: Define the Profile Entity (No Reference Back)

@Entity
public class Profile {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String bio;
}
Enter fullscreen mode Exit fullscreen mode
  • Profile does not reference User (unidirectional).
  • User owns the relationship and has the profile_id column.

πŸ”Ή Generated SQL

CREATE TABLE user (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255),
    profile_id BIGINT UNIQUE, -- βœ… Foreign key stored here
    FOREIGN KEY (profile_id) REFERENCES profile(id)
);

CREATE TABLE profile (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    bio VARCHAR(255)
);
Enter fullscreen mode Exit fullscreen mode

βœ… The profile_id foreign key is stored in User, ensuring a one-to-one relationship.


πŸ“Œ Saving Data in Hibernate

Profile profile = new Profile();
profile.setBio("Software Engineer");

User user = new User();
user.setUsername("JohnDoe");
user.setProfile(profile); // βœ… Link profile to user

entityManager.persist(profile); // βœ… Save Profile first
entityManager.persist(user); // βœ… Then save User
Enter fullscreen mode Exit fullscreen mode

πŸš€ Now the User is linked to the Profile.


πŸ“Œ Querying Data

User user = entityManager.find(User.class, 1L);
System.out.println(user.getProfile().getBio()); // βœ… Works!
Enter fullscreen mode Exit fullscreen mode

βœ… You can access the Profile from User, but not the other way around.


πŸš€ 2️⃣ Bidirectional @OneToOne Relationship

βœ… In a bidirectional @OneToOne, both entities know about each other.


πŸ“Œ Example: A User has a Profile, and a Profile belongs to a User

  • User owns the relationship (@OneToOne with @JoinColumn).
  • Profile has a reference back to User (@OneToOne(mappedBy = "profile")).

πŸ”Ή Step 1: Define the Owning Side (@OneToOne with @JoinColumn)

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;

    @OneToOne
    @JoinColumn(name = "profile_id") // βœ… Foreign key stored in User
    private Profile profile;
}
Enter fullscreen mode Exit fullscreen mode

βœ… User owns the relationship and has the foreign key.


πŸ”Ή Step 2: Define the Inverse Side (@OneToOne(mappedBy = "profile"))

@Entity
public class Profile {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String bio;

    @OneToOne(mappedBy = "profile") // βœ… Inverse side
    private User user;
}
Enter fullscreen mode Exit fullscreen mode

βœ… mappedBy = "profile" tells Hibernate:

  • "The foreign key is already in the User table."
  • "Don't create another column in Profile."

πŸ”Ή Generated SQL

CREATE TABLE user (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255),
    profile_id BIGINT UNIQUE, -- βœ… Foreign key stored here
    FOREIGN KEY (profile_id) REFERENCES profile(id)
);

CREATE TABLE profile (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    bio VARCHAR(255)
);
Enter fullscreen mode Exit fullscreen mode

βœ… The foreign key is only in User.profile_id, keeping the relationship correct.


πŸ“Œ Saving Data in Hibernate

Profile profile = new Profile();
profile.setBio("Software Engineer");

User user = new User();
user.setUsername("JohnDoe");
user.setProfile(profile);

profile.setUser(user); // βœ… Set reference back

entityManager.persist(profile); // βœ… Save Profile first
entityManager.persist(user); // βœ… Then save User
Enter fullscreen mode Exit fullscreen mode

πŸš€ Now, both User and Profile reference each other.


πŸ“Œ Querying Both Directions

βœ… User β†’ Profile

User user = entityManager.find(User.class, 1L);
System.out.println(user.getProfile().getBio()); // βœ… Works!
Enter fullscreen mode Exit fullscreen mode

βœ… Profile β†’ User

Profile profile = entityManager.find(Profile.class, 1L);
System.out.println(profile.getUser().getUsername()); // βœ… Works!
Enter fullscreen mode Exit fullscreen mode

βœ… Unlike the unidirectional version, now you can access both User β†’ Profile and Profile β†’ User.


πŸš€ 3️⃣ Summary: Unidirectional vs. Bidirectional @OneToOne

Feature Unidirectional (@OneToOne) Bidirectional (@OneToOne + mappedBy)
@OneToOne used? βœ… Yes βœ… Yes (Both Sides)
@JoinColumn used? βœ… Yes (Owning Side) βœ… Yes (Owning Side)
mappedBy used? ❌ No βœ… Yes (Inverse Side)
Foreign key location? In owning entity's table In owning entity's table
Reference back? ❌ No βœ… Yes (Both Can Access Each Other)

βœ… Best Practice: Use bidirectional @OneToOne if you need to query from both entities.

πŸš€ Unidirectional @OneToOne is simpler if you only query in one direction.


🎯 Final Takeaways

  • Unidirectional @OneToOne = Only one entity knows about the other (@JoinColumn in the owning side).
  • Bidirectional @OneToOne = Both entities reference each other (mappedBy used on the inverse side).
  • Always place the foreign key on the entity that owns the relationship.
  • Use bidirectional if you need to query both ways.

Top comments (0)