Skills101
← All posts

Book Review and Recommendation System with Spring Boot

Pankaj Sharma ·

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

  1. User Registration and Login
    • Registration and Login forms (HTML/Thymeleaf)
    • User entity and repository
    • Security configuration
  2. Book Review Management
    • Book and Review entities and repositories
    • Services for managing books and reviews
    • Controllers and views for adding, viewing, and editing reviews
  3. Book Recommendation System
    • Logic for recommending books based on ratings
  4. Database Configuration
    • Database schema and JPA configuration

Step-by-Step Implementation

Step 1: Set Up the Spring Boot Project

  1. 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)
  2. 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

  1. Start the Spring Boot Application: Run the BookReviewSystemApplication class as a Java application.
  2. 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.