SpringBoot + Hibernate Spring JPA One-to-One Association + PostgreSQL | CRUD RestAPIs Post/Get/Put/Delete

springboot-hibernate-spring-jpa-one-to-one-association-postgresql-crud-restapis-post-get-put-delete-feature-image

SpringBoot + Hibernate Spring JPA One-to-One Association + PostgreSQL | CRUD RestAPIs Post/Get/Put/Delete

In the tutorial, we show how to expose Crud RestAPIs Post/Get/Put/Delete to interact with Hibernate Spring JPA One-to-One relationship models using SpringBoot and PostgreSQL database.

Related posts:
How to use Spring JPA with PostgreSQL | Spring Boot
React Redux + Spring Boot + PostgreSQL CRUD example
How to query alter domain models by Spring JPA Projection
How to configure Spring JPA One to One Relationship – SpringBoot
Spring JPA Hibernate One to Many Relationship – SpringBoot + MySQL
Spring JPA Hibernate Many to Many – SpringBoot + PostgreSQL

Related page:

Technologies

  • Java 8
  • Maven 3.5.4
  • SpringToolSuite version 3.9.4.RELEASE
  • SpringBoot 2.0.4.RELEASE

Demo

Overview

We have 2 models Student & Contact with one-to-one relationship:

spring-boot-rest-apis-hibernate-spring-jpa-one-to-one-postgresql-uml-one-to-one-relationships

We create a SpringBoot project as below:

spring-boot-rest-apis-hibernate-spring-jpa-one-to-one-postgresql-architecture

spring-boot-rest-apis-spring-jpa-one-to-one-postgresql-project-structure

Hibernate JPA configuration for 2 models Student & Contact:

Student model ->


@Entity
@Table(name = "students")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
public class Student implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
    
    @Column(name = "name")
	private String name;
    
    @Column(name = "age")
	private int age;
    
    @OneToOne(fetch = FetchType.LAZY,
            cascade =  CascadeType.ALL,
            mappedBy = "student")
    private Contact contact;
	
	public Student() {}
	
	public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	// Getters & Setters methods
	// ...

Contact model ->


@Entity
@Table(name = "contacts")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
public class Contact implements Serializable{
	private static final long serialVersionUID = 1L;

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

    @Column(name = "city")
    private String city;
    
    @Column(name = "phone")
    private String phone;
    
    @OneToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "student_id", nullable = false)
    private Student student;
    
    public Contact() {
    }
    
    public Contact(String city, String phone) {
    	this.city = city;
    	this.phone = phone;
    }
	
	// Getters & Setters methods
	// ...

We exposes RestAPIs for Post/Get/Put/Delete Students & Contacts:

– Students ->

  • @GetMapping("/api/students"): get all Students
  • @GetMapping("/api/students/{id}"): get a Student by ID
  • @PostMapping("/api/students"): post a Student
  • @PutMapping("/api/students/{id}"): update a Student
  • @DeleteMapping("/api/students/{id}"): delete a Student

– Contacts ->

  • @GetMapping("/contacts"): get all Contacts
  • @GetMapping("/students/{studentId}/contacts"): get a Contact by Student’s ID
  • @PostMapping("/students/{studentId}/contacts"): add a Contact
  • @PutMapping("/contacts/{contactId}"): update a Contact
  • @DeleteMapping("/contacts/{contactId}"): delete a Contact by ID

Now we’ll create a project from scratch ->

Practice

Create SpringBoot project

We use SpringToolSuite to create a Java 8 SpringBoot project with below dependencies:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<scope>runtime</scope>
</dependency>

OneToOne Models

Student model ->


package com.grokonez.springrestapi.onetoone.model;

import java.io.Serializable;

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

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@Entity
@Table(name = "students")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
public class Student implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
    
    @Column(name = "name")
	private String name;
    
    @Column(name = "age")
	private int age;
    
    @OneToOne(fetch = FetchType.LAZY,
            cascade =  CascadeType.ALL,
            mappedBy = "student")
    private Contact contact;
	
	public Student() {}
	
	public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	public void setId(Long id) {
		this.id = id;
	}
	
	public Long getId() {
		return this.id;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return this.name;
	}
	
	public void setAge(int age) {
		this.age =age;
	}
	
	public int getAge() {
		return this.age;
	}
}

Contact model ->


package com.grokonez.springrestapi.onetoone.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
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 com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@Entity
@Table(name = "contacts")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
public class Contact implements Serializable{
	private static final long serialVersionUID = 1L;

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

    @Column(name = "city")
    private String city;
    
    @Column(name = "phone")
    private String phone;
    
    @OneToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "student_id", nullable = false)
    private Student student;
    
    public Contact() {
    }
    
    public Contact(String city, String phone) {
    	this.city = city;
    	this.phone = phone;
    }
    
    public void setId(Long id) {
    	this.id = id;
    }
    
    public Long getId() {
    	return this.id;
    }
    
    public void setCity(String city) {
    	this.city = city;
    }
    
    public String getCity() {
    	return this.city;
    }
    
    public void setPhone(String phone) {
    	this.phone = phone;
    }
    
    public String getPhone() {
    	return this.phone;
    }
    
    public void setStudent(Student student) {
    	this.student = student;
    }
    
    public Student getStudent() {
    	return this.student;
    }
}

JPA Repositories

StudentRepository ->


package com.grokonez.springrestapi.onetoone.jpa;

import org.springframework.data.jpa.repository.JpaRepository;

import com.grokonez.springrestapi.onetoone.model.Student;

public interface StudentRepository extends JpaRepository {
}

ContactRepository model ->


package com.grokonez.springrestapi.onetoone.jpa;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.grokonez.springrestapi.onetoone.model.Contact;

public interface ContactRepository extends JpaRepository {
	List findByStudentId(Long studentId);	
}

Add datasource configurations in application.properties file ->


spring.datasource.url=jdbc:postgresql://localhost/testdb
spring.datasource.username=postgres
spring.datasource.password=123
spring.jpa.generate-ddl=true
#spring.jackson.serialization.fail-on-empty-beans=false

Expose Rest APIs

StudentController ->


package com.grokonez.springrestapi.onetoone.rest;

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

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.grokonez.springrestapi.onetoone.exception.NotFoundException;
import com.grokonez.springrestapi.onetoone.jpa.StudentRepository;
import com.grokonez.springrestapi.onetoone.model.Student;

@RestController
@RequestMapping("/api")
public class StudentController {
	
	@Autowired
	private StudentRepository studentRepository;
	
    @GetMapping("/students")
    public List getAllStudents() {
    	return studentRepository.findAll();
    }
    
    @GetMapping("/students/{id}")
    public Student getStudentByID(@PathVariable Long id) {
    	Optional optStudent = studentRepository.findById(id);
    	if(optStudent.isPresent()) {
    		return optStudent.get();
    	}else {
    		throw new NotFoundException("Student not found with id " + id);
    	}
    }
    
    @PostMapping("/students")
    public Student createStudent(@Valid @RequestBody Student student) {
        return studentRepository.save(student);
    }
    
    @PutMapping("/students/{id}")
    public Student updateStudent(@PathVariable Long id,
                                   @Valid @RequestBody Student studentUpdated) {
        return studentRepository.findById(id)
                .map(student -> {
                    student.setName(studentUpdated.getName());
                    student.setAge(studentUpdated.getAge());
                    return studentRepository.save(student);
                }).orElseThrow(() -> new NotFoundException("Student not found with id " + id));
    }
    
    @DeleteMapping("/students/{id}")
    public String deleteStudent(@PathVariable Long id) {
        return studentRepository.findById(id)
                .map(student -> {
                    studentRepository.delete(student);
                    return "Delete Successfully!";
                }).orElseThrow(() -> new NotFoundException("Student not found with id " + id));
    }
}

ContactController ->


package com.grokonez.springrestapi.onetoone.rest;

import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.grokonez.springrestapi.onetoone.exception.NotFoundException;
import com.grokonez.springrestapi.onetoone.jpa.ContactRepository;
import com.grokonez.springrestapi.onetoone.jpa.StudentRepository;
import com.grokonez.springrestapi.onetoone.model.Contact;

@RestController
@RequestMapping("/api")
public class ContactController {
	@Autowired
	private ContactRepository contactRepository;
	
	@Autowired
	private StudentRepository studentRepository;
	
	@GetMapping("/contacts")
	public List getAllContacts(){
		return contactRepository.findAll();
	}
	
    @GetMapping("/students/{studentId}/contacts")
    public Contact getContactByStudentId(@PathVariable Long studentId) {
    	
        if(!studentRepository.existsById(studentId)) {
            throw new NotFoundException("Student not found!");
        }
    	
    	List contacts = contactRepository.findByStudentId(studentId);
    	if(contacts.size() > 0) {
    		return contacts.get(0);
    	}else {
    		throw new NotFoundException("Contact not found!");
    	}
    }
    
    @PostMapping("/students/{studentId}/contacts")
    public Contact addContact(@PathVariable Long studentId,
                            @Valid @RequestBody Contact contact) {
        return studentRepository.findById(studentId)
                .map(student -> {
                    contact.setStudent(student);
                    return contactRepository.save(contact);
                }).orElseThrow(() -> new NotFoundException("Student not found!"));
    }
    
    @PutMapping("/contacts/{contactId}")
    public Contact updateContact(@PathVariable Long contactId,
                               @Valid @RequestBody Contact contactUpdated) {
        return contactRepository.findById(contactId)
                .map(contact -> {
                    contact.setCity(contactUpdated.getCity());
                    contact.setPhone(contactUpdated.getPhone());
                    return contactRepository.save(contact);
                }).orElseThrow(() -> new NotFoundException("Contact not found!"));
    }
    
    @DeleteMapping("/contacts/{contactId}")
    public String deleteContact(@PathVariable Long contactId) {
        return contactRepository.findById(contactId)
                .map(contact -> {
                    contactRepository.delete(contact);
                    return "Deleted Successfully!";
                }).orElseThrow(() -> new NotFoundException("Contact not found!"));
    }
}

Run & Check Results

– Run the SpringBoot project with commandline mvn spring-boot:run.
2 tables is created in PostgreSQL ->

spring-boot-rest-apis-hibernate-spring-jpa-one-to-one-postgresql-tables-created-by-jpa

– Add Students ->

spring-boot-rest-apis-hibernate-spring-jpa-one-to-one-postgresql-tables-post-a-student

– Add Contacts ->

spring-boot-rest-apis-hibernate-spring-jpa-one-to-one-postgresql-post-a-contact

– Update Contact ->

spring-boot-rest-apis-hibernate-spring-jpa-one-to-one-postgresql-put-contact

– Update Student ->

spring-boot-rest-apis-hibernate-spring-jpa-one-to-one-postgresql-put-contact

– Get All Students ->

spring-boot-rest-apis-hibernate-spring-jpa-one-to-one-postgresql-post-get-all-student

– Get All Contacts ->

spring-boot-rest-apis-hibernate-spring-jpa-one-to-one-postgresql-get-all-contacts

– Delete a Student ->

spring-boot-rest-apis-hibernate-spring-jpa-one-to-one-postgresql-tables-delete-a-student

– Delete a Contact ->

spring-boot-rest-apis-hibernate-spring-jpa-one-to-one-postgresql-tables-delete-a-contact

Note: PostgreSQL commandline ->

PostgreSQL\9.6\bin>psql.exe --username="postgres" -W: connect to PostgreSQL ->


C:\Program Files\PostgreSQL\9.6\bin>psql.exe --username="postgres" -W
Password for user postgres:
psql (9.6.9)
WARNING: Console code page (437) differs from Windows code page (1252)
         8-bit characters might not work correctly. See psql reference
         page "Notes for Windows users" for details.
Type "help" for help.

\l: List of databases

\c testdb: connect to “testdb”

\d: List of relations ->


testdb=# \d
               List of relations
 Schema |      Name       |   Type   |  Owner
--------+-----------------+----------+----------
 public | contacts        | table    | postgres
 public | contacts_id_seq | sequence | postgres
 public | students        | table    | postgres
 public | students_id_seq | sequence | postgres
(4 rows)

testdb=# \q: Exit

SourceCode

SpringBootRestAPIsOneToOne



By grokonez | August 21, 2018.

Last updated on April 3, 2021.



Related Posts


2 thoughts on “SpringBoot + Hibernate Spring JPA One-to-One Association + PostgreSQL | CRUD RestAPIs Post/Get/Put/Delete”

  1. When I try to implement my one-to-one mapping it forces me to either add my custom exception to the method signature or surround my return statement with a try-catch. When I use .orElseThrow() with no args that same error isn’t thrown. I don’t understand why I need to catch the exception I want to be thrown.

  2. Hi great tutorial! I would like to post several contacts at the same time on ContactController… ie @PostMapping(“/students/{studentId}/contacts”) with List !! please help me! thank’s!!

Got Something To Say:

Your email address will not be published. Required fields are marked *

*