openplanning

Bắt đầu với JPA Criteria Query API

  1. Thư viện
  2. Entities
  3. Ví dụ đơn giản
  4. Sử dụng các biểu thức
  5. Kết hợp các điều kiện
  6. Sắp xếp (Order)
  7. Có thể bạn quan tâm
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
JPQLCriteria 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
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
  • Tạo đối tượng CriteriaBuilder. Đây là một đối tượng rất hữu ích, nó được sử dụng thường xuyên trong JPA Criteria API. Bạn sẽ hiểu hơn về nó trong các ví dụ.
2
CriteriaQuery<Employee> criteriaQuery = builder.createQuery(Employee.class);
  • Tạo đối tượng CriteriaQuery, là tiêu chí (criteria) để truy vấn vào cơ sở dữ liệu, với mong muốn kết quả trả về là Employee hoặc danh sách của nó.
3
Root<Employee> root = criteriaQuery.from(Employee.class);
  • Cái này đại diện cho ENTITY gốc. Bước này là sự chuẩn bị cho bước 4.
4
// Select * from Employee
criteriaQuery = criteriaQuery.select(root);
// (select, where, orderBy, groupBy, ...)
  • Thêm ENTITY gốc vào câu truy vấn. Mỗi câu truy vấn chỉ bao gồm một ENTITY gốc. Các ENTITIES khác sẽ tham gia vào câu truy vấn thông qua JOIN (LEFT JOIN, RIGHT JOIN, CROSS JOIN).
5
TypedQuery<Employee> query = entityManager.createQuery(criteriaQuery);
  • Từ đối tượng CriteriaQuery tạo ra đối tượng Query. Hay nói cách khác, từ tiêu chí tạo ra câu truy vấn.
6
List<Employee> allEmps = query.getResultList();
  • Từ câu truy vấn lấy ra kết quả.

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));

6. Sắp xếp (Order)

Sort the results:
criteriaQuery.orderBy( //
		builder.asc(root.get("firstName")), //
		builder.desc(root.get("startDate")) //
);

7. Có thể bạn quan tâm

  • JPA Criteria Query API Join
  • Spring Data JPA Specifications Join