JPA One To One Example – Spring Boot

We design database tables relationships by connecting them using foreign key reference columns. Most of the relationship between entities boils down to an entity class having one or many entities of another type. Also, 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 operations using the spring data JPA. In this article, we are going to learn about the JPA one-to-one entity mapping.

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

We will also create a RESTful JPA CRUD application with one-to-one JPA mapping in this article.

version details

  • Java version 1.8
  • Spring boot version : 2.1.5-RELEASE
  • Lombok : For avoiding boiler plate codes.
  • Postman : For testing REST end points.

Table of Contents

Creating the Spring boot application

Since we will 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 the below pom xml file. These are the spring boot starter dependencies needed for our application.

We have also added Lombok dependency, which is an open-source library used to reduce boilerplate code. Since we are using the PostgreSQL database, we also need to add the dependency to our application.

We can also add the spring boot devtools dependency, which helps in increasing the developer’s productivity.

<?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>

Adding the database configurations

Add the required database configuration properties to the application’s configuration file.

The first three properties are database connection properties having connection URL, database username, and password details.

spring.jpa.hibernate.naming.physical-strategy: The property value states 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 an underscore(“_”). More information is available here.

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

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

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);
	}
}

Creating Entity classes and repository interfaces

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

We are using the @OneToOne JPA annotationNotice on the field address. This annotation also indicates the one-to-one relationship between entities. In our example, an employee can have one address associated with him.

We are using the @JoinColumn annotation on the owning side of the entity. In our example, the employeedetails table has a foreign key reference to the address of the table’s Id column, so we are using the annotation in the Employee entity class with a foreign key reference column.

Also, notice that we are setting the cascade property value to ALL. The cascade type cascades all the entity operations(MERGE, DELETE, DETACH, REFRESH, REMOVE) between related entities.

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 as given below. This entity class contains address details related to the Employee entity.

Also, we are using a database sequence to generate Id by using the @SequenceGenerator annotation.

We will also create a database sequence with the name address_sequence in later steps.

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. Only the Employee entity has the mapping details. With the bidirectional mapping, the inverse side(Address Entity) will also have mapping details with the mappedBy property. The example is shown below.

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

Here the value address is the instance variable name created in the Employee Entity class. Also, adding the bidirectional mapping gives us two-way data access from both entity sides.


Create an EmployeeRepository interface and then extend the JpaRepository to inherit the built-in JPA operation support.

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 SQL scripts on the database. Here, two SQL scripts are to create database tables employeedetails and address, respectively.

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

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

Creating the service layer

Create an interface with the name EmployeeService. Also, add the required methods to it, as shown below.

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 the name EmployeeServiceImpl. We will also provide the required implementation for the defined methods as given below.

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();
	}
}

Creating the rest controller layer

Create a REST controller and add the required methods to support CRUD operations. The controller class will have RESTful service endpoints for CRUD operations.

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);
	}
}

Testing the application

Run the application and use the Postman tool to test the RESTful service endpoints, as explained below.

Create Employee

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

Also, make sure to set the required request header values in the postman header tab(ContentType: application/json and Accepts: application/json).

 to one jpa save example

Finally, the below image displays the SQL scripts generated by the JPA internally to persist the entity into the database.

one to one jpa save example

Update Employee

To update employee details, we use the HTTP PUT method.

Also, notice the Id details that we send on the request body. The header details remain the same, as we are sending and receiving JSON values.

jpa one to one

The below image shows the 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, the employee id is 11.

Also, this is an HTTP GET operation and the ContentType header can be ignored as there is no request input content in the request body.

jpa one to one

Get All the Employee details

This endpoint returns all 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.

jpa delete example

The below image shows the SQL delete script generated by the application.

one to one jpa delete example rest

database value

The below images show values stored in the PostgreSQL database. We can see both Employee and Address details are persisted in the database as expected.

one to one jpa save
one to one jpa save

Conclusion

Complete Source code is available here.

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

2 Comments

Comments are closed.