Mapping a one to one relationship with JPA and Hibernate

Mapping a One to One Relationship With JPA and Hibernate

There are several ways to map a one-to-one relationship with JPA and Hibernate, in this tutorial we will look at some of them.

One to One Relationship

In a one-to-one relationship, a record in a table is associated with a single record in another table, for example an Employee can correspond to only one EmployeeDetails and vice versa.

Configuration

Dependencies

The dependencies for this project are clearly Hibernate and finally the H2 database.

<dependencies>
	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-core</artifactId>
		<version>6.2.4.Final</version>
	</dependency>
	<dependency>
		<groupId>com.h2database</groupId>
		<artifactId>h2</artifactId>
		<version>2.1.214</version>
	</dependency>
</dependencies>

Hibernate

Let’s create the persistence.xml file, which is nothing more than the JPA persistence configuration. In this file we are going to define all the database connection parameters, we are also going to define where our entities are located.

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
	version="3.0">
	<persistence-unit name="default">
        <class>com.lorenzomiscoli.hibernate_one_to_one.model.Employee</class>
        <class>com.lorenzomiscoli.hibernate_one_to_one.model.EmployeeDetails</class>
        <properties>
			<property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver" />
			<property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:hibernate_one_to_one" />	
			<property name="jakarta.persistence.jdbc.user" value="sa" />
			<property name="jakarta.persistence.jdbc.password" value="" />
			<property name="hibernate.hbm2ddl.auto" value="update" />
        </properties>
    </persistence-unit>
</persistence>

Foreign key mapping

Diagram

Let’s look at the ER diagram which is based on a foreign key mapping.

Mapping a one to one relationship with JPA and Hibernate

In this example, the employee_id column of the employee_details table is the foreign key of employee.

Entity

Let’s create our first Employee entity.

@Entity
@Table(name = "employee")
public class Employee {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	@Column(name = "first_name")
	private String firstName;

	@Column(name = "last_name")
	private String lastName;

	@OneToOne(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
	private EmployeeDetails employeeDetails;

	// getters and setters

	public void setDetails(EmployeeDetails employeeDetails) {
		if (employeeDetails == null) {
			if (this.employeeDetails != null) {
				this.employeeDetails.setEmployee(null);
			}
		} else {
			employeeDetails.setEmployee(this);
		}
		this.employeeDetails = employeeDetails;
	}

}

With the @OneToOne annotation we configure a one to one relationship, we have also inserted the mappedBy attribute, in this way we tell JPA that the relationship is already mapped on the other side of the relationship, that is on EmployeeDetails.

Now let’s create the EmployeeDetails entity.

@Entity
@Table(name = "employee_details")
public class EmployeeDetails {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	private String department;

	private String address;

	@OneToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "employee_id")
	private Employee employee;

	// getters and setters
	
}

We inserted the @OneToOne annotation again but this time we inserted the @JoinColumn annotation right after it. With this last annotation we specify which column holds the foreign key.

This type of mapping is not recommended in this case, as there can only be one employee_details associated with an employee it would make more sense for the primary key of employee_details to be the same as employee.

Shared key mapping

Diagram

This ER diagram, unlike the previous one, is based on a shared key. The employee_id column of the employee_details table is both the primary key and the foreign key.

Entity

Now let’s edit the EmployeeDetails entity.

@Entity
@Table(name = "employee_details")
public class EmployeeDetails {

	@Id
	private Integer id;

	private String department;

	private String address;

	@OneToOne(fetch = FetchType.LAZY)
	@MapsId
	private Employee employee;
	
	// getters and setters
	
}

We have added the @MapsId annotation to the EmployeeDetails entity which indicates that the primary key values ​​will be copied from the Employee entity, we have also removed the @JoinColumn annotation.

The mappings we’ve seen so far are bidirectional, which means that while the unidirectional @OneToOne association can be lazy-fetched, the parent side of a bidirectional @OneToOne association is not. Even when we specify the FetchType.LAZY attribute, the parent side association behaves like a FetchType.EAGER relationship. So in this case, if we make one query to retrieve an Employee, JPA will make a second one to also retrieve EmployeeDetails.

Unidirectional mapping with shared key

A great alternative is to make use of the @MapsId annotation only on the child entity and not map the parent entity at all.
This way you don’t need to have a bidirectional association since you can still find the EmployeeDetails entity by using using the Employee entity identifier.

Conclusion

In this article we have seen some ways to map a one to one relationship with JPA and Hibernate, and we have also seen how using a shared key is more convenient and performant in a one to one association.
The source code of this project is available on GitHub.

Lorenzo Miscoli

Software Developer specialized in creating and designing web applications. I have always loved technology and dreamed of working in the IT world, to make full use of my creativity and realize my ideas.
Scroll to Top