Generate primary key trong Hibernate – JPA

Primary key(khoá chính) là thành phần không thể thiếu của một bảng trong database dùng để định danh một dòng dữ liệu trong database. Thông thường có 2 cách để chọn primary key là natural keystechnical keys.

Trong Natural keys các thuộc tính là những thông tin của dữ liệu thực ví dụ emailmssv được chọn là primary key của bảng SinhVien. Trong khi Technical keys thường chỉ chứa một thuộc tính và không liên quan đến thông tin thực ví dụ như một số nguyên tăng dần, UUID etc.

Mình đọc được một bài viết của một blogger là tác giả của More than 70 solutions to commom Hibernate problems – Thorben Janssen, một cuốn sách nổi tiếng về Hibernate, trong đó ông ấy khuyên nên sử dụng Technical keys vì các lợi ích nó mang lại như dễ quản lý, đánh index hiệu quả, cho phép bạn tập trung vào phần business của ứng dụng và tránh các lỗi về hiệu xuất.

Natural key

Đây là cách ánh xạ các thuộc tính của primary key đơn giản nhất, chúng ta chỉ cần đánh dấu một thuộc tính với @Id annotation.

@Entity
public class Student {
 
    @Id
    private long studentId;
    
    // standard constructor, getters, setters
}

Generate khoá chính trong JPA – Hibernate

JPA hỗ trợ generate primary key tự động với @GeneratedValue annotation bao gồm 4 kiểu: AUTO, IDENTITY, SEQUENCE, TABLE. Nếu chúng ta không chỉ định 1 trong 4 tuỳ chọn này thì Hibernate mặc định là AUTO.

GenerationType.AUTO

GenerationType.AUTO là kiểu generate primary key mặc định cho phép persistence provider tự lựa chọn kiểu mà nó muốn.

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

Nếu bạn đang sử dụng Hibernate là persistence provider thì nó sẽ chọn kiểu generate key dựa trên database specific dialect. Với những database phổ biến hiện nay sẽ là GenerationType.SEQUENCE sẽ được giải thích ở phần sau.

Kể từ Hibernate version 5.0 sẽ hỗ trợ UUIDGenerator. Để sử dụng chúng ta chỉ cần khai báo primary key với kiểu dữ liệu UUID đi cùng @GeneratedValue.

@Entity
public class Student {
 
    @Id
    @GeneratedValue
    private UUID id;
 
    // ...
}

GenerationType.IDENTITY

GenerationType.IDENTITY là kiểu dễ sử dụng nhất nhưng về mặt hiệu năng thì nó không phải là một lựa chọn hàng đầu. GenerationType.IDENTITY dựa trên một dữ liệu tăng dần (AUTO_INCREMENT) trong database, cho phép database sinh một giá trị mới với mỗi thao tác insert.

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

Tương ứng dưới database chúng ta sẽ tạo cột id theo cú pháp sau trong Mysql,

id int NOT NULL AUTO_INCREMENT,

Cách tiếp cận này có một nhược điểm rất lớn trong Hibernate vì nó luôn yêu cầu primary key của mỗi entity phải có giá trị để quản lý, nhưng để lấy được giá trị tiếp theo của Identity column chỉ có cách thực thi câu SQL Insert thì mới có thể biết được. Điều này dẫn đến các câu lệnh SQL Insert được thực thi ngay lập tức để lấy giá trị của Identity column, khiến Hibernate phải vô hiệu hoá tính năng Batch Insert JDBC.

GenerationType.SEQUENCE

Đây là kiểu generate được Hibernate khuyến khích sử dụng, GenerationType.SEQUENCE Hibernate sẽ tạo ra một bảng HIBERNATE_SEQUENCE trong database dùng để lưu trữ giá trị tiếp theo của primary key. Nó sẽ thực thi một câu lệnh SQL SELECT để lấy giá trị tiếp theo trong bảng HIBERNATE_SEQUENCE khi thực thi một câu lệnh SQL INSERTBởi vì giá trị tiếp theo có thể lấy được từ bảng HIBERNATE_SEQUENCE nên các câu lệnh SQL Insert không cần thiết phải thực hiện ngay lập tức, điều này cho phép Hibernate sử dụng tính năng JDBC Batch để tối ưu hóa hiệu xuất.

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

Ngoài ra bạn có thể thay đổi các thông tin mặc định được  GenerationType.SEQUENCE định nghĩa với @SequenceGenerator, nó cho phép bạn định nghĩa generator name, Sequence table name, initValue, size etc.

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_generator")
@SequenceGenerator(name="book_generator", sequenceName = "book_seq", initialValue = 10)
private Long id;

Như đoạn code trên thì HIBERNATE_SEQUENCE sẽ được đổi thành book_seq và giá trị ban đầu là 10 thay vì 1 như mặc định.

GenerationType.TABLE

Hiện nay rất ít ứng dụng sử dụng GenerationType.TABLE, nó mô phỏng một sequence bằng cách lưu trữ và cập nhật giá trị tiếp theo của primary key trong một table sử dụng cơ chế pessimistic locks bắt buộc các transaction phải được thực thi theo thứ tự gây ảnh hưởng nghiêm trọng đến hiệu suất của chương trình. 

Các bạn nên sử dụng GenerationType.SEQUENCE khi có thể, hiện nay hầu hết các database đều hỗ trợ kiểu generate này.

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

Tương tự SEQUENCE chúng ta có thể thay đổi các thông tin mặc định của GenerationType.TABLE thông qua @TableGenerator annotation.

@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "book_generator")
@TableGenerator(name="book_generator", table="id_generator", schema="bookstore")
private Long id;

Tóm lược

Các bạn đã biết được rằng có 4 kiểu generate primary key trong Hibernate mà trong đó:

  • AUTO – Hibernate sẽ tự chọn kiểu generate phù hợp, thường là SEQUENCE/
  • IDENTITY – Dựa trên AUTO_INCREMENT column trong database.
  • SEQUENCE – Lưu trữ giá trị tiếp theo của primary trong database ở một table riêng.
  • TABLE  – Tương tự SEQUENCE nhưng không được khuyến khích sử dụng.

Nếu được hãy sử dụng GenerationType.SEQUENCE vì đây là kiểu generate được Hibernate khuyến khích. Nó cho phép Hibernate triển khai các tính năng tối ưu hiệu xuất như Batch Insert JDBC etc.

Nguồn tham khảo

https://thorben-janssen.com/jpa-generate-primary-keys/

https://www.baeldung.com/hibernate-identifiers

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x