We design data base tables relationships by connecting them using foreign key reference columns. Most of the relationship between entities boils down into a entity class having one or many entity(entities) of another type. With different combinations we can have one-to-one, one-to-many, many-to-one and many-to-many relationships.

In the previous article, we learned how to perform RESTful CRUD operation using spring data JPA. In this article, we are going to learn about JPA one-to-one entity mapping.

We will implement JPA one to one by mapping an Employee object and Address object. In real world scenario, an employee may have different types of addresses(Ex: permanent address, current address, etc).

In our example we are assuming that an employee will have only one address mapped to every him/her, which is a one to one JPA mapping scenario.

We will be creating RESTful JPA CRUD application with one to one JPA mapping in this article.

Following are the version details

  • Java version 1.8
  • Spring boot version : 2.1.5-RELEASE
  • IDE: STS Version 4
  • Database : PostgreSQL 9.5 Java version 1.8
  • Lombok : For avoiding boiler plate codes.
  • Postman : For testing REST end points.

Steps to create spring boot JPA one to one mapping

  • Create a spring boot application with required dependencies.
  • Add database related configurations.
  • Create Entity classes, mapping and repository interface and database tables.
  • Create a service layer and a Rest(Representational State Transfer) controller layer.
Create a Spring boot application with required dependencies

Since we are going to create a spring boot RESTful web service application, create a spring boot application with required dependencies. We have to add spring-boot-starter-parent, spring-boot-starter-web, spring-boot-starter-data-jpa dependencies as given in below pom.xml file. These are the core spring boot starter dependencies needed for our application.

We have also added lombok dependency, which is a great open source library, used to reduce boiler plate code. Since we are using PostgreSQL database, we also need to add the dependency to our application.

An optional spring-boot-devtools dependency is also added, which helps in increasing the developers productivity.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.asb.example</groupId>
	<artifactId>spring-boot-jpa-one-to-one</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-jpa-one-to-one</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
		<dependency>
		    <groupId>org.postgresql</groupId>
		    <artifactId>postgresql</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>
Add database related configurations

Add the required database configuration properties to spring boot applications application.properties (under directory : /src/main/resources)file.

First 3 properties are database connection properties having connection url, database username and password details.

spring.jpa.hibernate.naming.physical-strategy : By setting this property value, we are stating that the database table and column values defined should strictly follow hibernate naming strategy conventions. Default spring’s naming convention replaces camel case entity names with underscore(“_”). More information is given here.

application.properties
spring.datasource.url=jdbc:postgresql://localhost/postgres
spring.datasource.username=postgres
spring.datasource.password=asbnotebook

#Enable physcical naming strategy.
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true

Following is the Spring boot application class, which is the entry point of spring boot application.

SpringBootJpaOneToOneApplication.java
package com.asb.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootJpaOneToOneApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootJpaOneToOneApplication.class, args);
	}
}
Create Entity classes, mapping and repository interface and database tables

Create an Entity class with name Employee.java. Add the required fields as given below.

Notice the field address, which is annotated with @OneToOne JPA annotation. This annotation indicates the one to one relation ship between entities. In our example, an employee can have one address associated with him.

@JoinColumn annotation is used on the owning side of the entity. In our example, employeedetails table is having foreign key reference to address table’s Id column, so annotation is used in Employee entity class with foreign key reference column.

Also notice that the cascade property value is set to CascadeType.ALL, which indicates that all entity related operations(MERGE, DELETE, DETACH, REFRESH, REMOVE) are cascaded between related entities.

Employee.java
package com.asb.example.model;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@AllArgsConstructor
@Getter
@Setter
@NoArgsConstructor
@Table(name="employeedetails")
public class Employee {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="Id")
	private Long id;
	
	@Column(name="EmployeeName")
	private String employeeName;
	
	@Column(name="EmployeeCode")
	private String employeeCode;
	
	@Column(name="Designation")
	private String designation;
	
	@OneToOne(cascade=CascadeType.ALL)
	@JoinColumn(name="addressid")
	private Address address;
}

Create another entity class named Address.java as given below. This entity class contains address details related to the Employee entity.

We are using database sequence to generate Id, by using @SequenceGenerator annotation. We will also going to create a sequence with name address_sequence in later steps.

Address.java
package com.asb.example.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import lombok.Getter;
import lombok.Setter;

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

	@Id
	@Column(name="id")
	@SequenceGenerator(initialValue=1, name="address_seq", sequenceName="address_sequence")
	@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="address_seq")
	private int id;
	
	@Column(name="doornumber")
	private String doorNumber;
	
	@Column(name="street")
	private String street;
	
	@Column(name="city")
	private String city;
}

Note that we have implemented unidirectional entity mapping in our example. i.e, only Employee entity is having mapping details. With bidirectional mapping, inverse side(Address Entity) will also have mapping details with mappedBy property as shown below.

@OneToOne(mappedBy="address")  
private List<Employee> empList;  

Here address is the instance variable name created in Employee Entity class. Adding bidirectional mapping gives two way data access from both entity sides.


Create a EmployeeRepository.java interface and extend JpaRepository to inherit the out of the box JPA basic operation support.

EmployeeRepository.java
package com.asb.example.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.asb.example.model.Employee;

public interface EmployeeRepository extends JpaRepository<Employee, Long> {

}

Run the below given SQL scripts on the database. Here, first two SQL scripts are to create database tables employeedetails and address respectively.

We are also creating a database sequence named address_sequence, which is used for id column of address entity in earlier part.

SQL scripts :
CREATE TABLE public.employeedetails
(
  id bigint NOT NULL,
  designation character varying(255),
  employeecode character varying(255),
  employeename character varying(255),
  addressid bigint,
  CONSTRAINT employee_pk PRIMARY KEY (id),
  FOREIGN KEY (addressid) REFERENCES address(id)
)

CREATE TABLE public.address
(
 id bigint NOT NULL PRIMARY KEY,
 doornumber varchar(3),
 street varchar(250),
 city varchar(50)
)

CREATE SEQUENCE address_sequence
START WITH 1
INCREMENT BY 1
Create a service layer and a Rest(Representational State Transfer) controller layer

Create an interface with name EmployeeService.java and add required methods to it as shown below.

EmployeeService.java
package com.asb.example.service;
import java.util.List;
import com.asb.example.model.Employee;

public interface EmployeeService {

	public Employee createEmployee(Employee emp);
	public Employee updateEmployee(Employee emp);
	public Employee getEmployee(Long empId);
	public void deleteEmployee(Long empId);
	public List<Employee> getAllEmployee();
}

Create an implementation class with name EmployeeServiceImpl.java and provide required implementation for the defined methods as given below.

EmployeeServiceImpl.java
package com.asb.example.service;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.asb.example.model.Employee;
import com.asb.example.repository.EmployeeRepository;

@Service
public class EmployeeServiceImpl implements EmployeeService {

	@Autowired
	private EmployeeRepository employeeRepository;

	@Override
	public Employee createEmployee(Employee emp) {
		return employeeRepository.save(emp);
	}

	@Override
	public Employee updateEmployee(Employee emp) {
		return employeeRepository.save(emp);
	}

	@Override
	public Employee getEmployee(Long empId) {
		Optional<Employee> optionalEmp = employeeRepository.findById(empId);
		if (optionalEmp.isPresent()) {
			return optionalEmp.get();
		}
		return null;
	}

	@Override
	public void deleteEmployee(Long empId) {
		employeeRepository.deleteById(empId);
	}

	@Override
	public List<Employee> getAllEmployee() {
		return employeeRepository.findAll();
	}
}

Create a REST controller and add required methods to support CRUD operations. This controller class will have RESTful service end points for create, update, delete, get employee by Id and get all available employee details operations.

EmployeeController.java
package com.asb.example.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.asb.example.model.Employee;
import com.asb.example.service.EmployeeService;

@RestController
public class EmployeeController {

	@Autowired
	private EmployeeService employeeService;

	@PostMapping(consumes = "application/json", produces = "application/json", path = "/employee")
	public ResponseEntity<Employee> createEmployee(@RequestBody Employee emp) {
		return new ResponseEntity<>(employeeService.createEmployee(emp), HttpStatus.CREATED);
	}

	@PutMapping(consumes = "application/json", produces = "application/json", path = "/employee")
	public ResponseEntity<Employee> updateEmployee(@RequestBody Employee emp) {
		return new ResponseEntity<>(employeeService.updateEmployee(emp), HttpStatus.CREATED);
	}

	@DeleteMapping(produces = "application/json", consumes = "text/plain", path = "/employee/{empId}")
	public ResponseEntity<String> deleteEmployee(@PathVariable(value = "empId") Long empId) {
		employeeService.deleteEmployee(empId);
		return new ResponseEntity<>("Employee with EmployeeId : " + empId + " deleted successfully", HttpStatus.OK);
	}

	@GetMapping(path = "/employee/{empId}", produces = "application/json")
	public ResponseEntity<Employee> getEmployee(@PathVariable(value = "empId") Long empId) {
		return new ResponseEntity<>(employeeService.getEmployee(empId), HttpStatus.OK);
	}

	@GetMapping(path = "/employees", produces = "application/json")
	public ResponseEntity<List<Employee>> getAllEmployees() {
		return new ResponseEntity<>(employeeService.getAllEmployee(), HttpStatus.OK);
	}

}

Output

Our spring boot JPA one to one mapping example application is ready now.!!

Run the application and use the Postman tool to test the RESTful service end points as explained below.

Create Employee :

Below image shows creation of employee entity. We are passing address details of employee in the input request to the URL : http://localhost:8080/employee/.

Make sure that required request header values are set in postman header tab(ContentType : application/json and Accepts : application/json).

 to one jpa save example

Below image displays the SQL scripts generated by the JPA internally to persist the entity into database.

one to one jpa save example

Update Employee:

To update employee details, we use same URL (same as create example) with HTTP method PUT.

Notice the Id details, which should be sent on request body. header details remains same, as we are sending and receiving JSON values.

jpa one to one update

Below image shows SQL script generated to update the entity details.

one to one jpa update example

Get Employee:

We can get the employee details by passing Employee Id with URL : http://localhost:8080/employee/{id}. In our example below, employee id is 11.

This is a HTTP GET operation and ContentType header can be ignored as there is no request input content in request body.

one to one jpa get

Get All Employee details :

This end point returns all available employee entities as shown below.

one to one jpa get all

Delete Employee:

To delete employee by Id, we use the URL : http//:localhost:8080/employee/{id} and HTTP request method DELETE as shown below.

one to one jpa delete example

Below image shows SQL delete script generated by the application.

one to one jpa delete example rest

database value:

Following images shows values stored in the PostgreSQL database. We can see both Employee and Address details are persisted in database as expected. well done !!! 🙂 🙂

one to one jpa save
one to one jpa save

Complete Source code is available here.

In this article, we learned about spring boot JPA one to one mapping.

You may also interested in :