/* * 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 http://www.gnu.org/licenses/lgpl-2.1.html */ package org.hibernate.query.sqm.produce.internal; import org.hibernate.persister.queryable.spi.EntityValuedExpressableType; import org.hibernate.query.sqm.domain.SqmExpressableTypeEmbedded; import org.hibernate.query.sqm.domain.SqmExpressableTypeEntity; import org.hibernate.query.sqm.ParsingException; import org.hibernate.query.sqm.produce.spi.AliasRegistry; import org.hibernate.query.sqm.produce.spi.ImplicitAliasGenerator; import org.hibernate.query.sqm.produce.spi.ParsingContext; import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.expression.domain.SqmAttributeReference; import org.hibernate.query.sqm.tree.expression.domain.SqmNavigableReference; import org.hibernate.query.sqm.tree.from.SqmFromElementSpace; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; import org.hibernate.query.sqm.tree.from.SqmCrossJoin; import org.hibernate.query.sqm.tree.from.SqmEntityJoin; import org.hibernate.query.sqm.tree.from.SqmFrom; import org.hibernate.query.sqm.tree.from.SqmRoot; import org.jboss.logging.Logger; /** * @author Steve Ebersole */ public class FromElementBuilder { // todo : I am pretty sure the uses of `{LHS-from-element}.getContainingSpace()` is incorrect when building SqmFrom elements below // instead we should be passing along the FromElementSpace to use. the big scenario I can // think of is correlated sub-queries where the "LHS" is actually part of the outer query - aside // from hoisting that is not the FromElementSpace we should be using. // todo : make AliasRegistry part of QuerySpecProcessingState - pass that reference in here too // but its odd to externally get the AliasRegistry from the FromElementBuilder when // we are dealing with result-variables (selection aliases) private static final Logger log = Logger.getLogger( FromElementBuilder.class ); private final ParsingContext parsingContext; private final AliasRegistry aliasRegistry; public FromElementBuilder(ParsingContext parsingContext, AliasRegistry aliasRegistry) { this.parsingContext = parsingContext; this.aliasRegistry = aliasRegistry; } public AliasRegistry getAliasRegistry(){ return aliasRegistry; } /** * Make the root entity reference for the FromElementSpace */ public SqmRoot makeRootEntityFromElement( SqmFromElementSpace fromElementSpace, EntityValuedExpressableType entityBinding, String alias) { if ( alias == null ) { alias = parsingContext.getImplicitAliasGenerator().buildUniqueImplicitAlias(); log.debugf( "Generated implicit alias [%s] for root entity reference [%s]", alias, entityBinding.getEntityName() ); } final SqmRoot root = new SqmRoot( fromElementSpace, parsingContext.makeUniqueIdentifier(), alias, entityBinding ); fromElementSpace.setRoot( root ); parsingContext.registerFromElementByUniqueId( root ); registerAlias( root ); return root; } /** * Make the root entity reference for the FromElementSpace */ public SqmCrossJoin makeCrossJoinedFromElement( SqmFromElementSpace fromElementSpace, String uid, EntityValuedExpressableType entityToJoin, String alias) { if ( alias == null ) { alias = parsingContext.getImplicitAliasGenerator().buildUniqueImplicitAlias(); log.debugf( "Generated implicit alias [%s] for cross joined entity reference [%s]", alias, entityToJoin.getEntityName() ); } final SqmCrossJoin join = new SqmCrossJoin( fromElementSpace, uid, alias, entityToJoin ); fromElementSpace.addJoin( join ); parsingContext.registerFromElementByUniqueId( join ); registerAlias( join ); return join; } public SqmEntityJoin buildEntityJoin( SqmFromElementSpace fromElementSpace, String alias, EntityValuedExpressableType entityToJoin, SqmJoinType joinType) { if ( alias == null ) { alias = parsingContext.getImplicitAliasGenerator().buildUniqueImplicitAlias(); log.debugf( "Generated implicit alias [%s] for entity join [%s]", alias, entityToJoin.getEntityName() ); } final SqmEntityJoin join = new SqmEntityJoin( fromElementSpace, parsingContext.makeUniqueIdentifier(), alias, entityToJoin, joinType ); fromElementSpace.addJoin( join ); parsingContext.registerFromElementByUniqueId( join ); registerAlias( join ); return join; } public SqmAttributeJoin buildAttributeJoin( SqmAttributeReference attributeBinding, String alias, EntityValuedExpressableType subclassIndicator, SqmJoinType joinType, boolean fetched, boolean canReuseImplicitJoins) { assert attributeBinding != null; assert joinType != null; assert attributeBinding.getSourceReference() != null; if ( fetched && canReuseImplicitJoins ) { throw new ParsingException( "Illegal combination of [fetched] and [canReuseImplicitJoins=true] passed to #buildAttributeJoin" ); } if ( alias != null && canReuseImplicitJoins ) { throw new ParsingException( "Unexpected combination of [non-null alias] and [canReuseImplicitJoins=true] passed to #buildAttributeJoin" ); } // todo : validate alias & fetched? JPA at least disallows specifying an alias for fetched associations if ( alias == null ) { alias = parsingContext.getImplicitAliasGenerator().buildUniqueImplicitAlias(); log.debugf( "Generated implicit alias [%s] for attribute join [%s.%s]", alias, attributeBinding.getSourceReference().getExportedFromElement().getIdentificationVariable(), attributeBinding.getReferencedNavigable().getAttributeName() ); } SqmAttributeJoin join = null; if ( canReuseImplicitJoins ) { final SqmNavigableReference navigableBinding = parsingContext.getCachedNavigableBinding( attributeBinding.getSourceReference(), attributeBinding.getReferencedNavigable() ); join = (SqmAttributeJoin) NavigableBindingHelper.resolveExportedFromElement( navigableBinding ); } if ( join == null ) { join = new SqmAttributeJoin( attributeBinding.getSourceReference().getExportedFromElement(), attributeBinding, parsingContext.makeUniqueIdentifier(), alias, subclassIndicator, joinType, fetched ); if ( canReuseImplicitJoins ) { parsingContext.cacheNavigableBinding( attributeBinding ); } parsingContext.registerFromElementByUniqueId( join ); registerAlias( join ); if ( !SqmExpressableTypeEmbedded.class.isInstance( attributeBinding.getReferencedNavigable() ) ) { // it's a composite-valued navigable, create a join but do not register it // as // unless this is a collection element or index... attributeBinding.getSourceReference().getExportedFromElement().getContainingSpace().addJoin( join ); } } return join; } private void registerAlias(SqmFrom sqmFrom) { final String alias = sqmFrom.getIdentificationVariable(); if ( alias == null ) { throw new ParsingException( "FromElement alias was null" ); } if ( ImplicitAliasGenerator.isImplicitAlias( alias ) ) { log.debug( "Alias registration for implicit FromElement alias : " + alias ); } aliasRegistry.registerAlias( sqmFrom.getBinding() ); } }