Breaking

14 tháng 7, 2021

tháng 7 14, 2021

Spring Data JPA

 


Spring Data JPA

1.      Giới thiệu:

Spring Data JPA cung cấp hỗ trợ kho lưu trữ cho Java Persistence API (JPA). Nó giúp giảm bớt sự phát triển của các ứng dụng cần truy cập JPA data source.

1.1  Dữ liệu dự án:

·         Kiểm soát phiên bản: https://github.com/spring-projects/spring-data-jpa

·         Trình sửa lỗi: https://github.com/spring-projects/spring-data-jpa/issues

·         Kho lưu trữ phát hành: https://repo.spring.io/libs-release

·         Kho Milestone:  https://repo.spring.io/libs-milestone

·         Kho chụp ảnh nhanh:  https://repo.spring.io/libs-snapshot

 

2.                  Các phần mới và đáng chú ý của các phiên bản

 

2.1  Phiên bản JPA 1.11:

Spring Data JPA 1.11 đã thêm các tính năng sau:

·         Cải thiện tương thích với Hibernate 5.2.

·         Hỗ trợ so sánh chế độ hỗ trợ cho truy vấn theo ví dụ.

·         Tối ưu hóa truy vấn theo trang.

·         Hỗ trợ cho tồn tại sao chép chiếu trong kho lưu trữ truy xuất đầu ra.

 

2.2  Phiên bản 1.10:

Spring Data JPA 1.10 đã thêm các tính năng sau:

·         Hỗ trợ các phép chiếu trong các kho lưu trữ phương pháp.

·         Hỗ trợ cho Truy vấn theo ví dụ.

·         Các chú thích sau đây đã được kích hoạt để xây dựng trên bao gồm chú thích: @EntityGraph, @Lock, @Modify, @Query, @QueryHints và @Procedure.

·         Từ khóa hỗ trợ trên tập biểu thức.

·         AttributeConverter triển khai ZoneIdJSR-310 và ThreeTenBP.

·         Nâng cấp lên Querydsl 4, Hibernate 5, OpenJPA 2.4 và EclipseLink 2.6.1.

 

3.       Sự phụ thuộc:

Do ngày ra đời của các mô-đun Dữ liệu Mùa xuân riêng lẻ khác nhau, hầu hết chúng đều mang số phiên bản chính và phụ khác nhau. Cách dễ nhất để tìm những cái tương thích là dựa vào BOM Tàu phát hành dữ liệu mùa xuân mà chúng tôi gửi với các phiên bản tương thích được xác định. Trong một dự án Maven, bạn sẽ khai báo sự phụ thuộc này trong <dependencyManagement />phần POM của bạn như sau:

       Ví dụ 1: sử dụng spring data trong BOM:

 



 

Phiên bản  phát hành hiện tại là 2021.0.2. Phiên bản xe lửa sử dụng calver với mẫu YYYY.MINOR.MICRO. Tên phiên bản theo sau ${calver}cho các bản phát hành GA và bản phát hành dịch vụ và mẫu sau cho tất cả các phiên bản khác:, ${calver}-${modifier}trong đó modifiercó thể là một trong các tên sau:

·         SNAPSHOT: Ảnh chụp nhanh hiện tại

·         M1, M2v.v: Các cột mốc

·         RC1, RC2v.v .: Giải phóng các cử nhân ứng dụng

Bạn có thể tìm thấy một hoạt động ví dụ về việc sử dụng BOM trong kho lưu trữ ví dụ về Dữ liệu spring (https://github.com/spring-projects/spring-data-examples/tree/main/bom)  của chúng tôi. Với điều đó, bạn có thể khai báo các mô-đun dữ liệu mùa xuân mà bạn muốn sử dụng mà không có phiên bản trong khối <dependencies />, chẳng hạn như sau:

Ví dụ 2. Khai báo một phần phụ thuộc vào một mô-đun dữ liệu spring:




3.1  Quản lý phụ thuộc với Spring Boot

    Spring Boot chọn một phiên bản gần đây của mô-đun Dữ liệu Mùa xuân cho bạn. Nếu bạn vẫn muốn nâng cấp lên phiên bản mới hơn, hãy đặt thuộc spring-data-releasetrain.versiontính thành phiên bản tàu và phép lặp mà bạn muốn sử dụng.

3.2  Spring Framework

Phiên bản hiện tại của mô-đun Dữ liệu Mùa xuân yêu cầu Spring Framework 5.3.8 trở lên. Các mô-đun cũng có thể hoạt động với phiên bản sửa lỗi cũ hơn của phiên bản nhỏ đó. Tuy nhiên, bạn nên sử dụng phiên bản mới nhất trong thế hệ đó.

 

4.  Làm việc với kho dữ liệu Spring

Mục tiêu của việc trừu tượng hóa kho lưu trữ Dữ liệu Spring là giảm đáng kể số lượng mã soạn sẵn cần thiết để triển khai các lớp truy cập dữ liệu cho các kho lưu trữ liên tục khác nhau.

Tài liệu về kho dữ liệu Spring và kho dữ liệu của bạn

Chương này giải thích các khái niệm và giao diện cốt lõi của kho dữ liệu Spring. Thông tin trong chương này được lấy từ mô-đun Spring Data Commons. Nó sử dụng cấu hình và mẫu mã cho mô-đun Java Persistence API (JPA). Bạn nên điều chỉnh khai báo không gian tên XML và các kiểu được mở rộng cho tương đương của mô-đun cụ thể mà bạn sử dụng. “Tham chiếu không gian tên” bao gồm cấu hình XML, được hỗ trợ trên tất cả các mô-đun Dữ liệu mùa xuân hỗ trợ API kho lưu trữ. “Từ khóa truy vấn kho lưu trữ” bao gồm các từ khóa của phương pháp truy vấn được hỗ trợ bởi tính trừu tượng của kho lưu trữ nói chung. Để biết thông tin chi tiết về các tính năng cụ thể của mô-đun của bạn, hãy xem chương về mô-đun đó của tài liệu này.

 

4.1 Tài liệu cốt lõi

Giao diện trung tâm trong kho lưu trữ Dữ liệu Spring trừu tượng là Kho lưu trữ. Nó sử dụng lớp miền để quản lý cũng như kiểu ID của lớp miền làm đối số kiểu. Giao diện này chủ yếu hoạt động như một giao diện đánh dấu để nắm bắt các loại để làm việc và giúp bạn khám phá các giao diện mở rộng giao diện này. Giao diện CrudRepository cung cấp chức năng CRUD tinh vi cho lớp thực thể đang được quản lý.

 

Example 3. CrudRepository Interface

public interface CrudRepository<T, ID> extends Repository<T, ID> {

 

  <S extends T> S save(S entity);     // lưu thực thể đã cho

 

  Optional<T> findById(ID primaryKey); // trả về thực thể được xác định bởi ID

 

  Iterable<T> findAll();   // trả về tất cả thực thể           

 

  long count();       // Trả về số lượng thực thể                

 

  void delete(T entity);   // Xóa thực thể đã cho           

 

  boolean existsById(ID primaryKey);  // cho biết thực thể có ID đã cho có tồn tại hay không

 

  // … more functionality omitted.

}

Trên đầu trang của CrudRepository, có một bản tóm tắt PagingAndSortingRepository bổ sung các phương thức bổ sung để dễ dàng truy cập phân trang vào các thực thể:

Ví dụ 4. Giao diện PagingAndSortingRepository

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {

 

  Iterable<T> findAll(Sort sort);

 

  Page<T> findAll(Pageable pageable);

}

Để truy cập trang thứ hai của Người dùng với kích thước trang là 20, bạn có thể làm như sau:

PagingAndSortingRepository<User, Long> repository = // … get access to a bean

Page<User> users = repository.findAll(PageRequest.of(1, 20));

Ngoài các phương pháp truy vấn, có sẵn truy xuất dẫn xuất truy vấn cho cả truy vấn đếm và xóa. Danh sách sau đây hiển thị định nghĩa giao diện cho truy vấn số lượng dẫn xuất:

Ví dụ 5. Truy vấn số lượng có nguồn gốc

 

interface UserRepository extends CrudRepository<User, Long> {

 

  long countByLastname(String lastname);

}

Danh sách sau đây hiển thị định nghĩa giao diện cho truy vấn xóa dẫn xuất:

Ví dụ 6. Truy vấn xóa có nguồn gốc

 

interface UserRepository extends CrudRepository<User, Long> {

 

  long deleteByLastname(String lastname);

 

  List<User> removeByLastname(String lastname);

}

4.2. Phương thức truy vấn

Các kho chức năng CRUD tiêu chuẩn thường có các truy vấn trên kho dữ liệu bên dưới. Với Dữ liệu mùa xuân, việc khai báo các truy vấn đó sẽ trở thành một quy trình gồm bốn bước:

1.       Khai báo một giao diện mở rộng Kho lưu trữ hoặc một trong các giao diện con của nó và nhập nó vào lớp miền và loại ID mà nó sẽ xử lý, như được hiển thị trong ví dụ sau:

 

interface PersonRepository extends Repository<Person, Long> { … }

2.       Khai báo các phương thức truy vấn trên giao diện.

interface PersonRepository extends Repository<Person, Long> {

            List<Person> findByLastname(String lastname);

}

 

3.       Thiết lập Spring để tạo các phiên bản proxy cho các giao diện đó, bằng JavaConfig hoặc với cấu hình XML.

a.       Để sử dụng cấu hình Java, hãy tạo một lớp tương tự như sau:

import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

 

@EnableJpaRepositories

class Config { … }

 

b.       Để sử dụng cấu hình XML, hãy xác định một bean tương tự như sau:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xmlns:jpa="http://www.springframework.org/schema/data/jpa"

   xsi:schemaLocation="http://www.springframework.org/schema/beans

     https://www.springframework.org/schema/beans/spring-beans.xsd

     http://www.springframework.org/schema/data/jpa

     https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

 

   <jpa:repositories base-package="com.acme.repositories"/>

 

</beans>

            Không gian tên JPA được sử dụng trong ví dụ này. Nếu bạn sử dụng trừu tượng kho lưu trữ cho bất kỳ cửa hàng nào khác, bạn cần thay đổi điều này thành khai báo không gian tên thích hợp của mô-đun cửa hàng của bạn. Nói cách khác, bạn nên trao đổi jpa có lợi, ví dụ như mongodb.

Ngoài ra, lưu ý rằng biến thể JavaConfig không định cấu hình gói một cách rõ ràng, vì gói của lớp chú thích được sử dụng theo mặc định. Để tùy chỉnh gói để quét, hãy sử dụng một trong các thuộc tính basePackage… của kho lưu trữ dữ liệu cụ thể của @ Enable $ {store} Repositories-annotation.

4.       Chèn cá thể kho lưu trữ và sử dụng nó, như được hiển thị trong ví dụ sau:

class SomeClient {

 

  private final PersonRepository repository;

 

  SomeClient(PersonRepository repository) {

    this.repository = repository;

  }

 

  void doSomething() {

    List<Person> persons = repository.findByLastname("Matthews");

  }

}

Các phần tiếp theo giải thích chi tiết từng bước:

-          Xác định giao diện kho lưu trữ

-          Định nghĩa các phương thức truy vấn

-          Tạo phiên bản kho lưu trữ

-          Triển khai tùy chỉnh cho Kho lưu trữ dữ liệu mùa xuân

4.3. Xác định giao diện kho lưu trữ

Để xác định giao diện kho lưu trữ, trước tiên bạn cần xác định giao diện kho lưu trữ dành riêng cho lớp miền. Giao diện phải mở rộng Kho lưu trữ và được nhập vào lớp miền và loại ID. Nếu bạn muốn hiển thị các phương thức CRUD cho loại miền đó, hãy mở rộng CrudRepository thay vì Repository.

4.3.1. Tinh chỉnh Định nghĩa Kho lưu trữ

Thông thường, giao diện kho lưu trữ của bạn mở rộng Kho lưu trữ, CrudRepository hoặc PagingAndSortingRepository. Ngoài ra, nếu bạn không muốn mở rộng giao diện Spring Data, bạn cũng có thể chú thích giao diện kho lưu trữ của mình bằng @RepositoryDefinition. Việc mở rộng CrudRepository cho thấy một bộ hoàn chỉnh các phương pháp để thao túng các thực thể của bạn. Nếu bạn muốn chọn lọc các phương pháp được hiển thị, hãy sao chép các phương thức bạn muốn hiển thị từ CrudRepository vào kho lưu trữ miền của bạn.

Làm như vậy cho phép bạn xác định các phần tóm tắt của riêng mình trên chức năng Kho lưu trữ dữ liệu mùa xuân được cung cấp.

Ví dụ sau đây cho thấy cách hiển thị có chọn lọc các phương thức CRUD (findById và save, trong trường hợp này):

Ví dụ 7. Hiển thị có chọn lọc các phương pháp CRUD

@NoRepositoryBean

interface MyBaseRepository<T, ID> extends Repository<T, ID> {

 

  Optional<T> findById(ID id);

 

  <S extends T> S save(S entity);

}

 

interface UserRepository extends MyBaseRepository<User, Long> {

  User findByEmailAddress(EmailAddress emailAddress);

}

Trong ví dụ trước, bạn đã xác định một giao diện cơ sở chung cho tất cả các kho lưu trữ miền của mình và hiển thị findById (…) cũng như save (…). ví dụ: nếu bạn sử dụng JPA, việc triển khai là SimpleJpaRepository), vì chúng khớp với chữ ký phương thức trong CrudRepository. Vì vậy, UserRepository hiện có thể lưu người dùng, tìm từng người dùng theo ID và kích hoạt truy vấn để tìm Người dùng theo địa chỉ email.

Giao diện kho lưu trữ trung gian được chú thích bằng @NoRepositoryBean. Đảm bảo rằng bạn thêm chú thích đó vào tất cả các giao diện kho lưu trữ mà Dữ liệu Spring sẽ không tạo phiên bản trong thời gian chạy.

4.3.2. Sử dụng kho lưu trữ với nhiều mô-đun dữ liệu mùa xuân

Sử dụng một mô-đun Dữ liệu Spring duy nhất trong ứng dụng của bạn làm cho mọi thứ trở nên đơn giản, bởi vì tất cả các giao diện kho lưu trữ trong phạm vi đã xác định đều bị ràng buộc với mô-đun Dữ liệu Spring. Đôi khi, các ứng dụng yêu cầu sử dụng nhiều hơn một mô-đun Dữ liệu Spring. Trong những trường hợp như vậy, định nghĩa kho lưu trữ phải phân biệt giữa các công nghệ bền bỉ. Khi nó phát hiện nhiều nhà máy kho lưu trữ trên đường dẫn lớp, Spring Data sẽ đi vào chế độ cấu hình kho lưu trữ nghiêm ngặt. Cấu hình nghiêm ngặt sử dụng chi tiết về kho lưu trữ hoặc lớp miền để quyết định về liên kết mô-đun Dữ liệu Spring cho định nghĩa kho lưu trữ:

1.       Nếu định nghĩa kho lưu trữ mở rộng kho lưu trữ dành riêng cho mô-đun, thì nó là một ứng cử viên hợp lệ cho mô-đun Dữ liệu mùa xuân cụ thể.

2.       Nếu lớp miền được chú thích bằng chú thích loại mô-đun cụ thể, thì nó là một ứng cử viên hợp lệ cho mô-đun Dữ liệu mùa xuân cụ thể. Mô-đun Spring Data chấp nhận các chú thích của bên thứ ba (chẳng hạn như JPA’s @Entity) hoặc cung cấp các chú thích của riêng họ (chẳng hạn như @Document cho Spring Data MongoDB và Spring Data Elasticsearch).

Ví dụ sau cho thấy một kho lưu trữ sử dụng các giao diện dành riêng cho mô-đun (JPA trong trường hợp này):

Ví dụ 8. Định nghĩa kho lưu trữ sử dụng giao diện dành riêng cho mô-đun

interface MyRepository extends JpaRepository<User, Long> { }

 

@NoRepositoryBean

interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> { … }

 

interface UserRepository extends MyBaseRepository<User, Long> { … }

MyRepository và UserRepository mở rộng JpaRepository trong hệ thống phân cấp loại của chúng. Họ là những ứng cử viên hợp lệ cho mô-đun Spring Data JPA.

Ví dụ sau đây cho thấy một kho lưu trữ sử dụng các giao diện chung:

Ví dụ 9. Định nghĩa kho lưu trữ sử dụng giao diện chung

interface AmbiguousRepository extends Repository<User, Long> { … }

 

@NoRepositoryBean

interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> { … }

 

interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }

 

AmbiguousRepository và AmbiguousUserRepository chỉ mở rộng Repository và CrudRepository trong hệ thống phân cấp loại của chúng. Mặc dù điều này là tốt khi sử dụng một mô-đun Dữ liệu mùa xuân duy nhất, nhưng nhiều mô-đun không thể phân biệt Dữ liệu mùa xuân cụ thể nào mà các kho lưu trữ này nên bị ràng buộc.

Ví dụ sau cho thấy một kho lưu trữ sử dụng các lớp miền với các chú thích:

Ví dụ 10. Định nghĩa kho lưu trữ sử dụng các lớp miền có chú thích

interface PersonRepository extends Repository<Person, Long> { … }

 

@Entity

class Person { … }

 

interface UserRepository extends Repository<User, Long> { … }

 

@Document

class User { … }

 

Ví dụ 11. Định nghĩa kho lưu trữ sử dụng các lớp miền với các chú thích hỗn hợp

interface JpaPersonRepository extends Repository<Person, Long> { … }

 

interface MongoDBPersonRepository extends Repository<Person, Long> { … }

 

@Entity

@Document

class Person { … }

Chi tiết loại kho lưu trữ và chú thích phân biệt lớp miền được sử dụng cho cấu hình kho lưu trữ nghiêm ngặt để xác định các ứng cử viên kho lưu trữ cho một mô-đun Dữ liệu mùa xuân cụ thể. Có thể sử dụng nhiều chú thích dành riêng cho công nghệ bền vững trên cùng một loại miền và cho phép sử dụng lại các loại miền trên nhiều công nghệ bền vững. Tuy nhiên, Spring Data sau đó không còn có thể xác định một mô-đun duy nhất để liên kết kho lưu trữ.

 

Cách cuối cùng để phân biệt các kho lưu trữ là xác định phạm vi các gói cơ sở của kho lưu trữ. Các gói cơ sở xác định các điểm bắt đầu để quét các định nghĩa giao diện kho lưu trữ, nghĩa là có các định nghĩa kho lưu trữ nằm trong các gói thích hợp. Theo mặc định, cấu hình theo hướng chú thích sử dụng gói của lớp cấu hình. Gói cơ sở trong cấu hình dựa trên XML là bắt buộc.

Ví dụ 12. Cấu hình theo hướng chú thích của gói cơ sở

@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")

@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")

class Configuration { … }

4.4. Định nghĩa các phương thức truy vấn

Proxy kho lưu trữ có hai cách để lấy một truy vấn cửa hàng cụ thể từ tên phương thức:

-          Bằng cách lấy truy vấn trực tiếp từ tên phương thức.

-          Bằng cách sử dụng một truy vấn được xác định theo cách thủ công.

Bằng cách sử dụng một truy vấn được xác định theo cách thủ công.

4.4.1. Các chiến lược tra cứu truy vấn

Các chiến lược sau đây có sẵn cho cơ sở hạ tầng kho lưu trữ để giải quyết truy vấn. Với cấu hình XML, bạn có thể định cấu hình chiến lược tại không gian tên thông qua thuộc tính chiến lược truy vấn-tra cứu. Đối với cấu hình Java, bạn có thể sử dụng thuộc tính queryLookupStrategy của chú thích Bật kho lưu trữ $ {store}. Một số chiến lược có thể không được hỗ trợ cho các kho dữ liệu cụ thể.

-          CREATE cố gắng tạo một truy vấn dành riêng cho cửa hàng từ tên phương thức truy vấn. Cách tiếp cận chung là loại bỏ một tập hợp các tiền tố nổi tiếng nhất định khỏi tên phương thức và phân tích cú pháp phần còn lại của phương thức. Bạn có thể đọc thêm về xây dựng truy vấn trong “Tạo Truy vấn”.

-          USE_DECLARED_QUERY cố gắng tìm một truy vấn đã khai báo và ném một ngoại lệ nếu nó không thể tìm thấy một ngoại lệ. Truy vấn có thể được xác định bằng chú thích ở đâu đó hoặc được khai báo bằng các phương tiện khác. Xem tài liệu của cửa hàng cụ thể để tìm các tùy chọn có sẵn cho cửa hàng đó. Nếu cơ sở hạ tầng kho lưu trữ không tìm thấy truy vấn đã khai báo cho phương thức tại thời điểm khởi động, thì nó không thành công.

-          CREATE_IF_NOT_FOUND (mặc định) kết hợp CREATE và USE_DECLARED_QUERY. Nó tìm kiếm một truy vấn đã khai báo trước và nếu không tìm thấy truy vấn đã khai báo nào, nó sẽ tạo một truy vấn dựa trên tên phương thức tùy chỉnh. Đây là chiến lược tra cứu mặc định và do đó, được sử dụng nếu bạn không định cấu hình bất cứ thứ gì một cách rõ ràng. Nó cho phép định nghĩa truy vấn nhanh theo tên phương thức nhưng cũng có thể điều chỉnh tùy chỉnh các truy vấn này bằng cách giới thiệu các truy vấn đã khai báo khi cần thiết.

4.4.2. Query Creation

Cơ chế trình tạo truy vấn được tích hợp trong cơ sở hạ tầng kho lưu trữ Dữ liệu Mùa xuân rất hữu ích để xây dựng các truy vấn ràng buộc trên các thực thể của kho lưu trữ.

Ví dụ sau đây cho thấy cách tạo một số truy vấn:

Ví dụ 13. Tạo truy vấn từ tên phương thức

interface PersonRepository extends Repository<Person, Long> {

 

  List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

 

  // Enables the distinct flag for the query

  List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);

  List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

 

  // Enabling ignoring case for an individual property

  List<Person> findByLastnameIgnoreCase(String lastname);

  // Enabling ignoring case for all suitable properties

  List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

 

  // Enabling static ORDER BY for a query

  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);

  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);

}

Phân tích cú pháp tên phương thức truy vấn được chia thành chủ đề và vị ngữ. Phần đầu tiên (tìm… Bởi, tồn tại… Bằng) xác định chủ đề của truy vấn, phần thứ hai tạo thành vị ngữ. Mệnh đề giới thiệu (chủ đề) có thể chứa các biểu thức khác. Bất kỳ văn bản nào giữa find (hoặc các từ khóa giới thiệu khác) và By đều được coi là mang tính mô tả trừ khi sử dụng một trong các từ khóa giới hạn kết quả, chẳng hạn như Distinction để đặt cờ khác biệt trên truy vấn sẽ được tạo hoặc Top / First để giới hạn kết quả truy vấn.

Phụ lục chứa danh sách đầy đủ các từ khóa chủ đề của phương pháp truy vấn và các từ khóa vị ngữ của phương thức truy vấn bao gồm các công cụ sắp xếp và sửa đổi cách viết hoa. Tuy nhiên, By đầu tiên hoạt động như một dấu phân cách để chỉ ra phần bắt đầu của vị từ tiêu chí thực tế. Ở cấp độ rất cơ bản, bạn có thể xác định các điều kiện trên thuộc tính thực thể và nối chúng với And và Or.

Kết quả thực sự của việc phân tích cú pháp phương thức phụ thuộc vào kho lưu trữ lâu dài mà bạn tạo truy vấn. Tuy nhiên, có một số điều chung cần lưu ý:

-          Các biểu thức thường là các đường truyền thuộc tính được kết hợp với các toán tử có thể được nối với nhau. Bạn có thể kết hợp các biểu thức thuộc tính với AND và OR. Bạn cũng nhận được hỗ trợ cho các toán tử như Between, LessThan, GreaterThan và Like cho các biểu thức thuộc tính. Các toán tử được hỗ trợ có thể khác nhau tùy theo kho dữ liệu, vì vậy hãy tham khảo phần thích hợp trong tài liệu tham khảo của bạn.

-          Bộ phân tích cú pháp phương thức hỗ trợ thiết lập cờ Bỏ qua cho các thuộc tính riêng lẻ (ví dụ: findByLastnameIgnoreCase (…)) hoặc cho tất cả các thuộc tính của kiểu hỗ trợ bỏ qua chữ hoa chữ thường (thường là các trường hợp Chuỗi - ví dụ: findByLastnameAndFirstnameAllIgnoreCase (…)). Việc bỏ qua các trường hợp được hỗ trợ có thể khác nhau tùy theo cửa hàng hay không, vì vậy hãy tham khảo các phần có liên quan trong tài liệu tham khảo để biết phương pháp truy vấn dành riêng cho cửa hàng.

-          Bạn có thể áp dụng thứ tự tĩnh bằng cách thêm mệnh đề OrderBy vào phương thức truy vấn tham chiếu đến thuộc tính và bằng cách cung cấp hướng sắp xếp (Asc hoặc Desc). Để tạo phương thức truy vấn hỗ trợ sắp xếp động, hãy xem "Xử lý tham số đặc biệt".

-           

4.4.3. Biểu thức tài sản

Biểu thức thuộc tính chỉ có thể tham chiếu đến thuộc tính trực tiếp của thực thể được quản lý, như được hiển thị trong ví dụ trước. Tại thời điểm tạo truy vấn, bạn đã đảm bảo rằng thuộc tính được phân tích cú pháp là thuộc tính của lớp miền được quản lý. Tuy nhiên, bạn cũng có thể xác định các ràng buộc bằng cách duyệt qua các thuộc tính lồng nhau. Hãy xem xét chữ ký phương thức sau:

List<Person> findByAddressZipCode(ZipCode zipCode);

Giả sử một người có địa chỉ với mã ZipCode. Trong trường hợp đó, phương thức tạo truyền tải thuộc tính x.address.zipCode. Thuật toán phân giải bắt đầu bằng cách diễn giải toàn bộ phần (AddressZipCode) là thuộc tính và kiểm tra lớp miền để tìm thuộc tính có tên đó (không viết hoa). Nếu thuật toán thành công, nó sẽ sử dụng thuộc tính đó. Nếu không, thuật toán sẽ tách nguồn tại các phần vỏ lạc đà từ phía bên phải thành đầu và đuôi và cố gắng tìm thuộc tính tương ứng - trong ví dụ của chúng tôi là AddressZip và Code. Nếu thuật toán tìm thấy một thuộc tính có phần đầu đó, nó sẽ lấy phần đuôi và tiếp tục xây dựng cây từ đó, tách phần đuôi lên theo cách vừa mô tả. Nếu lần phân tách đầu tiên không khớp, thuật toán sẽ di chuyển điểm phân tách sang trái (Địa chỉ, Mã ZipCode) và tiếp tục.

Mặc dù điều này sẽ hoạt động đối với hầu hết các trường hợp, nhưng thuật toán có thể chọn sai thuộc tính. Giả sử lớp Person cũng có thuộc tính addressZip. Thuật toán sẽ khớp trong vòng phân tách đầu tiên, chọn thuộc tính sai và thất bại (vì loại addressZip có thể không có thuộc tính mã).

 

Để giải quyết sự không rõ ràng này, bạn có thể sử dụng _ bên trong tên phương thức của mình để xác định thủ công các điểm truyền tải. Vì vậy, tên phương thức của chúng tôi sẽ như sau:

List<Person> findByAddress_ZipCode(ZipCode zipCode);

Bởi vì chúng tôi coi ký tự gạch dưới là ký tự dành riêng, chúng tôi đặc biệt khuyên bạn nên tuân theo các quy ước đặt tên tiêu chuẩn của Java (nghĩa là không sử dụng dấu gạch dưới trong tên thuộc tính mà thay vào đó sử dụng chữ hoa camel).

 

4.4.4. Xử lý thông số đặc biệt

Để xử lý các tham số trong truy vấn của bạn, hãy xác định các tham số phương thức như đã thấy trong các ví dụ trước. Bên cạnh đó, cơ sở hạ tầng nhận dạng một số loại cụ thể như Có thể gắn thẻ và Sắp xếp, để áp dụng phân trang và sắp xếp cho các truy vấn của bạn một cách linh hoạt. Ví dụ sau minh họa các tính năng này:

Ví dụ 14. Sử dụng Pagable, Slice và Sort trong các phương thức truy vấn

Page<User> findByLastname(String lastname, Pageable pageable);

 

Slice<User> findByLastname(String lastname, Pageable pageable);

 

List<User> findByLastname(String lastname, Sort sort);

 

List<User> findByLastname(String lastname, Pageable pageable);

Các API sử dụng Sắp xếp và Có thể gắn thẻ mong đợi các giá trị không rỗng sẽ được chuyển vào các phương thức. Nếu bạn không muốn áp dụng bất kỳ sắp xếp hoặc phân trang nào, hãy sử dụng Sort.unsorted () và Pagnable.unpaged ().

Phương thức đầu tiên cho phép bạn chuyển một phiên bản org.springframework.data.domain.Pagable vào phương thức truy vấn để thêm động phân trang vào truy vấn được xác định tĩnh của bạn. Một Trang biết về tổng số phần tử và số trang có sẵn. Nó làm như vậy bởi cơ sở hạ tầng kích hoạt một truy vấn đếm để tính toán con số tổng thể. Vì điều này có thể đắt (tùy thuộc vào cửa hàng được sử dụng), thay vào đó bạn có thể trả lại một Lát. Một Slice chỉ biết về việc có sẵn một Slice tiếp theo hay không, điều này có thể đủ khi xem qua một tập kết quả lớn hơn.

Các tùy chọn sắp xếp cũng được xử lý thông qua phiên bản Pagable. Nếu bạn chỉ cần sắp xếp, hãy thêm tham số org.springframework.data.domain.Sort vào phương thức của bạn. Như bạn có thể thấy, việc trả lại một Danh sách cũng có thể thực hiện được. Trong trường hợp này, siêu dữ liệu bổ sung cần thiết để xây dựng phiên bản Trang thực tế sẽ không được tạo (do đó, có nghĩa là truy vấn số lượng bổ sung đáng lẽ cần thiết sẽ không được đưa ra). Thay vào đó, nó hạn chế truy vấn chỉ tra cứu phạm vi thực thể nhất định.

 

Để biết bạn nhận được bao nhiêu trang cho toàn bộ truy vấn, bạn phải kích hoạt một truy vấn đếm bổ sung. Theo mặc định, truy vấn này có nguồn gốc từ truy vấn mà bạn thực sự kích hoạt.

 

Phân trang và sắp xếp

Bạn có thể xác định các biểu thức sắp xếp đơn giản bằng cách sử dụng tên thuộc tính. Bạn có thể nối các biểu thức để thu thập nhiều tiêu chí vào một biểu thức.

Ví dụ 15. Định nghĩa biểu thức sắp xếp

Sort sort = Sort.by("firstname").ascending()

  .and(Sort.by("lastname").descending());

Để có một cách an toàn hơn về kiểu để xác định biểu thức sắp xếp, hãy bắt đầu với kiểu để xác định biểu thức sắp xếp và sử dụng tham chiếu phương thức để xác định các thuộc tính cần sắp xếp.

Ví dụ 16. Định nghĩa các biểu thức sắp xếp bằng cách sử dụng API kiểu an toàn

TypedSort<Person> person = Sort.sort(Person.class);

 

Sort sort = person.by(Person::getFirstname).ascending()

  .and(person.by(Person::getLastname).descending());

TypedSort.by (…) sử dụng proxy thời gian chạy bằng cách (thường) sử dụng CGlib, điều này có thể cản trở quá trình biên dịch hình ảnh gốc khi sử dụng các công cụ như Graal VM Native.

Nếu triển khai cửa hàng của bạn hỗ trợ Querydsl, bạn cũng có thể sử dụng các loại siêu mô hình đã tạo để xác định biểu thức sắp xếp:

Ví dụ 17. Định nghĩa các biểu thức sắp xếp bằng cách sử dụng API Querydsl

QSort sort = QSort.by(QPerson.firstname.asc())

  .and(QSort.by(QPerson.lastname.desc()));

4.4.5. Giới hạn kết quả truy vấn

Bạn có thể giới hạn kết quả của các phương pháp truy vấn bằng cách sử dụng từ khóa đầu tiên hoặc từ khóa hàng đầu mà bạn có thể sử dụng thay thế cho nhau. Bạn có thể nối một giá trị số tùy chọn lên trên cùng hoặc đầu tiên để chỉ định kích thước kết quả tối đa được trả về. Nếu số bị bỏ trống, kích thước kết quả là 1 được giả định. Ví dụ sau cho thấy cách giới hạn kích thước truy vấn:

Ví dụ 18. Giới hạn kích thước kết quả của một truy vấn với Trên cùng và Đầu tiên

User findFirstByOrderByLastnameAsc();

 

User findTopByOrderByAgeDesc();

 

Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);

 

Slice<User> findTop3ByLastname(String lastname, Pageable pageable);

 

List<User> findFirst10ByLastname(String lastname, Sort sort);

 

List<User> findTop10ByLastname(String lastname, Pageable pageable);

Các biểu thức giới hạn cũng hỗ trợ từ khóa Khác biệt cho các kho dữ liệu hỗ trợ các truy vấn riêng biệt. Ngoài ra, đối với các truy vấn giới hạn kết quả được đặt thành một trường hợp, việc bao bọc kết quả bằng từ khóa Tùy chọn được hỗ trợ.

Nếu phân trang hoặc phân trang được áp dụng cho phân trang truy vấn giới hạn (và tính toán số lượng trang có sẵn), thì nó sẽ được áp dụng trong kết quả giới hạn.

Giới hạn kết quả kết hợp với sắp xếp động bằng cách sử dụng tham số Sắp xếp cho phép bạn thể hiện các phương pháp truy vấn cho phần tử 'K' nhỏ nhất cũng như cho phần tử lớn nhất 'K'.

4.4.6. Phương thức kho lưu trữ Trả lại Bộ sưu tập hoặc Lặp lại

Các phương thức truy vấn trả về nhiều kết quả có thể sử dụng Java Iterable, List và Set tiêu chuẩn. Ngoài ra, chúng tôi hỗ trợ trả lại Spring Data’s Streamable, một phần mở rộng tùy chỉnh của Iterable, cũng như các loại bộ sưu tập do Vavr cung cấp. Tham khảo phụ lục giải thích tất cả các kiểu trả về phương thức truy vấn có thể có.

 

Sử dụng Streamable làm Loại trả lại của phương thức truy vấn

Bạn có thể sử dụng Streamable để thay thế cho Iterable hoặc bất kỳ loại tập hợp nào. Nó cung cấp các phương thức tiện lợi để truy cập một Luồng không song song (không có trong Iterable) và khả năng trực tiếp… .filter (…) và… .map (…) qua các phần tử và nối Streamable với những phần tử khác:

Ví dụ 19. Sử dụng Streamable để kết hợp các kết quả của phương pháp truy vấn

interface PersonRepository extends Repository<Person, Long> {

  Streamable<Person> findByFirstnameContaining(String firstname);

  Streamable<Person> findByLastnameContaining(String lastname);

}

 

Streamable<Person> result = repository.findByFirstnameContaining("av")

  .and(repository.findByLastnameContaining("ea"));

 

Trả lại các loại trình bao bọc có thể truyền trực tiếp tùy chỉnh

Cung cấp các loại trình bao bọc chuyên dụng cho các bộ sưu tập là một mẫu thường được sử dụng để cung cấp một API cho một kết quả truy vấn trả về nhiều phần tử. Thông thường, những kiểu này được sử dụng bằng cách gọi một phương thức kho lưu trữ trả về kiểu giống như tập hợp và tạo một thể hiện của kiểu trình bao bọc theo cách thủ công. Bạn có thể tránh bước bổ sung đó vì Dữ liệu mùa xuân cho phép bạn sử dụng các loại trình bao bọc này làm loại trả về phương thức truy vấn nếu chúng đáp ứng các tiêu chí sau:

1.       Loại thực hiện Streamable.

2.       Kiểu hiển thị phương thức khởi tạo hoặc phương thức nhà máy tĩnh có tên là (…) hoặc valueOf (…) lấy Streamable làm đối số.

 

Danh sách sau đây cho thấy một ví dụ:

class Product {                                        

  MonetaryAmount getPrice() { … } //(1)

}

 

@RequiredArgConstructor(staticName = "of")

class Products implements Streamable<Product> { // (2)

 

  private Streamable<Product> streamable;

 

  public MonetaryAmount getTotal() {                   

    return streamable.stream()

      .map(Priced::getPrice)

      .reduce(Money.of(0), MonetaryAmount::add); (3)

  }

 

 

  @Override

  public Iterator<Product> iterator() {                

    return streamable.iterator();  //(4)

  }

}

 

interface ProductRepository implements Repository<Product, Long> {

  Products findAllByDescriptionContaining(String text); (5)

}

(1) Một thực thể product để lộ API để truy cập vào giá của sản phẩm.

(2) Một loại trình bao bọc cho một Streamable<Product> có thể truyền trực tuyến có thể được xây dựng bằng cách sử dụng Products.of (…) (phương thức gốc được tạo bằng chú thích Lombok).

Một hàm tạo chuẩn sử dụng Streamable<Product> có thể truyền trực tuyến cũng sẽ làm được điều đó.       

(3) Loại trình bao bọc hiển thị một API bổ sung, tính toán các giá trị mới trên Streamable<Product> có thể truyền trực tuyến.

(4) Triển khai giao diện Streamable và ủy quyền cho kết quả thực tế.

(5) Loại trình bao bọc đó Sản phẩm có thể được sử dụng trực tiếp làm kiểu trả về của phương thức truy vấn.

Bạn không cần phải trả lại Streamable <Sản phẩm> và bọc nó theo cách thủ công sau khi truy vấn trong ứng dụng khách kho lưu trữ.

 

Hỗ trợ cho Bộ sưu tập Vavr

Vavr là một thư viện bao gồm các khái niệm lập trình hàm trong Java. Nó đi kèm với một tập hợp các loại tập hợp tùy chỉnh mà bạn có thể sử dụng làm các loại trả về của phương thức truy vấn, như bảng sau cho thấy:

Bạn có thể sử dụng các kiểu trong cột đầu tiên (hoặc các kiểu con của chúng) làm kiểu trả về của phương thức truy vấn và lấy các kiểu trong cột thứ hai được sử dụng làm kiểu triển khai, tùy thuộc vào kiểu Java của kết quả truy vấn thực tế (cột thứ ba). Ngoài ra, bạn có thể khai báo Traversable (tương đương với Vavr Iterable) và sau đó chúng tôi lấy lớp triển khai từ giá trị trả về thực tế. Nghĩa là, java.util.List được chuyển thành Vavr List hoặc Seq, java.util.Set trở thành Vavr LinkedHashSet Set, v.v.

 

4.4.7. Xử lý vô hiệu các phương thức kho lưu trữ

Kể từ Spring Data 2.0, các phương thức CRUD của kho lưu trữ trả về một phiên bản tổng hợp riêng lẻ sử dụng Java 8’s Optional để chỉ ra khả năng không có giá trị. Bên cạnh đó, Spring Data hỗ trợ trả về các loại trình bao bọc sau trên các phương thức truy vấn:

·         com.google.common.base.Optional

·         scala.Option

·         io.vavr.control.Option

Ngoài ra, các phương pháp truy vấn có thể chọn hoàn toàn không sử dụng loại trình bao bọc. Khi đó, việc không có kết quả truy vấn được chỉ ra bằng cách trả về null. Các phương thức kho lưu trữ trả về tập hợp, lựa chọn thay thế tập hợp, trình bao bọc và luồng được đảm bảo không bao giờ trả về giá trị rỗng mà thay vào đó là biểu diễn trống tương ứng. Xem “Các kiểu trả về truy vấn kho lưu trữ” để biết chi tiết.

 

Chú thích vô hiệu

Bạn có thể thể hiện các ràng buộc về tính vô hiệu cho các phương thức kho lưu trữ bằng cách sử dụng các chú thích về tính vô hiệu của Spring Framework. Họ cung cấp một cách tiếp cận thân thiện với công cụ và chọn tham gia kiểm tra rỗng trong thời gian chạy, như sau:

·         @NonNullApi: Được sử dụng ở cấp độ gói để khai báo rằng hành vi mặc định cho các tham số và giá trị trả về tương ứng là không chấp nhận cũng không tạo ra giá trị null.

·         @NonNull: Được sử dụng trên một tham số hoặc giá trị trả về không được rỗng (không cần thiết cho một tham số và giá trị trả về nếu áp dụng @NonNullApi).

·         @Nullable: Được sử dụng trên một tham số hoặc giá trị trả về có thể là giá trị rỗng.

Chú thích Spring được chú thích meta với chú thích JSR 305 (một JSR không hoạt động nhưng được sử dụng rộng rãi). Các siêu chú thích JSR 305 cho phép các nhà cung cấp công cụ (chẳng hạn như IDEA, Eclipse và Kotlin) cung cấp hỗ trợ null-an toàn theo cách chung chung mà không cần phải hỗ trợ mã cứng cho các chú thích Spring. Để bật tính năng kiểm tra thời gian chạy các ràng buộc về tính nullability cho các phương thức truy vấn, bạn cần kích hoạt tính không nullability ở cấp độ gói bằng cách sử dụng Spring’s @NonNullApi trong package-info.java, như được hiển thị trong ví dụ sau:

Ví dụ 20. Khai báo tính không nullability trong package-info.java

@org.springframework.lang.NonNullApi

package com.acme;

Sau khi cài đặt mặc định không null, các lệnh gọi phương thức truy vấn kho lưu trữ sẽ được xác thực trong thời gian chạy đối với các ràng buộc về tính null. Nếu một kết quả truy vấn vi phạm ràng buộc đã xác định, một ngoại lệ sẽ được ném ra. Điều này xảy ra khi phương thức sẽ trả về null nhưng được khai báo là không thể null (mặc định với chú thích được xác định trên gói chứa kho lưu trữ). Nếu bạn muốn chọn lại kết quả có thể vô hiệu, hãy sử dụng có chọn lọc @Nullable trên các phương pháp riêng lẻ. Việc sử dụng các loại trình bao bọc kết quả được đề cập ở đầu phần này tiếp tục hoạt động như mong đợi: một kết quả trống được dịch thành giá trị đại diện cho sự vắng mặt.

Ví dụ sau đây cho thấy một số kỹ thuật vừa được mô tả:

Ví dụ 21. Sử dụng các ràng buộc nullability khác nhau

package com.acme;       (1)                                               

 

import org.springframework.lang.Nullable;

 

interface UserRepository extends Repository<User, Long> {

 

  User getByEmailAddress(EmailAddress emailAddress); (2)                   

 

  @Nullable

  User findByEmailAddress(@Nullable EmailAddress emailAdress);  (3)       

 

  Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); (4)

}

(1) Kho lưu trữ nằm trong một gói (hoặc gói con) mà chúng tôi đã xác định hành vi không rỗng.

(2) Ném một EmptyResultDataAccessException khi truy vấn không tạo ra kết quả. Ném một IllegalArgumentException khi emailAddress được chuyển đến phương thức là trống.

(3) Trả về null khi truy vấn không tạo ra kết quả. Cũng chấp nhận null làm giá trị cho emailAddress.

(4) Trả về Optional.empty () khi truy vấn không tạo ra kết quả. Ném một IllegalArgumentException khi emailAddress được chuyển đến phương thức là trống.

Tính vô hiệu trong Kho lưu trữ dựa trên Kotlin

Kotlin có định nghĩa về các ràng buộc vô hiệu được đưa vào ngôn ngữ. Mã Kotlin biên dịch thành bytecode, không thể hiện các ràng buộc về tính null thông qua các chữ ký phương thức mà là thông qua siêu dữ liệu được biên dịch trong. Đảm bảo bao gồm JAR phản ánh kotlin trong dự án của bạn để cho phép xem xét các ràng buộc về tính vô hiệu của Kotlin. Kho lưu trữ Dữ liệu mùa xuân sử dụng cơ chế ngôn ngữ để xác định các ràng buộc đó để áp dụng các kiểm tra thời gian chạy giống nhau, như sau:

Ví dụ 22. Sử dụng ràng buộc tính nullability trên kho lưu trữ Kotlin

interface UserRepository : Repository<User, String> {

 

  fun findByUsername(username: String): User     (1)

 

  fun findByFirstname(firstname: String?): User? (2)

}

(1) Phương thức xác định cả tham số và kết quả là không thể nullable (mặc định Kotlin). Trình biên dịch Kotlin từ chối các lệnh gọi phương thức chuyển null cho phương thức. Nếu truy vấn trả về một kết quả trống, thì một ngoại lệ EmptyResultDataAccessException sẽ được ném ra.

(2) Phương thức này chấp nhận null cho tham số firstname và trả về null nếu truy vấn không tạo ra kết quả.

4.4.8. Truyền trực tuyến kết quả truy vấn

Bạn có thể xử lý tăng dần kết quả của các phương thức truy vấn bằng cách sử dụng Java 8 Stream <T> làm kiểu trả về. Thay vì gói kết quả truy vấn trong Luồng, các phương pháp dành riêng cho kho dữ liệu được sử dụng để thực hiện luồng, như được minh họa trong ví dụ sau:

Ví dụ 23. Truyền kết quả của một truy vấn với Java 8 Stream <T>

@Query("select u from User u")

Stream<User> findAllByCustomQueryAndStream();

 

Stream<User> readAllByFirstnameNotNull();

 

@Query("select u from User u")

Stream<User> streamAllPaged(Pageable pageable);

Luồng có khả năng bao bọc các tài nguyên cơ bản dành riêng cho cửa hàng dữ liệu và do đó, phải đóng sau khi sử dụng. Bạn có thể đóng Luồng theo cách thủ công bằng cách sử dụng phương thức close () hoặc bằng cách sử dụng khối thử tài nguyên Java 7, như được minh họa trong ví dụ sau:

Ví dụ 24. Làm việc với Luồng <T> dẫn đến khối thử tài nguyên

try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {

  stream.forEach(…);

}

Không phải tất cả các mô-đun Dữ liệu mùa xuân hiện hỗ trợ Luồng Stream<T> như một kiểu trả về.

4.4.9. Kết quả truy vấn không đồng bộ

Bạn có thể chạy các truy vấn kho lưu trữ một cách không đồng bộ bằng cách sử dụng khả năng chạy phương thức không đồng bộ của Spring. Điều này có nghĩa là phương thức trả về ngay lập tức khi được gọi trong khi truy vấn thực sự xảy ra trong một tác vụ đã được gửi đến Spring TaskExecutor. Các truy vấn không đồng bộ khác với các truy vấn phản ứng và không được trộn lẫn. Xem tài liệu dành riêng cho cửa hàng để biết thêm chi tiết về hỗ trợ phản ứng. Ví dụ sau cho thấy một số truy vấn không đồng bộ:

@Async

Future<User> findByFirstname(String firstname);   (1)           

 

@Async

CompletableFuture<User> findOneByFirstname(String firstname); (2)

 

@Async

ListenableFuture<User> findOneByLastname(String lastname);    ” (3)

(1)    Sử dụng java.util.concurrent.Future làm kiểu trả về.

(2)    Sử dụng Java 8 java.util.concurrent.CompletableFuture làm kiểu trả về.

Sử dụng org.springframework.util.concurrent.ListenableFuture làm kiểu trả về.