hocvietcode.com
  • Trang chủ
  • Học lập trình
    • Lập trình C/C++
    • Cấu trúc dữ liệu và giải thuật
    • Lập trình HTML
    • Lập trình Javascript
      • Javascript cơ bản
      • ReactJS framework
      • AngularJS framework
      • Typescript cơ bản
      • Angular
      • NodeJS
    • Lập trình Mobile
      • Lập Trình Dart Cơ Bản
        • Dart Flutter Framework
    • Cơ sở dữ liệu
      • MySQL – MariaDB
      • Micrsoft SQL Server
      • Extensible Markup Language (XML)
      • JSON
      • MongoDB
    • Lập trình PHP
      • Lập trình PHP cơ bản
      • Laravel Framework
    • Lập trình Java
      • Java Cơ bản
      • Spring Boot
    • Lập trình C#
      • Lập Trình C# Cơ Bản
      • ASP.NET Core MVC
    • Lập trình Python
    • Git và Github
    • Machine Learning
  • WORDPRESS
    • WordPress cơ bản
    • WordPress nâng cao
    • Chia sẻ WordPress
  • Kiến thức hệ thống
    • Microsoft Azure
    • Docker
    • Linux
    • Mạng và truyền thông
  • Chia sẻ IT
    • Tin học văn phòng
      • Microsoft Word
      • Microsoft Excel
    • Marketing
      • Google Adwords
      • Facebook Ads
      • Kiến thức khác
    • Chia sẻ phần mềm
    • Review công nghệ
    • Công cụ – tiện ích
      • Kiểm tra bàn phím online
      • Kiểm tra webcam online
Đăng nhập
  • Đăng nhập / Đăng ký

Please enter key search to display results.

Home
  • Spring Boot
Spring Boot bài 14: Quan hệ JPA, DTO và tránh truy vấn N+1
Java và Spring Boot – MVC và REST API

Spring Boot bài 14: Quan hệ JPA, DTO và tránh truy vấn N+1

  • 18-10-2026
  • Toanngo92
  • 0 Comments

Giáo trình Spring Boot MVC và REST API · Chương 6/12: JPA, MySQL và transaction · Bài 14/28 · Biên soạn tháng 10/2026.

← Bài 13 · Mục lục 12 chương · Bài 15 →

Mục lục

  • Mục tiêu bài học
  • Quan hệ có hướng phục vụ nhu cầu hiện tại
  • DTO giữ response có phạm vi rõ ràng
  • N+1 xuất hiện như thế nào?
  • Khóa ngoại và version bảo vệ điều gì?
  • Lỗi thường gặp và lưu ý
  • Ghi nhớ
  • Tài liệu đối chiếu

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

Ánh xạ nhiều khóa học thuộc một danh mục, chuyển entity sang DTO trong transaction và hiểu vấn đề N+1.

Quan hệ có hướng phục vụ nhu cầu hiện tại

Mỗi Course thuộc một Category nên Course có @ManyToOne và category_id. Bản mẫu không thêm List<Course> ngược vào Category vì chưa có nhu cầu truy cập qua chiều đó. Quan hệ một chiều giúp người mới tập trung vào khóa ngoại và cách tải dữ liệu; không phải quan hệ nào cũng cần hai collection tham chiếu lẫn nhau.

FetchType.LAZY cho biết dữ liệu danh mục có thể được tải trễ. Với quan hệ to-one, hãy chú ý khả năng thực tế của provider và kiểm tra SQL. Cascade không được bật tùy tiện: xóa một khóa học không nên kéo theo xóa danh mục đang được nhiều khóa học dùng.

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "category_id")
private Category category;

@Version
private Long version;

DTO giữ response có phạm vi rõ ràng

Nếu trả thẳng entity qua JSON, serializer có thể đi vào quan hệ lazy, tạo truy vấn ngoài dự kiến hoặc lặp vô hạn với quan hệ hai chiều. Entity cũng có thể chứa trường không muốn công khai. CourseDto chỉ gồm dữ liệu cần hiển thị và định nghĩa hợp đồng ổn định hơn cho cả HTML và API.

Chuyển đổi trong CourseService khi transaction còn mở giúp categoryName được lấy đúng lúc. spring.jpa.open-in-view=false buộc ta chuẩn bị dữ liệu ở tầng nghiệp vụ, tránh để template hoặc serializer vô tình tiếp tục truy vấn sau đó. Nếu gặp LazyInitializationException, xem lại thời điểm mapping và cách fetch, không bật lại mọi cơ chế lazy theo thói quen.

Tệp trong mã nguồn mẫu: src/main/java/com/hocvietcode/academy/CourseDto.java. Đoạn dưới được trích từ tệp này; xem bản tải về để có đủ import và phần còn lại.

package com.hocvietcode.academy;
import java.math.BigDecimal;
public record CourseDto(Long id,String title,BigDecimal price,Long categoryId,String categoryName) {
    public static CourseDto from(Course c){return new CourseDto(c.getId(),c.getTitle(),c.getPrice(),c.getCategory().getId(),c.getCategory().getName());}
}

N+1 xuất hiện như thế nào?

Một truy vấn lấy N khóa học có thể kéo theo N truy vấn nữa khi đọc tên danh mục từng dòng. Với danh sách lớn, chi phí tăng dù code vòng lặp trông đơn giản. CourseRepository dùng @EntityGraph(attributePaths="category") trên phương thức tìm kiếm để tải quan hệ cần thiết cho trang dữ liệu.

Đây là lựa chọn có mục tiêu cho quan hệ to-one. Không nên chuyển mọi quan hệ thành EAGER hoặc fetch join bừa mọi collection, nhất là khi phân trang. Cần kiểm tra SQL và số truy vấn trong bối cảnh dữ liệu thực tế, vì một annotation không tự tối ưu mọi trường hợp.

Tệp trong mã nguồn mẫu: src/main/java/com/hocvietcode/academy/CourseRepository.java. Đoạn dưới được trích từ tệp này; xem bản tải về để có đủ import và phần còn lại.

public interface CourseRepository extends JpaRepository<Course,Long> {
    @EntityGraph(attributePaths="category")
    Page<Course> findByTitleContainingIgnoreCase(String title,Pageable pageable);
}

Khóa ngoại và version bảo vệ điều gì?

Khóa ngoại giữ cho category_id tham chiếu một danh mục tồn tại. Validation và Service giúp trả thông báo sớm, nhưng DB vẫn cần ràng buộc để bảo vệ tính toàn vẹn trước các luồng ghi khác. @Version hỗ trợ phát hiện thay đổi cạnh tranh ở mức entity/transaction.

Tuy nhiên mẫu update luôn đọc lại bản ghi hiện tại; nó chưa có cơ chế so sánh phiên bản người dùng đã mở trong form hoặc ETag của API. Vì vậy không nên tuyên bố đã giải quyết toàn bộ trường hợp hai người mở form lâu rồi ghi đè nhau. Khi mở rộng, cần mang phiên bản kỳ vọng từ client và xử lý xung đột rõ ràng.

Lỗi thường gặp và lưu ý

  • Không đặt CascadeType.ALL cho quan hệ nhiều-một chỉ để lỗi lưu biến mất.
  • Không trả entity ra JSON rồi dùng annotation che từng lỗi phát sinh mà chưa xác định hợp đồng dữ liệu.

Ghi nhớ

Chọn quan hệ theo nhu cầu, fetch dữ liệu có chủ đích và mapping DTO trong transaction giúp cả giao diện lẫn API dễ kiểm soát.

Tài liệu đối chiếu

  • Entity Graphs

← Bài 13 · Mục lục 12 chương · Bài 15 →

Bài viết liên quan:

Java và Spring Boot – MVC và REST API
Spring Boot bài 23: Gọi dịch vụ HTTP bằng RestClient và đặt timeout
Java và Spring Boot – MVC và REST API
Spring Boot bài 24: Gửi email và lập lịch tác vụ trong Spring Boot
Java và Spring Boot – MVC và REST API
Spring Boot bài 08: Fragment, layout và tài nguyên CSS trong Spring Boot
Java và Spring Boot – MVC và REST API
Spring Boot bài 25: Unit test Service với JUnit và Mockito
Java và Spring Boot – MVC và REST API
Spring Boot bài 09: Binding biểu mẫu tạo và sửa khóa học
Java và Spring Boot – MVC và REST API
Spring Boot bài 26: Integration test MVC, API, database và Security
Java và Spring Boot – MVC và REST API
Spring Boot bài 10: Validation và BindingResult: báo lỗi ngay trên biểu mẫu
Java và Spring Boot – MVC và REST API
Spring Boot bài 27: Logging, Actuator và quản lý schema bằng Flyway
Java và Spring Boot – MVC và REST API
Spring Boot bài 11: Post/Redirect/Get, flash message và session
Java và Spring Boot – MVC và REST API
Spring Boot bài 28: Đóng gói JAR, Docker và cấu hình triển khai
Java và Spring Boot – MVC và REST API
Spring Boot bài 12: JPA Entity, Repository, H2 và migration đầu tiên
Java và Spring Boot – MVC và REST API
Giáo trình Spring Boot MVC và REST API 2026: 12 chương, 28 bài

Dịch vụ thiết kế Wesbite

NỘI DUNG MỚI CẬP NHẬT

CSS cơ bản – 20 bài từ HTML đến website responsive với Flexbox, Grid và mobile-first

CSS cơ bản – Bài 05: Đơn vị và kích thước: px, rem, %, min và max

CSS cơ bản – 20 bài từ HTML đến website responsive với Flexbox, Grid và mobile-first

CSS cơ bản – Bài 02: Selector và class: chọn đúng phần tử cần sửa

CSS cơ bản – 20 bài từ HTML đến website responsive với Flexbox, Grid và mobile-first

CSS cơ bản – Bài 01: Kết nối CSS với HTML và sửa quy tắc đầu tiên

CSS cơ bản – 20 bài từ HTML đến website responsive với Flexbox, Grid và mobile-first

CSS cơ bản – Bài 20: Hoàn thiện website và sửa lỗi bằng DevTools

CSS cơ bản – 20 bài từ HTML đến website responsive với Flexbox, Grid và mobile-first

CSS cơ bản – Bài 03: Vì sao CSS không có tác dụng? Cascade và specificity

Giới thiệu

hocvietcode.com là website chia sẻ và cập nhật tin tức công nghệ, chia sẻ kiến thức, kỹ năng. Chúng tôi rất cảm ơn và mong muốn nhận được nhiều phản hồi để có thể phục vụ quý bạn đọc tốt hơn !

Liên hệ quảng cáo: [email protected]

Kết nối với HỌC VIẾT CODE

© hocvietcode.com - Tech888 Co .Ltd since 2019

Đăng nhập

Trở thành một phần của cộng đồng của chúng tôi!
Registration complete. Please check your email.
Đăng nhập bằng google
Đăng kýBạn quên mật khẩu?

Create an account

Welcome! Register for an account
The user name or email address is not correct.
Registration confirmation will be emailed to you.
Log in Lost your password?

Reset password

Recover your password
Password reset email has been sent.
The email could not be sent. Possible reason: your host may have disabled the mail function.
A password will be e-mailed to you.
Log in Register
×