Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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 databases

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

Thanks for reading!

Donate to the site — we work hard for you.