概述
JPA从2开始引入了criteria的api来编程式构建查询,对于变化参数的jpa编写提供了非常大的便利。Specitication并不能在查询便利上有多少优势,但是在动态添加条件进行多维组合的场景有非常好的效果。
Specification
jpa的接口要继承JpaSpecificationExecutor
public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor {
…
}
查询就可以使用
List<T> findAll(Specification<T> spec);
public class CustomerSpecs {
public static Specification<Customer> isLongTermCustomer() {
return (root, query, builder) -> {
LocalDate date = LocalDate.now().minusYears(2);
return builder.lessThan(root.get(Customer_.createdAt), date);
};
}
public static Specification<Customer> hasSalesOfMoreThan(MonetaryAmount value) {
return (root, query, builder) -> {
// build query here
};
}
}
Customer_ 是JPA元模型。Customer_.createdAt表示Customer对象有createAt属性(Date类型)
进行查询组合:
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR);
List<Customer> customers = customerRepository.findAll(
isLongTermCustomer().or(hasSalesOfMoreThan(amount)));
在Specification中,buider相当于进行条件判断的部分,root实际上相当于表,从表中挑选字段,添加条件,然后返回判断标准Specification,交给数据库去查询。
Example
(QBE)是一种用户界面友好的查询技术。 它允许动态创建查询,并且不需要编写包含字段名称的查询。 实际上,按示例查询不需要使用特定的数据库的查询语言来编写查询语句。
Example api的组成
Probe: 含有对应字段的实例对象。
ExampleMatcher:ExampleMatcher携带有关如何匹配特定字段的详细信息,相当于匹配条件。
Example:由Probe和ExampleMatcher组成,用于查询。
适用场景:
使用一组静态或动态约束来查询数据存储。
频繁重构域对象,而不必担心破坏现有查询。
独立于基础数据存储API进行工作。
按示例查询也有一些限制:
不支持嵌套或分组属性约束,例如firstname =?0或(firstname =?1和lastname =?2)。
仅支持字符串的开始/包含/结束/正则表达式匹配,以及其他属性类型的完全匹配。
同Specification类似,Example的使用需要Repository接口继承QueryByExampleExecutor
public interface QueryByExampleExecutor<T> {
<S extends T> S findOne(Example<S> example);
<S extends T> Iterable<S> findAll(Example<S> example);
// … more functionality omitted.
}
Person person = new Person();
person.setFirstname("Dave");
Example<Person> example = Example.of(person);
扩展:Example Matchers
对字段查询细粒度的控制是依靠Matcher来实现的。
如下的案例:
- 创建对象
- 对象赋值
- 创建ExampleMatcher
- 忽略 lastname属性路径
- 忽略 lastname属性路径并包含空值
- 忽略 lastname属性路径并包含空值并进行后缀字符串匹配
- 创建Example,配置对象与ExampleMatcher
Person person = new Person();
person.setFirstname("Dave");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("lastname")
.withIncludeNullValues()
.withStringMatcherEnding();
Example<Person> example = Example.of(person, matcher);