jpa specification predicates - java

I have a predicate but it doesn't work with if I try to compare with more than one element! getStatus returns an enum list, I don't understand how I can return multiple elements from the list if there is more than one value in the request, I think my predicate is written incorrectly.
List<Predicate> predicates = new ArrayList<>();
if (!CollectionUtils.isEmpty(request.getStatus())) {
predicates.add(criteriaBuilder.equal(root.get("status"), request.getStatus()));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));

Related

Specification with Spring JPA not applying the clauses mentioned

I have a requirement for building a dynamic query based on filter. I have an Array which contain the filter criteria based on the entries in the Array I am building the Predicate. But with my current implementation its like the spec is not getting passed to the findall query. The response I am getting has all the data. Could someone correct me if something is missing or wrong.
My Repository interface
#Repository
public interface Company extends JpaRepository<Comp, Integer>{
List<Comp> findAll(Specification spec);
}
My Spec builder
private Specification specificationOnFilter(List<Filter> filters) {
new Specification<Comp>() {
#Override
Predicate toPredicate(Root<Comp> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
// Building predicate, adding into the predicates object
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
}
My final call is like below
MyRepository.findAll(specificationOnFilter(filter));
This line of code:
List<Predicate> predicates = new ArrayList<>(); // Building predicate, adding into the predicates object
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
Seem's to just generate predicate from empty ArrayList of predicate's. Therefore your final predicate will pass all the data since it doesn't contain it was builded from zero predicates.
predicates.toArray(new Predicate[predicates.size()]) // will be empty in your case.
You should involve List<Filter> filter from argument of your method and build predicates List<Predicate> from it instead of just creating new ArrayList<>() which is empty.
Also in your question you don't provide any information on what libraries you are using. What are Filter and Specification and Scope classes (this comes from spring cloud i guess). Therefore it's hard to reproduce issue and you will be provided with abstract answer's.

How to map the Flux result to another object type with ReactiveMongoTemplate

The find API in java Spring #Repository class wants to return a Flux<AggregatedObject>, but the getReactiveMongoTemplate().find() returns a Document of different type. What could be the best way to convert the resultant Flux to AggregatedObject type?
public Flux<AggregatedObject> find() {
Criteria filterCriteria = getFilterCriteria();
Query query = new BasicQuery(filterCriteria.getCriteriaObject());
Flux flux = getReactiveMongoTemplate().find(query, Document.class, collectionName);
// how to convert Document to AggregatedObject and return?
}
Just add your AggregatedObject as parametrized type:
public Flux<AggregatedObject> find() {
Criteria filterCriteria = getFilterCriteria();
Query query = new Query(filterCriteria);
return getReactiveMongoTemplate().find(query, AggregatedObject.class, collectionName);
}
we can do it like this;
flux.map(d -> reactiveMongoTemplate.getConverter()
.read(AggregatedObject.class, d.get("value", Document.class)))

Criteria API. Predicate with OrderBy expression

I have a repo with own Specification implementation with toPredicate method as main query construction and I try to add order by expression:
public Predicate toPredicate(#NotNull Root<Strategy> root,
#NotNull CriteriaQuery<?> query,
#NotNull CriteriaBuilder builder) {
Predicate predicate = builder.conjunction();
List<Expression<Boolean>> exps = predicate.getExpressions();
... adding different expressions to exps.add(...)
// I get an id for descending sort due to Postgres just increment it.
Order orderBy = builder.desc(root.get("id"));
Expression<?> expression = orderBy.getExpression();
// Problem here.
exps.add(expression);
return predicate;
}
Expression from orderBy.getExpression() is <?> generic but original expressions list expect <Boolean> type. How to connect them?
Specification is only intended for encoding where-clauses. If you want to order your result use a Sort instance as an additional parameter.
Sorting with pagination means sorting should be field of Pageable like this:
Pageable pagination = PageRequest.of(pageNumber, pageSize, Sort.by("id").descending());

Combine Java 8 predicates with disjunction

Suppose I have an array or a list of status, and want to filter a list for elements whose status matches any of the given. So I go on creating a predicate. I started by initializing it with the comparison to the first, then adding more conditions with or, which resulted in the minimal predicate but was a lot of code:
Predicate<Rec> predicate = null;
for (SendStatus status : statuss) {
Predicate<Rec> innerPred = nr -> nr.getStatus() == status;
if (predicate == null)
predicate = innerPred;
else
predicate = predicate.or(innerpred);
}
More elegantly, I came up with the following code:
Predicate<Rec> predicate = nr -> false;
for (SendStatus status : statuss) {
predicate = predicate.or(nr -> nr.getStatus() == status);
}
This looks nicer, but has a useless predicate at the beginning of the chain. Apache Collections had an AnyPredicate that could be composed of any number of predicates, and I’m basically looking for a replacement.
Is this superfluous predicate acceptable? Is there an even more elegant way to write this?
How about this, assuming statuss is a Collection<SendStatus>:
Predicate<Rec> predicate = nr -> statuss.stream().anyMatch(status -> nr.getStatus() == status);
Or this, if statuss is a SendStatus[]:
Predicate<Rec> predicate = nr -> Arrays.stream(statuss).anyMatch(status -> nr.getStatus() == status);
Or do as suggested by #Jerry06 in a comment, which is faster if statuss is a Set<SendStatus>, and simpler than streaming collection solution above:
Predicate<Rec> predicate = nr -> statuss.contains(nr.getStatus());

Java 8 Stream API how to reuse custom method

I have a method like:
private List<Person> findFilteredPersons(List<Person> persons,
Predicate<Person> filter) {
return persons
.stream()
.filter(filter)
.sorted(BY_NAME.thenComparing(BY_AGE))
.collect(Collectors.toList());
}
Now I would like to use this method in several places, but in one place I don't need to filter the list, I just want to sort it. What is the best way to achieve this?
It’s unlikely that you will run into performance issue when simply using a x -> true predicate to indicate “no filtering”, however, if you want to express the unfiltered operation as such for clarity, the solution is straight-forward: as always for such kind of code organization, split the operation into the distinct parts and the common part:
private List<Person> findFilteredPersons(List<Person> persons, Predicate<Person> filter) {
return collectSortedPersons(persons.stream().filter(filter));
}
private List<Person> findPersons(List<Person> persons) {
return collectSortedPersons(persons.stream());
}
private List<Person> collectSortedPersons(Stream<Person> persons) {
return persons
.sorted(BY_NAME.thenComparing(BY_AGE))
.collect(Collectors.toList());
}
To take all items (practically ignoring the filter), pass a predicate that takes all people:
p -> true
You can create another method:
private List<Person> findPeople(List<Person> persons) {
return findFilteredPersons(persons, p -> true);
}

Categories

Resources