This tutorial will walk you through the steps of mapping a JPA and Hibernate bidirectional One To One shared primary key with @MapsId in Spring Boot, Spring Data JPA, Lombok, and MySQL

What you need

  • JDK 8+ or OpenJDK 8+

  • Maven 3+

  • MySQL Server 5+

  • Your favorite IDE

Init project structure

You can create and init a new Spring Boot project by using Spring Initializr or your IDE

Following is the final project structure with all the files we would create

├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── hellokoding
│       │           └── jpa
│       │               ├── model
│       │               │   ├──
│       │               │   └──
│       │               ├── repository
│       │               │   └──
│       │               └──
│       └── resources
│           └──
└── pom.xml

The One to One Shared Primary Key Mapping would be implemented in and

Project dependencies

We will use the following dependencies

  • spring-boot-starter-data-jpa provides Hibernate and autoconfigure Spring DataSource

  • mysql-connector-java provides MySQL Java Client

  • lombok for generating boilerplate-code


One-To-One Shared Primary Key Relationship

One to one shared primary key relationship refers to the relationship between two tables A and B in which

  • One row of A may be linked with only one row of B, and vice versa

  • Both tables A and B use the same primary key

In this example, person and idcard tables have a one-to-one shared primary key relationship. One person has only one id card, and one id card only belongs to one person

idcard.persion_id is a primary key and also a foreign key references to

Define JPA and Hibernate Entities

Create Person and IDCard JPA Entities corresponding to person and idcard tables in the database


package com.hellokoding.jpa.model;

import lombok.Getter;  
import lombok.Setter;

import javax.persistence.*;

public class Person {  
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "person")
    private IDCard idCard;

    public Person(String name, IDCard idCard) { = name;
        this.idCard = idCard;


package com.hellokoding.jpa.model;

import lombok.Data;  
import lombok.Getter;  
import lombok.Setter;

import javax.persistence.*;  
import java.util.UUID;

@Getter @Setter
public class IDCard {  
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @OneToOne(cascade = CascadeType.ALL, optional = false)
    @JoinColumn(name = "person_id")
    private Person person;

    @Column(unique = true, nullable = false)
    private String code = UUID.randomUUID().toString();

@Entity annotation is required to specify a JPA and Hibernate entity

@Id annotation is required to specify the identifier property of the entity

@OneToOne defines a one-to-one relationship between 2 entities

@JoinColumn defines a foreign key column

@MapsId maps to the parent entity primary key

mappedBy value points to the relationship owner

unique = true enforces the unique constraint

Spring Data JPA Repository

Spring Data JPA contains some built-in Repository abstracting common functions based on EntityManager to work with database such as findAll, findById, save, delete, deleteById. All we need for this example is extends JpaRepository.


package com.hellokoding.jpa.repository;

import com.hellokoding.jpa.model.Person;  

public interface PersonRepository extends JpaRepository<Person, Integer>{  

Application Properties

Configure the Spring Datasource JDBC URL, user name, and password of your local MySQL server in



Create the test database in your local MySQL server if not exists

We don't have to create table schemas, the ddl-auto=create config allows JPA and
Hibernate does that based on the entity-relationship mappings. In practice, consider to use ddl-auto=none (default) and use a migration tool such as Flyway for better database management for showing generated SQL queries in the application logs, consider to disable it on production environment

Creating data with JPA and Hibernate

Thanks to CascadeType.ALL, associated entity IDCard will be saved at the same time with Person without the need of calling its save function explicitly


package com.hellokoding.jpa;

import com.hellokoding.jpa.model.IDCard;  
import com.hellokoding.jpa.model.Person;  
import com.hellokoding.jpa.repository.PersonRepository;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.CommandLineRunner;  
import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.ArrayList;  
import java.util.List;

public class Application implements CommandLineRunner {  
    private PersonRepository personRepository;

    public static void main(String[] args) {, args);

    public void run(String... strings) throws Exception {
        // save idCard along with persons
        List<Person> persons = new ArrayList<>();
        persons.add(new Person("Tom", new IDCard()));
        persons.add(new Person("Daisy", new IDCard()));
        persons.add(new Person("Alex", new IDCard()));

Run and test

Type the below command at the project root directory

mvn clean spring-boot:run

Access to your local MySQL Server to query the schema and data created by JPA/Hibernate based on your mapping

mysql> describe person;  
| Field | Type         | Null | Key | Default | Extra          |
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(255) | YES  |     | NULL    |                |
2 rows in set (0.01 sec)

mysql> describe idcard;  
| Field     | Type         | Null | Key | Default | Extra |
| code      | varchar(255) | NO   | UNI | NULL    |       |
| person_id | int(11)      | NO   | PRI | NULL    |       |
2 rows in set (0.00 sec)

mysql> select * from person;  
| id | name  |
|  1 | Tom   |
|  2 | Daisy |
|  3 | Alex  |
3 rows in set (0.00 sec)

mysql> select * from idcard;  
| code                                 | person_id |
| 50ed2744-1ee9-4242-bccf-7a0a92af4ce3 |         1 |
| 244aa0bb-4188-4452-a74a-5d8e9e620c98 |         2 |
| 39c227da-520f-4c97-bc2d-1e6ebb62916f |         3 |
3 rows in set (0.00 sec)


In this tutorial, we learned to map a JPA and Hibernate bidirectional One to One foreign key in Spring Boot and MySQL. You can find the source code on Github

See also