
05 Aug Building a REST Service using Spring Boot and H2
Spring Boot is an open-source Java-based framework used to create a microservice.
It is used to build stand-alone and production-ready spring applications. Also, it offers benefits such as auto-configuration and dependency management.
H2 is an open-source lightweight Java database. It can be embedded in Java applications.
H2 is an in-memory database, that is, it never persists data on the disk. Instead, the data gets cleared once the database is refreshed. Thus, it is never used in production, mostly in development and test environments. Some of H2’s features are that it:
- is an extremely fast database engine
- has strong security features
- supports standard SQL and JDBC API
Getting Started
In this tutorial, I would be building a simple rest service for a school. It involves creating a student record, assigning a student to a class, assigning a student to a course, getting the lists of all students record and getting all the students taking a course.
The technologies used include Maven, Java 1.8, Spring boot 2.3.2, IntelliJ, H2, and JPA.
Setting up the project
There are two ways to setup a spring boot application. Using:
For this tutorial, I would be using spring initialzr.
Spring Initialzr
This is a more convenient way to generate a spring boot project, all the dependencies needed can be easily setup without any tools. To begin:
- Open Spring initialzr
- Select the project type, in this case, it will be maven
- Select the language; we are using Java
- Select the project metadata (group, artifact, name, description, package name). The meaning of each metadata can be found in this reference guide.
- Select dependencies. This project uses Spring Web, H2 Database and Spring Data JPA
- Generate project.

Importing the Project
After downloading the project, the next step is to extract the zip folder and import into the IDE. The pom.xml file contains the information about the project and configuration details (the ones selected above) used by Maven to run the project.

Project Structure
The project is split into six sections: Model, DTO, Repository, Service, Controller and Error.
1. Model
The model contains the data application. It contains the different entity classes that represent the database tables.
Defining the Entities
The first entity is the Student entity which represents the student data, it has four attributes to keep it simple. It also has a many-to-one relationship with two entity classes; the student class entity and the course entity.
The @Entity annotation specifies that the class is an entity and is mapped to a database table. The @Table annotation specifies the table name.
@Entity
@Table(name="student")
public class Student {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String age;
@JsonIgnore
@ManyToOne
@JoinColumn(name = "studentclassid")
private StudentClass studentClass;
@JsonIgnore
@ManyToOne
@JoinColumn(name = "course")
private Course course;
}
The second entity is the Student Class entity which represents the Student Class data. It has four attributes and a One-to-Many relationship with the Student Entity.
@Entity @Table(name="student_class") public class StudentClass { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) private Long id; private String className; private String noOfStudents; private String classLevel; @OneToMany(mappedBy="studentClass") @JsonManagedReference private List<Student> students; }
The Third Entity is the Course Entity. It has two attributes and a One-to-Many relationship with the Student Entity
@Entity @Table(name="course") public class Course { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) private Long id; private String courseName; @OneToMany(mappedBy="course") @JsonManagedReference private List<Student> students; }
2. DTO
The Data Transfer Object (DTO) is like a data structure. It is an object that carries data between processes, the means of data transport between systems.
The DTO’s are split into Request and Response DTO’s.
StudentRequestDTO
public class StudentRequestDTO { private String firstName; private String lastName; private String age; }
StudentResponseDTO
public class StudentResponseDTO { private Long id; private String firstName; private String lastName; private String age; private Long studentClassId; private String studentClassName; private Long courseId; private String courseName; }
CourseRequestDTO
The DTO for creating a new course
public class CourseRequestDTO { private String courseName; }
StudentClassRequestDTO
The DTO for creating a student class
public class StudentClassRequestDTO { private String className; private String noOfStudents; private String classLevel; }
AddStudentCourseDTO
The DTO for assigning a Student to a Course
public class AddStudentCourseDTO { private Long courseId; }
AddStudentClassDTO
The DTO for assign a Student to a Class
public class AddStudentClassDTO { private Long studentClassId; }
3. Repository
In Spring, a repository is a mechanism for encapsulating storage, retrieval and search behaviour which emulates a collection of objects.
Student Repository
import com.education.education.model.Student;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
import java.util.Optional;
public interface StudentRepository extends CrudRepository<Student, Long> {
Optional<Student> findById(Long studentId);
List<Student> findBycourse(Long studentCourseId);
}
Course Repository
public interface CourseRepository extends CrudRepository<Course, Long> {
Optional<Course> findById(Long courseId);
}
Student Class Repository
public interface StudentClassRepository extends CrudRepository<StudentClass, Long> {
Optional<StudentClass> findById(Long studentClassId);
}
4. Service
The Service implementation is where all of the implementation is done and interacts with the the repository. The Service exposes methods that will be called from the controller. The @Service annotation is used to mark the class as a service provider.
Student Service Implementation
@Service public class StudentServiceImpl implements StudentService { @Autowired private StudentRepository studentRepository; @Autowired private StudentClassRepository studentClassRepository; @Autowired private CourseRepository courseRepository; @Override public Student createStudent(StudentRequestDTO studentRequestDTO) { Student student = new Student(); student.setAge(studentRequestDTO.getAge()); student.setFirstName(studentRequestDTO.getFirstName()); student.setLastName(studentRequestDTO.getLastName()); return studentRepository.save(student); } @Override public Student addCourse(AddStudentCourseDTO addStudentCourseDTO, Long studentId) { Optional<Student> student = studentRepository.findById(studentId); Student student1 = student.get(); if(student1 ==null){ throw new ResourceNotFoundException("No student record found"); } if(addStudentCourseDTO.getCourseId() !=null){ Optional<Course> course = courseRepository.findById (addStudentCourseDTO.getCourseId()); Course course1 = course.get(); student1.setCourse(course1); } return studentRepository.save(student1); } @Override public Student addStudentClass(AddStudentClassDTO addStudentClassDTO, Long studentId) { Optional<Student> student = studentRepository.findById(studentId); Student student1 = student.get(); if(student1 ==null){ throw new ResourceNotFoundException("No student record found"); } if(addStudentClassDTO.getStudentClassId() !=null){ Optional<StudentClass> studentClass = studentClassRepository.findById (addStudentClassDTO.getStudentClassId()); StudentClass studentClass1 = studentClass.get(); student1.setStudentClass(studentClass1); } return studentRepository.save(student1); } @Override public ResponseEntity<List<StudentResponseDTO>> fetchStudents(Pageable pageable) { List<StudentResponseDTO> studentList = new ArrayList<>(); studentRepository.findAll().forEach(student -> { Course course = student.getCourse(); Long courseId = course.getId(); String courseName = course.getCourseName(); StudentClass studentClass = student.getStudentClass(); Long studentClassId = studentClass.getId(); String studentClassname = studentClass.getClassName(); studentList.add(new StudentResponseDTO(student.getId(), student.getFirstName(), student.getLastName(), student.getAge(), studentClassId, studentClassname, courseId, courseName)); }); return new ResponseEntity<>(studentList, HttpStatus.OK); } @Override public ResponseEntity<List<StudentResponseDTO>> fetchStudentsByCourse (Pageable pageable, Long studentCourseId) { List<StudentResponseDTO> studentList = new ArrayList<>(); studentRepository.findBycourse(studentCourseId).forEach(student -> { Course course = student.getCourse(); Long courseId = course.getId(); String courseName = course.getCourseName(); StudentClass studentClass = student.getStudentClass(); Long studentClassId = studentClass.getId(); String studentClassname = studentClass.getClassName(); studentList.add(new StudentResponseDTO(student.getId(), student.getFirstName(), student.getLastName(), student.getAge(), studentClassId, studentClassname, courseId, courseName)); }); return new ResponseEntity<>(studentList, HttpStatus.OK); }
Student Service
public interface StudentService { Student createStudent(StudentRequestDTO studentRequestDTO); Student addCourse(AddStudentCourseDTO addStudentCourseDTO, Long studentId); Student addStudentClass(AddStudentClassDTO addStudentClassDTO, Long studentId); ResponseEntity<List<StudentResponseDTO>> fetchStudents(Pageable pageable); ResponseEntity<List<StudentResponseDTO>> fetchStudentsByCourse (Pageable pageable, Long studentCourseId); }
Course Service Implementation
@Service
public class CourseServiceImpl implements CourseService {
@Autowired
private CourseRepository courseRepository;
@Autowired
private StudentRepository studentRepository;
@Override
public Course createCourse(CourseRequestDTO courseRequestDTO) {
Course course = new Course();
course.setCourseName(courseRequestDTO.getCourseName());
course.setStudents(null);
return courseRepository.save(course);
}
}
Course Service
public interface CourseService {
Course createCourse(CourseRequestDTO courseRequestDTO);
}
Student Class Service Implementation
@Service public class StudentClassServiceImpl implements StudentClassService { @Autowired private StudentRepository studentRepository; @Autowired private StudentClassRepository studentClassRepository; @Override public StudentClass createStudentClass(StudentClassRequestDTO studentClassRequestDTO) { StudentClass studentClass = new StudentClass(); studentClass.setClassLevel(studentClassRequestDTO.getClassLevel()); studentClass.setClassName(studentClassRequestDTO.getClassName()); studentClass.setNoOfStudents(studentClassRequestDTO.getNoOfStudents()); return studentClassRepository.save(studentClass); } }
Student Class Service
public interface StudentClassService {
StudentClass createStudentClass(StudentClassRequestDTO studentClassRequestDTO);
}
5. Controllers
Different methods are defined in the controller, these methods will be called by different endpoints. The @RestController
annotation is a convenience annotation for creating restful controllers.
Student Controller
@RestController @RequestMapping("/v1") public class StudentController { @Autowired private StudentService studentService; @RequestMapping(path = "/createStudent", method = RequestMethod.POST) public Student createStudent(@RequestBody StudentRequestDTO studentRequestDTO) { Student createStudent = studentService.createStudent(studentRequestDTO); return createStudent; } @RequestMapping(path = "/addStudentCourse/{studentid}", method = RequestMethod.PUT) public Student addCourse(@RequestBody AddStudentCourseDTO addStudentCourseDTO, @PathVariable Long studentid ) { Student addCourse = studentService.addCourse(addStudentCourseDTO, studentid); return addCourse; } @RequestMapping(path = "/addStudentClass/{studentid}" , method = RequestMethod.PUT) public Student addClass(@RequestBody AddStudentClassDTO addStudentClassDTO, @PathVariable Long studentid ) { Student addClass = studentService.addStudentClass(addStudentClassDTO, studentid); return addClass; } @RequestMapping(value = "/students", method = RequestMethod.GET) public ResponseEntity<List<StudentResponseDTO>>getStudents (@PageableDefault(value = 20) Pageable pageable) { return studentService.fetchStudents(pageable); } @RequestMapping(value = "/students/{studentcourseid}", method = RequestMethod.GET) public ResponseEntity<List<StudentResponseDTO>>getStudentsByCourse (@PathVariable(value = "studentcourseid") Long studentCourseId, @PageableDefault(value = 20) Pageable pageable) { return studentService.fetchStudentsByCourse(pageable,studentCourseId); } }
Student Class Controller
@RestController @RequestMapping("/v1") public class StudentClassController { @Autowired private StudentClassService studentClassService; @RequestMapping(path = "/createStudentClass", method = RequestMethod.POST) public StudentClass createStudentClass (@RequestBody StudentClassRequestDTO studentClassRequestDTO) { StudentClass createStudentClass = studentClassService. createStudentClass(studentClassRequestDTO); return createStudentClass; } }
Course Controller
@RestController @RequestMapping("/v1") public class CourseController { @Autowired private CourseService courseService; @RequestMapping(path = "/createCourse", method = RequestMethod.POST) public Course createCourse(@RequestBody CourseRequestDTO courseRequestDTO) { Course createCourse = courseService.createCourse(courseRequestDTO); return createCourse; } }
6. Error
Create a ResourceNotFound exception to be thrown when a resource cannot be found.
public class ResourceNotFoundException extends RuntimeException{
public ResourceNotFoundException(){
}
public ResourceNotFoundException(String msg){
super(msg);
}
}
Testing the Endpoint
Postman will be used to test and run the endpoints defined above. The project will be using the default localhost port; 8080.
Lets start the application by running the main class EducationApplication.java
The expected output looks like this;

Test Cases
Case 1
Create New Course Object
Steps:
- Open Postman
- Set Request Type as POST
- Set Url = http://localhost:8080/v1/createCourse
- Set Request Header = Content-Type:application/json
- Set the Body as {“courseName”: “Software Engineering”}
- Click Send
- In the response, we will get the id, course name and the student list displayed as null

To validate that the data got inserted into the H2 Database, first set up the H2 Database in the application.properties.
H2 Database Properties
# To Enable H2 Console
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
# For in memory db
#spring.datasource.url=jdbc:h2:mem:testdb
# H2 driver
spring.datasource.driverClassName=org.h2.Driver
# Default user name
spring.datasource.username=sa
# Default password
spring.datasource.password=
# JPA Platform
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
After setting the properties and restarting the application, click on the url; http://localhost:8080/h2-console/login.jsp?jsessionid=02859894ca79ad783c164c9ea0468638 and set the fields as required

Connect to the database and select all from the Course Table

Case 2
Create a new Student Object
Steps;
- Open Postman
- Set Request Type as POST
- Set Url = http://localhost:8080/v1/createStudent
- Set Request Header = Content-Type:application/json
- Set the Body as {“firstName”: “kate”, “lastName”: “henshaw”, “age”: “20” }
- Click Send
- In the response, we will get the id, first name, last name and the age

To validate that the data got inserted into the H2 Database , run the “select * from student” query.

Case 3
Create a new Student Class Object
Steps;
- Open Postman
- Set Request Type as POST
- Set Url = http://localhost:8080/v1/createStudentClass
- Set Request Header = Content-Type:application/json
- Set the Body as {“className”: “alpha”, “noOfStudents”: “10”, “classLevel”: “1001” }
- Click Send
- In the response, we will get the id, class name, no of students, class level and the list of students set to null
To validate that the data got inserted into the H2 Database , run the “select * from student_class” query.

Case 4
Assigning a Student to a Course
Steps;
- Open Postman
- Set Request Type as PUT
- Set Url = http://localhost:8080/v1/addStudentCourse/{studentId} which in this case is 1
- Set Request Header = Content-Type:application/json
- Set the Body as {“courseId”: “1” }
- Click Send
- In the response, we get the id, first name, last name, age. The course assigned doesn’t show in the response because the @JsonIgnore annotation was added to the course object

Case 5
Assigning a Student to a Class
Steps;
- Open Postman
- Set Request Type as PUT
- Set Url = http://localhost:8080/v1/addStudentClass/{studentId} which in this case is 1
- Set Request Header = Content-Type:application/json
- Set the Body as {“studentClassId”: “1” }
- Click Send
- In the response, we get the id, first name, last name, age. The Student Class assigned doesn’t show in the response because the @JsonIgnore annotation was added to the Student Class object

Case 6
Retrieving the Student list
Steps;
- Open Postman
- Set Request Type as GET
- Set Url = http://localhost:8080/v1/students
- Set Request Header = Content-Type:application/json
- Click Send
- In the response, we will two student data because two student objects were created

Case 7
Retrieving the List of students taking a particular course
Steps;
- Open Postman
- Set Request Type as GET
- Set Url = http://localhost:8080/v1/students/{courseid} which is 1
- Set Request Header = Content-Type:application/json
- Click Send
- In the response, we will two student data because two student objects were created

Conclusion
In this tutorial, we’ve covered building a rest service using Spring Boot with the H2 database. The demo student application covers the POST, PUT and GET Methods.
Feel free to play around with the code and experiment! Reach out to me on Twitter if you have any questions
Adesope Yusuf
Posted at 18:37h, 07 AugustYou killed it