This tutorial will walk you through the steps of mapping a JPA and Hibernate bidirectional One To One foreign key with Spring Boot, Spring Data JPA, Lombok, and MySQL
What you will 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
│ │ │ ├── IDCard.java
│ │ │ └── Person.java
│ │ ├── repository
│ │ │ └── PersonRepository.java
│ │ └── Application.java
│ └── resources
│ └── application.properties
└── pom.xml
The One to One Foreign Key Mapping would be implemented in Person.java and IDCard.java
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
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
One-To-One Foreign Key Relationship
One to one relationship refers to the relationship between two entities/tables A and B in which one item/row of A may be linked with only one item/row of B, and vice versa.
In this example, person and idcard tables have a one-to-one relationship. One person has only one id card, and one id card is only belong to one person.

person.id_card_id is a foreign key references to idcard.id
Define JPA and Hibernate Entities
Create Person and IDCard JPA Entities corresponding to person and idcard tables in the database
[Person.java]
package com.hellokoding.jpa.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Getter
@Setter
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne(cascade = CascadeType.ALL, optional = false)
@JoinColumn(name = "id_card_id")
private IDCard idCard;
public Person(String name, IDCard idCard) {
this.name = name;
this.idCard = idCard;
this.idCard.setPerson(this);
}
}
[IDCard.java]
package com.hellokoding.jpa.model;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.UUID;
@Getter @Setter
@Entity
public class IDCard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(unique = true, nullable = false)
private String code = UUID.randomUUID().toString();
@OneToOne(mappedBy = "idCard")
private Person person;
}
@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
mappedBy 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.
[PersonRepository.java]
package com.hellokoding.jpa.repository;
import com.hellokoding.jpa.model.Person;
import org.springframework.data.jpa.repository.JpaRepository;
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 application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=hellokoding
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create
spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
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
spring.jpa.show-sql=true 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
[Application.java]
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;
@SpringBootApplication
public class Application implements CommandLineRunner {
@Autowired
private PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
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()));
personRepository.saveAll(persons);
}
}
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 | |
| id_card_id | int(11) | NO | UNI | NULL | |
+------------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> describe idcard;
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| code | varchar(255) | NO | UNI | 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;
+----+--------------------------------------+
| id | code |
+----+--------------------------------------+
| 2 | 36d0e8f6-8367-41de-b53d-0c1e28213e72 |
| 3 | 5657b19d-9a78-4c02-bb39-c9b54881e733 |
| 1 | e8a8f44c-a0af-49f2-9d2c-9363732d1fcf |
+----+--------------------------------------+
3 rows in set (0.00 sec)
Conclusion
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