Học viên : Lê Văn Tiến (Java 23)
Bài viết gốc: jpa-generate-primary-keys

Làm thế nào để có được các giá trị khóa chính trong ứng dụng của bạn? Bạn sử dụng các natural keys hay tạo ra các technical keys?

Tôi thích tạo technical keys đơn giản, số học, như bạn có thể thấy trong đoạn mã dưới đây thay vì sử dụng các natural keys mà thường yêu cầu sự kết hợp của nhiều thuộc tính

@ID
private Long id;

Các technical keys dễ quản lý hơn và tất cả các hệ thống liên quan, chủ yếu là cơ sở dữ liệu và Hibernate, có thể chỉ mục chúng một cách rất hiệu quả. Điều này cho phép bạn tập trung vào business logic trong ứng dụng của bạn và tránh các vấn đề về hiệu suất.

4 Cách để tạo khóa chính

JPA hỗ trợ 4 cách tạo khóa chính khác nhau, tạo ra các giá trị khóa chính theo cách tự động hoặc sử dụng các tính năng của cơ sở dữ liệu, như các cột tự tăng hoặc chuỗi. Điều duy nhất bạn phải làm là thêm annotation @GeneratedValue vào thuộc tính khóa chính của bạn và chọn một cách tạo khóa.

@Id
@GeneratedValue
private Long id;

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 Hibernatepersistence 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.

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. Nó dựa trên AUTO_INCREMENT column trong database và cho phép database tạo ra một giá trị mới với mỗi thao tác insert. Từ quan điểm của database, điều này rất hiệu quả vì các cột tự tăng được tối ưu hóa cao và không đòi hỏi bất kỳ câu lệnh bổ sung nào.

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

Cách tiếp cận này có một nhược điểm rất lớn trong Hibernate, vì nó yêu cầu một giá trị khóa chính cho mỗi đối tượng được quản lý và do đó phải thực hiện câu lệnh insert ngay lập tức. Điều này ngăn nó khỏi việc sử dụng các kỹ thuật tối ưu hóa khác nhau như JDBC batching.

GenerationType.SEQUENCE

GenerationType.SEQUENCE là cách tôi ưa thích để tạo ra các giá trị khóa chính và sử dụng một database sequence để tạo ra các giá trị duy nhất.

Nó yêu cầu các câu lệnh select bổ sung để lấy giá trị tiếp theo từ một database sequence. Nhưng điều này không ảnh hưởng đến hiệu suất của hầu hết các ứng dụng. Và nếu ứng dụng của bạn phải lưu trữ một lượng lớn các đối tượng mới, bạn có thể sử dụng một số tối ưu hóa cụ thể của Hibernate để giảm số lượng câu lệnh.

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

Nếu bạn không cung cấp bất kỳ thông tin bổ sung nào, Hibernate sẽ yêu cầu giá trị tiếp theo từ seqence mặc định của mình. 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", allocationSize=50)
private Long id;

GenerationType.TABLE

GenerationType.TABLE hiện nay rất ít ứng dụng sử dụng, 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

Như bạn đã thấy, JPA cung cấp 4 cách khác nhau để tạo ra các giá trị khóa chính:

  • AUTO: Hibernate sẽ tự chọn kiểu generate phù hợp dựa trên database specific dialect
  • IDENTITY: Hibernate dựa trên AUTO_INCREMENT column trong database để tạo khóa chính
  • SEQUENCE: Hibernate yêu cầu giá trị khóa chính từ một database sequence.
  • TABLE: *Hibernate sử dụng một database table để mô phỏng một sequence

Tôi ưa thích sử dụng GenerationType.SEQUENCE vì nó rất hiệu quả và cho phép Hibernate quyết định khi nào thực hiện câu lệnh insert. Điều này cung cấp tính linh hoạt cần thiết để sử dụng các kỹ thuật tối ưu hóa hiệu suất khác như JDBC batching.