231 lines
9.5 KiB
Java
231 lines
9.5 KiB
Java
package eu.eudat.queryable.hibernatequeryablelist;
|
|
|
|
|
|
import eu.eudat.entities.DataEntity;
|
|
import eu.eudat.queryable.QueryableList;
|
|
import eu.eudat.queryable.exceptions.NotSingleResultException;
|
|
import eu.eudat.queryable.predicates.OrderByPredicate;
|
|
import eu.eudat.queryable.predicates.SelectPredicate;
|
|
import eu.eudat.queryable.predicates.SinglePredicate;
|
|
import org.springframework.scheduling.annotation.Async;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
import javax.persistence.EntityManager;
|
|
import javax.persistence.TypedQuery;
|
|
import javax.persistence.criteria.*;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.stream.Collectors;
|
|
|
|
public class QueryableHibernateList<T extends DataEntity<T>> implements QueryableList<T> {
|
|
|
|
private EntityManager manager;
|
|
private CriteriaQuery<T> query;
|
|
private Class<T> tClass;
|
|
private Root<T> root;
|
|
private Subquery<T> subquery;
|
|
private List<SinglePredicate<T>> predicates = new LinkedList<>();
|
|
private List<OrderByPredicate<T>> orderings = new LinkedList<>();
|
|
private List<Selection> fields = new LinkedList<>();
|
|
private Integer length;
|
|
private Integer offset;
|
|
private Set<String> hints;
|
|
private String hint;
|
|
|
|
public QueryableHibernateList(EntityManager manager, Class<T> tClass) {
|
|
this.manager = manager;
|
|
this.tClass = tClass;
|
|
}
|
|
|
|
public QueryableHibernateList<T> setManager(EntityManager manager) {
|
|
this.manager = manager;
|
|
return this;
|
|
}
|
|
|
|
public QueryableList<T> withHint(String hint) {
|
|
this.hint = hint;
|
|
return this;
|
|
}
|
|
|
|
public QueryableList<T> setHints(Set<String> hints) {
|
|
this.hints = hints;
|
|
return this;
|
|
}
|
|
|
|
public QueryableHibernateList<T> setEntity(Class<T> type) {
|
|
CriteriaBuilder builder = this.manager.getCriteriaBuilder();
|
|
this.query = builder.createQuery(type);
|
|
this.root = this.query.from(this.tClass);
|
|
return this;
|
|
}
|
|
|
|
public void initiateQueryableList(Class<T> type) {
|
|
CriteriaBuilder builder = this.manager.getCriteriaBuilder();
|
|
this.query = builder.createQuery(type);
|
|
}
|
|
|
|
@Override
|
|
public QueryableList<T> skip(Integer offset) {
|
|
this.offset = offset;
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public QueryableList<T> take(Integer length) {
|
|
this.length = length;
|
|
return this;
|
|
}
|
|
|
|
public QueryableList<T> where(SinglePredicate<T> predicate) {
|
|
this.predicates.add(predicate);
|
|
return this;
|
|
}
|
|
|
|
public <R> List<R> select(SelectPredicate<T, R> predicate) {
|
|
List<T> list = this.toList();
|
|
List<R> newlist = new LinkedList<R>();
|
|
for (T item : list) {
|
|
newlist.add(predicate.applySelection(item));
|
|
}
|
|
return newlist;
|
|
}
|
|
|
|
public QueryableList<T> distinct() {
|
|
this.query.distinct(true);
|
|
return this;
|
|
}
|
|
|
|
public QueryableList<T> orderBy(OrderByPredicate<T> predicate) {
|
|
this.orderings.add(predicate);
|
|
return this;
|
|
}
|
|
|
|
public Long count() {
|
|
CriteriaBuilder criteriaBuilder = this.manager.getCriteriaBuilder();
|
|
CriteriaQuery<Long> criteriaQuery = criteriaBuilder.createQuery(Long.class);
|
|
Root<T> root = criteriaQuery.from(tClass);
|
|
criteriaQuery.select(criteriaBuilder.count(root));
|
|
criteriaQuery.where(this.generateWherePredicates(this.predicates, root));
|
|
return this.manager.createQuery(criteriaQuery).getSingleResult();
|
|
}
|
|
|
|
public CompletableFuture<Long> countAsync() {
|
|
CriteriaBuilder criteriaBuilder = this.manager.getCriteriaBuilder();
|
|
CriteriaQuery<Long> criteriaQuery = criteriaBuilder.createQuery(Long.class);
|
|
Root<T> root = criteriaQuery.from(tClass);
|
|
criteriaQuery.select(criteriaBuilder.count(root));
|
|
criteriaQuery.where(this.generateWherePredicates(this.predicates, root));
|
|
return CompletableFuture.supplyAsync(() -> this.manager.createQuery(criteriaQuery).getSingleResult());
|
|
}
|
|
|
|
private Predicate[] generateWherePredicates(List<SinglePredicate<T>> singlePredicates, Root<T> root) {
|
|
List<Predicate> predicates = new LinkedList<>();
|
|
for (SinglePredicate<T> singlePredicate : singlePredicates) {
|
|
predicates.add(singlePredicate.applyPredicate(this.manager.getCriteriaBuilder(), root));
|
|
}
|
|
return predicates.toArray(new Predicate[predicates.size()]);
|
|
}
|
|
|
|
private Order[] generateOrderPredicates(List<OrderByPredicate<T>> orderByPredicates, Root<T> root) {
|
|
List<Order> predicates = new LinkedList<>();
|
|
for (OrderByPredicate<T> orderPredicate : orderByPredicates) {
|
|
predicates.add(orderPredicate.applyPredicate(this.manager.getCriteriaBuilder(), root));
|
|
}
|
|
return predicates.toArray(new Order[predicates.size()]);
|
|
}
|
|
|
|
public List<T> toList() {
|
|
|
|
this.query.where(this.generateWherePredicates(this.predicates, this.root));
|
|
if (!this.orderings.isEmpty()) this.query.orderBy(this.generateOrderPredicates(this.orderings, this.root));
|
|
TypedQuery<T> typedQuery = this.manager.createQuery(this.query);
|
|
if (this.offset != null) typedQuery.setFirstResult(this.offset);
|
|
if (this.length != null) typedQuery.setMaxResults(this.length);
|
|
if (this.hint != null && this.hints.contains(hint)) {
|
|
List ids = typedQuery.getResultList().stream().map(item -> item.getKeys()[0]).collect(Collectors.toList());
|
|
if (ids != null && !ids.isEmpty()) typedQuery = queryWithHint(ids);
|
|
}
|
|
return typedQuery.getResultList();
|
|
}
|
|
|
|
public CompletableFuture<List<T>> toListAsync() {
|
|
this.query.where(this.generateWherePredicates(this.predicates, this.root));
|
|
if (!this.orderings.isEmpty()) this.query.orderBy(this.generateOrderPredicates(this.orderings, this.root));
|
|
TypedQuery<T> typedQuery = this.manager.createQuery(this.query);
|
|
if (this.offset != null) typedQuery.setFirstResult(this.offset);
|
|
if (this.length != null) typedQuery.setMaxResults(this.length);
|
|
if (this.hint != null && this.hints.contains(hint)) {
|
|
List ids = typedQuery.getResultList().stream().map(item -> item.getKeys()[0]).collect(Collectors.toList());
|
|
if (ids != null && !ids.isEmpty()) typedQuery = queryWithHint(ids);
|
|
}
|
|
TypedQuery<T> finalQuery = typedQuery;
|
|
return CompletableFuture.supplyAsync(() -> finalQuery.getResultList());
|
|
}
|
|
|
|
public T getSingle() {
|
|
this.query.where(this.generateWherePredicates(this.predicates, this.root));
|
|
TypedQuery<T> typedQuery = this.manager.createQuery(this.query);
|
|
if (this.hint != null)
|
|
typedQuery.setHint("javax.persistence.fetchgraph", this.manager.getEntityGraph(this.hint));
|
|
return typedQuery.getSingleResult();
|
|
}
|
|
|
|
public CompletableFuture<T> getSingleAsync() {
|
|
this.query.where(this.generateWherePredicates(this.predicates, this.root));
|
|
TypedQuery<T> typedQuery = this.manager.createQuery(this.query);
|
|
if (this.hint != null)
|
|
typedQuery.setHint("javax.persistence.fetchgraph", this.manager.getEntityGraph(this.hint));
|
|
return CompletableFuture.supplyAsync(() -> typedQuery.getSingleResult());
|
|
}
|
|
|
|
public T getSingleOrDefault() {
|
|
this.query.where(this.generateWherePredicates(this.predicates, this.root));
|
|
|
|
TypedQuery<T> typedQuery = this.manager.createQuery(this.query);
|
|
if (this.hint != null)
|
|
typedQuery.setHint("javax.persistence.fetchgraph", this.manager.getEntityGraph(this.hint));
|
|
List<T> results = typedQuery.getResultList();
|
|
if (results.size() == 0) return null;
|
|
if (results.size() == 1) return results.get(0);
|
|
else throw new NotSingleResultException("Query returned more than one items");
|
|
}
|
|
|
|
public CompletableFuture<T> getSingleOrDefaultAsync() {
|
|
this.query.where(this.generateWherePredicates(this.predicates, this.root));
|
|
|
|
TypedQuery<T> typedQuery = this.manager.createQuery(this.query);
|
|
if (this.hint != null)
|
|
typedQuery.setHint("javax.persistence.fetchgraph", this.manager.getEntityGraph(this.hint));
|
|
return CompletableFuture.supplyAsync(() -> typedQuery.getResultList()).thenApply(list -> {
|
|
if (list.size() == 0) return null;
|
|
if (list.size() == 1) return list.get(0);
|
|
else throw new NotSingleResultException("Query returned more than one items");
|
|
});
|
|
}
|
|
|
|
private TypedQuery<T> queryWithHint(List ids) {
|
|
CriteriaBuilder criteriaBuilder = this.manager.getCriteriaBuilder();
|
|
CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(tClass);
|
|
Root<T> criteriaRoot = criteriaQuery.from(this.tClass);
|
|
|
|
criteriaQuery.where(criteriaRoot.get("id").in(ids));
|
|
if (!this.orderings.isEmpty())
|
|
criteriaQuery.orderBy(this.generateOrderPredicates(this.orderings, criteriaRoot));
|
|
|
|
TypedQuery<T> typedQuery = this.manager.createQuery(criteriaQuery);
|
|
typedQuery.setHint("javax.persistence.fetchgraph", this.manager.getEntityGraph(this.hint));
|
|
return typedQuery;
|
|
}
|
|
|
|
@Override
|
|
public Subquery<T> subQuery(SinglePredicate<T> predicate, List<String> fields) {
|
|
Subquery<T> subquery = this.manager.getCriteriaBuilder().createQuery().subquery(this.tClass);
|
|
Root<T> root = subquery.from(this.tClass);
|
|
subquery.where(predicate.applyPredicate(this.manager.getCriteriaBuilder(), root));
|
|
subquery.select(root.get(fields.get(0)));
|
|
return subquery;
|
|
}
|
|
}
|