When modeling One-to-One relationships, you can approach it in several ways depending on the ownership, lifecycle, and data modeling constraints. While Java and Hibernate offer flexibility in how this relationship is implemented at the code level, the root of it lies in database design. So let’s start there.
One-to-One Relationship Strategies in Database Engineering
In relational databases, a One-to-One relationship means each row in Table A corresponds to exactly one row in Table B, and vice versa. But there are multiple ways to implement this, depending on the direction and ownership of the relationship.
1. Foreign Key in Parent Table (Shared Primary Key strategy – reversed)
- How it works: The parent table (e.g.,
Person
) holds a foreign key to the child table (Passport.id
). - Pros:
- Easy to query from parent.
- Clear ownership.
- Cons:
- Implies that the parent cannot exist without the child — or needs
NULL
values. - Foreign key can be
NULL
if optional.
- Implies that the parent cannot exist without the child — or needs
CREATE TABLE person (
id BIGINT PRIMARY KEY,
name VARCHAR(100),
passport_id BIGINT UNIQUE,
FOREIGN KEY (passport_id) REFERENCES passport(id)
);
2. Child Table Uses Parent’s Primary Key (Shared Primary Key strategy)
- How it works: The child table (e.g.,
Passport
) uses the same ID as the parent (Person
) — usually both primary key and foreign key. - Pros:
- Enforces strict 1:1 relationship.
- Cleaner data integrity —
Passport
cannot exist withoutPerson
.
- Cons:
- Less flexible if the child becomes optional.
- Harder to manage outside ORMs.
CREATE TABLE person (
id BIGINT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE passport (
id BIGINT PRIMARY KEY,
number VARCHAR(50),
FOREIGN KEY (id) REFERENCES person(id)
);
3. Join Table Strategy (Less common for One-to-One)
- How it works: A third table (e.g.,
person_passport
) holdsperson_id
andpassport_id
with a unique constraint. - Pros:
- Very flexible and decoupled.
- If the relation has a tendency to become 1xN or Nx1 or NxN then it’s a good choice because it will make the transition easier.
- Cons:
- Overkill unless mapping polymorphic or cross-entity relations.
- High complexity for something that can be simple.
Entity Mapping in Hibernate & Java
Now let’s translate this to Java and Hibernate.
Shared Primary Key Strategy (Child uses parent’s ID)
@Entity
public class Person {
@Id
private Long id;
@OneToOne(mappedBy = "person", cascade = CascadeType.ALL)
private Passport passport;
}
@Entity
public class Passport {
@Id
private Long id;
@OneToOne
@MapsId
@JoinColumn(name = "id")
private Person person;
private String number;
}
@MapsId
indicates thatPassport
shares the same primary key asPerson
.- Very tight coupling — used when lifecycle is tightly bound.
Foreign Key in Parent Table
@Entity
public class Person {
@Id
private Long id;
@OneToOne
@JoinColumn(name = "passport_id")
private Passport passport;
}
@Entity
public class Passport {
@Id
private Long id;
private String number;
}
- More flexible:
Person
can exist withoutPassport
. - You control fetch/lazy, cascade, optionality more easily.
- More flexible:
Person
can exist withoutPassport
. - You control fetch/lazy, cascade, optionality more easily.
What about property with JoinTables?
Sometimes, your model may evolve and you’d rather keep the base entity clean and extend when needed. For example, instead of putting a Passport
inside Person
, you could have:
@Entity
public class Passport {
@OneToOne
@JoinTable(
name = "person_passport",
joinColumns = @JoinColumn(name = "person_id"),
inverseJoinColumns = @JoinColumn(name = "passport_id")
)
private Passport passport;
}
This approach uses:
- Join Table in JPA.
@JoinTable
allows modeling more flexible or optional relationships.- Useful when only some persons have passport.
- A smart choice when there’s a possibility that the relationship’s cardinality may change.
It’s especially powerful when you want to modularize behavior and avoid cluttering the base Person
class but makes the entity mapping very complex.
What About Inheritance with Joined Strategy?
Sometimes you just want a easier way to navigate the entity properties and treat the two tables as a single entity. For example, instead of putting a Passport
inside Person
, you could extend it:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Person {
@Column
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
@Entity
public class Passport extends Person {
@Column
private String number;
}
Summary
- InheritanceType.JOINED allows you to split the data into separate tables for each class in the inheritance hierarchy.
- The child class has its own table and its joined with the parent class, so the child entity has all properties from the parent as its own.
- Hibernate will automatically handle the joins when querying entities from the inheritance hierarchy.
This strategy is useful when you want to follow database normalization principles but be mindful of the performance overhead due to the necessary joins when querying data.
Final Thoughts: Choosing the Right Strategy
Your choice of strategy in the database layer directly impacts your entity mapping in Java:
- Use Shared Primary Keys when the two entities are tightly bound and always created/removed together.
- Use a Foreign Key in Parent when the child is optional or loosely coupled.
- Use Join Tables or Inheritance when dealing with complex relationships, partial behaviors, or domain subtypes.
Choosing the right one-to-one mapping isn’t just about the ORM — it’s about data consistency, flexibility, and how your domain actually behaves.