Spring Data JPA & Hibernate — Kiến thức nền tảng

Vấn đề — Tại sao không viết SQL thuần trong Java?

Trong Bài 4, bạn đã viết SQL trực tiếp trong shell MariaDB. Điều đó ổn cho quản trị database, nhưng còn việc thực thi SQL từ ứng dụng Java thì sao?

JDBC — Cách truyền thống

JDBC (Java Database Connectivity) là API Java tiêu chuẩn cho truy cập database. Đây là giao diện của nó:

public User findUserById(Long id) {
    String sql = "SELECT id, username, email, full_name, bio, role, " +
                 "is_active, created_at, updated_at FROM users WHERE id = ?";
    
    Connection connection = null;
    PreparedStatement statement = null;
    ResultSet resultSet = null;
    
    try {
        // 1. Lấy kết nối database
        connection = DriverManager.getConnection(
            "jdbc:mariadb://localhost:3306/blogdb", "bloguser", "blogpass");
        
        // 2. Chuẩn bị câu lệnh SQL
        statement = connection.prepareStatement(sql);
        statement.setLong(1, id);
        
        // 3. Thực thi truy vấn
        resultSet = statement.executeQuery();
        
        // 4. Ánh xạ kết quả sang đối tượng Java — THỦ CÔNG, từng cột một
        if (resultSet.next()) {
            User user = new User();
            user.setId(resultSet.getLong("id"));
            user.setUsername(resultSet.getString("username"));
            user.setEmail(resultSet.getString("email"));
            user.setFullName(resultSet.getString("full_name"));
            user.setBio(resultSet.getString("bio"));
            // ... và còn nhiều cột nữa
            return user;
        }
        return null;
        
    } catch (SQLException e) {
        throw new RuntimeException("Không thể tìm người dùng", e);
    } finally {
        // 5. Đóng mọi thứ — theo thứ tự ngược, kiểm tra null
        try { if (resultSet != null) resultSet.close(); } catch (SQLException e) { }
        try { if (statement != null) statement.close(); } catch (SQLException e) { }
        try { if (connection != null) connection.close(); } catch (SQLException e) { }
    }
}

Đó là 40 dòng code chỉ để thực thi một truy vấn SELECT đơn giản. Hãy tưởng tượng viết như vậy cho mọi truy vấn trong ứng dụng.

Các vấn đề với JDBC thuần

  1. Boilerplate khổng lồ — Quản lý kết nối, chuẩn bị statement, xử lý result set, và dọn dẹp tài nguyên cho mỗi truy vấn.
  2. Ánh xạ thủ công — Bạn ánh xạ từng cột sang từng trường bằng tay.
  3. Dễ lỗi — Quên đóng kết nối? Rò rỉ bộ nhớ. Gõ sai tên cột? Lỗi runtime.
  4. Không an toàn kiểu — SQL được viết dưới dạng chuỗi. Trình biên dịch không kiểm tra SQL có hợp lệ không.
  5. SQL đặc thù database — Nếu bạn chuyển từ MariaDB sang PostgreSQL, cú pháp SQL có thể cần thay đổi.

Chúng ta cần cách tiếp cận tốt hơn. Cách đó gọi là ORM.


Khái niệm ORM — Object-Relational Mapping

Sự không tương thích về trở kháng

Java và cơ sở dữ liệu quan hệ suy nghĩ về dữ liệu theo cách khác nhau cơ bản:

Java (Hướng đối tượng) Cơ sở dữ liệu quan hệ
Đối tượng với trường và phương thức Hàng với cột
Kế thừa (cấu trúc phân cấp class) Không có kế thừa native
Tham chiếu (đối tượng trỏ đến đối tượng khác) Khóa ngoại (số nguyên trỏ đến bảng khác)
Collection (List, Set, Map) Truy vấn JOIN giữa các bảng

ORM là gì?

Object-Relational Mapping (ORM) là kỹ thuật tự động chuyển đổi dữ liệu giữa đối tượng Java và bảng database. Framework ORM xử lý việc ánh xạ để bạn không phải viết thủ công.

Ý tưởng cốt lõi đơn giản:

Class Java    ←→  Bảng Database
Đối tượng     ←→  Hàng
Trường        ←→  Cột
Tham chiếu    ←→  Khóa ngoại
Collection    ←→  Truy vấn JOIN

Với ORM, thay vì viết SQL và ánh xạ kết quả thủ công, bạn làm việc trực tiếp với đối tượng Java:

// KHÔNG CÓ ORM (JDBC) — 40 dòng boilerplate
User user = findUserById(1L);  // Code JDBC phức tạp

// CÓ ORM (JPA/Hibernate) — một dòng
User user = entityManager.find(User.class, 1L);

JPA Specification vs Hibernate Implementation

Sự phân biệt này khiến nhiều người mới nhầm lẫn, nên hãy làm rõ.

JPA — Đặc tả (Specification)

JPA (Jakarta Persistence API)đặc tả — một tập hợp interface và annotation định nghĩa ORM nên hoạt động như thế nào trong Java. Nó là tài liệu nói: “Framework ORM phải cung cấp các annotation, interface, và hành vi này.”

JPA bản thân không chứa code triển khai nào. Nó giống như bản thiết kế.

Hibernate — Triển khai (Implementation)

Hibernatetriển khai phổ biến nhất của JPA. Nó là thư viện thực tế làm công việc — sinh SQL, quản lý kết nối, caching, lazy loading, và mọi thứ khác.

Nghĩ theo cách này:

  • JPA giống như interface Java — định nghĩa những phương thức gì tồn tại
  • Hibernate giống như class Java implements interface đó — định nghĩa cách các phương thức hoạt động

Spring Data JPA — Tầng Spring

Trên JPA và Hibernate, Spring thêm một tầng nữa gọi là Spring Data JPA. Nó đơn giản hóa truy cập database hơn nữa bằng cách cung cấp:

  • Repository interface — Bạn khai báo interface, Spring sinh implementation tại runtime.
  • Derived query method — Spring sinh truy vấn SQL từ tên phương thức.
  • Phân trang và sắp xếp — Hỗ trợ sẵn.

Stack trông như thế này:

Code của bạn
    ↓
Spring Data JPA  (Repository interface, derived query, phân trang)
    ↓
JPA              (Annotation và interface tiêu chuẩn)
    ↓
Hibernate        (Triển khai ORM thực tế, sinh SQL)
    ↓
JDBC             (Giao tiếp database cấp thấp)
    ↓
MariaDB Driver   (Giao thức đặc thù MariaDB)
    ↓
MariaDB Server

Thêm dependency Spring Data JPA & MariaDB

Thêm các dependency cần thiết vào pom.xml:

<dependencies>
    <!-- Dependency hiện có -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- MỚI: Spring Data JPA — bao gồm Hibernate, JPA, và Spring Data -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- MỚI: JDBC Driver MariaDB — cho phép Java giao tiếp với MariaDB -->
    <dependency>
        <groupId>org.mariadb.jdbc</groupId>
        <artifactId>mariadb-java-client</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

spring-boot-starter-data-jpa kéo về: Spring Data JPA, Hibernate, Jakarta Persistence API, HikariCP (connection pool hiệu suất cao), Spring JDBC và Spring Transaction.

mariadb-java-client là JDBC driver MariaDB. runtime có nghĩa thư viện này chỉ cần khi ứng dụng chạy, không cần khi biên dịch (vì code của bạn chỉ tham chiếu interface JPA, không phải class đặc thù MariaDB).


Cấu hình DataSource trong application.properties

# ============================================
# Cấu hình DataSource
# ============================================
spring.datasource.url=jdbc:mariadb://localhost:3306/blogdb
spring.datasource.username=bloguser
spring.datasource.password=blogpass
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver

# ============================================
# Cấu hình JPA / Hibernate
# ============================================
# Chiến lược DDL Auto — kiểm soát cách Hibernate quản lý schema database
spring.jpa.hibernate.ddl-auto=update

# Hiển thị câu lệnh SQL Hibernate sinh ra (hữu ích cho học tập/debug)
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

# Dialect Hibernate — đảm bảo SQL tối ưu được sinh cho MariaDB
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect

# ============================================
# Cấu hình HikariCP Connection Pool
# ============================================
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000

Connection Pool là gì?

Tạo kết nối database tốn kém — bao gồm TCP handshake, xác thực, và đàm phán giao thức. Connection pool duy trì một tập kết nối đã tạo sẵn. Khi code cần kết nối, nó mượn từ pool. Khi xong, kết nối quay lại pool thay vì bị đóng.

Không có pool:     Request → Tạo Connection → Truy vấn → Đóng Connection → Response
                   (100ms)       (5ms)     (100ms)

Có pool:           Request → Mượn Connection → Truy vấn → Trả Connection → Response
                   (0.1ms)        (5ms)      (0.1ms)

Ánh xạ Entity — @Entity, @Table, @Id, @GeneratedValue

Entity là gì?

Entity là class Java đại diện cho một bảng database. Mỗi instance của entity đại diện cho một hàng trong bảng. JPA sử dụng annotation để định nghĩa ánh xạ.

package com.example.blogapi.model;

import jakarta.persistence.*;
import java.time.LocalDateTime;

// @Entity đánh dấu class này là JPA entity — sẽ được ánh xạ đến bảng database.
@Entity

// @Table chỉ định bảng nào entity này ánh xạ đến.
// Chúng ta chỉ định "users" vì "user" là từ khóa dành riêng trong một số database.
@Table(name = "users")
public class User {

    // @Id đánh dấu trường này là khóa chính.
    // Mỗi entity PHẢI có đúng một trường @Id.
    @Id

    // @GeneratedValue cho JPA biết database sinh giá trị này.
    // GenerationType.IDENTITY nghĩa là database dùng AUTO_INCREMENT.
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String email;

    // JPA yêu cầu constructor không tham số (có thể protected).
    // Hibernate dùng constructor này để tạo instance qua reflection.
    protected User() { }

    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }

    // Getter và setter...
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

Các tùy chọn GenerationType

// IDENTITY — dùng tính năng auto-increment của database (MariaDB, MySQL).
// Đây là lựa chọn tốt nhất cho MariaDB.
@GeneratedValue(strategy = GenerationType.IDENTITY)

// SEQUENCE — dùng sequence database (PostgreSQL, Oracle).
@GeneratedValue(strategy = GenerationType.SEQUENCE)

// AUTO — để JPA provider chọn chiến lược. Có thể không dự đoán được.
@GeneratedValue(strategy = GenerationType.AUTO)

Với MariaDB, luôn dùng GenerationType.IDENTITY. Nó ánh xạ trực tiếp đến AUTO_INCREMENT.


Ánh xạ cột — @Column, @Enumerated, @Temporal

@Column — Ánh xạ cột chi tiết

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "username", nullable = false, unique = true, length = 50)
    private String username;

    @Column(name = "password_hash", nullable = false)
    private String passwordHash;

    // TEXT — dùng columnDefinition cho kiểu đặc thù database
    @Column(columnDefinition = "TEXT")
    private String bio;

    // Cột không cập nhật — JPA sẽ bao gồm trong INSERT nhưng không trong UPDATE
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;
}

Thuộc tính @Column chính:

Thuộc tính Mặc định Mô tả
name tên trường Tên cột trong database
nullable true Cột cho phép NULL hay không
unique false Giá trị phải duy nhất hay không
length 255 Độ dài tối đa cho cột VARCHAR
updatable true Cột có trong câu UPDATE hay không
columnDefinition Định nghĩa cột SQL thô (ví dụ “TEXT”, “DECIMAL(10,2)”)

Chiến lược đặt tên — camelCase sang snake_case

Spring Boot tự động chuyển đổi camelCase sang snake_case:

// Trường Java:       fullName
// Cột được sinh ra:  full_name   (tự động chuyển đổi!)

@Enumerated — Ánh xạ Enum Java

public enum PostStatus {
    DRAFT, PUBLISHED, ARCHIVED
}

@Entity
public class Post {
    // EnumType.STRING lưu tên enum dưới dạng chuỗi trong database.
    // LUÔN dùng STRING. Với ORDINAL, nếu bạn thêm constant mới giữa chừng,
    // dữ liệu cũ bị lỗi!
    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 20)
    private PostStatus status = PostStatus.DRAFT;
}

Annotation thời gian

Với kiểu Java 8+ (LocalDateTime, LocalDate), bạn không cần @Temporal — Hibernate ánh xạ tự động:

private LocalDateTime createdAt;   // → TIMESTAMP trong MariaDB
private LocalDate birthDate;       // → DATE trong MariaDB

Luôn sử dụng kiểu java.time trong code mới. Chúng bất biến, an toàn kiểu, và hoạt động với Hibernate không cần annotation thêm.


Chiến lược DDL Auto — create, update, validate, none

Property spring.jpa.hibernate.ddl-auto kiểm soát Hibernate làm gì với schema database khi khởi động.

# CREATE — Xóa tất cả bảng và tạo lại mỗi khi app khởi động.
# CẢNH BÁO: MẤT TẤT CẢ DỮ LIỆU mỗi lần khởi động lại!
spring.jpa.hibernate.ddl-auto=create

# UPDATE — So sánh entity class với database và áp dụng thay đổi.
# Thêm bảng và cột mới, nhưng KHÔNG BAO GIỜ xóa hay đổi tên.
spring.jpa.hibernate.ddl-auto=update

# VALIDATE — So sánh entity với database và ném lỗi nếu không khớp.
# KHÔNG sửa đổi database.
spring.jpa.hibernate.ddl-auto=validate

# NONE — Không làm gì. Hibernate bỏ qua khác biệt schema.
spring.jpa.hibernate.ddl-auto=none

Chiến lược nào cho môi trường nào?

Môi trường Chiến lược Lý do
Development update Tiện lợi — Hibernate thêm cột/bảng mới khi code
Testing create-drop Database sạch cho mỗi lần chạy test
Production validate hoặc none Không bao giờ để Hibernate sửa schema production

Trong Bài 10, chúng ta sẽ thay thế cách tiếp cận này bằng Flyway cho migration production.


Giới thiệu Spring Data Repository — JpaRepository

Đây là nơi phép thuật thực sự xảy ra. Spring Data JPA cho phép bạn tạo repository chức năng đầy đủ với không cần code triển khai.

Repository Interface

Nhớ UserRepository chúng ta viết ở Bài 2? Nó là class @Repository với các phương thức save(), findById(), findAll(), và deleteById(). Chúng ta viết tất cả code thủ công.

Với Spring Data JPA, bạn thay thế tất cả bằng một interface duy nhất:

package com.example.blogapi.repository;

import com.example.blogapi.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

// Chỉ vậy thôi. Không class, không implementation, không cần @Repository.
// Spring Data JPA sinh implementation tại runtime.
public interface UserRepository extends JpaRepository<User, Long> {
    // JpaRepository<User, Long> nghĩa là:
    // - User là kiểu entity
    // - Long là kiểu khóa chính (trường id)
}

Bằng cách extend JpaRepository, bạn tự động có các phương thức này mà không cần viết code nào:

// TẠO / CẬP NHẬT
User save(User entity);                          // Lưu hoặc cập nhật
List<User> saveAll(Iterable<User> entities);      // Lưu nhiều

// ĐỌC
Optional<User> findById(Long id);                 // Tìm theo khóa chính
List<User> findAll();                              // Lấy tất cả
long count();                                      // Đếm tổng
boolean existsById(Long id);                       // Kiểm tra tồn tại

// XÓA
void deleteById(Long id);                          // Xóa theo ID
void delete(User entity);                          // Xóa theo entity

// PHÂN TRANG & SẮP XẾP
Page<User> findAll(Pageable pageable);             // Tìm tất cả có phân trang
List<User> findAll(Sort sort);                     // Tìm tất cả có sắp xếp

Hơn 15 phương thức miễn phí. Không SQL, không JDBC, không boilerplate.

Sử dụng Repository

Inject repository vào service giống như bất kỳ Spring bean nào:

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User createUser(User user) {
        return userRepository.save(user);  // Thực thi: INSERT INTO users ...
    }

    public User getUserById(Long id) {
        return userRepository.findById(id) // Thực thi: SELECT * FROM users WHERE id = ?
                .orElseThrow(() -> new RuntimeException("Không tìm thấy người dùng id: " + id));
    }

    public List<User> getAllUsers() {
        return userRepository.findAll();   // Thực thi: SELECT * FROM users
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);     // Thực thi: DELETE FROM users WHERE id = ?
    }
}

So sánh với 40 dòng code JDBC ở Phần 1. Tầng service sạch sẽ, tập trung vào logic nghiệp vụ, và hoàn toàn không có boilerplate database.


Thực hành: Kết nối Blog API với MariaDB bằng JPA Entity

Hãy thay thế lưu trữ in-memory bằng database MariaDB thật.

Bước 1: Tạo Entity User

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true, length = 50)
    private String username;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(name = "password_hash", nullable = false)
    private String passwordHash;

    @Column(name = "full_name", length = 100)
    private String fullName;

    @Column(columnDefinition = "TEXT")
    private String bio;

    @Column(nullable = false, length = 20)
    private String role = "ROLE_USER";

    @Column(name = "is_active", nullable = false)
    private boolean active = true;

    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @Column(name = "updated_at", nullable = false)
    private LocalDateTime updatedAt;

    // Callback vòng đời: được gọi tự động trước khi entity được lưu lần đầu
    @PrePersist
    protected void onCreate() {
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }

    // Callback vòng đời: được gọi tự động trước mỗi lần cập nhật
    @PreUpdate
    protected void onUpdate() {
        this.updatedAt = LocalDateTime.now();
    }

    protected User() { }

    public User(String username, String email, String passwordHash) {
        this.username = username;
        this.email = email;
        this.passwordHash = passwordHash;
    }

    // --- Getter và Setter ---
    // (bỏ qua cho ngắn gọn — tương tự các bài trước)
}

Bước 2: Tạo Repository Interface

public interface UserRepository extends JpaRepository<User, Long> {

    // Spring Data JPA sinh implementation từ tên phương thức!
    // findByEmail → SELECT * FROM users WHERE email = ?
    Optional<User> findByEmail(String email);

    // findByUsername → SELECT * FROM users WHERE username = ?
    Optional<User> findByUsername(String username);

    // existsByEmail → SELECT EXISTS(SELECT 1 FROM users WHERE email = ?)
    boolean existsByEmail(String email);

    boolean existsByUsername(String username);
}

Bước 3: Tạo Post Entity với quan hệ

@Entity
@Table(name = "posts")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 500)
    private String title;

    @Column(nullable = false, length = 500, unique = true)
    private String slug;

    @Column(nullable = false, columnDefinition = "TEXT")
    private String content;

    @Column(nullable = false, length = 20)
    private String status = "DRAFT";

    // @ManyToOne định nghĩa quan hệ: nhiều bài viết thuộc một user.
    // FetchType.LAZY nghĩa là: KHÔNG tải User khi tải Post.
    // User chỉ được tải khi bạn gọi post.getAuthor().
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author_id", nullable = false)
    private User author;

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

    @PrePersist
    protected void onCreate() {
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }

    @PreUpdate
    protected void onUpdate() {
        this.updatedAt = LocalDateTime.now();
    }

    // ... constructor, getter, setter
}

Bước 4: Kiểm thử — Dữ liệu tồn tại qua khởi động lại!

# Tạo dữ liệu test trong database
docker exec -it blogdb mariadb -u bloguser -pblogpass blogdb \
  -e "INSERT INTO users (username, email, password_hash, full_name, role, is_active, created_at, updated_at) VALUES ('alice', 'alice@example.com', 'temphash', 'Alice Johnson', 'ROLE_ADMIN', true, NOW(), NOW());"

# Tạo bài viết qua API
curl -X POST http://localhost:8080/api/posts \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Bắt đầu với Spring Data JPA",
    "content": "Spring Data JPA giúp truy cập database cực kỳ đơn giản...",
    "authorId": 1
  }'

# Khởi động lại ứng dụng, rồi lấy bài viết
curl http://localhost:8080/api/posts

# Bài viết VẪN CÒN ĐÓ! Dữ liệu được lưu bền vững trong MariaDB.

Đây là khoảnh khắc — dữ liệu của bạn tồn tại qua khởi động lại ứng dụng. Vấn đề lưu trữ in-memory từ Bài 2 và 3 đã được giải quyết.

Bài tập

  1. Tạo UserController: Xây dựng endpoint GET /api/usersGET /api/users/{id}.
  2. Tạo CategoryController: Xây dựng CRUD endpoint cho category, bao gồm GET /api/categories/{slug}.
  3. Thêm derived query method: Trong PostRepository, thêm findByAuthorIdAndStatus(Long authorId, String status).
  4. Kiểm thử persistence: Tạo nhiều bài viết, khởi động lại ứng dụng, xác nhận dữ liệu vẫn còn.

Tổng kết

Bài giảng này là bước ngoặt — bạn chuyển từ lưu trữ in-memory sang database bền vững thật:

  • Vấn đề JDBC: JDBC thuần đòi hỏi boilerplate khổng lồ cho mỗi truy vấn.
  • ORM: Cầu nối giữa đối tượng Java và bảng database. Bạn đánh dấu class bằng metadata, ORM xử lý sinh SQL và ánh xạ kết quả.
  • JPA vs Hibernate: JPA là đặc tả (bản thiết kế), Hibernate là triển khai (code thực tế). Spring Data JPA thêm tầng tiện lợi với repository tự sinh.
  • Dependency: spring-boot-starter-data-jpa kéo về mọi thứ cần thiết. mariadb-java-client là JDBC driver.
  • Ánh xạ entity: @Entity đánh dấu class cho ORM, @Id đánh dấu khóa chính, @GeneratedValue(IDENTITY) dùng AUTO_INCREMENT.
  • Ánh xạ cột: @Column tùy chỉnh thuộc tính cột, @Enumerated(STRING) ánh xạ enum an toàn, @PrePersist/@PreUpdate xử lý timestamp tự động.
  • DDL Auto: Dùng update cho development, validate cho production.
  • JpaRepository: Extend interface này và có 15+ phương thức CRUD miễn phí. Spring sinh tại runtime.

Bài tiếp theo

Trong Bài 6, chúng ta sẽ khám phá thao tác CRUD với Spring Data JPA chuyên sâu — derived query method, JPQL tùy chỉnh, native SQL, phân trang, sắp xếp, và auditing.


Tham chiếu nhanh

Khái niệm Mô tả
JDBC API Java cấp thấp cho truy cập database (nhiều boilerplate)
ORM Kỹ thuật ánh xạ đối tượng Java đến bảng database tự động
JPA Jakarta Persistence API — đặc tả tiêu chuẩn cho ORM trong Java
Hibernate Triển khai JPA phổ biến nhất
Spring Data JPA Tầng tiện lợi — tự sinh repository từ interface
@Entity Đánh dấu class là JPA entity (ánh xạ đến bảng)
@Table(name = "...") Chỉ định tên bảng database
@Id Đánh dấu trường khóa chính
@GeneratedValue(IDENTITY) Dùng AUTO_INCREMENT cho sinh ID
@Column Tùy chỉnh ánh xạ cột (tên, nullable, unique, length)
@Enumerated(STRING) Ánh xạ enum Java dưới dạng chuỗi trong database
@PrePersist Callback vòng đời — chạy trước INSERT đầu tiên
@PreUpdate Callback vòng đời — chạy trước mỗi UPDATE
@ManyToOne Định nghĩa quan hệ nhiều-một (ví dụ nhiều bài → một tác giả)
FetchType.LAZY Tải entity liên quan chỉ khi truy cập (khuyến nghị)
JpaRepository Interface cung cấp đầy đủ CRUD + phân trang
ddl-auto=update Hibernate tự áp dụng thay đổi schema (chỉ cho dev)
ddl-auto=validate Hibernate kiểm tra schema không thay đổi (production)
HikariCP Connection pool hiệu suất cao mặc định trong Spring Boot

Để 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 *