定义查询方法
存储库代理有两种方法可以从方法名称派生特定于商店的查询。
派生查询两种方式:
- 可以直接从方法名称派生查询。
- 手动定义查询。
1、查询查找策略:
- CREATE
- USE_DECLARED_QUERY
- CREATE_IF_NOT_FOUND(默认):整合了 CREATE和USE_DECLARED_QUERY
(暂时没理解)
2、查询创建
该机制条前缀find…By
,read…By
,query…By
,count…By
,和get…By
从所述方法和开始分析它的其余部分。
从方法名称创建查询:
public interface PersonRepository extends Repository<User, 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);
}
3、属性表达式
假设拥有两个类Person、ZipCode,Person类中拥有一个ZipCode类的属性。可用以下表达式查询ZipCode对应的Person
List<Person> findByAddressZipCode(ZipCode zipCode);
首先,算法解析会将整个AdderssZipCode作为一个整体属性来查找,如果成功,则使用该属性。如果没有查询到对应该名称的属性,算法将By右边的驼峰部分分为头部和尾部,并尝试找到相应的属性,在我们的示例中,AddressZip
和Code
。如果算法找到具有该头部的属性,则它采用尾部并继续从那里构建树,以刚刚描述的方式将尾部分开。如果第一个分割不匹配,算法会将分割点移动到左侧(Address
,ZipCode
)并继续。
虽然这应该适用于大多数情况,但算法可能会选择错误的属性。假设Person
该类也具有addressZip
属性。该算法将在第一轮拆分中匹配,并且基本上选择了错误的属性并最终失败(因为类型addressZip
可能没有code
属性)。
要解决这种歧义,您可以_
在方法名称中使用手动定义遍历点。所以我们的方法名称最终会像这样:
List<Person> findByAddress_ZipCode(ZipCode zipCode);
4、特殊参数处理
基础结构将识别某些特定类型Pageable
,Sort
并动态地对您的查询应用分页和排序。
在查询方法中使用Pageable,Slice和Sort
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);
5、限制查询结果
查询方法的结果可以通过关键字来限制,first
或者top
可以互换使用。
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);
6、流式查询结果
使用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);
注意:
- Stream在使用完毕之后需要关闭资源,使用close()方法或者JDK1.7之后的try-with-resources语法。
- 不是所有的Spring Data模板都支持Stream<T>作为返回类型。
=======================(题外话)try-with-resources使用=======================
try-with-resources是JDK实现的语法糖,JVM虚拟机内部其实还是try-catch-finally语法。
public static void main(String[] args) {
try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
System.out.println(inputStream.read());
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
使用注意:
- 一个外部资源的句柄对象需要实现AutoCloseable接口;
- try-with-resource时,如果对外部资源的处理和对外部资源的关闭均遭遇了异常,“关闭异常”将被抑制,“处理异常”将被抛出,但“关闭异常”并没有丢失,而是存放在“处理异常”的被抑制的异常列表中。
7、异步查询结果
通过Spring的异步方法执行异步查询,@Async将一个task提交到Spring TaskExecutor
@Async
Future<User> findByFirstname(String firstname);
@Async
CompletableFuture<User> findOneByFirstname(String firstname);
@Async
ListenableFuture<User> findOneByLastname(String lastname);
- 使用 java.util.concurrent.Future作为返回类型
- 使用Java 8 java.util.concurrent.CompletableFuture作为返回类型
- 使用 org.springframework.util.concurrent.ListenableFuture作为返回类型