Hello Java programmers, if you want to learn Spring Boot and Reactjs and looking for a full-stack Java example or a mini project then you have come to the right place. Earlier, I have shared the best Spring Boot courses, books, and best reactjs courses as well as many courses to learn full-stack development in Java and become a full-stack java developer. In this mini project, you will learn how to create an application that is capable of creating, deleting, updating, and retrieving functions using the Spring Boot, React, and H2 Database. In this tutorial you will create a School Classroom Application that can insert, update, delete and search students. The frontend of the application is developed using Reactjs and also the backend is developed using spring boot. And, most importantly communication between the two platforms will be done using the REST APIs.
How to create Student Management Mini Project using Spring Boot and React.js
Here are step by step guide to create a full stack project in Java using Spring Boot and React.js. We will create a
1. Tools and technologies which is used in this application.
- You can use any IDE to develop the backend(spring boot) and the frontend of the application.
- Server: Apache Tomcat
- Spring Boot 2
- Reactjs
- H2 Database
2. The Spring Boot Application
<?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 https://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.6.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.student</groupId>
<artifactId>crudapp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>crudapp</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<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.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3. The Application.properties file
SpringApplication will load properties from application.properties files in the following locations and add them to the Spring Environment:1. config subdirectory of the current directory.2. The current directory3. A classpath /config package4. The classpath rootBelow is the used applicaiton.properties file in this Student Crud application.server.port=8090
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:mem:cmpe172
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
JPA Entity Class
Below is the used JPA entity class in this application. This is responsible for modeling Students.package com.student.crudapp.model;
import javax.persistence.*;
@Entity
@Table(name = "STUDENT")
public class Student {
@Column(name = "id")
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;
@Column(name = "name")
private String name;
@Column(name = "email")
private String email;
@Column(name = "grade")
private String grade;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", grade='" + grade + '\'' +
'}';
}
}
4. The StudentRepository Interface
package com.student.crudapp.repository;
import com.student.crudapp.model.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface StudentRepository extends JpaRepository<Student, Integer> {
List<Student> findAll();
Student findById(int id);
}
5. The StudentController Class
package com.student.crudapp.controller;
import com.student.crudapp.model.Student;
import com.student.crudapp.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
@CrossOrigin(origins = "http://localhost:8090")
public class StudentController {
@Autowired
StudentRepository studentRepository;
//check the api's working correctly api
@RequestMapping(value="/ping", method=RequestMethod.GET)
@ResponseBody
public String healthCheck() {
return "This is working well";
}
@RequestMapping(value="/students", method=RequestMethod.GET)
@ResponseBody
public List<Student> getAllStudents() {
return studentRepository.findAll();
}
@RequestMapping(value="/student", method=RequestMethod.POST)
@ResponseBody
public Student addStudent(Student student) {
return studentRepository.save(student);
}
@RequestMapping(value="/findstudent", method = RequestMethod.GET)
@ResponseBody
public Student findStudent(@RequestParam("studentId") int studentId) {
return studentRepository.findById(studentId);
}
@RequestMapping(value= "/updatestudent", method = RequestMethod.GET)
@ResponseBody
public Student updateStudent(@RequestBody Student student){
return studentRepository.save(student);
}
@RequestMapping(value="/deletestudent", method = RequestMethod.GET)
@ResponseBody
public int deleteStudent(@RequestParam("studentId") int studentId) {
return studentRepository.deleteById(studentId);
}
}
In the above controller, we used the @CrossOrigin annotation, in order to enable Cross-Origin Resource Sharing (CORS) on the server.
You think this is unnecessary, but the thing is we're deploying our React frontend to http://localhost:4200, and our Boot backend to http://localhost:8090, the browser would otherwise deny requests from one to the other. the server.
So below are the created API's in order to deal with frontend of the application.
1. Add a new Student (POST request)
http://localhost:8090/student
{"name": "Test","email": "test@gmail.com","grade": "05"}2. Get all students (GET request)
http://localhost:8090/students
3. Find specific student(GET request)http://localhost:8090/findstudent?studentId=1
4. Update student(GET Request)
http://localhost:8090/updatestudent
{"id": 1,"name": "Testupdated","email": "testupdated@gmail.com","grade": "05"}5. Delete student(GET request)
http://localhost:8090/deletestudent?studentId=1
Here is the screenshot of the H2 database that we have created.
6.The Reactjs Application.
7. Reactjs Project Structure.
8. React Application Entry Point
import React from "react"; import logo from "./logo.svg"; import "./App.css"; import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; import ListStudentComponent from "./components/ListStudentComponent"; import HeaderComponent from "./components/HeaderComponent"; import FooterComponent from "./components/FooterComponent"; import CreateStudentComponent from "./components/CreateStudentComponent"; import UpdateStudentComponent from "./components/UpdateStudentComponent"; import ViewStudentComponent from "./components/ViewStudentComponent"; function App() { return ( <div> <Router> <HeaderComponent /> <div className="container"> <Switch> <Route path="/" exact component={ListStudentComponent}></Route> <Route path="/students" component={ListStudentComponent}></Route> <Route path="/add-student/:id" component={CreateStudentComponent} ></Route> <Route path="/view-student/:id" component={ViewStudentComponent} ></Route> <Route path="/update-student/:id" component={UpdateStudentComponent} ></Route> </Switch> </div> <FooterComponent /> </Router> </div> ); } export default App;
9. Build Components
- CreateStudentComponent.jsx
- FooterComponent.jsx
- HeaderComponent.jsx
- ListStudentComponent.jsx
- UpdateStudentComponent.jsx
- ViewStudentComponent.jsx
10. CreateStudentComponent.jsx
import React, { Component } from "react"; import StudentService from "../services/StudentService"; class CreateStudentComponent extends Component { constructor(props) { super(props); this.state = { // step 2 id: this.props.match.params.id, name: "", email: "", grade: "", }; this.changeNameHandler = this.changeNameHandler.bind(this); this.changeEmailHandler = this.changeEmailHandler.bind(this); this.changeGradeHandler = this.changeGradeHandler.bind(this); this.saveOrUpdateStudent = this.saveOrUpdateStudent.bind(this); } // step 3 componentDidMount() { // step 4 if (this.state.id === "_add") { return; } else { StudentService.getStudentById(this.state.id).then((res) => { let student = res.data; this.setState({ name: student.name, email: student.email, grade: student.grade, }); }); } } saveOrUpdateStudent = (e) => { e.preventDefault(); let student = { name: this.state.name, email: this.state.email, grade: this.state.grade, }; console.log("Student => " + JSON.stringify(student)); // step 5 if (this.state.id === "_add") { StudentService.createStudent(student).then((res) => { this.props.history.push("/students"); }); } else { StudentService.updateStudent(student, this.state.id).then((res) => { this.props.history.push("/students"); }); } }; changeNameHandler = (event) => { this.setState({ name: event.target.value }); }; changeGradeHandler = (event) => { this.setState({ grade: event.target.value }); }; changeEmailHandler = (event) => { this.setState({ email: event.target.value }); }; cancel() { this.props.history.push("/students"); } getTitle() { if (this.state.id === "_add") { return; <h3 className="text-center">Add Student</h3>; } else { return; <h3 className="text-center">Update Student</h3>; } } render() { return ( <div> <br></br> <div className="container"> <div className="row"> <div className="card col-md-6 offset-md-3 offset-md-3"> {this.getTitle()} <div className="card-body"> <form> <div className="form-group"> <label> Name:</label> <input placeholder="Name" name="Name" className="form-control" value={this.state.name} onChange={this.changeNameHandler} /> </div> <div className="form-group"> <label> Email:</label> <input placeholder="Email Address" name="email" className="form-control" value={this.state.email} onChange={this.changeEmailHandler} /> </div> <div className="form-group"> <label> Grade:</label> <input placeholder="Grade" name="grade" className="form-control" value={this.state.grade} onChange={this.changeGradeHandler} /> </div> <button className="btn btn-success" onClick={this.saveOrUpdateStudent} > Save </button> <button className="btn btn-danger" onClick={this.cancel.bind(this)} style={{ marginLeft: "10px" }} > Cancel </button> </form> </div> </div> </div> </div> </div> ); } } export default CreateStudentComponent;
11. ListStudentComponent.jsx
import React, { Component } from "react"; import StudentService from "../services/StudentService"; class ListStudentComponent extends Component { constructor(props) { super(props); this.state = { students: [], }; this.addStudent = this.addStudent.bind(this); this.editStudent = this.editStudent.bind(this); this.deleteStudent = this.deleteStudent.bind(this); } deleteStudent(id) { StudentService.deleteStudent(id).then((res) => { this.setState({ students: this.state.students.filter((student) => student.id !== id), }); }); } viewStudent(id) { this.props.history.push(`/view-student/${id}`); } editStudent(id) { this.props.history.push(`/add-student/${id}`); } componentDidMount() { StudentService.getStudent().then((res) => { this.setState({ students: res.data }); }); } addStudent() { this.props.history.push("/add-student/_add"); } render() { return ( <div> <h2 className="text-center">Students List</h2> <div className="row"> <button className="btn btn-primary" onClick={this.addStudent}> {" "} Add Student </button> </div> <br></br> <div className="row"> <table className="table table-striped table-bordered"> <thead> <tr> <th> Student Name</th> <th> Student Email</th> <th> Student Grade</th> <th> Actions</th> </tr> </thead> <tbody> {this.state.students.map((student) => ( <tr key={student.id}> <td>{student.firstName}</td> <td>{student.lastName}</td> <td>{student.emailId}</td> <td> <button onClick={() => this.editStudent(student.id)} className="btn btn-info" > Update </button> <button style={{ marginLeft: "10px" }} onClick={() => this.deleteStudent(student.id)} className="btn btn-danger" > Delete </button> <button style={{ marginLeft: "10px" }} onClick={() => this.viewStudent(student.id)} className="btn btn-info" > View </button> </td> </tr> ))} </tbody> </table> </div> </div> ); } } export default ListStudentComponent;
12. UpdateStudentComponent.jsx
import React, { Component } from "react"; import StudentService from "../services/StudentService"; class UpdateStudentComponent extends Component { constructor(props) { super(props); this.state = { id: this.props.match.params.id, name: "", email: "", grade: "", }; this.changeNameHandler = this.changeFirstNameHandler.bind(this); this.changeEmailHandler = this.changeEmailHandler.bind(this); this.changeGradeHandler = this.changeGradeHandler.bind(this); this.updateStudent = this.updateStudent.bind(this); } componentDidMount() { StudentService.getStudentById(this.state.id).then((res) => { let student = res.data; this.setState({ name: student.name, email: student.email, grade: student.grade, }); }); } updateStudent = (e) => { e.preventDefault(); let student = { name: this.state.name, email: this.state.email, grade: this.state.grade, }; console.log("student => " + JSON.stringify(student)); console.log("id => " + JSON.stringify(this.state.id)); StudentService.updateStudent(student, this.state.id).then((res) => { this.props.history.push("/students"); }); }; changeNameHandler = (event) => { this.setState({ name: event.target.value }); }; changeGradeHandler = (event) => { this.setState({ grade: event.target.value }); }; changeEmailHandler = (event) => { this.setState({ emailId: event.target.value }); }; cancel() { this.props.history.push("/students"); } render() { return ( <div> <br></br> <div className="container"> <div className="row"> <div className="card col-md-6 offset-md-3 offset-md-3"> <h3 className="text-center">Update Employee</h3> <div className="card-body"> <form> <div className="form-group"> <label> Name:</label> <input placeholder="First Name" name="firstName" className="form-control" value={this.state.firstName} onChange={this.changeNameHandler} /> </div> <div className="form-group"> <label> Email Id:</label> <input placeholder="Email Address" name="emailId" className="form-control" value={this.state.emailId} onChange={this.changeEmailHandler} /> </div> <div className="form-group"> <label> Grade:</label> <input placeholder="Last Name" name="lastName" className="form-control" value={this.state.lastName} onChange={this.changeLastNameHandler} /> </div> <button className="btn btn-success" onClick={this.updateStudent} > Save </button> <button className="btn btn-danger" onClick={this.cancel.bind(this)} style={{ marginLeft: "10px" }} > Cancel </button> </form> </div> </div> </div> </div> </div> ); } } export default UpdateStudentComponent;
13. ViewStudentComponent.jsx
import React, { Component } from "react"; import StudentService from "../services/StudentService"; class ViewStudentComponent extends Component { constructor(props) { super(props); this.state = { id: this.props.match.params.id, students: {}, }; } componentDidMount() { StudentService.getStudentById(this.state.id).then((res) => { this.setState({ students: res.data }); }); } render() { return ( <div> <br></br> <div className="card col-md-6 offset-md-3"> <h3 className="text-center"> View Student Details</h3> <div className="card-body"> <div className="row"> <label> Student Name:</label> <div>{this.state.students.name}</div> </div> <div className="row"> <label> Student Email :</label> <div>{this.state.students.email}</div> </div> <div className="row"> <label> Student Grade:</label> <div>{this.state.students.grade}</div> </div> </div> </div> </div> ); } } export default ViewStudentComponent;
14. FooterComponent.jsx and HeaderComponent.jsx
import React, { Component } from "react"; class FooterComponent extends Component { constructor(props) { super(props); this.state = {}; } render() { return ( <div> <footer className="footer"> <span className="text-muted">All Rights Reserved</span> </footer> </div> ); } } export default FooterComponent;
import React, { Component } from "react"; class HeaderComponent extends Component { constructor(props) { super(props); this.state = {}; } render() { return ( <div> <header> <nav className="navbar navbar-expand-md navbar-dark bg-dark"> <div> <a href="" className="navbar-brand"> Student Management Application </a> </div> </nav> </header> </div> ); } } export default HeaderComponent;
15. API Integration
import axios from "axios"; const STUDENT_API_BASE_URL = "http://localhost:8090"; class StudentService { getStudent() { return; axios.get(STUDENT_API_BASE_URL); } createStudent(student) { return; axios.post(STUDENT_API_BASE_URL + "/student", student); } getStudentById(studentId) { return; axios.get(STUDENT_API_BASE_URL + "/findstudent" + studentId); } updateStudent(student, studentId) { return; axios.put(STUDENT_API_BASE_URL + "/updatestudent" + studentId, student); } deleteStudent(studentId) { return; axios.delete(STUDENT_API_BASE_URL + "/deletestudent" + studentId); } } export default new StudentService();
16. Interfaces of the project.
- How to update an entity using Spring Data JPA? (example)
- 20 Spring Boot Interview Questions with answers (questions)
- What is @Conditional annotation in Spring? (conditional example)
- How Spring MVC works internally? (answer)
- Spring Data JPA @Query Example (query example)
- 10 Advanced Spring Boot Courses for Java developers (courses)
- Spring Data JPA Repository Example in Java (JpaReposistory example)
- 20+ Spring MVC Interview Questions for Programmers (answer)
- 13 Spring Boot Actuator Interview questions (boot questions)
- Difference between @Autowired and @Inject in Spring? (answer)
- Top 5 Frameworks Java Developer Should Know (frameworks)
- Difference between @RequestParam and @PathVariable in Spring (answer)
- Top 7 Courses to learn Microservices in Java (courses)
- How to use @Bean in Spring framework (Example)
- How to fix No property type found in Spring Data JPA? [Solution]
- 5 Spring Cloud annotations Java programmer should learn (cloud)
- Top 5 Courses to Learn and Master Spring Cloud (courses)
- 5 Courses to Learn Spring Security for Java programmers (courses)
- 10 Spring MVC annotations Java developer should know (annotations)
- @SpringBootApplication vs. @EnableAutoConfiguration? (answer)
- 15 Spring Boot Interview Questions for Java Developers (questions)
- Difference between @Component, @Service, and @Controller in Spring (answer)
P. S. - If you are a Spring Boot beginner and want to learn the Spring Boot framework from scratch and look for some of the best online resources, you can also check out these best Spring Boot courses for Java developers. This list contains free Udemy and Pluralsight courses to learn Spring Boot from scratch.
The source code is in a very terrible format. StudentService Where do you mention/create this service in the tutorial? There are a lot of topics that aren't explained.
ReplyDelete