Bắt đầu với JPA Criteria Query API
JPA Criteria Query API là một API được định nghĩa sẵn, được sử dụng để định nghĩa các câu truy vấn cho các Entities. Nó là cách khác để định nghĩa một câu truy vấn so với cách thông thường là sử dụng JPQL. JPA Criteria Query là an toàn, có tính di động và dễ dàng sửa đổi bằng cách thay đổi cú pháp.
Ưu điểm chính của Criteria Query API là lỗi có thể được phát hiện sớm hơn trong thời gian biên dịch. JPQL được viết dưới dạng văn bản, còn Criteria Query được xây dựng dựa trên các tiêu chí (criteria), mang màu sắc Java và trừu tượng hơn so với JPQL. Tuy nhiên cả hai đều giống nhau về hiệu suất và hiệu quả.
- Bắt đầu với JPA JPQL
JPQL và Criteria Query có mối quan hệ chặt chẽ với nhau, cả hai đều sử dụng các toán tử tương tự nhau trong các truy vấn của chúng và đều tuân thủ theo gói jakarta.persistence.criteria.
Criteria Query API bao gồm rất nhiều tính năng và cú pháp, và không thể đề cập toàn bộ trong một bài viết. Vì vậy, bài viết này chỉ hướng dẫn bạn khai báo các thư viện và bắt đầu với các ví dụ Criteria Query cơ bản. Bạn sẽ tìm thấy các bài viết liên quan được đề xuất ở phía cuối bài viết này.
1. Thư viện
Nếu bạn sử dụng JPA Criteria Query API trong dự án Spring Boot, hãy thêm thư viện sau:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2. Entities
Hãy xem mô hình dữ liệu mẫu mà chúng tôi sẽ sử dụng trong các ví dụ.
Employee.java
package org.o7planning.sample_bank_db.entity;
// Imports
@Entity
@Table(name = "Employee")
public class Employee implements Serializable {
@Id
@GeneratedValue
@Column(name = "Emp_Id", nullable = false)
private Long empId;
@Column(name = "Start_Date", nullable = false)
private LocalDate startDate;
@Column(name = "End_Date", nullable = true)
private LocalDate endDate;
@Column(name = "First_Name", length = 32, nullable = false)
private String firstName;
@Column(name = "Last_Name", length = 32, nullable = false)
private String lastName;
@Column(name = "Title", length = 32, nullable = false)
private String title;
@ManyToOne
@JoinColumn(name = "Dept_Id", nullable = true, //
foreignKey = @ForeignKey(name = "Employee_Department_Fk"))
private Department department;
@ManyToOne
@JoinColumn(name = "Superior_Emp_Id", nullable = true, //
foreignKey = @ForeignKey(name = "Employee_Employee_Fk"))
private Employee superiorEmployee;
// Other Properties, Setters and Getters.
}
Mô hình dữ liệu được sử dụng trong bài viết này là một cơ sở dữ liệu mẫu, mô phỏng dữ liệu của một ngân hàng. Nếu quan tâm, bạn có thể download dự án dưới đây, nó được viết trên Spring Boot + JPA và hỗ trợ mọi loại cơ sở dữ liệu. Khi chạy dự án, các bảng và dữ liệu sẽ tự động được tạo ra.
- JPA Sample Bank Database
3. Ví dụ đơn giản
Hãy bắt đầu với một ví dụ đơn giản, truy vấn tất cả các bản ghi trong bảng Employee với JPA Criteria API. Sau đó chúng ta sẽ giải thích từng dòng mã.
Chú ý: Đây là ví dụ đơn giản sử dụng JPA trong Spring. Nếu bạn sử dụng JPA trong một dự án Java thông thường thì cũng không có nhiều khác biệt, chỉ cần tạo đối tượng EntityManager một cách thủ công.
SimpleExample.java
package org.o7planning.jpa_criteria_query.java_11215;
import java.util.List;
import org.o7planning.sample_bank_db.entity.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
@Component
public class SimpleExample {
@Autowired
private EntityManager entityManager;
public void test() {
// (1)
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
// (2)
CriteriaQuery<Employee> criteriaQuery = builder.createQuery(Employee.class);
// (3)
Root<Employee> root = criteriaQuery.from(Employee.class);
// (4) - (select, where, orderBy, groupBy, ...)
// --> Select * from EMPLOYEE
criteriaQuery = criteriaQuery.select(root);
// (5)
TypedQuery<Employee> query = entityManager.createQuery(criteriaQuery);
// (6)
List<Employee> allEmps = query.getResultList();
System.out.println("Emp Count: " + allEmps.size());
}
}
No | Code/Description |
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
4. Sử dụng các biểu thức
Tìm tất cả các nhân viên có tên "Susan".
//
// Select e from Employee e where e.firstName = 'Susan'
//
criteriaQuery //
.select(root) // --> from Employee e
// where e.firstName ='Susan'
.where(builder.equal(root.get("firstName"), "Susan"));
Tìm kiếm tất cả các nhân viên có khác "Susan".
//
// Select e from Employee e where e.firstName != 'Susan'
//
criteriaQuery //
.select(root) // --> from Employee e
// where e.firstName != 'Susan'
.where(builder.notEqual(root.get("firstName"), "Susan"));
Tìm các nhân viên có tên trong một danh sách được chỉ định.
criteriaQuery //
.select(root) // --> from Employee e
// where e.firstName in ('Susan', 'Robert')
.where(root.get("firstName").in("Susan", "Robert"));
Tìm các nhân viên với "empId" nhỏ hơn 10.
criteriaQuery //
.select(root) // --> from Employee e
// where e.empId < 10
.where(builder.lt(root.get("empId"), 10));
Tìm các nhân viên bắt đầu làm việc tại công ty sau một ngày được chỉ định.
criteriaQuery //
.select(root) // --> from Employee e
// where e.startDate > LocalDate.of(2022, 1, 1)
.where(builder.greaterThan(root.get("startDate"), LocalDate.of(2002, 1, 1)));
Tìm các nhân viên vẫn đang làm việc tại công ty.
criteriaQuery //
.select(root) // --> from Employee e
// where e.endDate is null
.where(builder.isNull(root.get("endDate")));
Chú ý: Các ví dụ được đề cập trong bài viết này là cơ bản, chỉ có một Entity tham gia trong câu truy vấn, nếu bạn muốn có ví dụ với nhiều Entities, bạn có thể xem bài viết dưới đây:
- JPA Criteria Query API Join
5. Kết hợp các điều kiện
JPA Criteria API cho phép bạn kết hơp nhiều điều kiện thành một điều kiện.
Predicate[] predicates = new Predicate[2];
predicates[0] = builder.greaterThan(root.get("startDate"), LocalDate.of(2002, 01, 01));
predicates[1] = builder.equal(root.get("firstName"), "Susan");
//
criteriaQuery //
.select(root) // --> from Employee e
// where e.startDate > LocalDate.of(2002, 01, 01) and e.firstName = 'Susan'
.where(predicates);
Sử dụng toán tử logic "AND":
Predicate predicate1 = builder.greaterThan(root.get("startDate"), LocalDate.of(2002, 01, 01));
Predicate predicate2 = builder.equal(root.get("firstName"), "Susan");
// (4)
criteriaQuery //
.select(root) // --> from Employee e
// where e.startDate > LocalDate.of(2002, 01, 01) and e.firstName = 'Susan'
.where(builder.and(predicate1, predicate2));
Sử dụng toán tử logic "OR":
Predicate predicate1 = builder.greaterThan(root.get("startDate"), LocalDate.of(2002, 01, 01));
Predicate predicate2 = builder.equal(root.get("firstName"), "Susan");
// (4)
criteriaQuery = criteriaQuery //
.select(root) // --> from Employee e
// where e.startDate > LocalDate.of(2002, 01, 01) or e.firstName = 'Susan'
.where(builder.or(predicate1, predicate2));