org.batoo.jpa.core.impl.criteria.AbstractCriteriaQueryImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.batoo.jpa.core.impl.criteria.AbstractCriteriaQueryImpl.java

Source

/*
 * Copyright (c) 2012-2013, Batu Alp Ceylan
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */

package org.batoo.jpa.core.impl.criteria;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.PersistenceException;
import javax.persistence.criteria.AbstractQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Predicate.BooleanOperator;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import javax.persistence.metamodel.EntityType;

import org.apache.commons.lang.StringUtils;
import org.batoo.common.log.BLogger;
import org.batoo.common.log.BLoggerFactory;
import org.batoo.common.util.BatooUtils;
import org.batoo.jpa.core.impl.criteria.expression.AbstractExpression;
import org.batoo.jpa.core.impl.criteria.expression.ParameterExpressionImpl;
import org.batoo.jpa.core.impl.criteria.expression.PredicateImpl;
import org.batoo.jpa.core.impl.criteria.join.Joinable;
import org.batoo.jpa.core.impl.model.EntityTypeImpl;
import org.batoo.jpa.core.impl.model.MetamodelImpl;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * the definition of the functionality that is common to both top-level queries and subqueries.
 * <p>
 * It is not intended to be used directly in query construction.
 * 
 * <p>
 * All queries must have a set of root entities (which may in turn own joins).
 * <p>
 * All queries may have a conjunction of restrictions.
 * 
 * @param <T>
 *            the type of the result
 * 
 * @author hceylan
 * @since 2.0.0
 */
@SuppressWarnings("unchecked")
public abstract class AbstractCriteriaQueryImpl<T> extends BaseQueryImpl<T> implements AbstractQuery<T> {

    private static final BLogger LOG = BLoggerFactory.getLogger(AbstractCriteriaQueryImpl.class);

    private Class<T> resultType;
    private boolean internal;
    private final Set<RootImpl<?>> roots = Sets.newHashSet();

    private AbstractSelection<T> selection;
    private PredicateImpl restriction;
    private PredicateImpl groupRestriction;
    private boolean distinct;
    private final List<AbstractExpression<?>> groupList = Lists.newArrayList();

    private final List<ParameterExpressionImpl<?>> parameterOrder = Lists.newArrayList();

    /**
     * 
     * @param metamodel
     *            the metamodel
     * 
     * @since 2.0.1
     */
    public AbstractCriteriaQueryImpl(MetamodelImpl metamodel) {
        super(metamodel);
    }

    /**
     * @param metamodel
     *            the metamodel
     * @param resultType
     *            the result type
     * 
     * @since 2.0.0
     */
    public AbstractCriteriaQueryImpl(MetamodelImpl metamodel, Class<T> resultType) {
        super(metamodel);

        this.resultType = resultType;
    }

    /**
     * 
     * @param metamodel
     *            the metamodel
     * @param qlString
     *            the JPQL query
     * 
     * @since 2.0.1
     */
    public AbstractCriteriaQueryImpl(MetamodelImpl metamodel, String qlString) {
        super(metamodel, qlString);
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public AbstractQuery<T> distinct(boolean distinct) {
        this.distinct = distinct;

        return this;
    }

    /**
     * Ensures that there is a valid selection.
     * 
     * @return the selection
     * 
     * @since 2.0.0
     */
    protected AbstractSelection<T> ensureSelection() {
        if (this.selection == null) {
            if (this.getRoots().size() == 1) {
                return this.selection = (AbstractSelection<T>) this.getRoots().iterator().next();
            } else {
                throw new PersistenceException("Selection is not specified");
            }
        }

        return this.selection;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public <X> RootImpl<X> from(Class<X> entityClass) {
        final EntityTypeImpl<X> entity = this.getMetamodel().entity(entityClass);

        return this.from(entity);
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public <X> RootImpl<X> from(EntityType<X> entity) {
        final RootImpl<X> r = new RootImpl<X>((EntityTypeImpl<X>) entity);

        this.roots.add(r);

        return r;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public String generateJpql() {
        final StringBuilder builder = new StringBuilder();

        this.ensureSelection();

        builder.append("select ");

        // append distinct if necessary
        if (this.distinct) {
            builder.append("distinct ");
        }

        builder.append(this.selection.generateJpqlSelect(this, true));

        final Collection<String> roots = Collections2.transform(this.getRoots(), new Function<Root<?>, String>() {

            @Override
            public String apply(Root<?> input) {
                final RootImpl<?> root = (RootImpl<?>) input;

                final StringBuilder builder = new StringBuilder(input.getModel().getName());

                if (StringUtils.isNotBlank(input.getAlias())) {
                    builder.append(" as ").append(input.getAlias());
                }

                final String joins = root.generateJpqlJoins(AbstractCriteriaQueryImpl.this);

                if (StringUtils.isNotBlank(joins)) {
                    builder.append("\n").append(BatooUtils.indent(joins));
                }
                return builder.toString();
            }
        });
        builder.append("\nfrom ").append(Joiner.on(", ").join(roots));

        if (this.getRestriction() != null) {
            builder.append("\nwhere\n\t").append(this.getRestriction().generateJpqlRestriction(this));
        }

        if (this.getGroupList().size() > 0) {
            final String groupBy = Joiner.on(", ")
                    .join(Lists.transform(this.getGroupList(), new Function<Expression<?>, String>() {

                        @Override
                        public String apply(Expression<?> input) {
                            return ((AbstractExpression<?>) input)
                                    .generateJpqlRestriction(AbstractCriteriaQueryImpl.this);
                        }
                    }));

            builder.append("\ngroup by\n\t").append(groupBy);
        }

        if (this.getGroupRestriction() != null) {
            builder.append("\nhaving\n\t").append(this.getGroupRestriction().generateJpqlRestriction(this));
        }

        return builder.toString();
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public String generateSql() {
        this.ensureSelection();

        AbstractCriteriaQueryImpl.LOG.debug("Preparing SQL for {0}", AbstractCriteriaQueryImpl.LOG.lazyBoxed(this));

        final Map<Joinable, String> joins = Maps.newLinkedHashMap();

        // generate the select chunk
        final StringBuilder select = new StringBuilder();
        select.append("SELECT");
        if (this.distinct && !this.internal) {
            select.append(" DISTINCT");
        }
        select.append("\n");
        select.append(BatooUtils.indent(this.selection.generateSqlSelect(this, true)));

        // generate from chunk
        final List<String> froms = Lists.newArrayList();
        for (final Root<?> r : this.getRoots()) {
            final RootImpl<?> root = (RootImpl<?>) r;
            froms.add(root.generateSqlFrom(this));
        }

        final String where = this.generateSqlRestriction();
        final String groupBy = this.getGroupList().size() == 0 ? null
                : Joiner.on(", ").join(Lists.transform(this.getGroupList(), new Function<Expression<?>, String>() {

                    @Override
                    public String apply(Expression<?> input) {
                        return ((AbstractExpression<?>) input).generateSqlSelect(AbstractCriteriaQueryImpl.this,
                                false);
                    }
                }));

        final String having = this.getGroupRestriction() != null
                ? this.getGroupRestriction().generateSqlRestriction(this)
                : null;

        for (final Root<?> root : this.getRoots()) {
            ((RootImpl<?>) root).generateSqlJoins(this, joins);
        }

        final String from = "FROM " + Joiner.on(", ").join(froms);
        final String join = Joiner.on("\n").skipNulls().join(joins.values());

        return Joiner.on("\n").skipNulls().join(//
                select, //
                from, //
                StringUtils.isBlank(join) ? null : BatooUtils.indent(join), //
                StringUtils.isBlank(where) ? null : "WHERE\n\t" + where, //
                StringUtils.isBlank(groupBy) ? null : "GROUP BY\n\t" + groupBy, //
                StringUtils.isBlank(having) ? null : "HAVING\n\t" + having);
    }

    /**
     * Returns the restriction for the query.
     * 
     * @return the restriction
     * 
     * @since 2.0.0
     */
    private String generateSqlRestriction() {
        final String[] restrictions = new String[this.getRoots().size() + 1];

        if (this.getRestriction() != null) {
            restrictions[0] = this.restriction.generateSqlRestriction(this);
        }

        int i = 0;
        final Iterator<Root<?>> j = this.getRoots().iterator();
        while (j.hasNext()) {
            final Root<?> root = j.next();
            restrictions[++i] = ((RootImpl<?>) root).generateDiscrimination(false);

        }

        final String restriction = Joiner.on(") AND (").skipNulls().join(restrictions);
        if (StringUtils.isBlank(restriction)) {
            return null;
        }

        return "(" + restriction + ")";
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public List<Expression<?>> getGroupList() {
        final List<Expression<?>> groupList = Lists.newArrayList();
        groupList.addAll(this.groupList);

        return groupList;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public PredicateImpl getGroupRestriction() {
        return this.groupRestriction;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public PredicateImpl getRestriction() {
        return this.restriction;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public Class<T> getResultType() {
        return this.resultType;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public Set<Root<?>> getRoots() {
        return Sets.<Root<?>>newHashSet(this.roots);
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public AbstractSelection<T> getSelection() {
        return this.ensureSelection();
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public AbstractQuery<T> groupBy(Expression<?>... grouping) {
        this.groupList.clear();

        for (final Expression<?> expression : grouping) {
            this.groupList.add((AbstractExpression<?>) expression);
        }

        return this;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public AbstractQuery<T> groupBy(List<Expression<?>> grouping) {
        this.groupList.clear();

        for (int i = 0; i < grouping.size(); i++) {
            this.groupList.add((AbstractExpression<?>) grouping.get(i));
        }

        return this;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public AbstractQuery<T> having(Expression<Boolean> restriction) {
        if (restriction instanceof PredicateImpl) {
            this.groupRestriction = (PredicateImpl) restriction;
        } else {
            this.groupRestriction = new PredicateImpl((AbstractExpression<Boolean>) restriction);
        }

        return this;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public AbstractQuery<T> having(Predicate... restrictions) {
        this.groupRestriction = new PredicateImpl(false, BooleanOperator.AND, restrictions);

        return this;
    }

    /**
     * Marks the query as internal entity query.
     * 
     * @return self
     * 
     * @since 2.0.0
     */
    public AbstractCriteriaQueryImpl<T> internal() {
        this.internal = true;
        this.distinct(true);

        return this;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public boolean isDistinct() {
        return this.distinct;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public boolean isInternal() {
        return this.internal;
    }

    /**
     * Registers the parameter as the nex SQL parameter
     * 
     * @param parameter
     *            the parameter to register
     * 
     * @since 2.0.0
     */
    public void registerParameter(ParameterExpressionImpl<?> parameter) {
        this.parameterOrder.add(parameter);
    }

    /**
     * Sets the selection
     * 
     * @param selection
     *            the selection
     * @return the modified query
     * 
     * @since 2.0.0
     */
    protected AbstractCriteriaQueryImpl<T> select(Selection<? extends T> selection) {
        this.selection = (AbstractSelection<T>) selection;

        return this;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public <U> SubqueryImpl<U> subquery(Class<U> type) {
        return new SubqueryImpl<U>(this.getMetamodel(), this, type);
    }

    /**
     * @param selections
     *            the selections
     * 
     * @since 2.0.0
     */
    public void updateResultClass(List<Selection<?>> selections) {
        if (selections.size() == 1) {
            final Selection<?> selection = selections.get(0);
            this.resultType = (Class<T>) selection.getJavaType();
        } else {
            this.resultType = (Class<T>) Object[].class;
        }
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public AbstractQuery<T> where(Expression<Boolean> restriction) {
        if (restriction instanceof PredicateImpl) {
            final PredicateImpl predicate = (PredicateImpl) restriction;
            if (predicate.getExpressions().size() > 0) {
                this.restriction = predicate;
            }
        } else {
            this.restriction = new PredicateImpl((AbstractExpression<Boolean>) restriction);
        }

        return this;
    }

    /**
     * {@inheritDoc}
     * 
     */
    @Override
    public AbstractQuery<T> where(Predicate... restrictions) {
        this.restriction = new PredicateImpl(false, BooleanOperator.AND, restrictions);

        return this;
    }
}