org.hibernate.hql.internal.ast.tree.FromElement 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.hql.internal.ast.tree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import antlr.collections.AST;
import org.hibernate.QueryException;
import org.hibernate.engine.internal.JoinSequence;
import org.hibernate.hql.internal.CollectionProperties;
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
import org.hibernate.hql.internal.antlr.SqlTokenTypes;
import org.hibernate.hql.internal.ast.TypeDiscriminatorMetadata;
import org.hibernate.hql.internal.ast.util.ASTUtil;
import org.hibernate.hql.spi.QueryTranslator;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.param.DynamicFilterParameterSpecification;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.DiscriminatorMetadata;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
/**
* Represents a single mapped class mentioned in an HQL FROM clause. Each
* class reference will have the following symbols:
*
* - A class name - This is the name of the Java class that is mapped by Hibernate.
* - [optional] an HQL alias for the mapped class.
* - A table name - The name of the table that is mapped to the Java class.
* - A table alias - The alias for the table that will be used in the resulting SQL.
*
*
* @author josh
*/
public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, ParameterContainer {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( FromElement.class );
private String className;
private String classAlias;
private String tableAlias;
private String collectionTableAlias;
private FromClause fromClause;
private boolean includeSubclasses = true;
private boolean collectionJoin;
private FromElement origin;
private String[] columns;
private String role;
private boolean fetch;
private boolean isAllPropertyFetch;
private boolean filter;
private int sequence = -1;
private boolean useFromFragment;
private boolean initialized;
private FromElementType elementType;
private boolean useWhereFragment = true;
private List destinations;
private boolean manyToMany;
private AST withClauseAst;
private String withClauseFragment;
private boolean dereferencedBySuperclassProperty;
private boolean dereferencedBySubclassProperty;
public FromElement() {
}
/**
* Constructor form used to initialize {@link ComponentJoin}
*
* @param fromClause The FROM clause to which this element belongs
* @param origin The origin (LHS) of this element
* @param alias The alias applied to this element
*/
protected FromElement(
FromClause fromClause,
FromElement origin,
String alias) {
this.fromClause = fromClause;
this.origin = origin;
this.classAlias = alias;
this.tableAlias = origin.getTableAlias();
super.initialize( fromClause.getWalker() );
}
public FromElementType getElementType() {
return elementType;
}
protected void initializeComponentJoin(FromElementType elementType) {
fromClause.registerFromElement( this );
elementType.applyTreatAsDeclarations( getWalker().getTreatAsDeclarationsByPath( classAlias ) );
this.elementType = elementType;
initialized = true;
}
public String getCollectionSuffix() {
return elementType.getCollectionSuffix();
}
public void setCollectionSuffix(String suffix) {
elementType.setCollectionSuffix(suffix);
}
public void initializeCollection(FromClause fromClause, String classAlias, String tableAlias) {
doInitialize( fromClause, tableAlias, null, classAlias, null, null );
initialized = true;
}
public void initializeEntity(
FromClause fromClause,
String className,
EntityPersister persister,
EntityType type,
String classAlias,
String tableAlias) {
doInitialize( fromClause, tableAlias, className, classAlias, persister, type );
this.sequence = fromClause.nextFromElementCounter();
initialized = true;
}
protected void doInitialize(
FromClause fromClause,
String tableAlias,
String className,
String classAlias,
EntityPersister persister,
EntityType type) {
if ( initialized ) {
throw new IllegalStateException( "Already initialized!!" );
}
this.fromClause = fromClause;
this.tableAlias = tableAlias;
this.className = className;
this.classAlias = classAlias;
this.elementType = new FromElementType( this, persister, type );
// Register the FromElement with the FROM clause, now that we have the names and aliases.
fromClause.registerFromElement( this );
LOG.debugf( "%s : %s (%s) -> %s", fromClause, className, classAlias == null ? "" : classAlias, tableAlias );
}
public EntityPersister getEntityPersister() {
return elementType.getEntityPersister();
}
@Override
public Type getDataType() {
return elementType.getDataType();
}
public Type getSelectType() {
return elementType.getSelectType();
}
public Queryable getQueryable() {
return elementType.getQueryable();
}
public String getClassName() {
return className;
}
public String getClassAlias() {
return classAlias;
//return classAlias == null ? className : classAlias;
}
public String getTableName() {
Queryable queryable = getQueryable();
return ( queryable != null ) ? queryable.getTableName() : "{none}";
}
public String getTableAlias() {
return tableAlias;
}
/**
* Render the identifier select, but in a 'scalar' context (i.e. generate the column alias).
*
* @param i the sequence of the returned type
* @return the identifier select with the column alias.
*/
String renderScalarIdentifierSelect(int i) {
return elementType.renderScalarIdentifierSelect( i );
}
void checkInitialized() {
if ( !initialized ) {
throw new IllegalStateException( "FromElement has not been initialized!" );
}
}
/**
* Returns the identifier select SQL fragment.
*
* @param size The total number of returned types.
* @param k The sequence of the current returned type.
* @return the identifier select SQL fragment.
*/
String renderIdentifierSelect(int size, int k) {
return elementType.renderIdentifierSelect( size, k );
}
/**
* Returns the property select SQL fragment.
*
* @param size The total number of returned types.
* @param k The sequence of the current returned type.
* @return the property select SQL fragment.
*/
String renderPropertySelect(int size, int k) {
return elementType.renderPropertySelect( size, k, isAllPropertyFetch );
}
public String renderMapKeyPropertySelectFragment(int size, int k) {
return elementType.renderMapKeyPropertySelectFragment( size, k );
}
public String renderMapEntryPropertySelectFragment(int size, int k) {
return elementType.renderMapEntryPropertySelectFragment( size, k );
}
String renderCollectionSelectFragment(int size, int k) {
return elementType.renderCollectionSelectFragment( size, k );
}
String renderValueCollectionSelectFragment(int size, int k) {
return elementType.renderValueCollectionSelectFragment( size, k );
}
public FromClause getFromClause() {
return fromClause;
}
/**
* Returns true if this FromElement was implied by a path, or false if this FROM element is explicitly declared in
* the FROM clause.
*
* @return true if this FromElement was implied by a path, or false if this FROM element is explicitly declared
*/
public boolean isImplied() {
return false; // This is an explicit FROM element.
}
/**
* Returns additional display text for the AST node.
*
* @return String - The additional display text.
*/
public String getDisplayText() {
StringBuilder buf = new StringBuilder();
buf.append( "FromElement{" );
appendDisplayText( buf );
buf.append( "}" );
return buf.toString();
}
protected void appendDisplayText(StringBuilder buf) {
buf.append( isImplied() ? (
isImpliedInFromClause() ? "implied in FROM clause" : "implied" )
: "explicit" );
buf.append( "," ).append( isCollectionJoin() ? "collection join" : "not a collection join" );
buf.append( "," ).append( fetch ? "fetch join" : "not a fetch join" );
buf.append( "," ).append( isAllPropertyFetch ? "fetch all properties" : "fetch non-lazy properties" );
buf.append( ",classAlias=" ).append( getClassAlias() );
buf.append( ",role=" ).append( role );
buf.append( ",tableName=" ).append( getTableName() );
buf.append( ",tableAlias=" ).append( getTableAlias() );
FromElement origin = getRealOrigin();
buf.append( ",origin=" ).append( origin == null ? "null" : origin.getText() );
buf.append( ",columns={" );
if ( columns != null ) {
for ( int i = 0; i < columns.length; i++ ) {
buf.append( columns[i] );
if ( i < columns.length - 1 ) {
buf.append( " " );
}
}
}
buf.append( ",className=" ).append( className );
buf.append( "}" );
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals( obj );
}
public void setJoinSequence(JoinSequence joinSequence) {
elementType.setJoinSequence( joinSequence );
}
public JoinSequence getJoinSequence() {
return elementType.getJoinSequence();
}
public void setIncludeSubclasses(boolean includeSubclasses) {
if ( !includeSubclasses && isDereferencedBySuperclassOrSubclassProperty() && LOG.isTraceEnabled() ) {
LOG.trace( "Attempt to disable subclass-inclusions : ", new Exception( "Stack-trace source" ) );
}
this.includeSubclasses = includeSubclasses;
}
public boolean isIncludeSubclasses() {
return includeSubclasses;
}
public boolean isDereferencedBySuperclassOrSubclassProperty() {
return dereferencedBySubclassProperty || dereferencedBySuperclassProperty;
}
public String getIdentityColumn() {
final String[] cols = getIdentityColumns();
if ( cols.length == 1 ) {
return cols[0];
}
else {
return "(" + String.join( ", ", cols ) + ")";
}
}
public String[] getIdentityColumns() {
checkInitialized();
final String table = getTableAlias();
if ( table == null ) {
throw new IllegalStateException( "No table alias for node " + this );
}
final String[] propertyNames = getIdentifierPropertyNames();
List columns = new ArrayList<>();
final boolean inSelect = getWalker().getStatementType() == HqlSqlTokenTypes.SELECT;
for ( String propertyName : propertyNames ) {
String[] propertyNameColumns = toColumns( table, propertyName, inSelect );
for ( String propertyNameColumn : propertyNameColumns ) {
columns.add( propertyNameColumn );
}
}
return columns.toArray( new String[columns.size()] );
}
public void setCollectionJoin(boolean collectionJoin) {
this.collectionJoin = collectionJoin;
}
public boolean isCollectionJoin() {
return collectionJoin;
}
public void setRole(String role) {
this.role = role;
applyTreatAsDeclarations( getWalker().getTreatAsDeclarationsByPath( role ) );
}
public void applyTreatAsDeclarations(Set treatAsDeclarationsByPath) {
elementType.applyTreatAsDeclarations( treatAsDeclarationsByPath );
}
public String getRole() {
return role;
}
public void setQueryableCollection(QueryableCollection queryableCollection) {
elementType.setQueryableCollection( queryableCollection );
}
public QueryableCollection getQueryableCollection() {
return elementType.getQueryableCollection();
}
public void setColumns(String[] columns) {
this.columns = columns;
}
public void setOrigin(FromElement origin, boolean manyToMany) {
this.origin = origin;
this.manyToMany = manyToMany;
origin.addDestination( this );
if ( origin.getFromClause() == this.getFromClause() ) {
// TODO: Figure out a better way to get the FROM elements in a proper tree structure.
// If this is not the destination of a many-to-many, add it as a child of the origin.
if ( manyToMany ) {
ASTUtil.appendSibling( origin, this );
}
else {
if ( !getWalker().isInFrom() && !getWalker().isInSelect() && !getWalker().isInEntityGraph() ) {
getFromClause().addChild( this );
}
else {
origin.addChild( this );
}
}
}
else if ( !getWalker().isInFrom() ) {
// HHH-276 : implied joins in a subselect where clause - The destination needs to be added
// to the destination's from clause.
getFromClause().addChild( this ); // Not sure if this is will fix everything, but it works.
}
else {
// Otherwise, the destination node was implied by the FROM clause and the FROM clause processor
// will automatically add it in the right place.
}
}
public boolean isManyToMany() {
return manyToMany;
}
private void addDestination(FromElement fromElement) {
if ( destinations == null ) {
destinations = new LinkedList();
}
destinations.add( fromElement );
}
public List getDestinations() {
if ( destinations == null ) {
return Collections.emptyList();
}
else {
return destinations;
}
}
public FromElement getOrigin() {
return origin;
}
public FromElement getRealOrigin() {
if ( origin == null ) {
return null;
}
if ( StringHelper.isEmpty( origin.getText() ) ) {
return origin.getRealOrigin();
}
return origin;
}
public FromElement getFetchOrigin() {
if ( origin == null ) {
return null;
}
if ( !origin.isFetch() ) {
return origin;
}
if ( StringHelper.isEmpty( origin.getText() ) ) {
return origin.getFetchOrigin();
}
return origin;
}
public static final String DISCRIMINATOR_PROPERTY_NAME = "class";
private TypeDiscriminatorMetadata typeDiscriminatorMetadata;
private static class TypeDiscriminatorMetadataImpl implements TypeDiscriminatorMetadata {
private final DiscriminatorMetadata persisterDiscriminatorMetadata;
private final String alias;
private TypeDiscriminatorMetadataImpl(
DiscriminatorMetadata persisterDiscriminatorMetadata,
String alias) {
this.persisterDiscriminatorMetadata = persisterDiscriminatorMetadata;
this.alias = alias;
}
@Override
public String getSqlFragment() {
return persisterDiscriminatorMetadata.getSqlFragment( alias );
}
@Override
public Type getResolutionType() {
return persisterDiscriminatorMetadata.getResolutionType();
}
}
public TypeDiscriminatorMetadata getTypeDiscriminatorMetadata() {
if ( typeDiscriminatorMetadata == null ) {
typeDiscriminatorMetadata = buildTypeDiscriminatorMetadata();
}
return typeDiscriminatorMetadata;
}
private TypeDiscriminatorMetadata buildTypeDiscriminatorMetadata() {
final String aliasToUse = getTableAlias();
Queryable queryable = getQueryable();
if ( queryable == null ) {
QueryableCollection collection = getQueryableCollection();
if ( ! collection.getElementType().isEntityType() ) {
throw new QueryException( "type discrimination cannot be applied to value collection [" + collection.getRole() + "]" );
}
queryable = (Queryable) collection.getElementPersister();
}
handlePropertyBeingDereferenced( getDataType(), DISCRIMINATOR_PROPERTY_NAME );
return new TypeDiscriminatorMetadataImpl( queryable.getTypeDiscriminatorMetadata(), aliasToUse );
}
public Type getPropertyType(String propertyName, String propertyPath) {
return elementType.getPropertyType( propertyName, propertyPath );
}
public String getPropertyTableName(String propertyName) {
return elementType.getPropertyTableName( propertyName );
}
public String[] toColumns(String tableAlias, String path, boolean inSelect) {
return elementType.toColumns( tableAlias, path, inSelect );
}
public String[] toColumns(String tableAlias, String path, boolean inSelect, boolean forceAlias) {
return elementType.toColumns( tableAlias, path, inSelect, forceAlias );
}
public PropertyMapping getPropertyMapping(String propertyName) {
return elementType.getPropertyMapping( propertyName );
}
public CollectionPropertyReference getCollectionPropertyReference(String propertyName) {
return elementType.getCollectionPropertyReference( propertyName );
}
public String[] getIdentifierPropertyNames() {
return elementType.getIdentifierPropertyNames();
}
public void setFetch(boolean fetch) {
this.fetch = fetch;
// Fetch can't be used with scroll() or iterate().
if ( fetch && getWalker().isShallowQuery() ) {
throw new QueryException( QueryTranslator.ERROR_CANNOT_FETCH_WITH_ITERATE );
}
}
public boolean isFetch() {
return fetch;
}
public int getSequence() {
return sequence;
}
public void setFilter(boolean b) {
filter = b;
}
public boolean isFilter() {
return filter;
}
public boolean useFromFragment() {
checkInitialized();
// If it's not implied or it is implied and it's a many to many join where the target wasn't found.
return !isImplied() || this.useFromFragment;
}
public void setUseFromFragment(boolean useFromFragment) {
this.useFromFragment = useFromFragment;
}
public boolean useWhereFragment() {
return useWhereFragment;
}
public void setUseWhereFragment(boolean b) {
useWhereFragment = b;
}
public void setCollectionTableAlias(String collectionTableAlias) {
this.collectionTableAlias = collectionTableAlias;
}
public String getCollectionTableAlias() {
return collectionTableAlias;
}
public boolean isCollectionOfValuesOrComponents() {
return elementType.isCollectionOfValuesOrComponents();
}
public boolean isEntity() {
return elementType.isEntity();
}
public void setImpliedInFromClause(boolean flag) {
throw new UnsupportedOperationException( "Explicit FROM elements can't be implied in the FROM clause!" );
}
public boolean isImpliedInFromClause() {
return false; // Since this is an explicit FROM element, it can't be implied in the FROM clause.
}
public void setInProjectionList(boolean inProjectionList) {
// Do nothing, eplicit from elements are *always* in the projection list.
}
public boolean inProjectionList() {
return !isImplied() && isFromOrJoinFragment();
}
public boolean isFromOrJoinFragment() {
return getType() == SqlTokenTypes.FROM_FRAGMENT
|| getType() == SqlTokenTypes.JOIN_FRAGMENT
|| getType() == SqlTokenTypes.ENTITY_JOIN;
}
public boolean isAllPropertyFetch() {
return isAllPropertyFetch;
}
public void setAllPropertyFetch(boolean fetch) {
isAllPropertyFetch = fetch;
}
public AST getWithClauseAst() {
return withClauseAst;
}
public String getWithClauseFragment() {
return withClauseFragment;
}
public void setWithClauseFragment(AST ast, String withClauseFragment) {
this.withClauseAst = ast;
this.withClauseFragment = withClauseFragment;
}
public void handlePropertyBeingDereferenced(Type propertySource, String propertyName) {
if ( getQueryableCollection() != null && CollectionProperties.isCollectionProperty( propertyName ) ) {
// propertyName refers to something like collection.size...
return;
}
if ( propertySource.isComponentType() ) {
// property name is a sub-path of a component...
return;
}
Queryable persister = getQueryable();
if ( persister != null ) {
try {
Queryable.Declarer propertyDeclarer = persister.getSubclassPropertyDeclarer( propertyName );
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Handling property dereference [{0} ({1}) -> {2} ({3})]",
persister.getEntityName(), getClassAlias(), propertyName, propertyDeclarer );
}
if ( propertyDeclarer == Queryable.Declarer.SUBCLASS ) {
dereferencedBySubclassProperty = true;
includeSubclasses = true;
}
else if ( propertyDeclarer == Queryable.Declarer.SUPERCLASS ) {
dereferencedBySuperclassProperty = true;
}
}
catch( QueryException ignore ) {
// ignore it; the incoming property could not be found so we
// cannot be sure what to do here. At the very least, the
// safest is to simply not apply any dereference toggling...
}
}
}
public boolean isDereferencedBySuperclassProperty() {
return dereferencedBySuperclassProperty;
}
public boolean isDereferencedBySubclassProperty() {
return dereferencedBySubclassProperty;
}
// ParameterContainer impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private List embeddedParameters = new ArrayList<>( );
@Override
public void addEmbeddedParameter(ParameterSpecification specification) {
embeddedParameters.add( specification );
}
@Override
public boolean hasEmbeddedParameters() {
return !embeddedParameters.isEmpty();
}
@Override
public ParameterSpecification[] getEmbeddedParameters() {
final List parameterSpecification = getParameterSpecification();
return parameterSpecification.toArray( new ParameterSpecification[ parameterSpecification.size() ] );
}
private List getParameterSpecification() {
List parameterSpecifications =
embeddedParameters.stream()
.filter( o -> o instanceof DynamicFilterParameterSpecification )
.collect( Collectors.toList() );
parameterSpecifications.addAll(
embeddedParameters.stream()
.filter( o -> ! (o instanceof DynamicFilterParameterSpecification ) )
.collect( Collectors.toList() ) );
return parameterSpecifications;
}
public ParameterSpecification getIndexCollectionSelectorParamSpec() {
return elementType.getIndexCollectionSelectorParamSpec();
}
public void setIndexCollectionSelectorParamSpec(ParameterSpecification indexCollectionSelectorParamSpec) {
if ( indexCollectionSelectorParamSpec == null ) {
if ( elementType.getIndexCollectionSelectorParamSpec() != null ) {
embeddedParameters.remove( elementType.getIndexCollectionSelectorParamSpec() );
elementType.setIndexCollectionSelectorParamSpec( null );
}
}
else {
elementType.setIndexCollectionSelectorParamSpec( indexCollectionSelectorParamSpec );
addEmbeddedParameter( indexCollectionSelectorParamSpec );
}
}
}