1.泛型接口
我们举个例子,以前来看一下JPA定义的写法:
Specification接口为:
public interface Specification<T> { Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3); }
以前的写法为(定义一个静态内部类实现):
public static Specification<AlarmLog> searchKeyword(final String key, final Date startTime, final Date endTime) { return new Specification<AlarmLog>() { @Override public Predicate toPredicate(Root<AlarmLog> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { List<Predicate> predicates = new ArrayList<Predicate>(); if (StringUtils.isNotEmpty(key)) { predicates.add(criteriaBuilder.or( criteriaBuilder.like(root.<String>get("description"), "%" + key + "%"), criteriaBuilder.like(root.<String>get("exceptionMessage"), "%" + key + "%"))); } if (startTime != null && endTime != null){ predicates.add(criteriaBuilder.and( criteriaBuilder.greaterThan(root.<Date>get("createTime"), startTime), criteriaBuilder.lessThan(root.<Date>get("createTime"), endTime) )); } return criteriaBuilder.and(predicates.toArray(new Predicate[]{})); } }; }
1.8以后的写法为:
public static Specification<RecorderTask> searchKeyword(final String key, final Boolean isDeleted) { return (root, criteriaQuery, criteriaBuilder) -> { List<Predicate> predicates = new ArrayList<>(); if (StringUtils.isNotEmpty(key)) { predicates.add(criteriaBuilder.or( criteriaBuilder.like(root.<String>get("fileName"), "%" + key + "%"), criteriaBuilder.like(root.<String>get("outputPath"), "%" + key + "%"))); } if (isDeleted != null) { predicates.add(criteriaBuilder.or( criteriaBuilder.equal(root.<Boolean>get("isDeleted"), isDeleted))); } return criteriaBuilder.and(predicates.toArray(new Predicate[]{})); }; }
2.谓词链(讨论在Java 8中链接Predicates的不同方法):
首先,让我们看看如何使用简单的谓词来过滤名称列表:
-
@Test
-
public void whenFilterList_thenSuccess(){
-
List<String> names = Arrays.asList("Adam", "Alexander", "John", "Tom");
-
List<String> result = names.stream()
-
.filter(name -> name.startsWith("A"))
-
.collect(Collectors.toList());
-
assertEquals(2, result.size());
-
assertThat(result, contains("Adam","Alexander"));
-
}
在这个例子中,我们过滤了名称列表,只使用谓词保留以“A”开头的名称:
-
name -> name.startsWith("A")
但是,如果我们想要应用多个Predicates呢?
3.多个过滤器
-
如果我们想要应用多个谓词,一个选项是简单地链接多个过滤器:
-
@Test
-
public void whenFilterListWithMultipleFilters_thenSuccess(){
-
List<String> result = names.stream()
-
.filter(name -> name.startsWith("A"))
-
.filter(name -> name.length() < 5)
-
.collect(Collectors.toList());
-
assertEquals(1, result.size());
-
assertThat(result, contains("Adam"));
-
}
我们现在更新了我们的示例,通过提取以“A”开头并且长度小于5的名称来过滤我们的列表,我们使用了两种过滤修饰-每个谓词。
4.复杂的谓词
现在,我们可以使用一个带有复杂Predicate的过滤器,而不是使用多个过滤器:
-
@Test
-
public void whenFilterListWithComplexPredicate_thenSuccess(){
-
List<String> result = names.stream()
-
.filter(name -> name.startsWith("A") && name.length() < 5)
-
.collect(Collectors.toList());
-
assertEquals(1, result.size());
-
assertThat(result, contains("Adam"));
-
}
这个选项比第一个选项更灵活,因为我们可以使用按位运算来构建 我们想要的复杂谓词。
5.结合谓词
接下来,如果我们不想使用按位运算构建复杂的谓词,Java 8 Predicate可以使用有用的方法来组合谓词我们将使用Predicate.and(),Predicate.or()和Predicate.negate()方法组合谓词。
5.1 Predicate.and()
在这个例子中,我们将明确定义我们的谓词,然后我们将使用Predicate.and()组合它们:
-
@Test
-
public void whenFilterListWithCombinedPredicatesUsingAnd_thenSuccess(){
-
Predicate<String> predicate1 = str -> str.startsWith("A");
-
Predicate<String> predicate2 = str -> str.length() < 5;
-
List<String> result = names.stream()
-
.filter(predicate1.and(predicate2))
-
.collect(Collectors.toList());
-
assertEquals(1, result.size());
-
assertThat(result, contains("Adam"));
-
}
我们可以看到,语法非常直观,方法名称表明了操作的类型。使用Predicate.and(),我们通过仅提取满足两个条件的名称来过滤我们的列表。
5.2 Predicate.or()
我们也可以使用 Predicate.or()来组合Predicates。 让我们提取名称以“J”开头,以及长度小于4的名称:
-
@Test
-
public void whenFilterListWithCombinedPredicatesUsingOr_thenSuccess(){
-
Predicate<String> predicate1 = str -> str.startsWith("J");
-
Predicate<String> predicate2 = str -> str.length() < 4;
-
List<String> result = names.stream()
-
.filter(predicate1.or(predicate2))
-
.collect(Collectors.toList());
-
assertEquals(2, result.size());
-
assertThat(result, contains("John","Tom"));
-
}
5.3 Predicate.negate()
在组合我们的Predicates时我们也可以使用Predicate.negate():
-
@Test
-
public void whenFilterListWithCombinedPredicatesUsingOrAndNegate_thenSuccess(){
-
Predicate<String> predicate1 = str -> str.startsWith("J");
-
Predicate<String> predicate2 = str -> str.length() < 4;
-
List<String> result = names.stream()
-
.filter(predicate1.or(predicate2.negate()))
-
.collect(Collectors.toList());
-
assertEquals(3, result.size());
-
assertThat(result, contains("Adam","Alexander","John"));
-
}
在这里,我们使用or()和negate()的组合来按名称以“J”开头或长度不小于4 来过滤List
5.4 结合谓词内联
我们不需要明确定义要使用的谓词and(), or(),以及negate()。 我们也可以通过强制谓词来内联它们:
-
@Test
-
public void whenFilterListWithCombinedPredicatesInline_thenSuccess(){
-
List<String> result = names.stream()
-
.filter(((Predicate<String>)name -> name.startsWith("A"))
-
.and(name -> name.length()<5))
-
.collect(Collectors.toList());
-
assertEquals(1, result.size());
-
assertThat(result, contains("Adam"));
-
}
6.结合一组谓词
最后,让我们看看如何通过减少它们来链接一组Predicates。在下面的例子中,我们有一个列表的谓词,我们使用组合Predicate.and():
-
@Test
-
public void whenFilterListWithCollectionOfPredicatesUsingAnd_thenSuccess(){
-
List<Predicate<String>> allPredicates = new ArrayList<Predicate<String>>();
-
allPredicates.add(str -> str.startsWith("A"));
-
allPredicates.add(str -> str.contains("d"));
-
allPredicates.add(str -> str.length() > 4);
-
List<String> result = names.stream()
-
.filter(allPredicates.stream().reduce(x->true, Predicate::and))
-
.collect(Collectors.toList());
-
assertEquals(1, result.size());
-
assertThat(result, contains("Alexander"));
-
}
注意,我们使用基本标识作为:
-
x->true
但是如果我们想要使用Predicate.or()组合它们会有所不同:
-
@Test
-
public void whenFilterListWithCollectionOfPredicatesUsingOr_thenSuccess(){
-
List<String> result = names.stream()
-
.filter(allPredicates.stream().reduce(x->false, Predicate::or))
-
.collect(Collectors.toList());
-
assertEquals(2, result.size());
-
assertThat(result, contains("Adam","Alexander"));
-
}