Java Spring Boot Bài 7: CRUD cho danh mục (categories) – Spring Boot + Thymeleaf + JPA + Tìm kiếm + Sắp xếp

Giới thiệu khóa học: Xây dựng Web Bán Hàng Online với Java Spring Boot, Oracle DB và Thymeleaf

Mục tiêu bài học:

  • Tạo cơ sở dữ liệu và cấu hình kết nối SQL Server.
  • CRUD danh mục (Category) với Spring Boot + JPA.
  • Tìm kiếm danh mục theo tên.
  • Sắp xếp danh mục theo ID (tăng/giảm).

✅ Cấu trúc thư mục như bài trước bạn nên để như sau

src/
├─ main/
│ ├─ java/com/lungcode/
│ │ ├─ entity/Product.java
│ │ ├─ repository/ProductRepository.java
│ │ ├─ service/ProductService.java
│ │ ├─ service/impl/ProductServiceImpl.java
│ │ └─ controller/ProductController.java
│ └─ resources/
│ ├─ templates/product/list.html
│ ├─ templates/product/form.html
│ └─ application.properties

🗄️ 1️⃣ Tạo CSDL và cấu hình kết nối

Tạo cơ sở dữ liệu SQL Server:

CREATE DATABASE WebBanHangDB;
GO

USE WebBanHangDB;
GO

CREATE TABLE Category (
    id INT IDENTITY(1,1) PRIMARY KEY,
    name NVARCHAR(255) NOT NULL
);
GO

INSERT INTO Category (name) VALUES
(N'Điện thoại'),
(N'Máy tính'),
(N'Thiết bị văn phòng'),
(N'Phụ kiện'),
(N'Đồ gia dụng');
GO

📄 File: src/main/resources/application.properties

spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=WebBanHangDB
spring.datasource.username=sa
spring.datasource.password=your_password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

📦 2️⃣ Entity – Thực thể danh mục

📄 File: src/main/java/com/lungcode/entity/Category.java

package com.lungcode.entity;

import jakarta.persistence.*;

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

  private String name;

  // Getters and Setters</code></pre>
}

📂 3️⃣ Repository – Truy vấn dữ liệu

📄 File: src/main/java/com/lungcode/repository/CategoryRepository.java

package com.lungcode.repository;

import com.lungcode.entity.Category;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

public interface CategoryRepository extends JpaRepository<Category, Integer> {
    List<Category> findByNameContainingIgnoreCase(String name);
}

🔧 4️⃣ Service – Xử lý nghiệp vụ

📄 File: src/main/java/com/lungcode/service/CategoryService.java

package com.lungcode.service;

import com.lungcode.entity.Category;
import java.util.List;

public interface CategoryService {
    List<Category> getAll(String keyword, String sort);
    Category save(Category category);
    Category getById(Integer id);
    void deleteById(Integer id);
}

📄 File: src/main/java/com/lungcode/service/impl/CategoryServiceImpl.java

package com.lungcode.service.impl;

import com.lungcode.entity.Category;
import com.lungcode.repository.CategoryRepository;
import com.lungcode.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CategoryServiceImpl implements CategoryService {
<pre><code>@Autowired
private CategoryRepository repository;

@Override
public List<Category> getAll(String keyword, String sort) {
    if (keyword != null && !keyword.isEmpty()) {
        return repository.findByNameContainingIgnoreCase(keyword);
    }
    Sort sortOrder = Sort.by("id");
    sortOrder = "desc".equalsIgnoreCase(sort) ? sortOrder.descending() : sortOrder.ascending();
        return repository.findAll(sortOrder);
    }

    @Override
    public Category save(Category category) {
        return repository.save(category);
    }

    @Override
    public Category getById(Integer id) {
        return repository.findById(id).orElse(null);
    }

    @Override
    public void deleteById(Integer id) {
        repository.deleteById(id);
    }
}

🌐 5️⃣ Controller – Điều hướng trang web

📄 File: src/main/java/com/lungcode/controller/CategoryController.java

package com.lungcode.controller;

import com.lungcode.entity.Category;
import com.lungcode.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/categories")
public class CategoryController {

@Autowired
private CategoryService service;

  @GetMapping
  public String listCategories(Model model,
                               @RequestParam(name = "keyword", required = false) String keyword,
                               @RequestParam(name = "sort", required = false) String sort) {
      model.addAttribute("categories", service.getAll(keyword, sort));
      return "category/list";
  }

  @GetMapping("/create")
  public String createForm(Model model) {
      model.addAttribute("category", new Category());
      return "category/form";
  }

  @PostMapping("/save")
  public String saveCategory(@ModelAttribute Category category) {
      service.save(category);
      return "redirect:/categories";
  }

  @GetMapping("/edit/{id}")
  public String editCategory(@PathVariable Integer id, Model model) {
      Category category = service.getById(id);
      model.addAttribute("category", category);
      return "category/form";
  }

  @GetMapping("/delete/{id}")
  public String deleteCategory(@PathVariable Integer id) {
      service.deleteById(id);
      return "redirect:/categories";
  }
}

🎨 6️⃣ Giao diện Thymeleaf

📄 File: src/main/resources/templates/category/list.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
  <title>Danh sách danh mục</title>
</head>

<body>
  <h2>Danh sách danh mục</h2>
  <form method="get" th:action="@{/categories}">
    Tìm tên: <input type="text" name="keyword" placeholder="Nhập tên..." />
    <button type="submit">Tìm</button>
    <a th:href="@{/categories?sort=asc}">Sắp xếp ID ↑</a>
    <a th:href="@{/categories?sort=desc}">Sắp xếp ID ↓</a>
  </form><a th:href="@{/categories/create}">Thêm mới</a>
  <table border="1">
    <tr>
      <th>ID</th>
      <th>Tên</th>
      <th>Hành động</th>
    </tr>
    <tr th:each="c : ${categories}">
      <td th:text="${c.id}"></td>
      <td th:text="${c.name}"></td>
      <td>
        <a th:href="@{'/categories/edit/' + ${c.id}}">Sửa</a>
        <a th:href="@{'/categories/delete/' + ${c.id}}" onclick="return confirm('Xoá danh mục này?')">Xoá</a>
      </td>
    </tr>
  </table>
</body>

</html>

📄 File: src/main/resources/templates/category/form.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
  <title>Thêm / Sửa danh mục</title>
</head>

<body>
  <h2 th:text="${category.id == null} ? 'Thêm danh mục' : 'Sửa danh mục'"></h2>
  <form th:action="@{/categories/save}" th:object="${category}" method="post">
    <input type="hidden" th:field="*{id}" />
    Tên: <input type="text" th:field="*{name}" required />
    <button type="submit">Lưu</button>
  </form><a th:href="@{/categories}">Quay lại danh sách</a>
</body>

</html>

Total
0
Shares
Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Previous Post
Giới thiệu khóa học: Xây dựng Web Bán Hàng Online với Java Spring Boot, Oracle DB và Thymeleaf

Java Spring Boot bài 6: Giới thiệu Kiến trúc MVC trong Spring Boot

Next Post
Giới thiệu khóa học: Xây dựng Web Bán Hàng Online với Java Spring Boot, Oracle DB và Thymeleaf

Java Spring Boot Bài 8: CRUD Sản phẩm – Phân Trang + Validate + Upload File + JOIN categories

Related Posts