Java tutorial
/* * 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.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: * <ul> * <li>A class name - This is the name of the Java class that is mapped by Hibernate.</li> * <li>[optional] an HQL alias for the mapped class.</li> * <li>A table name - The name of the table that is mapped to the Java class.</li> * <li>A table alias - The alias for the table that will be used in the resulting SQL.</li> * </ul> * * @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<FromElement> 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()); } 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 ? "<no alias>" : 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<String> 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<String> 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<FromElement>(); } destinations.add(fromElement); } public List<FromElement> 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<ParameterSpecification> 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> parameterSpecification = getParameterSpecification(); return parameterSpecification.toArray(new ParameterSpecification[parameterSpecification.size()]); } private List<ParameterSpecification> getParameterSpecification() { List<ParameterSpecification> 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); } } }