
org.dellroad.querystream.jpa.SearchStreamImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of querystream-jpa Show documentation
Show all versions of querystream-jpa Show documentation
Build JPA Criteria queries using a Stream-like API
The newest version!
/*
* Copyright (C) 2018 Archie L. Cobbs. All rights reserved.
*/
package org.dellroad.querystream.jpa;
import jakarta.persistence.EntityManager;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.LockModeType;
import jakarta.persistence.Parameter;
import jakarta.persistence.TemporalType;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.AbstractQuery;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Order;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Selection;
import jakarta.persistence.metamodel.PluralAttribute;
import jakarta.persistence.metamodel.SingularAttribute;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import org.dellroad.querystream.jpa.querytype.SearchType;
class SearchStreamImpl>
extends QueryStreamImpl, CriteriaQuery, TypedQuery, SearchType>
implements SearchStream {
// Constructors
SearchStreamImpl(EntityManager entityManager, SearchType queryType,
QueryConfigurer, X, ? extends S> configurer, QueryInfo queryInfo) {
super(entityManager, queryType, configurer, queryInfo);
}
// Superclass overrides
@Override
SearchStream create(EntityManager entityManager, SearchType queryType,
QueryConfigurer, X, ? extends S> configurer, QueryInfo queryInfo) {
return new SearchStreamImpl<>(entityManager, queryType, configurer, queryInfo);
}
@Override
SearchStream withConfig(QueryConfigurer, X, ? extends S> configurer) {
return (SearchStream)super.withConfig(configurer);
}
@Override
final CriteriaQuery select(CriteriaQuery query, S selection) {
return query.select(selection);
}
// Subclass required methods
SearchValue toValue() {
return this.toValue(false);
}
SearchValue toValue(boolean forceLimit) {
return new SearchValueImpl<>(this.entityManager, this.queryType,
this.configurer, forceLimit ? this.queryInfo.withMaxResults(1) : this.queryInfo);
}
// CriteriaQuery stuff
@Override
public SearchStream distinct() {
QueryStreamImpl.checkOffsetLimit(this, "distinct()");
return this.modQuery((query, selection) -> query.distinct(true));
}
@Override
public SearchStream orderBy(Ref, ? extends Expression>> ref, boolean asc) {
if (ref == null)
throw new IllegalArgumentException("null ref");
return this.orderBy(selection -> ref.get(), asc, false);
}
@Override
public SearchStream thenOrderBy(Ref, ? extends Expression>> ref, boolean asc) {
if (ref == null)
throw new IllegalArgumentException("null ref");
return this.orderBy(selection -> ref.get(), asc, true);
}
@Override
public SearchStream orderBy(Order... orders) {
if (orders == null)
throw new IllegalArgumentException("null orders");
return this.orderByMulti(selection -> Arrays.asList(orders), false);
}
@Override
public SearchStream thenOrderBy(Order... orders) {
if (orders == null)
throw new IllegalArgumentException("null orders");
return this.orderByMulti(selection -> Arrays.asList(orders), true);
}
@Override
public SearchStream orderBy(SingularAttribute super X, ?> attribute, boolean asc) {
if (attribute == null)
throw new IllegalArgumentException("null attribute");
return this.orderBy(selection -> ((Path)selection).get(attribute), asc, false); // cast is valid if attribute exists
}
@Override
public SearchStream thenOrderBy(SingularAttribute super X, ?> attribute, boolean asc) {
if (attribute == null)
throw new IllegalArgumentException("null attribute");
return this.orderBy(selection -> ((Path)selection).get(attribute), asc, true); // cast is valid if attribute exists
}
@Override
public SearchStream orderBy(
SingularAttribute super X, ?> attribute1, boolean asc1,
SingularAttribute super X, ?> attribute2, boolean asc2) {
if (attribute1 == null)
throw new IllegalArgumentException("null attribute1");
if (attribute2 == null)
throw new IllegalArgumentException("null attribute2");
return this.orderByMulti(selection -> {
final Path path = (Path)selection; // cast must be valid if attribute exists
return Arrays.asList(
asc1 ? this.builder().asc(path.get(attribute1)) : this.builder().desc(path.get(attribute1)),
asc2 ? this.builder().asc(path.get(attribute2)) : this.builder().desc(path.get(attribute2)));
}, false);
}
@Override
public SearchStream orderBy(
SingularAttribute super X, ?> attribute1, boolean asc1,
SingularAttribute super X, ?> attribute2, boolean asc2,
SingularAttribute super X, ?> attribute3, boolean asc3) {
if (attribute1 == null)
throw new IllegalArgumentException("null attribute1");
if (attribute2 == null)
throw new IllegalArgumentException("null attribute2");
if (attribute3 == null)
throw new IllegalArgumentException("null attribute3");
return this.orderByMulti(selection -> {
final Path path = (Path)selection; // cast must be valid if attribute exists
return Arrays.asList(
asc1 ? this.builder().asc(path.get(attribute1)) : this.builder().desc(path.get(attribute1)),
asc2 ? this.builder().asc(path.get(attribute2)) : this.builder().desc(path.get(attribute2)),
asc3 ? this.builder().asc(path.get(attribute3)) : this.builder().desc(path.get(attribute3)));
}, false);
}
@Override
public SearchStream orderBy(Function super S, ? extends Expression>> orderExprFunction, boolean asc) {
return this.orderBy(orderExprFunction, asc, false);
}
@Override
public SearchStream thenOrderBy(Function super S, ? extends Expression>> orderExprFunction, boolean asc) {
return this.orderBy(orderExprFunction, asc, true);
}
private SearchStream orderBy(Function super S, ? extends Expression>> orderExprFunction, boolean asc, boolean add) {
if (orderExprFunction == null)
throw new IllegalArgumentException("null orderExprFunction");
return this.orderByMulti(selection -> {
final Expression> expr = orderExprFunction.apply(selection);
return Collections.singletonList(asc ? this.builder().asc(expr) : this.builder().desc(expr));
}, add);
}
@Override
public SearchStream orderByMulti(Function super S, ? extends List extends Order>> orderListFunction) {
return this.orderByMulti(orderListFunction, false);
}
private SearchStream orderByMulti(Function super S, ? extends List extends Order>> orderListFunction, boolean add) {
if (orderListFunction == null)
throw new IllegalArgumentException("null orderListFunction");
QueryStreamImpl.checkOffsetLimit(this, "sorting");
return this.withConfig((builder, query) -> {
if (!(query instanceof CriteriaQuery)) {
throw new UnsupportedOperationException("sorry, can't sort a subquery because"
+ " the JPA Criteria API doesn't support sorting in subqueries");
}
final CriteriaQuery> criteriaQuery = (CriteriaQuery>)query;
final S selection = this.configure(builder, query);
final ArrayList newOrders = new ArrayList<>();
if (add)
newOrders.addAll(criteriaQuery.getOrderList());
newOrders.addAll(orderListFunction.apply(selection));
criteriaQuery.orderBy(newOrders);
return selection;
});
}
@Override
public SearchStream groupBy(Ref, ? extends Expression>> ref) {
if (ref == null)
throw new IllegalArgumentException("null ref");
return this.groupBy(selection -> ref.get());
}
@Override
public SearchStream groupBy(SingularAttribute super X, ?> attribute) {
if (attribute == null)
throw new IllegalArgumentException("null attribute");
return this.groupBy(selection -> ((Path)selection).get(attribute)); // cast must be valid if attribute exists
}
@Override
public SearchStream groupBy(Function super S, ? extends Expression>> groupFunction) {
if (groupFunction == null)
throw new IllegalArgumentException("null groupFunction");
return this.groupByMulti(selection -> Collections.singletonList(groupFunction.apply(selection)));
}
@Override
public SearchStream groupByMulti(Function super S, ? extends List>> groupFunction) {
if (groupFunction == null)
throw new IllegalArgumentException("null groupFunction");
QueryStreamImpl.checkOffsetLimit(this, "grouping");
return this.withConfig((builder, query) -> {
final S selection = this.configure(builder, query);
query.groupBy(groupFunction.apply(selection));
return selection;
});
}
@Override
public SearchStream having(Function super S, ? extends Expression> havingFunction) {
if (havingFunction == null)
throw new IllegalArgumentException("null havingFunction");
QueryStreamImpl.checkOffsetLimit(this, "grouping");
return this.withConfig((builder, query) -> {
final S selection = this.configure(builder, query);
query.having(havingFunction.apply(selection));
return selection;
});
}
// Streamy stuff
@Override
public boolean allMatch(SingularAttribute super X, Boolean> attribute) {
if (attribute == null)
throw new IllegalArgumentException("null attribute");
QueryStreamImpl.checkOffsetLimit(this, "allMatch()");
return !this.withConfig((builder, query) -> {
final S selection = this.configure(builder, query);
this.and(builder, query, builder.not(((Path)selection).get(attribute))); // cast must be valid if attribute exists
return selection;
}).getResultStream().findAny().isPresent();
}
@Override
public boolean allMatch(Function super S, ? extends Expression> predicateBuilder) {
if (predicateBuilder == null)
throw new IllegalArgumentException("null predicateBuilder");
QueryStreamImpl.checkOffsetLimit(this, "allMatch()");
return !this.withConfig((builder, query) -> {
final S selection = this.configure(builder, query);
this.and(builder, query, builder.not(predicateBuilder.apply(selection)));
return selection;
}).getResultStream().findAny().isPresent();
}
@Override
public boolean anyMatch(SingularAttribute super X, Boolean> attribute) {
return this.filter(attribute).getResultStream().findAny().isPresent();
}
@Override
public boolean anyMatch(Function super S, ? extends Expression> predicateBuilder) {
return this.filter(predicateBuilder).getResultStream().findAny().isPresent();
}
@Override
public boolean noneMatch(SingularAttribute super X, Boolean> attribute) {
return !this.anyMatch(attribute);
}
@Override
public boolean noneMatch(Function super S, ? extends Expression> predicateBuilder) {
return !this.anyMatch(predicateBuilder);
}
@Override
public boolean isEmpty() {
return !this.getResultStream().findAny().isPresent();
}
@Override
public SearchValue findAny() {
return this instanceof SearchValue ? (SearchValue)this : this.toValue(true);
}
@Override
public SearchValue findFirst() {
return this instanceof SearchValue ? (SearchValue)this : this.toValue(true);
}
@Override
public SearchValue findSingle() {
return this.toValue(false);
}
// Binding
@Override
public SearchStream addRoot(Ref> ref, Class type) {
if (ref == null)
throw new IllegalArgumentException("null ref");
if (type == null)
throw new IllegalArgumentException("null type");
QueryStreamImpl.checkOffsetLimit(this, "roots must be added prior to skip() or limit()");
return this.withConfig((builder, query) -> {
final S selection = this.configure(builder, query);
ref.bind(query.from(type));
return selection;
});
}
// Fetches
@Override
public SearchStream fetch(SingularAttribute super X, ?> attribute) {
return this.fetch(attribute, JoinType.INNER);
}
@Override
public SearchStream fetch(SingularAttribute super X, ?> attribute, JoinType joinType) {
if (attribute == null)
throw new IllegalArgumentException("null attribute");
if (joinType == null)
throw new IllegalArgumentException("null joinType");
throw new UnsupportedOperationException(); // this should never happen if "attribute" is really valid
}
@Override
public SearchStream fetch(PluralAttribute super X, ?, ?> attribute) {
return this.fetch(attribute, JoinType.INNER);
}
@Override
public SearchStream fetch(PluralAttribute super X, ?, ?> attribute, JoinType joinType) {
if (attribute == null)
throw new IllegalArgumentException("null attribute");
if (joinType == null)
throw new IllegalArgumentException("null joinType");
throw new UnsupportedOperationException(); // this should never happen if "attribute" is really valid
}
// Narrowing overrides (QueryStreamImpl)
@Override
public SearchType getQueryType() {
return this.queryType;
}
@Override
SearchStream modQuery(BiConsumer super AbstractQuery>, ? super S> modifier) {
return (SearchStream)super.modQuery(modifier);
}
@Override
public SearchStream bind(Ref ref) {
return (SearchStream)super.bind(ref);
}
@Override
public SearchStream peek(Consumer super S> peeker) {
return (SearchStream)super.peek(peeker);
}
@Override
public > SearchStream bind(
Ref ref, Function super S, ? extends S2> refFunction) {
return (SearchStream)super.bind(ref, refFunction);
}
@Override
public SearchStream filter(SingularAttribute super X, Boolean> attribute) {
return (SearchStream)super.filter(attribute);
}
@Override
public SearchStream filter(Function super S, ? extends Expression> predicateBuilder) {
return (SearchStream)super.filter(predicateBuilder);
}
@Override
public SearchStream limit(int limit) {
return (SearchStream)super.limit(limit);
}
@Override
public SearchStream skip(int skip) {
return (SearchStream)super.skip(skip);
}
@Override
public SearchStream withFlushMode(FlushModeType flushMode) {
return (SearchStream)super.withFlushMode(flushMode);
}
@Override
public SearchStream withLockMode(LockModeType lockMode) {
return (SearchStream)super.withLockMode(lockMode);
}
@Override
public SearchStream withHint(String name, Object value) {
return (SearchStream)super.withHint(name, value);
}
@Override
public SearchStream withHints(Map hints) {
return (SearchStream)super.withHints(hints);
}
@Override
public SearchStream withParam(Parameter parameter, T value) {
return (SearchStream)super.withParam(parameter, value);
}
@Override
public SearchStream withParam(Parameter parameter, Date value, TemporalType temporalType) {
return (SearchStream)super.withParam(parameter, value, temporalType);
}
@Override
public SearchStream withParam(Parameter parameter, Calendar value, TemporalType temporalType) {
return (SearchStream)super.withParam(parameter, value, temporalType);
}
@Override
public SearchStream withParams(Iterable extends ParamBinding>> params) {
return (SearchStream)super.withParams(params);
}
@Override
public SearchStream withLoadGraph(String name) {
return (SearchStream)super.withLoadGraph(name);
}
@Override
public SearchStream withFetchGraph(String name) {
return (SearchStream)super.withFetchGraph(name);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy