All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.e6tech.elements.persist.criteria.Select Maven / Gradle / Ivy

There is a newer version: 2.7.9
Show newest version
/*
 * Copyright 2015-2019 Futeh Kao
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.e6tech.elements.persist.criteria;

import net.e6tech.elements.common.interceptor.Interceptor;
import net.e6tech.elements.common.reflection.Primitives;
import net.e6tech.elements.common.reflection.Reflection;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.*;
import javax.persistence.metamodel.EntityType;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Created by futeh.
 */
@SuppressWarnings("unchecked")
public class Select extends Statement {

    private static final String GETTER_MSG = "Only accepts getter";

    private Select parent;
    private int maxResults = -1;
    private int firstResult = -1;
    private List> selections = new ArrayList<>();
    private Map hints = new HashMap<>();

    public Select(Where where, Root root) {
        super(where, root);
    }

    protected Select(Select parent, Where where, From root) {
        super(where, root);
        this.parent = parent;
        this.selections = parent.selections;
    }

    public static  Select create(EntityManager entityManager, Class cls) {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery query = builder.createQuery();
        Root root = query.from(cls);
        Where where = new Where(entityManager, builder, query, root);
        return new Select<>(where, root);
    }

    public Select hint(String hint, Object value) {
        hints.put(hint, value);
        return this;
    }

    public Select removeHint(String hint) {
        hints.remove(hint);
        return this;
    }

    public  void addConverter(Class from, Class to, Function function) {
        where.addConverter(from, to, function);
    }

    public  void removeConverter(Class from, Class to) {
        where.removeConverter(from, to);
    }

    public  T withConverter(Class from, Class to, Function function) {
        addConverter(from, to, function);
        return where.getTemplate();
    }

    public Select where(Consumer consumer) {
        consumer.accept(where.getTemplate());
        where.onQuery();
        return this;
    }

    public Select where(T template) {
        Where.interceptor.runAnonymous(null, where.getTemplate(), template);
        where.onQuery();
        return this;
    }

    public Select where(BiConsumer, T> consumer) {
        consumer.accept(this, where.getTemplate());
        where.onQuery();
        return this;
    }

    public  R getSingleResult() {
        where.onQuery();
        if (selections.size() == 1) {
            getQuery().select((Selection) selections.get(0));
        } else if (!selections.isEmpty()) {
            getQuery().multiselect(selections);
        } else {
            getQuery().select(getFrom());
        }

        Query query = where.getEntityManager().createQuery(getQuery());
        if (maxResults >= 0)
            query.setMaxResults(maxResults);

        if (firstResult >= 0)
            query.setFirstResult(firstResult);

        setHints(query);
        return (R) query.getSingleResult();
    }

    public  List getResultList() {
        where.onQuery();
        if (selections.size() == 1) {
            getQuery().select((Selection) selections.get(0));
        } else if (!selections.isEmpty()) {
            getQuery().multiselect(selections.toArray(new Selection[selections.size()]));
        } else {
            getQuery().select(getFrom());
        }

        Query query = where.getEntityManager().createQuery(getQuery());
        if (maxResults >= 0)
            query.setMaxResults(maxResults);

        if (firstResult >= 0)
            query.setFirstResult(firstResult);

        setHints(query);
        return query.getResultList();
    }

    private void setHints(Query query) {
        for (Map.Entry entry : hints.entrySet())
            query.setHint(entry.getKey(), entry.getValue());
    }

    public Path path(Consumer consumer) {
        Class entityClass = Interceptor.getTargetClass(where.getTemplate());
        AtomicReference ref = new AtomicReference<>();
        T t = applyGetter(entityClass, ref::set);
        consumer.accept(t);
        return ref.get();
    }

    public Select selectEntity() {
        selections.add(getFrom());
        return this;
    }

    public Select select(Function> builder, Consumer consumer) {
        Path path = path(consumer);
        select(builder.apply(getBuilder()).apply(path));
        return this;
    }

    public  Select select(Expression expression) {
        selections.add(expression);
        return this;
    }

    public Select select(Runnable runnable) {
        Interceptor.setInterceptorHandler(where.getTemplate(), getter(path -> selections.add(path)));
        runnable.run();
        Interceptor.setInterceptorHandler(where.getTemplate(), where);
        return this;
    }

    public Select select(Consumer consumer) {
        Class entityClass = Interceptor.getTargetClass(where.getTemplate());
        T t = applyGetter(entityClass, path -> selections.add(path));
        consumer.accept(t);
        return this;
    }

    public  Select crossJoinManyToOneWhere(Class entityClass, Consumer joinCondition, Consumer consumer) {
        return crossJoinManyToOne(entityClass, joinCondition, nestedSelect -> nestedSelect.where(consumer));
    }

    public  Select crossJoinManyToOne(Class entityClass, Consumer joinCondition, BiConsumer, R> consumer) {
        return crossJoinManyToOne(entityClass, joinCondition, nestedSelect -> nestedSelect.where(consumer));
    }

    @SuppressWarnings("squid:S1188")
    public  Select crossJoinManyToOne(Class entityClass, Consumer joinCondition, Consumer> consumer) {
        From jointRoot = getQuery().from(entityClass);
        Interceptor.setInterceptorHandler(where.getTemplate(), frame -> {
            PropertyDescriptor desc = Reflection.propertyDescriptor(frame.getMethod());
            String property = desc.getName();
            if (frame.getMethod().equals(desc.getReadMethod())) {
                Predicate joinPredicate;
                if (getFrom().get(property).getJavaType().equals(jointRoot.getJavaType())) {
                    joinPredicate = getBuilder().equal(getFrom().get(property), jointRoot);
                } else {
                    EntityType type = (EntityType) jointRoot.getModel();
                    String parentIdAttribute = type.getId(type.getIdType().getJavaType()).getName();
                    if (!jointRoot.get(parentIdAttribute).getJavaType().equals(getFrom().get(property).getJavaType())) {
                        throw new IllegalArgumentException("Type mismatch: cannot join " + type.getName() + "." + parentIdAttribute + " to " +
                                getFrom().get(property));
                    }
                    joinPredicate = getBuilder().equal(jointRoot.get(parentIdAttribute), getFrom().get(property));
                }
                this.where.getPredicates().add(joinPredicate);
            } else {
                throw new UnsupportedOperationException(GETTER_MSG);
            }
            return null;
        });
        joinCondition.accept(getTemplate());
        Interceptor.setInterceptorHandler(where.getTemplate(), where);

        Where where = new Where<>(this.where, jointRoot);
        Select joinSelect = new Select<>(this, where, jointRoot);
        consumer.accept(joinSelect);
        return this;
    }

    public  Select crossJoinOneToManyWhere(Class entityClass, Consumer joinCondition, Consumer consumer) {
        return crossJoinOneToMany(entityClass, joinCondition, nestedSelect -> nestedSelect.where(consumer));
    }

    public  Select crossJoinOneToMany(Class entityClass, Consumer joinCondition, BiConsumer, R> consumer) {
        return crossJoinOneToMany(entityClass, joinCondition, nestedSelect -> nestedSelect.where(consumer));
    }

    @SuppressWarnings("squid:S1188")
    public  Select crossJoinOneToMany(Class entityClass, Consumer joinCondition, Consumer> consumer) {
        From joinRoot = getQuery().from(entityClass);
        R joinTemplate = Handler.interceptor.newInstance(entityClass,  frame -> {
            PropertyDescriptor desc = Reflection.propertyDescriptor(frame.getMethod());
            String property = desc.getName();
            if (frame.getMethod().equals(desc.getReadMethod())) {
                Predicate joinPredicate;
                if (joinRoot.get(property).getJavaType().equals(getFrom().getJavaType())) {
                    joinPredicate = getBuilder().equal(getFrom(), joinRoot.get(property));
                } else {
                    EntityType type = (EntityType) getFrom().getModel();
                    String parentIdAttribute = type.getId(type.getIdType().getJavaType()).getName();
                    if (!getFrom().get(parentIdAttribute).getJavaType().equals(joinRoot.get(property).getJavaType())) {
                        throw new IllegalArgumentException("Type mismatch: cannot join " + type.getName() + "." + parentIdAttribute + " to " +
                        joinRoot.get(property));
                    }
                    joinPredicate = getBuilder().equal(getFrom().get(parentIdAttribute), joinRoot.get(property));
                }
                this.where.getPredicates().add(joinPredicate);
            } else {
                throw new UnsupportedOperationException(GETTER_MSG);
            }
            return Primitives.defaultValue(desc.getPropertyType());
        });
        joinCondition.accept(joinTemplate);

        Where where = new Where<>(this.where, joinRoot);
        Select joinSelect = new Select<>(this, where, joinRoot);
        consumer.accept(joinSelect);
        return this;
    }

    public  Select leftJoin(Runnable joinCondition, BiConsumer, R> consumer) {
        return join(JoinType.LEFT, joinCondition, consumer);
    }

    public  Select leftJoin(Runnable joinCondition, Consumer> consumer ) {
        return this.join(JoinType.LEFT, joinCondition, (sel, r) -> consumer.accept(sel));
    }

    public Select leftJoin(Runnable joinCondition) {
        return join(JoinType.LEFT, joinCondition, (sel, r) -> { /* no-op */});
    }

    public  Select join(Runnable joinCondition, BiConsumer, R> consumer) {
        return join(JoinType.INNER, joinCondition, consumer);
    }

    public  Select join(Runnable joinCondition, Consumer> consumer ) {
        return this.join(JoinType.INNER, joinCondition, (sel, r) -> consumer.accept(sel));
    }

    public Select join(Runnable joinCondition) {
        return join(JoinType.INNER, joinCondition, (sel, r) -> { /* no-op */});
    }

    protected  Select join(JoinType type, Runnable joinCondition, BiConsumer, R> consumer) {
        Interceptor.setInterceptorHandler(where.getTemplate(), frame -> {
            PropertyDescriptor desc = Reflection.propertyDescriptor(frame.getMethod());
            String property = desc.getName();
            if (frame.getMethod().equals(desc.getReadMethod())) {
                Join join = getFrom().join(property, type);
                Where where = new Where<>(this.where, join);
                Select joinSelect = new Select<>(this, where, join);
                consumer.accept(joinSelect, where.getTemplate());
            } else {
                throw new UnsupportedOperationException(GETTER_MSG);
            }
            return Primitives.defaultValue(desc.getPropertyType());
        });
        joinCondition.run();
        Interceptor.setInterceptorHandler(where.getTemplate(), where);
        return this;
    }

    /**
     * fetch is used to load detail objects.  In most case, you probably want to use leftFetch because if
     * the master instance does not have detail instances, it will not be returned whereas for leftFetch the master
     * instance is returned.
     *
     * @param joinCondition runnable to specify the join condition
     * @return Select instance
     */
    public Select fetch(Runnable joinCondition) {
        return fetch(JoinType.INNER, joinCondition);
    }

    /**
     * See description on fetch.
     *
     * @param joinCondition runnable to specify the join condition
     * @return Select instance
     */
    public Select leftFetch(Runnable joinCondition) {
        return fetch(JoinType.LEFT, joinCondition);
    }

    protected Select fetch(JoinType type, Runnable joinCondition) {
        Interceptor.setInterceptorHandler(where.getTemplate(), frame -> {
            PropertyDescriptor desc = Reflection.propertyDescriptor(frame.getMethod());
            String property = desc.getName();
            if (frame.getMethod().equals(desc.getReadMethod())) {
                getFrom().fetch(property, type);
            } else {
                throw new UnsupportedOperationException(GETTER_MSG);
            }
            return Primitives.defaultValue(desc.getPropertyType());
        });
        joinCondition.run();
        Interceptor.setInterceptorHandler(where.getTemplate(), where);
        return this;
    }

    public Select setMaxResults(int maxResults) {
        this.maxResults = maxResults;
        if (parent != null)
            parent.setMaxResults(maxResults);
        return this;
    }

    public Select setFirstResult(int firstResult) {
        this.firstResult = firstResult;
        if (parent != null)
            parent.setFirstResult(firstResult);
        return this;
    }

    public void count() {
        selections.add(getBuilder().count(getFrom()));
    }

    public Select asc(Runnable runnable) {
        OrderBy orderBy = new OrderBy(where.getEntityManager(), where.getBuilder(), where.getQuery(), getFrom());
        Interceptor.setInterceptorHandler(where.getTemplate(), orderBy);
        orderBy.desc = false;
        orderBy.orderByList = where.orderByList;
        runnable.run();
        Interceptor.setInterceptorHandler(where.getTemplate(), where);
        return this;
    }

    public Select desc(Runnable runnable) {
        OrderBy orderBy = new OrderBy(where.getEntityManager(), where.getBuilder(), where.getQuery(), getFrom());
        Interceptor.setInterceptorHandler(where.getTemplate(), orderBy);
        orderBy.desc = true;
        orderBy.orderByList = where.orderByList;
        runnable.run();
        Interceptor.setInterceptorHandler(where.getTemplate(), where);
        return this;
    }

    public  Expression sum(String attribute) {
        return getBuilder().sum(where.getPath().get(attribute));
    }

    public  Expression coalesce(Expression expression, N value) {
        CriteriaBuilder.Coalesce coalesce = getBuilder().coalesce();
        coalesce.value(expression);
        coalesce.value(value);
        return coalesce;
    }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy