hibernate-008: Unidirectional vs Bidirectional @OneToOne Relationship
Hunor Vadasz-Perhat

Hunor Vadasz-Perhat @hunor85

About: Software Engineer II @DXC Technology

Location:
Denmark
Joined:
Jul 3, 2023

hibernate-008: Unidirectional vs Bidirectional @OneToOne Relationship

Publish Date: Feb 10
0 0

🚀 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.

Comments 0 total

    Add comment