Sử dụng tham số truy vấn JPA

1. Giới thiệu

Xây dựng các truy vấn bằng JPA không khó; tuy nhiên, đôi khi chúng ta quên mất những điều đơn giản tạo nên sự khác biệt rất lớn.

Một trong những thứ này là các tham số truy vấn JPA, và đây là những gì chúng ta sẽ nói đến.

2. Tham số truy vấn là gì?

Hãy bắt đầu bằng cách giải thích các tham số truy vấn là gì.

Tham số truy vấn là một cách để xây dựng và thực thi các truy vấn được tham số hóa. Vì vậy, thay vì:

SELECT * FROM employees e WHERE e.emp_number = '123';

Chúng tôi sẽ làm:

SELECT * FROM employees e WHERE e.emp_number = ?;

Bằng cách sử dụng một câu lệnh được chuẩn bị sẵn trong JDBC, chúng ta cần đặt tham số trước khi thực hiện truy vấn:

pStatement.setString(1, 123);

3. Tại sao chúng ta nên sử dụng tham số truy vấn?

Tuy nhiên, thay vì sử dụng các tham số truy vấn, chúng tôi có thể đã chọn sử dụng các ký tự, đó không phải là cách được khuyến nghị để làm điều đó, như chúng ta sẽ thấy bây giờ.

Hãy viết lại truy vấn trước đó để lấy nhân viên bằng emp_number bằng cách sử dụng API JPA, nhưng thay vì sử dụng một tham số, chúng tôi sẽ sử dụng một chữ để chúng tôi có thể minh họa rõ ràng tình huống:

String empNumber = "A123"; TypedQuery query = em.createQuery( "SELECT e FROM Employee e WHERE e.empNumber = '" + empNumber + "'", Employee.class); Employee employee = query.getSingleResult();

Cách tiếp cận này có một số hạn chế:

  • Việc nhúng các tham số gây ra rủi ro bảo mật khiến chúng ta dễ bị tấn công JPQL. Thay vì giá trị mong đợi, kẻ tấn công có thể đưa vào bất kỳ biểu thức JPQL không mong đợi và có thể nguy hiểm nào
  • Tùy thuộc vào việc triển khai JPA mà chúng tôi sử dụng và tính toán kinh nghiệm của ứng dụng của chúng tôi, bộ nhớ cache truy vấn có thể cạn kiệt. Một truy vấn mới có thể được tạo, biên dịch và lưu vào bộ nhớ đệm mỗi khi chúng tôi sử dụng nó với mỗi giá trị / tham số mới. Ở mức tối thiểu, nó sẽ không hiệu quả và nó cũng có thể dẫn đến lỗi OutOfMemoryError không mong muốn

4. Tham số truy vấn JPA

Tương tự như các tham số câu lệnh được chuẩn bị sẵn trong JDBC, JPA chỉ định hai cách khác nhau để viết các truy vấn được tham số hóa bằng cách sử dụng:

  • Tham số vị trí
  • Các thông số được đặt tên

Chúng tôi có thể sử dụng tham số vị trí hoặc tham số được đặt tên nhưng chúng tôi không được kết hợp chúng trong cùng một truy vấn.

4.1. Tham số vị trí

Sử dụng các tham số vị trí là một cách để tránh các vấn đề nói trên được liệt kê trước đó.

Hãy xem cách chúng ta viết một truy vấn như vậy với sự trợ giúp của các tham số vị trí:

TypedQuery query = em.createQuery( "SELECT e FROM Employee e WHERE e.empNumber = ?1", Employee.class); String empNumber = "A123"; Employee employee = query.setParameter(1, empNumber).getSingleResult();

Như chúng ta đã thấy trong ví dụ trước, chúng ta khai báo các tham số này trong truy vấn bằng cách nhập dấu chấm hỏi theo sau là một số nguyên dương . Chúng tôi sẽ bắt đầu với số 1 và tiến lên, tăng dần từng số một.

Chúng tôi có thể sử dụng cùng một tham số nhiều lần trong cùng một truy vấn, điều này làm cho các tham số này giống với các tham số được đặt tên hơn.

Đánh số tham số là một tính năng rất hữu ích vì nó cải thiện khả năng sử dụng, khả năng đọc và bảo trì.

Điều đáng nói là liên kết tham số vị trí cũng được hỗ trợ bởi các truy vấn SQL gốc .

4.2. Các thông số vị trí có giá trị bộ sưu tập

Như đã nêu trước đây, chúng tôi cũng có thể sử dụng các tham số có giá trị tập hợp:

TypedQuery query = entityManager.createQuery( "SELECT e FROM Employee e WHERE e.empNumber IN (?1)" , Employee.class); List empNumbers = Arrays.asList("A123", "A124"); List employees = query.setParameter(1, empNumbers).getResultList();

4.3. Tham số được đặt tên

Các tham số được đặt tên khá giống với các tham số vị trí; tuy nhiên, bằng cách sử dụng chúng, chúng tôi làm cho các tham số rõ ràng hơn và truy vấn trở nên dễ đọc hơn:

TypedQuery query = em.createQuery( "SELECT e FROM Employee e WHERE e.empNumber = :number" , Employee.class); String empNumber = "A123"; Employee employee = query.setParameter("number", empNumber).getSingleResult();

Truy vấn mẫu trước đó giống như truy vấn đầu tiên nhưng chúng tôi đã sử dụng : number , một tham số được đặt tên, thay vì ? 1 .

Chúng ta có thể thấy chúng ta đã khai báo tham số bằng dấu hai chấm theo sau là số nhận dạng chuỗi (định danh JPQL) là trình giữ chỗ cho giá trị thực sẽ được đặt trong thời gian chạy. Trước khi thực hiện truy vấn, tham số hoặc các tham số phải được đặt bằng cách đưa ra phương thức setParameter .

Một điều thú vị để nhận xét là các TypedQuery hỗ trợ phương pháp chaining mà trở nên rất hữu ích khi nhiều tham số phải được thiết lập.

Hãy tiếp tục và tạo một biến thể của truy vấn trước đó bằng cách sử dụng hai tham số được đặt tên để minh họa chuỗi phương thức:

TypedQuery query = em.createQuery( "SELECT e FROM Employee e WHERE e.name = :name AND e.age = :empAge" , Employee.class); String empName = "John Doe"; int empAge = 55; List employees = query .setParameter("name", empName) .setParameter("empAge", empAge) .getResultList();

Tại đây, chúng tôi đang truy xuất tất cả nhân viên có tên và tuổi đã cho. Như chúng ta thấy rõ ràng và người ta có thể mong đợi, chúng ta có thể xây dựng các truy vấn với nhiều tham số và số lần xuất hiện của chúng theo yêu cầu.

Nếu vì lý do nào đó, chúng tôi cần sử dụng cùng một tham số nhiều lần trong cùng một truy vấn, chúng tôi chỉ cần đặt nó một lần bằng cách phát hành phương thức “ setParameter ”. Trong thời gian chạy, các giá trị được chỉ định sẽ thay thế mỗi lần xuất hiện của tham số.

Cuối cùng, điều đáng nói là đặc tả Java Persistence API không bắt buộc các tham số được đặt tên phải được hỗ trợ bởi các truy vấn gốc . Ngay cả khi một số triển khai như Hibernate hỗ trợ nó, chúng tôi cần lưu ý rằng nếu chúng tôi sử dụng nó, truy vấn sẽ không di động được.

4.4. Các thông số được đặt tên theo giá trị của bộ sưu tập

Để rõ ràng hơn, hãy cũng chứng minh cách hoạt động của điều này với các tham số có giá trị tập hợp:

TypedQuery query = entityManager.createQuery( "SELECT e FROM Employee e WHERE e.empNumber IN (:numbers)" , Employee.class); List empNumbers = Arrays.asList("A123", "A124"); List employees = query.setParameter("numbers", empNumbers).getResultList();

Như chúng ta có thể thấy, nó hoạt động theo cách tương tự như các tham số vị trí.

5. Tham số truy vấn tiêu chí

A JPA query may be built by using the JPA Criteria API, which Hibernate's official documentation explains in great detail.

In this type of query, we represent parameters by using objects instead of names or indices.

Let's build the same query again but this time using the Criteria API to demonstrate how to handle query parameters when dealing with CriteriaQuery:

CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery cQuery = cb.createQuery(Employee.class); Root c = cQuery.from(Employee.class); ParameterExpression paramEmpNumber = cb.parameter(String.class); cQuery.select(c).where(cb.equal(c.get(Employee_.empNumber), paramEmpNumber)); TypedQuery query = em.createQuery(cQuery); String empNumber = "A123"; query.setParameter(paramEmpNumber, empNumber); Employee employee = query.getResultList();

For this type of query, the parameter's mechanic is a little bit different since we use a parameter object but in essence, there's no difference.

Within the previous example, we can see the usage of the Employee_ class. We generated this class with the Hibernate metamodel generator. These components are part of the static JPA metamodel, which allows criteria queries to be built in a strongly-typed manner.

6. Kết luận

Trong bài viết này, chúng tôi tập trung vào cơ chế xây dựng truy vấn bằng cách sử dụng tham số truy vấn JPA hoặc tham số đầu vào.

Chúng tôi đã biết rằng chúng tôi có hai loại tham số truy vấn, vị trí và được đặt tên. Tùy thuộc vào chúng tôi cái nào phù hợp nhất với mục tiêu của chúng tôi.

Cũng cần lưu ý rằng tất cả các tham số truy vấn phải có giá trị đơn ngoại trừ trong biểu thức. Đối với trong biểu thức, chúng ta có thể sử dụng các tham số đầu vào có giá trị tập hợp, chẳng hạn như mảng hoặc Danh sách như được hiển thị trong các ví dụ trước.

Mã nguồn của hướng dẫn này, như thường lệ, có sẵn trên GitHub.