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

org.hibernate.query.criteria.internal.QueryStructure Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.query.criteria.internal;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.criteria.AbstractQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import javax.persistence.criteria.Subquery;
import javax.persistence.metamodel.EntityType;

import org.hibernate.query.criteria.internal.compile.RenderingContext;
import org.hibernate.query.criteria.internal.path.RootImpl;
import org.hibernate.query.criteria.internal.path.RootImpl.TreatedRoot;

/**
 * Models basic query structure.  Used as a delegate in implementing both
 * {@link javax.persistence.criteria.CriteriaQuery} and
 * {@link javax.persistence.criteria.Subquery}.
 * 

* Note the ORDER BY specs are neglected here. That's because it is not valid * for a subquery to define an ORDER BY clause. So we just handle them on the * root query directly... * * @author Steve Ebersole */ public class QueryStructure implements Serializable { private final AbstractQuery owner; private final CriteriaBuilderImpl criteriaBuilder; private final boolean isSubQuery; public QueryStructure(AbstractQuery owner, CriteriaBuilderImpl criteriaBuilder) { this.owner = owner; this.criteriaBuilder = criteriaBuilder; this.isSubQuery = Subquery.class.isInstance( owner ); } private boolean distinct; private Selection selection; private Set> roots = new LinkedHashSet>(); private Set correlationRoots; private Predicate restriction; private List> groupings = Collections.emptyList(); private Predicate having; private List> subqueries; // PARAMETERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public Set> getParameters() { final Set> parameters = new LinkedHashSet>(); final ParameterRegistry registry = new ParameterRegistry() { public void registerParameter(ParameterExpression parameter) { parameters.add( parameter ); } }; ParameterContainer.Helper.possibleParameter(selection, registry); ParameterContainer.Helper.possibleParameter(restriction, registry); ParameterContainer.Helper.possibleParameter(having, registry); if ( subqueries != null ) { for ( Subquery subquery : subqueries ) { ParameterContainer.Helper.possibleParameter(subquery, registry); } } // both group-by and having expressions can (though unlikely) contain parameters... ParameterContainer.Helper.possibleParameter(having, registry); if ( groupings != null ) { for ( Expression grouping : groupings ) { ParameterContainer.Helper.possibleParameter(grouping, registry); } } return parameters; } // SELECTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public boolean isDistinct() { return distinct; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public Selection getSelection() { return selection; } public void setSelection(Selection selection) { this.selection = selection; } // ROOTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public Set> getRoots() { return roots; } public Root from(Class entityClass) { EntityType entityType = criteriaBuilder.getEntityManagerFactory() .getMetamodel() .entity( entityClass ); if ( entityType == null ) { throw new IllegalArgumentException( entityClass + " is not an entity" ); } return from( entityType ); } public Root from(EntityType entityType) { RootImpl root = new RootImpl( criteriaBuilder, entityType ); roots.add( root ); return root; } // CORRELATION ROOTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void addCorrelationRoot(FromImplementor fromImplementor) { if ( !isSubQuery ) { throw new IllegalStateException( "Query is not identified as sub-query" ); } if ( correlationRoots == null ) { correlationRoots = new HashSet(); } correlationRoots.add( fromImplementor ); } public Set> collectCorrelatedJoins() { if ( !isSubQuery ) { throw new IllegalStateException( "Query is not identified as sub-query" ); } final Set> correlatedJoins; if ( correlationRoots != null ) { correlatedJoins = new HashSet>(); for ( FromImplementor correlationRoot : correlationRoots ) { if (correlationRoot instanceof Join && correlationRoot.isCorrelated()) { correlatedJoins.add( (Join) correlationRoot ); } correlatedJoins.addAll( correlationRoot.getJoins() ); } } else { correlatedJoins = Collections.emptySet(); } return correlatedJoins; } // RESTRICTIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public Predicate getRestriction() { return restriction; } public void setRestriction(Predicate restriction) { this.restriction = restriction; } // GROUPINGS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public List> getGroupings() { return groupings; } public void setGroupings(List> groupings) { this.groupings = groupings; } public void setGroupings(Expression... groupings) { if ( groupings != null && groupings.length > 0 ) { this.groupings = Arrays.asList( groupings ); } else { this.groupings = Collections.emptyList(); } } public Predicate getHaving() { return having; } public void setHaving(Predicate having) { this.having = having; } // SUB-QUERIES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public List> getSubqueries() { return subqueries; } public List> internalGetSubqueries() { if ( subqueries == null ) { subqueries = new ArrayList>(); } return subqueries; } public Subquery subquery(Class subqueryType) { CriteriaSubqueryImpl subquery = new CriteriaSubqueryImpl( criteriaBuilder, subqueryType, owner ); internalGetSubqueries().add( subquery ); return subquery; } @SuppressWarnings({ "unchecked" }) public void render(StringBuilder jpaqlQuery, RenderingContext renderingContext) { jpaqlQuery.append( "select " ); if ( isDistinct() ) { jpaqlQuery.append( "distinct " ); } if ( getSelection() == null ) { jpaqlQuery.append( locateImplicitSelection().renderProjection( renderingContext ) ); } else { jpaqlQuery.append( ( (Renderable) getSelection() ).renderProjection( renderingContext ) ); } renderFromClause( jpaqlQuery, renderingContext ); if ( getRestriction() != null) { jpaqlQuery.append( " where " ) .append( ( (Renderable) getRestriction() ).render( renderingContext ) ); } if ( ! getGroupings().isEmpty() ) { jpaqlQuery.append( " group by " ); String sep = ""; for ( Expression grouping : getGroupings() ) { jpaqlQuery.append( sep ) .append( ( (Renderable) grouping ).renderGroupBy( renderingContext ) ); sep = ", "; } if ( getHaving() != null ) { jpaqlQuery.append( " having " ) .append( ( (Renderable) getHaving() ).render( renderingContext ) ); } } } private FromImplementor locateImplicitSelection() { FromImplementor implicitSelection = null; if ( ! isSubQuery ) { // we should have only a single root (query validation should have checked this...) implicitSelection = (FromImplementor) getRoots().iterator().next(); } else { // we should only have a single "root" which can act as the implicit selection final Set> correlatedJoins = collectCorrelatedJoins(); if ( correlatedJoins != null ) { if ( correlatedJoins.size() == 1 ) { implicitSelection = (FromImplementor) correlatedJoins.iterator().next(); } } } if ( implicitSelection == null ) { throw new IllegalStateException( "No explicit selection and an implicit one could not be determined" ); } return implicitSelection; } @SuppressWarnings({ "unchecked" }) private void renderFromClause(StringBuilder jpaqlQuery, RenderingContext renderingContext) { jpaqlQuery.append( " from " ); String sep = ""; for ( Root root : getRoots() ) { ( (FromImplementor) root ).prepareAlias( renderingContext ); jpaqlQuery.append( sep ); jpaqlQuery.append( ( (FromImplementor) root ).renderTableExpression( renderingContext ) ); sep = ", "; } for ( Root root : getRoots() ) { renderJoins( jpaqlQuery, renderingContext, root.getJoins() ); if (root instanceof RootImpl) { Set treats = ((RootImpl)root).getTreats(); for ( TreatedRoot treat : treats ) { renderJoins( jpaqlQuery, renderingContext, treat.getJoins() ); } } renderFetches( jpaqlQuery, renderingContext, root.getFetches() ); } if ( isSubQuery ) { if ( correlationRoots != null ) { for ( FromImplementor correlationRoot : correlationRoots ) { final FromImplementor correlationParent = correlationRoot.getCorrelationParent(); correlationParent.prepareAlias( renderingContext ); final String correlationRootAlias = correlationParent.getAlias(); for ( Join correlationJoin : correlationRoot.getJoins() ) { final JoinImplementor correlationJoinImpl = (JoinImplementor) correlationJoin; // IMPL NOTE: reuse the sep from above! jpaqlQuery.append( sep ); correlationJoinImpl.prepareAlias( renderingContext ); jpaqlQuery.append( correlationRootAlias ) .append( '.' ) .append( correlationJoinImpl.getAttribute().getName() ) .append( " as " ) .append( correlationJoinImpl.getAlias() ); sep = ", "; renderJoins( jpaqlQuery, renderingContext, correlationJoinImpl.getJoins() ); } } } } } @SuppressWarnings({ "unchecked" }) private void renderJoins( StringBuilder jpaqlQuery, RenderingContext renderingContext, Collection> joins) { if ( joins == null ) { return; } for ( Join join : joins ) { ( (FromImplementor) join ).prepareAlias( renderingContext ); jpaqlQuery.append( renderJoinType( join.getJoinType() ) ) .append( ( (FromImplementor) join ).renderTableExpression( renderingContext ) ); renderJoins( jpaqlQuery, renderingContext, join.getJoins() ); renderFetches( jpaqlQuery, renderingContext, join.getFetches() ); } } private String renderJoinType(JoinType joinType) { switch ( joinType ) { case INNER: { return " inner join "; } case LEFT: { return " left join "; } case RIGHT: { return " right join "; } } throw new IllegalStateException( "Unknown join type " + joinType ); } @SuppressWarnings({ "unchecked" }) private void renderFetches( StringBuilder jpaqlQuery, RenderingContext renderingContext, Collection fetches) { if ( fetches == null ) { return; } for ( Fetch fetch : fetches ) { ( (FromImplementor) fetch ).prepareAlias( renderingContext ); jpaqlQuery.append( renderJoinType( fetch.getJoinType() ) ) .append( "fetch " ) .append( ( (FromImplementor) fetch ).renderTableExpression( renderingContext ) ); renderFetches( jpaqlQuery, renderingContext, fetch.getFetches() ); } } }