One-to-one
Some data belongs to exactly one other object: each author has one profile with a biography, and each profile belongs to one author. That is a one-to-one relationship. In the database it is again a foreign key, but a unique one: two profiles cannot point to the same author.
The owning side
The foreign key is placed in the table that "depends" on the other — here in the profile:
// src/main/java/com/example/blog/Profile.java
package com.example.blog;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
@Entity
public class Profile {
@Id
@GeneratedValue
private Long id;
private String bio;
@OneToOne
@JoinColumn(name = "author_id", nullable = false)
private Author author;
protected Profile() {
}
public Profile(Author author, String bio) {
this.author = author;
this.bio = bio;
author.setProfile(this);
}
public String getBio() {
return bio;
}
public Author getAuthor() {
return author;
}
}The other side
// src/main/java/com/example/blog/Author.java
@Entity
public class Author {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToOne(mappedBy = "author")
private Profile profile;
// constructors, getName(), getProfile()
void setProfile(Profile profile) {
this.profile = profile;
}
}As with @OneToMany, mappedBy points to the field of the owning side. The Profile constructor fills both sides at once.
Using it
// src/main/java/com/example/blog/Main.java
emf.runInTransaction(em -> {
Author anna = new Author("Anna");
Profile profile = new Profile(anna, "Writes about Java and databases");
em.persist(anna);
em.persist(profile);
});
emf.runInTransaction(em -> {
Author author = em.find(Author.class, 1L);
IO.println(author.getName() + ": " + author.getProfile().getBio());
});Hibernate: create table Author (id bigint not null, name varchar(255), primary key (id))
Hibernate: create table Profile (author_id bigint not null unique, id bigint not null, bio varchar(255), primary key (id))
Hibernate: alter table if exists Profile add constraint FK4tbt6ixibsme3rpfs7n6iagoa foreign key (author_id) references Author
Hibernate: insert into Author (name,id) values (?,?)
Hibernate: insert into Profile (author_id,bio,id) values (?,?,?)
Hibernate: select a1_0.id,a1_0.name,p1_0.id,p1_0.author_id,p1_0.bio from Author a1_0 left join Profile p1_0 on a1_0.id=p1_0.author_id where a1_0.id=?
Anna: Writes about Java and databasesHibernate added unique to author_id itself. When loading an author it joins the profile in the same query, because it must know whether the profile exists.
A shared primary key
A profile can use the author's id as its own id. Then the extra column disappears:
// src/main/java/com/example/blog/Profile.java
@Id
private Long id;
@OneToOne
@MapsId
private Author author;Hibernate: create table Profile (author_id bigint not null, bio varchar(255), primary key (author_id))
Hibernate: insert into Profile (bio,author_id) values (?,?)@MapsId means "take the id from the author relationship". The profile with id 1 always belongs to the author with id 1, and @GeneratedValue is not needed. The rest of the code stays the same.
Common mistake
The unique key forbids a second profile for the same author. An attempt to save one fails at commit:
Exception in thread "main" jakarta.persistence.RollbackException: Error while committing the transaction [could not execute statement [Unique index or primary key violation: "PUBLIC.CONSTRAINT_1 INDEX PUBLIC.CONSTRAINT_INDEX_1 ON PUBLIC.PROFILE(AUTHOR_ID NULLS FIRST) VALUES ( /* 1 */ CAST(1 AS BIGINT) )"; ...To replace a profile, change the fields of the existing one instead of creating a new object.