Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

NPI – JPA Buddy – JPA (partner)
announcement - icon

JPA is huge! It covers nearly every aspect of communication between relational databases and the Java application and is deeply integrated into all major frameworks.

If you're using IntelliJ, JPA Buddy is super helpful. The plugin gently guides you through the subtleties of the most popular JPA implementations, visually reminds you of JPA features, generates code that follows best practices, and integrates intelligent inspections to improve your existing persistence code.

More concretely, it provides powerful tooling to generate Spring Data JPA repositories and methods, Flyway Versioned Migrations, Liquibase Differential Changelogs, DDL and SQL statements, DTO objects, and MapStruct interfaces.

Oh, and it actually generates JPA entities from an existing database and gradually update the data model as the database evolves! Yeah.

>> Become a lot more productive with JPA Buddy

November Discount Launch 2022 – Top
We’re finally running a Black Friday launch. All Courses are 30% off until next Friday:

>> GET ACCESS NOW

November Discount Launch 2022 – TEMP TOP (NPI)
We’re finally running a Black Friday launch. All Courses are 30% off until next Friday:

>> GET ACCESS NOW

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

1. Overview

In this tutorial, we'll have a look at different ways of creating one-to-one mappings in JPA.

We'll need a basic understanding of the Hibernate framework, so please check out our guide to Hibernate 5 with Spring for extra background.

Further reading:

Overview of JPA/Hibernate Cascade Types

A quick and practical overview of JPA/Hibernate Cascade Types.

Hibernate One to Many Annotation Tutorial

In this tutorial we'll have a look at the one-to-many mapping using JPA annotations with a practical example.

2. Description

Let's suppose we are building a user management system, and our boss asks us to store a mailing address for each user. A user will have one mailing address, and a mailing address will have only one user tied to it.

This is an example of a one-to-one relationship, in this case between user and address entities.

Let's see how we can implement this in the next sections.

3. Using a Foreign Key

3.1. Modeling With a Foreign Key

Let's have a look at the following ER diagram, which represents a foreign key-based one-to-one mapping:

An ER Diagram mapping Users to Addresses via an address_id foreign key

In this example, the address_id column in users is the foreign key to address.

3.2. Implementing With a Foreign Key in JPA

First, let's create the User class and annotate it appropriately:

@Entity
@Table(name = "users")
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;
    //... 

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;

    // ... getters and setters
}

Note that we place the @OneToOne annotation on the related entity field, Address.

Also, we need to place the @JoinColumn annotation to configure the name of the column in the users table that maps to the primary key in the address table. If we don't provide a name, Hibernate will follow some rules to select a default one.

Finally, note in the next entity that we won't use the @JoinColumn annotation there. This is because we only need it on the owning side of the foreign key relationship. Simply put, whoever owns the foreign key column gets the @JoinColumn annotation.

The Address entity turns out a bit simpler:

@Entity
@Table(name = "address")
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;
    //...

    @OneToOne(mappedBy = "address")
    private User user;

    //... getters and setters
}

We also need to place the @OneToOne annotation here too. That's because this is a bidirectional relationship. The address side of the relationship is called the non-owning side. 

4. Using a Shared Primary Key

4.1. Modeling With a Shared Primary Key

In this strategy, instead of creating a new column address_id, we'll mark the primary key column (user_id) of the address table as the foreign key to the users table:

An ER diagram with Users Tied to Addresses where they share the same primary key values

We've optimized the storage space by utilizing the fact that these entities have a one-to-one relationship between them.

4.2. Implementing With a Shared Primary Key in JPA

Notice that our definitions change only slightly:

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    //...

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private Address address;

    //... getters and setters
}
@Entity
@Table(name = "address")
public class Address {

    @Id
    @Column(name = "user_id")
    private Long id;

    //...

    @OneToOne
    @MapsId
    @JoinColumn(name = "user_id")
    private User user;
   
    //... getters and setters
}

The mappedBy attribute is now moved to the User class since the foreign key is now present in the address table. We've also added the @PrimaryKeyJoinColumn annotation, which indicates that the primary key of the User entity is used as the foreign key value for the associated Address entity.

We still have to define an @Id field in the Address class. But note that this references the user_id column, and it no longer uses the @GeneratedValue annotation. Also, on the field that references the User, we've added the @MapsId annotation, which indicates that the primary key values will be copied from the User entity.

5. Using a Join Table

One-to-one mappings can be of two types: optional and mandatory. So far, we've seen only mandatory relationships.

Now let's imagine that our employees get associated with a workstation. It's one-to-one, but sometimes an employee might not have a workstation and vice versa.

5.1. Modeling With a Join Table

The strategies that we have discussed until now force us to put null values in the column to handle optional relationships.

Typically, we think of many-to-many relationships when we consider a join table, but using a join table in this case can help us to eliminate these null values:

An ER diagram relating Employees to Workstations via a Join Table

Now, whenever we have a relationship, we'll make an entry in the emp_workstation table and avoid nulls altogether.

5.2. Implementing With a Join Table in JPA

Our first example used @JoinColumn. This time, we'll use @JoinTable:

@Entity
@Table(name = "employee")
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    //...

    @OneToOne(cascade = CascadeType.ALL)
    @JoinTable(name = "emp_workstation", 
      joinColumns = 
        { @JoinColumn(name = "employee_id", referencedColumnName = "id") },
      inverseJoinColumns = 
        { @JoinColumn(name = "workstation_id", referencedColumnName = "id") })
    private WorkStation workStation;

    //... getters and setters
}
@Entity
@Table(name = "workstation")
public class WorkStation {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    //...

    @OneToOne(mappedBy = "workStation")
    private Employee employee;

    //... getters and setters
}

@JoinTable instructs Hibernate to employ the join table strategy while maintaining the relationship.

Also, Employee is the owner of this relationship, as we chose to use the join table annotation on it.

6. Conclusion

In this article, we learned different ways of maintaining a one-to-one association in JPA and Hibernate, and when to use each.

The source code for this article can be found over on GitHub.

November Discount Launch 2022 – Bottom
We’re finally running a Black Friday launch. All Courses are 30% off until next Friday:

>> GET ACCESS NOW

Persistence footer banner
Comments are closed on this article!