Product Management System - Spring Boot Thymeleaf PostgreSQL

Here’s a step-by-step guide for building a Spring Boot CRUD application using Thymeleaf as the template engine, PostgreSQL as the database, and Bootstrap for UI styling. This includes a basic explanation of each component.


Project Setup

To get started, set up your Spring Boot project.

  1. Dependencies:
    • Spring Web
    • Spring Data JPA
    • PostgreSQL Driver
    • Thymeleaf
    • Spring Boot DevTools (optional for live reload)
    • Lombok (optional for boilerplate reduction)

You can create this setup via Spring Initializr:
https://start.spring.io/


Project Structure

src/main/java └── com.example.demo ├── controller ├── model ├── repository ├── service └── DemoApplication.java src/main/resources └── templates └── (Thymeleaf templates) └── static └── (CSS, JS, Bootstrap) └── application.properties

Steps

1. Configure PostgreSQL

In application.properties:

spring.datasource.url=jdbc:postgresql://localhost:5432/your_database_name
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.thymeleaf.cache=false

2. Create Entity (Model)

This entity represents the table in the PostgreSQL database.

package com.example.demo.model;

import jakarta.persistence.*;

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private Double price;

    @Column(length = 1000)
    private String description;

    //getters, setters, constructor
}
  • @Entity marks this class as a JPA entity.
  • @Id and @GeneratedValue handle primary key and auto-generation.

3. Create Repository

The repository handles database operations using Spring Data JPA.

package com.example.demo.repository;

import com.example.demo.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductRepository extends JpaRepository<Product, Long> {
}

4. Create Service Layer

The service layer handles business logic.

package com.example.demo.service;

import com.example.demo.model.Product;
import com.example.demo.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class ProductService {

    @Autowired
    private ProductRepository repository;

    public List<Product> getAllProducts() {
        return repository.findAll();
    }

    public void saveProduct(Product product) {
        repository.save(product);
    }

    public Product getProductById(Long id) {
        return repository.findById(id).orElse(null);
    }

    public void deleteProduct(Long id) {
        repository.deleteById(id);
    }
}

5. Create Controller

The controller handles incoming HTTP requests and maps them to views (Thymeleaf templates).

package com.example.demo.controller;

import com.example.demo.model.Product;
import com.example.demo.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
public class ProductController {

    @Autowired
    private ProductService productService;

    // Show all products
    @GetMapping("/")
    public String viewHomePage(Model model) {
        model.addAttribute("products", productService.getAllProducts());
        return "index";
    }

    // Show form to add a new product
    @GetMapping("/showNewProductForm")
    public String showNewProductForm(Model model) {
        Product product = new Product();
        model.addAttribute("product", product);
        return "new_product";
    }

    // Save a product
    @PostMapping("/saveProduct")
    public String saveProduct(@ModelAttribute("product") Product product) {
        productService.saveProduct(product);
        return "redirect:/";
    }

    // Show form to update a product
    @GetMapping("/showFormForUpdate/{id}")
    public String showFormForUpdate(@PathVariable(value = "id") Long id, Model model) {
        Product product = productService.getProductById(id);
        model.addAttribute("product", product);
        return "update_product";
    }

    // Delete a product
    @GetMapping("/deleteProduct/{id}")
    public String deleteProduct(@PathVariable(value = "id") Long id) {
        productService.deleteProduct(id);
        return "redirect:/";
    }
}

6. Create Thymeleaf Templates

1. index.html (List all products with Bootstrap UI):

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Product Management</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <h2 class="text-center mt-4">Product List</h2>
    <a th:href="@{/showNewProductForm}" class="btn btn-primary mb-2">Add Product</a>
    <table class="table table-bordered table-striped">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Price</th>
                <th>Description</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="product : ${products}">
                <td th:text="${product.id}"></td>
                <td th:text="${product.name}"></td>
                <td th:text="${product.price}"></td>
                <td th:text="${product.description}"></td>
                <td>
                    <a th:href="@{/showFormForUpdate/{id}(id=${product.id})}" class="btn btn-warning">Update</a>
                    <a th:href="@{/deleteProduct/{id}(id=${product.id})}" class="btn btn-danger">Delete</a>
                </td>
            </tr>
        </tbody>
    </table>
</div>
</body>
</html>

2. new_product.html (Form for adding a product):

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Add Product</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <h2 class="text-center mt-4">Add New Product</h2>
    <form th:action="@{/saveProduct}" th:object="${product}" method="post">
        <div class="form-group">
            <label>Name</label>
            <input type="text" th:field="*{name}" class="form-control" required />
        </div>
        <div class="form-group">
            <label>Price</label>
            <input type="number" th:field="*{price}" class="form-control" step="0.01" required />
        </div>
        <div class="form-group">
            <label>Description</label>
            <textarea th:field="*{description}" class="form-control"></textarea>
        </div>
        <button type="submit" class="btn btn-success">Save</button>
    </form>
    <a th:href="@{/}" class="btn btn-secondary mt-2">Back</a>
</div>
</body>
</html>


3. update_product.html (Form for updating product):

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Update Product</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <h2 class="text-center mt-4">Update Product</h2>
    
    <form th:action="@{/saveProduct}" th:object="${product}" method="post">
        <!-- Hidden field to include product ID for update -->
        <input type="hidden" th:field="*{id}" />
        
        <div class="form-group">
            <label for="name">Name</label>
            <input type="text" th:field="*{name}" class="form-control" required />
        </div>
        
        <div class="form-group">
            <label for="price">Price</label>
            <input type="number" th:field="*{price}" class="form-control" step="0.01" required />
        </div>

        <div class="form-group">
            <label for="description">Description</label>
            <textarea th:field="*{description}" class="form-control"></textarea>
        </div>

        <button type="submit" class="btn btn-success">Update</button>
    </form>

    <a th:href="@{ / }" class="btn btn-secondary mt-2">Back</a>
</div>
</body>
</html>

7. Run the Application

Run the main application:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

How It Works

  1. Create/Read/Update/Delete operations are handled via the ProductController and ProductService.
  2. Thymeleaf templates are used to render views.
  3. Bootstrap is used to style the pages.
  4. PostgreSQL stores product data.
  5. The flow follows:
    • Display products on the homepage.
    • Use forms to create and update products.
    • Handle deletion through buttons.

Testing

  • Access the application via http://localhost:8080.
  • Add, edit, and delete products.
  • Verify changes in your PostgreSQL database.

You now have a working CRUD application using Spring Boot, Thymeleaf, PostgreSQL, and Bootstrap! 🎉

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete