✅ 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>