Creating the Book Review and Recommendation System with Spring Boot involves a series of steps that include setting up the project, defining entities, creating repositories, services, controllers, and views. Below is a detailed guide to achieve this using Spring Boot, Spring Data JPA, and Thymeleaf.
Project Structure
- User Registration and Login
- Registration and Login forms (HTML/Thymeleaf)
- User entity and repository
- Security configuration
- Book Review Management
- Book and Review entities and repositories
- Services for managing books and reviews
- Controllers and views for adding, viewing, and editing reviews
- Book Recommendation System
- Logic for recommending books based on ratings
- Database Configuration
- Database schema and JPA configuration
Step-by-Step Implementation
Step 1: Set Up the Spring Boot Project
- Initialize Spring Boot Project: Use Spring Initializr to create a new project with the following dependencies:
- Spring Web
- Spring Data JPA
- Thymeleaf
- Spring Security
- H2 Database (or MySQL)
- Project Directory Structure:
src/
├── main/
│ ├── java/com/example/bookreviewsystem/
│ │ ├── BookReviewSystemApplication.java
│ │ ├── config/
│ │ │ └── SecurityConfig.java
│ │ ├── controller/
│ │ │ ├── UserController.java
│ │ │ ├── BookController.java
│ │ │ └── ReviewController.java
│ │ ├── entity/
│ │ │ ├── User.java
│ │ │ ├── Book.java
│ │ │ └── Review.java
│ │ ├── repository/
│ │ │ ├── UserRepository.java
│ │ │ ├── BookRepository.java
│ │ │ └── ReviewRepository.java
│ │ ├── service/
│ │ │ ├── UserService.java
│ │ │ ├── BookService.java
│ │ │ └── ReviewService.java
│ ├── resources/
│ │ ├── static/
│ │ ├── templates/
│ │ │ ├── register.html
│ │ │ ├── login.html
│ │ │ ├── profile.html
│ │ │ ├── addReview.html
│ │ │ ├── viewReviews.html
│ │ │ ├── recommend.html
│ │ ├── application.properties
Step 2: Define Entities
User.java
package com.example.bookreviewsystem.entity;
import javax.persistence.*;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
// Getters and Setters
}
Book.java
package com.example.bookreviewsystem.entity;
import javax.persistence.*;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
// Getters and Setters
}
Review.java
package com.example.bookreviewsystem.entity;
import javax.persistence.*;
@Entity
public class Review {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "book_id")
private Book book;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
private int rating;
private String reviewText;
// Getters and Setters
}
Step 3: Create Repositories
UserRepository.java
package com.example.bookreviewsystem.repository;
import com.example.bookreviewsystem.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
BookRepository.java
package com.example.bookreviewsystem.repository;
import com.example.bookreviewsystem.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book, Long> {
}
ReviewRepository.java
package com.example.bookreviewsystem.repository;
import com.example.bookreviewsystem.entity.Review;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ReviewRepository extends JpaRepository<Review, Long> {
}
Step 4: Define Services
UserService.java
package com.example.bookreviewsystem.service;
import com.example.bookreviewsystem.entity.User;
import com.example.bookreviewsystem.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
public void save(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userRepository.save(user);
}
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
}
BookService.java
package com.example.bookreviewsystem.service;
import com.example.bookreviewsystem.entity.Book;
import com.example.bookreviewsystem.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookService {
@Autowired
private BookRepository bookRepository;
public List<Book> findAll() {
return bookRepository.findAll();
}
public Book save(Book book) {
return bookRepository.save(book);
}
}
ReviewService.java
package com.example.bookreviewsystem.service;
import com.example.bookreviewsystem.entity.Review;
import com.example.bookreviewsystem.repository.ReviewRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ReviewService {
@Autowired
private ReviewRepository reviewRepository;
public List<Review> findAll() {
return reviewRepository.findAll();
}
public Review save(Review review) {
return reviewRepository.save(review);
}
}
Step 5: Configure Security
SecurityConfig.java
package com.example.bookreviewsystem.config;
import com.example.bookreviewsystem.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private UserService userService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/register", "/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/profile")
.permitAll()
.and()
.logout()
.permitAll();
return http.build();
}
}
Step 6: Create Controllers
UserController.java
package com.example.bookreviewsystem.controller;
import com.example.bookreviewsystem.entity.User;
import com.example.bookreviewsystem.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/register")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new User());
return "register";
}
@PostMapping("/register")
public String registerUser(User user) {
userService.save(user);
return "redirect:/login";
}
@GetMapping("/login")
public String showLoginForm() {
return "login";
}
}
BookController.java
package com.example.bookreviewsystem.controller;
import com.example.bookreviewsystem.entity.Book;
import com.example.bookreviewsystem.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class BookController {
@Autowired
private BookService bookService;
@GetMapping("/addReview")
public String showAddReviewForm(Model model) {
model.addAttribute("book", new Book());
return "addReview";
}
@PostMapping("/addReview")
public String addReview(Book book) {
bookService.save(book);
return "redirect:/profile";
}
}
ReviewController.java
package com.example.bookreviewsystem.controller;
import com.example.bookreviewsystem.entity.Review;
import com.example.bookreviewsystem.service.ReviewService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class ReviewController {
@Autowired
private ReviewService reviewService;
@GetMapping("/viewReviews")
public String viewReviews(Model model) {
model.addAttribute("reviews", reviewService.findAll());
return "viewReviews";
}
@PostMapping("/viewReviews")
public String addReview(Review review) {
reviewService.save(review);
return "redirect:/profile";
}
}
Step 7: Create Views
register.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Register</title>
</head>
<body>
<h2>Register</h2>
<form th:action="@{/register}" method="post" th:object="${user}">
<label for="username">Username:</label>
<input type="text" id="username" name="username" th:field="*{username}" required/><br/>
<label for="password">Password:</label>
<input type="password" id="password" name="password" th:field="*{password}" required/><br/>
<button type="submit">Register</button>
</form>
</body>
</html>
login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
</head>
<body>
<h2>Login</h2>
<form th:action="@{/login}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required/><br/>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required/><br/>
<button type="submit">Login</button>
</form>
</body>
</html>
profile.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Profile</title>
</head>
<body>
<h2>Welcome</h2>
<p>Welcome, <span th:text="${#httpServletRequest.remoteUser}"></span>!</p>
<a th:href="@{/addReview}">Add Review</a><br/>
<a th:href="@{/viewReviews}">View Reviews</a><br/>
<a th:href="@{/recommend}">Recommendations</a><br/>
<a th:href="@{/logout}">Logout</a>
</body>
</html>
addReview.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Review</title>
</head>
<body>
<h2>Add Review</h2>
<form th:action="@{/addReview}" method="post" th:object="${book}">
<label for="title">Title:</label>
<input type="text" id="title" name="title" th:field="*{title}" required/><br/>
<label for="author">Author:</label>
<input type="text" id="author" name="author" th:field="*{author}" required/><br/>
<button type="submit">Submit</button>
</form>
</body>
</html>
viewReviews.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>View Reviews</title>
</head>
<body>
<h2>Book Reviews</h2>
<div th:each="review : ${reviews}">
<p>
<strong>Title:</strong> <span th:text="${review.book.title}"></span><br/>
<strong>Author:</strong> <span th:text="${review.book.author}"></span><br/>
<strong>Rating:</strong> <span th:text="${review.rating}"></span><br/>
<strong>Review:</strong> <span th:text="${review.reviewText}"></span><br/>
</p>
<hr/>
</div>
<a th:href="@{/profile}">Back to Profile</a>
</body>
</html>
recommend.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Recommendations</title>
</head>
<body>
<h2>Recommended Books</h2>
<div th:each="book : ${recommendedBooks}">
<p>
<strong>Title:</strong> <span th:text="${book.title}"></span><br/>
<strong>Author:</strong> <span th:text="${book.author}"></span><br/>
<strong>Average Rating:</strong> <span th:text="${book.avgRating}"></span><br/>
</p>
<hr/>
</div>
<a th:href="@{/profile}">Back to Profile</a>
</body>
</html>
Step 8: Database Configuration
application.properties
# H2 Database configuration
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.jpa.show-sql=true
Step 9: Running the Application
- Start the Spring Boot Application: Run the
BookReviewSystemApplication
class as a Java application. - Access the Application: Open a web browser and navigate to
http://localhost:8080/register
to start using the application.
Conclusion
This comprehensive project showcases how to create a full-featured web application using Spring Boot, Spring Data JPA, and Thymeleaf. The application includes user authentication, book review management, and a basic recommendation system, providing a robust foundation for further extension and learning in web development with Spring Boot.