Học viên : Nguyễn Thu Hằng (Java 08)
Email : 14thuhang@gmail.com
Bài viết gốc : https://thorben-janssen.com/native-queries-with-spring-data-jpa/
Spring Data JPA hỗ trợ chúng ta nhiều cách khác nhau để lấy dữ liệu từ cơ sở dữ liệu. Một số cách rất dễ sử dụng như derived queries, custom queries hay native queries. Trong đó native queries là cách linh hoạt và mạnh mẽ nhất giúp chúng ta đọc dữ liệu.
Native queries trong JPA cho phép chúng ta sử dụng tất cả các tính năng được hỗ trợ bởi cơ sở dữ liệu. Điều này rất phù hợp cho một số trường hợp cần sử dụng câu lệnh truy vấn phức tạp để trích xuất thông tin cần thiết. Tất nhiên, điều đó cũng đúng nếu chúng ta đang sử dụng các Native queries trong Spring Data JPA vì sử dụng cùng một cơ chế. Spring Data JPA giúp cho việc định nghĩa và thực thi một Native queries dễ dàng hơn một chút.
Định nghĩa Native queries
Khi sử dụng JPA thuần hoặc Hibernate, việc định nghĩa và thực hiện một native query yêu cầu nhiều bước. Annotation @Query
của Spring Data sẽ loại bỏ tất cả boilerplate code đó
Khi định nghĩa một native query, chúng ta chú thích repository method của mình bằng anotation @Query
, đặt thuộc tính nativeQuery=true
và cung cấp một câu lệnh SQL làm giá trị. Trong ví dụ dưới đây, chúng ta có thể sử dụng các tham số giống như trong custom JPQL query.
@Repository
public interface AuthorRepository extends CrudRepository<Author, Long>, PagingAndSortingRepository<Author, Long> {
@Query(value="select * from author a where a.first_name= :firstName", nativeQuery=true)
List<Author> getAuthorsByFirstName(String firstName);
}
Khi hoàn thành, chúng ta chỉ việc inject repository vào service tương ứng và gọi phương thức getAuthorsByFirstName trong repository với firstName muốn tìm kiếm.
List<Author> authors = authorRepository.getAuthorsByFirstName("Janssen");
Spring Data repository sẽ thực thi và khởi tạo câu lệnh truy vấn được cung cấp trong annotation @Query
. Sau đó, nó sẽ truyền các giá trị tương ứng vào trong câu lệnh query và thực thi nó
Viết hoạt động (delete, update) dưới dạng Native Queries
Operations thường xuyên được sử dụng để cập nhật hoặc loại bỏ một số lượng lớn các bản ghi trong cơ sở dữ liệu bằng cách sử dụng JPQL, Criteria hoặc navtive queries
Chúng ta có thể sử dụng annotation @Query
để định nghĩa một câu lệnh JPQL hoặc native SQL. Vì các thao tác ghi cần được thực hiện khác với các thao tác đọc, chúng cũng cần chú thích repository method bằng annotation @Modizing
. Đó là sự khác biệt duy nhất so với các câu lệnh native SQL SELECT
@Repository
public interface AuthorRepository extends CrudRepository<Author, Long>, PagingAndSortingRepository<Author, Long> {
@Modifying
@Query(value="delete from author a where a.last_name= :lastName", nativeQuery = true)
void deleteAuthorByLastName(@Param("lastName") String lastName);
@Modifying
@Query(value="update author set last_name= :lastName where first_name = :firstName", nativeQuery=true)
void updateAuthorByFirstName(String firstName, String lastName);
}
Hạn chế của Native Queries trong Spring Data JPA
Khi dùng native queries, có những hạn chế sau :
- Spring Data JPA và persistence provider không tự điều chỉnh truy vấn theo ngôn ngữ SQL của một cơ sở dữ liệu cụ thể. Do đó, chúng ta cần đảm bảo rằng tất cả RDBMS được hỗ trợ bởi ứng dụng và có thể xử lý các câu lệnh được cung cấp.
- Khi phân trang kết quả truy vấn native yêu cầu thêm một bước.
- Spring Data JPA không hỗ trợ sắp xếp động (dynamic sorting) cho các câu lệnh native SQL.
Chúng ta cùng xem xét kỹ hơn hạn chế thứ 2 và thứ 3.
Thêm count query để phân trang
Khi làm việc với custom JPQL query, chúng ta có thể thêm tham số có kiểu Pageable vào trong repository method. Điều này cho phép chúng ta phân trang với các kết quả truy vấn được. Spring Data JPA sẽ bổ sung một số boilerplate code để truy xuất kết quả trên từng trang.
Tuy nhiên đối với với native query cần có thêm 1 bước, chúng ta cần cung cấp câu lệnh count query để trả về tổng số bản ghi hiện có. Để làm được điều đó chúng ta cần cung cấp giá trị cho thuộc tính countQuery trong annotation @Query
.
@Repository
public interface AuthorRepository extends CrudRepository<Author, Long>, PagingAndSortingRepository<Author, Long> {
@Query(value="select * from author a where a.last_name= ?1",
countQuery = "select count(id) from author a where a.last_name= ?1",
nativeQuery = true)
Page<Author> getAuthorsByLastName(String lastname, Pageable page);
...
}
Nếu repository method tham chiếu đến named native query, chúng ta cần bổ sung thêm một named native query nữa để lấy tổng số bản ghi và thêm hậu tố .count
vào tên của nó.
@NamedNativeQuery(name = "Author.getAuthorsByLastName",
query = "select * from author a where a.last_name= ?1",
resultClass = Author.class)
@NamedNativeQuery(name = "Author.getAuthorsByLastName.count",
query = "select count(id) from author a where a.last_name= ?1")
@Entity
public class Author { ... }
Không có sắp xếp động
Khi làm việc với truy vấn JPQL, chúng ta có thể thêm tham số có kiểu Sort vào trong repository method. Điều này cho phép chúng ta xác định được tiêu chí sắp xếp trong thời gian chạy. Spring Data JPA sau đó sẽ tạo mệnh đề ORDER BY dựa trên giá trị tham số được cung cấp.
Rất tiếc, Spring Data JPA không hỗ trợ tính năng này cho các native query. Điều này yêu cầu Spring Data phân tích câu lệnh được cung cấp và tạo mệnh đề ORDER BY dành riêng cho cơ sở dữ liệu. Đây sẽ là một hoạt động rất phức tạp và hiện không được Spring Data JPA hỗ trợ.
Tất nhiên, chúng ta có thể thêm mệnh đề ORDER BY vào câu lệnh truy vấn. Nhưng điều đó sẽ giới hạn tính linh động của câu truy vấn. Nếu cần hỗ trợ nhiều hơn, sử dụng composite repository là cách tốt hơn. Chúng ta có thể thực thi các query method bằng cách sử dụng Criteria API của JPA và chỉ định mệnh đề ORDER BY dựa trên các tham số đầu vào được cung cấp.
Kết luận
Native queries rất mạnh mẽ và linh hoạt nhất để thực thi các thao tác đọc dữ liệu. Nó cho phép chúng ta sử dụng tất cả các tính năng được cơ sở dữ liệu hỗ trợ và Spring Data JPA xử lý các câu lệnh được yêu cầu
Nhưng việc sử dụng chúng tốn nhiều công sức hơn so với derived query và chúng cũng có một vài hạn chế so với custom JPQL query. Những điều đáng chú ý nhất là:
- Để sử dụng phân trang cho các kết quả truy vấn, cần bổ sung thêm count query. Chúng ta có thể làm điều đó bằng cách đặt thuộc tính countQuery trong annotation
@Query
. - Spring Data JPA không hỗ trợ sắp xếp tự động cho các Native queries. Nếu chúng ta muốn truy xuất kết quả truy vấn theo một thứ tự cụ thể, cần bổ sung thêm mệnh đề
ORDER BY
vào câu lệnh truy vấn của mình. - Spring Data JPA và persistence provider không tự điều chỉnh câu lệnh Native query của bạn theo ngôn ngữ SQL trong cơ sở dữ liệu. Do đó, chúng ta cần đảm bảo rằng tất cả các DBMS được hỗ trợ đều hỗ trợ câu lệnh SQL.
Bình luận