Spring Boot JPA One-to-Many Example Rest

Spring Boot JPA One-to-Many Example Rest

Tutorial: “Spring Boot JPA One-to-Many Example Rest – Spring JPA/Hibernate One-to-Many Association + PostgreSQL | SpringBoot CRUD RestAPIs Post/Get/Put/Delete example”

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


* Technologies:

– Java 8
– Maven
– SpringToolSuite
– SpringBoot 2.0.4.RELEASE

Overview – Spring Boot JPA One-to-Many Example Rest

We have 2 models Student & Assignment with One-to-Many association relationship:

Spring Boot Rest Apis Spring JPA - One-to-one PostgreSQL Association Relationship
Spring Boot Rest Apis Spring JPA – One-to-Oone PostgreSQL Association Relationship

We create a SpringBoot project as below:

JPA Hibernate One To Many Spring Boot Rest APIs Architecture - Spring Boot JPA One-to-Many Example Rest
JPA Hibernate One To Many Spring Boot Rest APIs Architecture
JPA Hibernate One-to-many Spring Boot Rest APIs PostgreSQL Project Structure
JPA Hibernate One-to-many Spring Boot Rest APIs PostgreSQL Project Structure

Hibernate JPA configuration for 2 models Student & Assignment:

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;
	
    @OneToMany(mappedBy = "student", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Set assignments;
	
	public Student() {}
	
	public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	// Getter & Setter methods
	// ...

Assignment model ->


@Entity
@Table(name = "assignments")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
public class Assignment implements Serializable{
	private static final long serialVersionUID = 1L;
	
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	@Column(name = "name")
	private String name;
	
	@Column(name = "grade")
	private int grade;

	@ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "student_id", nullable = false)
    @JsonIgnore
    private Student student;
	
	public Assignment() {}
	
	public Assignment(String name, int grade) {
		this.name = name;
		this.grade = grade;
	}

	// Getter & Setter methods
	// ...

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

– Student ->

  • @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

– Assignments ->

  • @GetMapping("/students/{studentId}/assignments"): get a Assignment by Student’s ID
  • @PostMapping("/students/{studentId}/assignments"): add an Assignment
  • @PutMapping("/students/{studentId}/assignments/{assignmentId}"): update an Assignment
  • @DeleteMapping("/students/{studentId}/assignments/{assignmentId}"): delete an Assignment by ID

Now we’ll create a project from scratch , let’s go!

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>

SpringBoot One-To-Many Models

Student model:


package com.ozenero.springrestapi.onetomany.model;

import java.io.Serializable;
import java.util.Set;

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.OneToMany;
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;
	
    @OneToMany(mappedBy = "student", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Set assignments;
	
	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;
	}
	
	public void setAssignments(Set assignments) {
		this.assignments = assignments;
	}
	
	public Set getAssignments(){
		return this.assignments;
	}
}

Assignment model :


package com.ozenero.springrestapi.onetomany.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.ManyToOne;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@Entity
@Table(name = "assignments")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
public class Assignment implements Serializable{
	private static final long serialVersionUID = 1L;
	
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	@Column(name = "name")
	private String name;
	
	@Column(name = "grade")
	private int grade;

	@ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "student_id", nullable = false)
    @JsonIgnore
    private Student student;
	
	public Assignment() {}
	
	public Assignment(String name, int grade) {
		this.name = name;
		this.grade = grade;
	}
	
	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 setGrade(int grade) {
		this.grade = grade;
	}
	
	public int getGrade() {
		return this.grade;
	}
	
	public void setStudent(Student student) {
		this.student = student;
	}
	
	public Student getStudent() {
		return this.student;
	}
}

SpringBoot JPA Repositories – Spring Boot JPA One-to-Many Example Rest

StudentRepository :


package com.ozenero.springrestapi.onetomany.jpa;

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

import com.ozenero.springrestapi.onetomany.model.Student;

public interface StudentRepository extends JpaRepository {
}

AssignmentRepository :


package com.ozenero.springrestapi.onetomany.jpa;

import java.util.List;

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

import com.ozenero.springrestapi.onetomany.model.Assignment;

public interface AssignmentRepository 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

SpringBoot Expose Rest APIs – Spring Boot JPA One-to-Many Example Rest

Create NotFoundException :


package com.ozenero.springrestapi.onetomany.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "NotFoundException not found")
public class NotFoundException extends RuntimeException {
	private static final long serialVersionUID = 1L;
	
    public NotFoundException(String message) {
        super(message);
    }

    public NotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

StudentController APIs :


package com.ozenero.springrestapi.onetomany.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.ozenero.springrestapi.onetomany.exception.NotFoundException;
import com.ozenero.springrestapi.onetomany.jpa.StudentRepository;
import com.ozenero.springrestapi.onetomany.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));
    }
}

AssignmentController APIs :


package com.ozenero.springrestapi.onetomany.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.ozenero.springrestapi.onetomany.exception.NotFoundException;
import com.ozenero.springrestapi.onetomany.jpa.AssignmentRepository;
import com.ozenero.springrestapi.onetomany.jpa.StudentRepository;
import com.ozenero.springrestapi.onetomany.model.Assignment;

@RestController
@RequestMapping("/api")
public class AssignmentController {
	@Autowired
	private AssignmentRepository assignmentRepository;
	
	@Autowired
	private StudentRepository studentRepository;
	
    @GetMapping("/students/{studentId}/assignments")
    public List getContactByStudentId(@PathVariable Long studentId) {
    	
        if(!studentRepository.existsById(studentId)) {
            throw new NotFoundException("Student not found!");
        }
    	
    	return assignmentRepository.findByStudentId(studentId);
    }
    
    @PostMapping("/students/{studentId}/assignments")
    public Assignment addAssignment(@PathVariable Long studentId,
                            @Valid @RequestBody Assignment assignment) {
        return studentRepository.findById(studentId)
                .map(student -> {
                    assignment.setStudent(student);
                    return assignmentRepository.save(assignment);
                }).orElseThrow(() -> new NotFoundException("Student not found!"));
    }
    
    @PutMapping("/students/{studentId}/assignments/{assignmentId}")
    public Assignment updateAssignment(@PathVariable Long studentId,
    								@PathVariable Long assignmentId,
    								@Valid @RequestBody Assignment assignmentUpdated) {
    	
    	if(!studentRepository.existsById(studentId)) {
    		throw new NotFoundException("Student not found!");
    	}
    	
        return assignmentRepository.findById(assignmentId)
                .map(assignment -> {
                    assignment.setName(assignmentUpdated.getName());
                    assignment.setGrade(assignmentUpdated.getGrade());
                    return assignmentRepository.save(assignment);
                }).orElseThrow(() -> new NotFoundException("Assignment not found!"));
    }
    
    @DeleteMapping("/students/{studentId}/assignments/{assignmentId}")
    public String deleteAssignment(@PathVariable Long studentId,
    							   @PathVariable Long assignmentId) {
    	
    	if(!studentRepository.existsById(studentId)) {
    		throw new NotFoundException("Student not found!");
    	}
    	
        return assignmentRepository.findById(assignmentId)
                .map(assignment -> {
                    assignmentRepository.delete(assignment);
                    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 :

Initial Tables - Spring Boot JPA One-to-Many Example Rest
Initial Tables

– Add Students :

Post a Student - Spring Boot JPA One-to-Many Example Rest
Post a Student

– Add Assignments :

Post a Assignment - Spring Boot JPA One-to-Many Example Rest
Post a Assignment

– Get All Students :

Get All Students
Get All Students

– Get All Assignments of a Student :

Get all Assignment of a student
Get all Assignment of a student

– Update Student :

Update a student
Update a student

– Update Assignment ->

Update a assignment
Update a assignment

– Delete a Student :

Delete a Student
Delete a Student

– Delete an Assignment :

Delete an Assignment
Delete an Assignment

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 | assignments        | table    | postgres
 public | assignments_id_seq | sequence | postgres
 public | students           | table    | postgres
 public | students_id_seq    | sequence | postgres
(4 rows)

\d assignments: Get Schema of a table assignments ->


testdb=# \d assignments;
                                   Table "public.assignments"
   Column   |          Type          |                        Modifiers
------------+------------------------+----------------------------------------------------------
 id         | bigint                 | not null default nextval('assignments_id_seq'::regclass)
 grade      | integer                |
 name       | character varying(255) |
 student_id | bigint                 | not null
Indexes:
    "assignments_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "fkn1mo9c6bajjx15gthydqdnv4b" FOREIGN KEY (student_id) REFERENCES students(id)

Read More

Related posts:


SourceCode

SpringBoot-RestAPI-OneToMany

Leave a Reply

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