org.hibernate.query.criteria.internal.QueryStructure Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
Hibernate's core ORM functionality
/*
* 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() );
}
}
}