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ề.