Spring Boot bài 12: JPA Entity, Repository, H2 và migration đầu tiên
- 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 12/28 · Biên soạn tháng 10/2026.
Mục lục
Mục tiêu bài học
Ánh xạ dữ liệu bằng JPA, thực hiện CRUD qua Repository và tạo schema bằng Flyway.
Entity là đối tượng có danh tính trong database
Course có id làm khóa chính, title, price, category và version. @Entity báo đây là đối tượng JPA; @Table chỉ rõ tên bảng. @GeneratedValue với IDENTITY dùng mã tự tăng của database. Constructor không tham số protected phục vụ JPA, còn constructor công khai tạo đối tượng nghiệp vụ hợp lệ.
BigDecimal ánh xạ sang DECIMAL(12,2), phù hợp lưu số thập phân chính xác cho ví dụ giá. @Column mô tả nullability và chiều dài; các thông tin này không thay cho @Valid ở HTTP. Khi schema do migration quản lý, Hibernate dùng chúng để kiểm tra sự phù hợp chứ không tự sửa bảng.
Tệp trong mã nguồn mẫu: src/main/java/com/hocvietcode/academy/Course.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.
@Entity @Table(name="courses")
public class Course {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id;
@Column(nullable=false,length=120) private String title;
@Column(nullable=false,precision=12,scale=2) private BigDecimal price;
@ManyToOne(fetch=FetchType.LAZY,optional=false) @JoinColumn(name="category_id") private Category category;
@Version private Long version;
Repository cung cấp thao tác dữ liệu
JpaRepository<Course,Long> cho biết entity và kiểu id. Ta có findById, save, delete và các phương thức phân trang mà không tự viết câu SQL CRUD cơ bản. Điều đó không có nghĩa JPA không chạy SQL: nó vẫn gửi truy vấn tới database, quản lý entity và theo dõi thay đổi trong transaction.
findById trả Optional để người gọi quyết định trường hợp không tồn tại. Không gọi get() tùy tiện khi chưa biết có bản ghi. Service dùng orElseThrow để tạo lỗi nghiệp vụ rõ ràng. Với tìm kiếm theo tên, Spring Data phân tích tên phương thức; @EntityGraph sẽ được giải thích cùng quan hệ và DTO.
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.
package com.hocvietcode.academy;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.repository.*;
import org.springframework.data.repository.query.Param;
public interface CourseRepository extends JpaRepository<Course,Long> {
@EntityGraph(attributePaths="category")
Page<Course> findByTitleContainingIgnoreCase(String title,Pageable pageable);
}
Ai tạo bảng?
Mã mẫu chọn Flyway làm nguồn quản lý schema và đặt spring.jpa.hibernate.ddl-auto=validate. Tệp V1__create_courses.sql tạo hai bảng, khóa ngoại và dữ liệu mẫu. Flyway chạy migration chưa áp dụng; Hibernate kiểm tra schema trước khi ứng dụng phục vụ request.
H2 trong bộ nhớ giúp chạy ngay mà chưa cài MySQL. MODE=MySQL hỗ trợ một số cú pháp tương thích, không biến H2 thành MySQL thật. Các khác biệt về kiểu dữ liệu, collation, khóa và truy vấn vẫn cần kiểm thử với hệ quản trị sẽ triển khai.
Tệp trong mã nguồn mẫu: src/main/resources/db/migration/V1__create_courses.sql. Đ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.
CREATE TABLE categories (id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(80) NOT NULL);
CREATE TABLE courses (id BIGINT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(120) NOT NULL, price DECIMAL(12,2) NOT NULL, category_id BIGINT NOT NULL, version BIGINT, CONSTRAINT fk_course_category FOREIGN KEY(category_id) REFERENCES categories(id));
INSERT INTO categories(name) VALUES ('Java'), ('Python');
INSERT INTO courses(title,price,category_id,version) VALUES ('Spring Boot MVC',0,1,0),('Python cơ bản',0,2,0);
Dữ liệu demo và dữ liệu lâu dài
Mỗi lần tiến trình H2 này khởi động lại, database được tạo mới và migration chạy trên cơ sở dữ liệu trống. Đây là hành vi có chủ đích để học thử dễ dàng. Nếu cần giữ dữ liệu, chuyển sang profile MySQL ở bài sau, không đổi ddl-auto sang update chỉ để mong dữ liệu tự tồn tại.
Nếu ứng dụng báo table not found hoặc schema validation failed, kiểm tra migration có được đóng gói vào resources/db/migration và có chạy thành công không. Nếu Flyway báo checksum thay đổi trên database đã dùng, không sửa bảng lịch sử bằng tay để bỏ qua nguyên nhân; bài vận hành trình bày cách thêm migration mới.
Lỗi thường gặp và lưu ý
- JPA entity cần constructor không tham số phù hợp; record trong bộ bài dành cho DTO, không dùng làm entity này.
- Không dùng đồng thời Flyway và Hibernate update để mỗi công cụ tự sửa một kiểu schema.
Ghi nhớ
Entity mô tả dữ liệu, Repository truy xuất và Flyway quản lý cấu trúc; mỗi phần có một trách nhiệm rõ ràng.

